Skip to content

Commit

Permalink
cgroupv1/FindCgroupMountpoint: add a fast path
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
kolyshkin committed Jul 3, 2020
1 parent 3cb1909 commit 18c07e9
Showing 1 changed file with 55 additions and 3 deletions.
58 changes: 55 additions & 3 deletions libcontainer/cgroups/v1_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import (
"os"
"path/filepath"
"strings"
"syscall"

securejoin "github.com/cyphar/filepath-securejoin"
"golang.org/x/sys/unix"
)

// Code in this source file are specific to cgroup v1,
Expand All @@ -19,6 +23,8 @@ const (

var (
errUnified = errors.New("not implemented for cgroup v2 unified hierarchy")

defaultPrefix = "/sys/fs/cgroup"
)

type NotFoundError struct {
Expand All @@ -43,11 +49,59 @@ func IsNotFound(err error) bool {
return ok
}

func tryDefaultPath(cgroupPath, subsystem string) string {
if !strings.HasPrefix(defaultPrefix, cgroupPath) {
return ""
}

// remove possible prefix
subsystem = strings.TrimPrefix(subsystem, CgroupNamePrefix)

// Make sure we're still under defaultPrefix, and resolve
// a possible symlink (like cpu -> cpu,cpuacct).
path, err := securejoin.SecureJoin(defaultPrefix, subsystem)
if err != nil {
return ""
}

// (1) path should be a directory.
st, err := os.Lstat(path)
if err != nil || !st.IsDir() {
return ""
}

// (2) path should be a mount point.
pst, err := os.Lstat(filepath.Dir(path))
if err != nil {
return ""
}

if st.Sys().(*syscall.Stat_t).Dev == pst.Sys().(*syscall.Stat_t).Dev {
// parent dir has the same dev -- path is not a mount point
return ""
}

// (3) path should have 'cgroup' fs type.
fst := unix.Statfs_t{}
err = unix.Statfs(path, &fst)
if err != nil || fst.Type != unix.CGROUP_SUPER_MAGIC {
return ""
}

return path
}

// https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt
func FindCgroupMountpoint(cgroupPath, subsystem string) (string, error) {
if IsCgroup2UnifiedMode() {
return "", errUnified
}

// Avoid parsing mountinfo by trying the default path first, if possible.
if path := tryDefaultPath(cgroupPath, subsystem); path != "" {
return path, nil
}

mnt, _, err := FindCgroupMountpointAndRoot(cgroupPath, subsystem)
return mnt, err
}
Expand All @@ -57,9 +111,7 @@ func FindCgroupMountpointAndRoot(cgroupPath, subsystem string) (string, string,
return "", "", errUnified
}

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

0 comments on commit 18c07e9

Please sign in to comment.