summaryrefslogtreecommitdiff
path: root/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/memory.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/memory.go')
-rw-r--r--vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/memory.go216
1 files changed, 216 insertions, 0 deletions
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/memory.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/memory.go
new file mode 100644
index 000000000..adbc4b230
--- /dev/null
+++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/memory.go
@@ -0,0 +1,216 @@
+package fs2
+
+import (
+ "bufio"
+ "errors"
+ "math"
+ "os"
+ "strconv"
+ "strings"
+
+ "golang.org/x/sys/unix"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+// numToStr converts an int64 value to a string for writing to a
+// cgroupv2 files with .min, .max, .low, or .high suffix.
+// The value of -1 is converted to "max" for cgroupv1 compatibility
+// (which used to write -1 to remove the limit).
+func numToStr(value int64) (ret string) {
+ switch {
+ case value == 0:
+ ret = ""
+ case value == -1:
+ ret = "max"
+ default:
+ ret = strconv.FormatInt(value, 10)
+ }
+
+ return ret
+}
+
+func isMemorySet(r *configs.Resources) bool {
+ return r.MemoryReservation != 0 || r.Memory != 0 || r.MemorySwap != 0
+}
+
+func setMemory(dirPath string, r *configs.Resources) error {
+ if !isMemorySet(r) {
+ return nil
+ }
+ swap, err := cgroups.ConvertMemorySwapToCgroupV2Value(r.MemorySwap, r.Memory)
+ if err != nil {
+ return err
+ }
+ swapStr := numToStr(swap)
+ if swapStr == "" && swap == 0 && r.MemorySwap > 0 {
+ // memory and memorySwap set to the same value -- disable swap
+ swapStr = "0"
+ }
+ // never write empty string to `memory.swap.max`, it means set to 0.
+ if swapStr != "" {
+ if err := cgroups.WriteFile(dirPath, "memory.swap.max", swapStr); err != nil {
+ return err
+ }
+ }
+
+ if val := numToStr(r.Memory); val != "" {
+ if err := cgroups.WriteFile(dirPath, "memory.max", val); err != nil {
+ return err
+ }
+ }
+
+ // cgroup.Resources.KernelMemory is ignored
+
+ if val := numToStr(r.MemoryReservation); val != "" {
+ if err := cgroups.WriteFile(dirPath, "memory.low", val); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func statMemory(dirPath string, stats *cgroups.Stats) error {
+ const file = "memory.stat"
+ statsFile, err := cgroups.OpenFile(dirPath, file, os.O_RDONLY)
+ if err != nil {
+ return err
+ }
+ defer statsFile.Close()
+
+ sc := bufio.NewScanner(statsFile)
+ for sc.Scan() {
+ t, v, err := fscommon.ParseKeyValue(sc.Text())
+ if err != nil {
+ return &parseError{Path: dirPath, File: file, Err: err}
+ }
+ stats.MemoryStats.Stats[t] = v
+ }
+ if err := sc.Err(); err != nil {
+ return &parseError{Path: dirPath, File: file, Err: err}
+ }
+ stats.MemoryStats.Cache = stats.MemoryStats.Stats["file"]
+ // Unlike cgroup v1 which has memory.use_hierarchy binary knob,
+ // cgroup v2 is always hierarchical.
+ stats.MemoryStats.UseHierarchy = true
+
+ memoryUsage, err := getMemoryDataV2(dirPath, "")
+ if err != nil {
+ if errors.Is(err, unix.ENOENT) && dirPath == UnifiedMountpoint {
+ // The root cgroup does not have memory.{current,max}
+ // so emulate those using data from /proc/meminfo.
+ return statsFromMeminfo(stats)
+ }
+ return err
+ }
+ stats.MemoryStats.Usage = memoryUsage
+ swapUsage, err := getMemoryDataV2(dirPath, "swap")
+ if err != nil {
+ return err
+ }
+ // As cgroup v1 reports SwapUsage values as mem+swap combined,
+ // while in cgroup v2 swap values do not include memory,
+ // report combined mem+swap for v1 compatibility.
+ swapUsage.Usage += memoryUsage.Usage
+ if swapUsage.Limit != math.MaxUint64 {
+ swapUsage.Limit += memoryUsage.Limit
+ }
+ stats.MemoryStats.SwapUsage = swapUsage
+
+ return nil
+}
+
+func getMemoryDataV2(path, name string) (cgroups.MemoryData, error) {
+ memoryData := cgroups.MemoryData{}
+
+ moduleName := "memory"
+ if name != "" {
+ moduleName = "memory." + name
+ }
+ usage := moduleName + ".current"
+ limit := moduleName + ".max"
+
+ value, err := fscommon.GetCgroupParamUint(path, usage)
+ if err != nil {
+ if name != "" && os.IsNotExist(err) {
+ // Ignore EEXIST as there's no swap accounting
+ // if kernel CONFIG_MEMCG_SWAP is not set or
+ // swapaccount=0 kernel boot parameter is given.
+ return cgroups.MemoryData{}, nil
+ }
+ return cgroups.MemoryData{}, err
+ }
+ memoryData.Usage = value
+
+ value, err = fscommon.GetCgroupParamUint(path, limit)
+ if err != nil {
+ return cgroups.MemoryData{}, err
+ }
+ memoryData.Limit = value
+
+ return memoryData, nil
+}
+
+func statsFromMeminfo(stats *cgroups.Stats) error {
+ const file = "/proc/meminfo"
+ f, err := os.Open(file)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ // Fields we are interested in.
+ var (
+ swap_free uint64
+ swap_total uint64
+ main_total uint64
+ main_free uint64
+ )
+ mem := map[string]*uint64{
+ "SwapFree": &swap_free,
+ "SwapTotal": &swap_total,
+ "MemTotal": &main_total,
+ "MemFree": &main_free,
+ }
+
+ found := 0
+ sc := bufio.NewScanner(f)
+ for sc.Scan() {
+ parts := strings.SplitN(sc.Text(), ":", 3)
+ if len(parts) != 2 {
+ // Should not happen.
+ continue
+ }
+ k := parts[0]
+ p, ok := mem[k]
+ if !ok {
+ // Unknown field -- not interested.
+ continue
+ }
+ vStr := strings.TrimSpace(strings.TrimSuffix(parts[1], " kB"))
+ *p, err = strconv.ParseUint(vStr, 10, 64)
+ if err != nil {
+ return &parseError{File: file, Err: errors.New("bad value for " + k)}
+ }
+
+ found++
+ if found == len(mem) {
+ // Got everything we need -- skip the rest.
+ break
+ }
+ }
+ if err := sc.Err(); err != nil {
+ return &parseError{Path: "", File: file, Err: err}
+ }
+
+ stats.MemoryStats.SwapUsage.Usage = (swap_total - swap_free) * 1024
+ stats.MemoryStats.SwapUsage.Limit = math.MaxUint64
+
+ stats.MemoryStats.Usage.Usage = (main_total - main_free) * 1024
+ stats.MemoryStats.Usage.Limit = math.MaxUint64
+
+ return nil
+}