From 6477190927cec5e20393c303bf7a4049539273bf Mon Sep 17 00:00:00 2001 From: zhongjiawei Date: Wed, 26 Jul 2023 15:25:25 +0800 Subject: [PATCH] runc:cgroup apply method modify --- libcontainer/cgroups/fs/cpuset.go | 12 +++- libcontainer/cgroups/fs/files.go | 27 +++----- libcontainer/cgroups/fs/utils.go | 97 +++++++++++++++++++++++++++++ libcontainer/rootfs_linux.go | 1 + libcontainer/standard_init_linux.go | 6 +- 5 files changed, 120 insertions(+), 23 deletions(-) create mode 100644 libcontainer/cgroups/fs/utils.go diff --git a/libcontainer/cgroups/fs/cpuset.go b/libcontainer/cgroups/fs/cpuset.go index ef9164b..01388f7 100644 --- a/libcontainer/cgroups/fs/cpuset.go +++ b/libcontainer/cgroups/fs/cpuset.go @@ -1,6 +1,7 @@ package fs import ( + "fmt" "errors" "io/ioutil" "os" @@ -145,7 +146,7 @@ func (s *CpusetGroup) setCpuset(path, cpuset string) error { func (s *CpusetGroup) Set(path string, r *configs.Resources) error { var ret error if r.CpusetCpus != "" { - return s.setCpuset(path, cgroup.Resources.CpusetCpus) + return s.setCpuset(path, r.CpusetCpus) } if r.CpusetMems != "" { if err := cgroups.WriteFile(path, "cpuset.mems", r.CpusetMems); err != nil { @@ -352,14 +353,19 @@ func cpusetEnsureParent(current string) error { // cpusetCopyIfNeeded copies the cpuset.cpus and cpuset.mems from the parent // directory to the current directory if the file's contents are 0 func cpusetCopyIfNeeded(current, parent string) error { - if currentCpus, currentMems, err := getCpusetSubsystemSettings(current); err != nil { + var ( + err error + currentCpus, currentMems string + parentCpus, parentMems string + ) + if currentCpus, currentMems, err = getCpusetSubsystemSettings(current); err != nil { ret := fmt.Errorf("failed copy current cgroup setting, %v", err) if _, err := os.Stat(current); err != nil { ret = fmt.Errorf("%v, %v", ret, err) } return ret } - if parentCpus, parentMems, err := getCpusetSubsystemSettings(parent); err != nil { + if parentCpus, parentMems, err = getCpusetSubsystemSettings(parent); err != nil { ret := fmt.Errorf("failed copy parent cgroup setting, %v", err) if _, err := os.Stat(parent); err != nil { ret = fmt.Errorf("%v, %v", ret, err) diff --git a/libcontainer/cgroups/fs/files.go b/libcontainer/cgroups/fs/files.go index 3315cda..9dcc54b 100644 --- a/libcontainer/cgroups/fs/files.go +++ b/libcontainer/cgroups/fs/files.go @@ -13,34 +13,29 @@ import ( "fmt" "strconv" + "path/filepath" + "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/configs" - "path/filepath" ) -type FilesGroup struct { -} +type FilesGroup struct{} func (s *FilesGroup) Name() string { return "files" } -func (s *FilesGroup) Apply(d *cgroupData) error { - _, err := d.join("files") - if err != nil && !cgroups.IsNotFound(err) { - return err - } - return nil +func (s *FilesGroup) Apply(path string, _ *configs.Resources, pid int) error { + return apply(path, pid) } -func (s *FilesGroup) Set(path string, cgroup *configs.Cgroup) error { - if cgroup.Resources.FilesLimit != 0 { +func (s *FilesGroup) Set(path string, r *configs.Resources) error { + if r.FilesLimit != 0 { // "max" is the fallback value. limit := "max" - if cgroup.Resources.FilesLimit > 0 { - limit = strconv.FormatInt(cgroup.Resources.FilesLimit, 10) + if r.FilesLimit > 0 { + limit = strconv.FormatInt(r.FilesLimit, 10) } - if err := writeFile(path, "files.limit", limit); err != nil { return err } @@ -49,10 +44,6 @@ func (s *FilesGroup) Set(path string, cgroup *configs.Cgroup) error { return nil } -func (s *FilesGroup) Remove(d *cgroupData) error { - return removePath(d.path("files")) -} - func (s *FilesGroup) GetStats(path string, stats *cgroups.Stats) error { usage, err := getCgroupParamUint(path, "files.usage") if err != nil { diff --git a/libcontainer/cgroups/fs/utils.go b/libcontainer/cgroups/fs/utils.go new file mode 100644 index 0000000..38820a8 --- /dev/null +++ b/libcontainer/cgroups/fs/utils.go @@ -0,0 +1,97 @@ +// +build linux + +package fs + +import ( + "errors" + "os" + "fmt" + "io/ioutil" + "path/filepath" + "strconv" + "strings" +) + +var ( + ErrNotValidFormat = errors.New("line is not a valid key value format") +) + +// Saturates negative values at zero and returns a uint64. +// Due to kernel bugs, some of the memory cgroup stats can be negative. +func parseUint(s string, base, bitSize int) (uint64, error) { + value, err := strconv.ParseUint(s, base, bitSize) + if err != nil { + intValue, intErr := strconv.ParseInt(s, base, bitSize) + // 1. Handle negative values greater than MinInt64 (and) + // 2. Handle negative values lesser than MinInt64 + if intErr == nil && intValue < 0 { + return 0, nil + } else if intErr != nil && intErr.(*strconv.NumError).Err == strconv.ErrRange && intValue < 0 { + return 0, nil + } + + return value, err + } + + return value, nil +} + +// Parses a cgroup param and returns as name, value +// i.e. "io_service_bytes 1234" will return as io_service_bytes, 1234 +func getCgroupParamKeyValue(t string) (string, uint64, error) { + parts := strings.Fields(t) + switch len(parts) { + case 2: + value, err := parseUint(parts[1], 10, 64) + if err != nil { + return "", 0, fmt.Errorf("unable to convert param value (%q) to uint64: %v", parts[1], err) + } + + return parts[0], value, nil + default: + return "", 0, ErrNotValidFormat + } +} + +// Gets a single uint64 value from the specified cgroup file. +func getCgroupParamUint(cgroupPath, cgroupFile string) (uint64, error) { + fileName := filepath.Join(cgroupPath, cgroupFile) + contents, err := ioutil.ReadFile(fileName) + if err != nil { + return 0, err + } + + res, err := parseUint(strings.TrimSpace(string(contents)), 10, 64) + if err != nil { + return res, fmt.Errorf("unable to parse %q as a uint from Cgroup file %q", string(contents), fileName) + } + return res, nil +} + +// Gets a string value from the specified cgroup file +func getCgroupParamString(cgroupPath, cgroupFile string) (string, error) { + contents, err := ioutil.ReadFile(filepath.Join(cgroupPath, cgroupFile)) + if err != nil { + return "", err + } + + return strings.TrimSpace(string(contents)), nil +} + +func writeFile(dir, file, data string) error { + // Normally dir should not be empty, one case is that cgroup subsystem + // is not mounted, we will get empty dir, and we want it fail here. + if dir == "" { + return fmt.Errorf("no such directory for %s", file) + } + if err := ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700); err != nil { + ret := fmt.Errorf("failed to write %v to %v: %v", data, file, err) + if _, err = os.Stat(dir); err != nil { + ret = fmt.Errorf("%v, failed to stat %v, %v", ret, dir, err) + } + + return ret + } + return nil +} + diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go index e7de071..499d753 100644 --- a/libcontainer/rootfs_linux.go +++ b/libcontainer/rootfs_linux.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strconv" "strings" + "syscall" "time" securejoin "github.com/cyphar/filepath-securejoin" diff --git a/libcontainer/standard_init_linux.go b/libcontainer/standard_init_linux.go index 542edba..cd962c8 100644 --- a/libcontainer/standard_init_linux.go +++ b/libcontainer/standard_init_linux.go @@ -6,6 +6,8 @@ import ( "os" "os/exec" "strconv" + "strings" + "syscall" "time" "github.com/opencontainers/runtime-spec/specs-go" @@ -260,11 +262,11 @@ func (l *linuxStandardInit) Init() error { }() select { - case chErr := <- ch: + case chErr := <-ch: if chErr != nil { return chErr } - case <- time.After(120 * time.Second): + case <-time.After(120 * time.Second): return fmt.Errorf("wait for the fifo to be opened on the other side timeout ") } // Close the O_PATH fifofd fd before exec because the kernel resets -- 2.33.0