summaryrefslogtreecommitdiff
path: root/vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go')
-rw-r--r--vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go155
1 files changed, 94 insertions, 61 deletions
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go
index f8487b0a9..95ec9dff0 100644
--- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go
+++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go
@@ -1,13 +1,17 @@
package cgroups
import (
- "bufio"
"errors"
"fmt"
- "io"
"os"
"path/filepath"
"strings"
+ "sync"
+ "syscall"
+
+ securejoin "github.com/cyphar/filepath-securejoin"
+ "github.com/moby/sys/mountinfo"
+ "golang.org/x/sys/unix"
)
// Code in this source file are specific to cgroup v1,
@@ -15,10 +19,16 @@ import (
const (
CgroupNamePrefix = "name="
+ defaultPrefix = "/sys/fs/cgroup"
)
var (
- errUnified = errors.New("not implemented for cgroup v2 unified hierarchy")
+ errUnified = errors.New("not implemented for cgroup v2 unified hierarchy")
+ ErrV1NoUnified = errors.New("invalid configuration: cannot use unified on cgroup v1")
+
+ readMountinfoOnce sync.Once
+ readMountinfoErr error
+ cgroupMountinfo []*mountinfo.Info
)
type NotFoundError struct {
@@ -43,11 +53,74 @@ 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
+}
+
+// readCgroupMountinfo returns a list of cgroup v1 mounts (i.e. the ones
+// with fstype of "cgroup") for the current running process.
+//
+// The results are cached (to avoid re-reading mountinfo which is relatively
+// expensive), so it is assumed that cgroup mounts are not being changed.
+func readCgroupMountinfo() ([]*mountinfo.Info, error) {
+ readMountinfoOnce.Do(func() {
+ cgroupMountinfo, readMountinfoErr = mountinfo.GetMounts(
+ mountinfo.FSTypeFilter("cgroup"),
+ )
+ })
+
+ return cgroupMountinfo, readMountinfoErr
+}
+
// 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
}
@@ -57,58 +130,28 @@ 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.
- if !isSubsystemAvailable(subsystem) {
- return "", "", NewNotFoundError(subsystem)
- }
-
- f, err := os.Open("/proc/self/mountinfo")
+ mi, err := readCgroupMountinfo()
if err != nil {
return "", "", err
}
- defer f.Close()
- return findCgroupMountpointAndRootFromReader(f, cgroupPath, subsystem)
+ return findCgroupMountpointAndRootFromMI(mi, cgroupPath, subsystem)
}
-func findCgroupMountpointAndRootFromReader(reader io.Reader, cgroupPath, subsystem string) (string, string, error) {
- scanner := bufio.NewScanner(reader)
- for scanner.Scan() {
- txt := scanner.Text()
- fields := strings.Fields(txt)
- if len(fields) < 9 {
- continue
- }
- if strings.HasPrefix(fields[4], cgroupPath) {
- for _, opt := range strings.Split(fields[len(fields)-1], ",") {
+func findCgroupMountpointAndRootFromMI(mounts []*mountinfo.Info, cgroupPath, subsystem string) (string, string, error) {
+ for _, mi := range mounts {
+ if strings.HasPrefix(mi.Mountpoint, cgroupPath) {
+ for _, opt := range strings.Split(mi.VFSOptions, ",") {
if opt == subsystem {
- return fields[4], fields[3], nil
+ return mi.Mountpoint, mi.Root, nil
}
}
}
}
- if err := scanner.Err(); err != nil {
- return "", "", err
- }
return "", "", NewNotFoundError(subsystem)
}
-func isSubsystemAvailable(subsystem string) bool {
- if IsCgroup2UnifiedMode() {
- panic("don't call isSubsystemAvailable from cgroupv2 code")
- }
-
- cgroups, err := ParseCgroupFile("/proc/self/cgroup")
- if err != nil {
- return false
- }
- _, avail := cgroups[subsystem]
- return avail
-}
-
func (m Mount) GetOwnCgroup(cgroups map[string]string) (string, error) {
if len(m.Subsystems) == 0 {
return "", fmt.Errorf("no subsystem for mount")
@@ -117,25 +160,15 @@ func (m Mount) GetOwnCgroup(cgroups map[string]string) (string, error) {
return getControllerPath(m.Subsystems[0], cgroups)
}
-func getCgroupMountsHelper(ss map[string]bool, mi io.Reader, all bool) ([]Mount, error) {
+func getCgroupMountsHelper(ss map[string]bool, mounts []*mountinfo.Info, all bool) ([]Mount, error) {
res := make([]Mount, 0, len(ss))
- scanner := bufio.NewScanner(mi)
numFound := 0
- for scanner.Scan() && numFound < len(ss) {
- txt := scanner.Text()
- sepIdx := strings.Index(txt, " - ")
- if sepIdx == -1 {
- return nil, fmt.Errorf("invalid mountinfo format")
- }
- if txt[sepIdx+3:sepIdx+10] == "cgroup2" || txt[sepIdx+3:sepIdx+9] != "cgroup" {
- continue
- }
- fields := strings.Split(txt, " ")
+ for _, mi := range mounts {
m := Mount{
- Mountpoint: fields[4],
- Root: fields[3],
+ Mountpoint: mi.Mountpoint,
+ Root: mi.Root,
}
- for _, opt := range strings.Split(fields[len(fields)-1], ",") {
+ for _, opt := range strings.Split(mi.VFSOptions, ",") {
seen, known := ss[opt]
if !known || (!all && seen) {
continue
@@ -148,19 +181,18 @@ func getCgroupMountsHelper(ss map[string]bool, mi io.Reader, all bool) ([]Mount,
if len(m.Subsystems) > 0 || all {
res = append(res, m)
}
- }
- if err := scanner.Err(); err != nil {
- return nil, err
+ if !all && numFound >= len(ss) {
+ break
+ }
}
return res, nil
}
func getCgroupMountsV1(all bool) ([]Mount, error) {
- f, err := os.Open("/proc/self/mountinfo")
+ mi, err := readCgroupMountinfo()
if err != nil {
return nil, err
}
- defer f.Close()
allSubsystems, err := ParseCgroupFile("/proc/self/cgroup")
if err != nil {
@@ -171,7 +203,8 @@ func getCgroupMountsV1(all bool) ([]Mount, error) {
for s := range allSubsystems {
allMap[s] = false
}
- return getCgroupMountsHelper(allMap, f, all)
+
+ return getCgroupMountsHelper(allMap, mi, all)
}
// GetOwnCgroup returns the relative path to the cgroup docker is running in.