Skip to content

Commit a73ce38

Browse files
committed
cgroupv1/FindCgroupMountpoint: add a fast path
In case cgroupPath is under the default cgroup prefix, let's try to guess the mount point by adding the subsystem name to the default prefix, and resolving the resulting path in case it's a symlink. In most cases, given the default cgroup setup, this trick should result in returning the same result faster, and avoiding /proc/self/mountinfo parsing which is relatively slow and problematic. Be very careful with the default path, checking it is - a directory; - a mount point; - has cgroup fstype. If something is not right, fall back to parsing mountinfo. While at it, remove the obsoleted comment about mountinfo parsing. The comment belongs to findCgroupMountpointAndRootFromReader(), but rather than moving it there, let's just remove it, since it does not add any value in understanding the current code. Signed-off-by: Kir Kolyshkin <[email protected]>
1 parent 819fcc6 commit a73ce38

File tree

1 file changed

+54
-3
lines changed

1 file changed

+54
-3
lines changed

libcontainer/cgroups/v1_utils.go

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,18 @@ import (
88
"os"
99
"path/filepath"
1010
"strings"
11+
"syscall"
12+
13+
securejoin "github.com/cyphar/filepath-securejoin"
14+
"golang.org/x/sys/unix"
1115
)
1216

1317
// Code in this source file are specific to cgroup v1,
1418
// and must not be used from any cgroup v2 code.
1519

1620
const (
1721
CgroupNamePrefix = "name="
22+
defaultPrefix = "/sys/fs/cgroup"
1823
)
1924

2025
var (
@@ -43,11 +48,59 @@ func IsNotFound(err error) bool {
4348
return ok
4449
}
4550

51+
func tryDefaultPath(cgroupPath, subsystem string) string {
52+
if !strings.HasPrefix(defaultPrefix, cgroupPath) {
53+
return ""
54+
}
55+
56+
// remove possible prefix
57+
subsystem = strings.TrimPrefix(subsystem, CgroupNamePrefix)
58+
59+
// Make sure we're still under defaultPrefix, and resolve
60+
// a possible symlink (like cpu -> cpu,cpuacct).
61+
path, err := securejoin.SecureJoin(defaultPrefix, subsystem)
62+
if err != nil {
63+
return ""
64+
}
65+
66+
// (1) path should be a directory.
67+
st, err := os.Lstat(path)
68+
if err != nil || !st.IsDir() {
69+
return ""
70+
}
71+
72+
// (2) path should be a mount point.
73+
pst, err := os.Lstat(filepath.Dir(path))
74+
if err != nil {
75+
return ""
76+
}
77+
78+
if st.Sys().(*syscall.Stat_t).Dev == pst.Sys().(*syscall.Stat_t).Dev {
79+
// parent dir has the same dev -- path is not a mount point
80+
return ""
81+
}
82+
83+
// (3) path should have 'cgroup' fs type.
84+
fst := unix.Statfs_t{}
85+
err = unix.Statfs(path, &fst)
86+
if err != nil || fst.Type != unix.CGROUP_SUPER_MAGIC {
87+
return ""
88+
}
89+
90+
return path
91+
}
92+
4693
// https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt
4794
func FindCgroupMountpoint(cgroupPath, subsystem string) (string, error) {
4895
if IsCgroup2UnifiedMode() {
4996
return "", errUnified
5097
}
98+
99+
// Avoid parsing mountinfo by trying the default path first, if possible.
100+
if path := tryDefaultPath(cgroupPath, subsystem); path != "" {
101+
return path, nil
102+
}
103+
51104
mnt, _, err := FindCgroupMountpointAndRoot(cgroupPath, subsystem)
52105
return mnt, err
53106
}
@@ -57,9 +110,7 @@ func FindCgroupMountpointAndRoot(cgroupPath, subsystem string) (string, string,
57110
return "", "", errUnified
58111
}
59112

60-
// We are not using mount.GetMounts() because it's super-inefficient,
61-
// parsing it directly sped up x10 times because of not using Sscanf.
62-
// It was one of two major performance drawbacks in container start.
113+
// Avoid parsing mountinfo by checking if subsystem is valid/available.
63114
if !isSubsystemAvailable(subsystem) {
64115
return "", "", NewNotFoundError(subsystem)
65116
}

0 commit comments

Comments
 (0)