|
@@ -21,13 +21,11 @@ import (
|
|
"context"
|
|
"context"
|
|
"errors"
|
|
"errors"
|
|
"fmt"
|
|
"fmt"
|
|
- "io"
|
|
|
|
"math"
|
|
"math"
|
|
"os"
|
|
"os"
|
|
"path/filepath"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strconv"
|
|
"strings"
|
|
"strings"
|
|
- "syscall"
|
|
|
|
"time"
|
|
"time"
|
|
|
|
|
|
"github.com/containerd/cgroups/v3/cgroup2/stats"
|
|
"github.com/containerd/cgroups/v3/cgroup2/stats"
|
|
@@ -43,13 +41,12 @@ const (
|
|
subtreeControl = "cgroup.subtree_control"
|
|
subtreeControl = "cgroup.subtree_control"
|
|
controllersFile = "cgroup.controllers"
|
|
controllersFile = "cgroup.controllers"
|
|
killFile = "cgroup.kill"
|
|
killFile = "cgroup.kill"
|
|
|
|
+ typeFile = "cgroup.type"
|
|
defaultCgroup2Path = "/sys/fs/cgroup"
|
|
defaultCgroup2Path = "/sys/fs/cgroup"
|
|
defaultSlice = "system.slice"
|
|
defaultSlice = "system.slice"
|
|
)
|
|
)
|
|
|
|
|
|
-var (
|
|
|
|
- canDelegate bool
|
|
|
|
-)
|
|
|
|
|
|
+var canDelegate bool
|
|
|
|
|
|
type Event struct {
|
|
type Event struct {
|
|
Low uint64
|
|
Low uint64
|
|
@@ -99,7 +96,9 @@ func (r *Resources) Values() (o []Value) {
|
|
func (r *Resources) EnabledControllers() (c []string) {
|
|
func (r *Resources) EnabledControllers() (c []string) {
|
|
if r.CPU != nil {
|
|
if r.CPU != nil {
|
|
c = append(c, "cpu")
|
|
c = append(c, "cpu")
|
|
- c = append(c, "cpuset")
|
|
|
|
|
|
+ if r.CPU.Cpus != "" || r.CPU.Mems != "" {
|
|
|
|
+ c = append(c, "cpuset")
|
|
|
|
+ }
|
|
}
|
|
}
|
|
if r.Memory != nil {
|
|
if r.Memory != nil {
|
|
c = append(c, "memory")
|
|
c = append(c, "memory")
|
|
@@ -238,6 +237,35 @@ func setResources(path string, resources *Resources) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// CgroupType represents the types a cgroup can be.
|
|
|
|
+type CgroupType string
|
|
|
|
+
|
|
|
|
+const (
|
|
|
|
+ Domain CgroupType = "domain"
|
|
|
|
+ Threaded CgroupType = "threaded"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+func (c *Manager) GetType() (CgroupType, error) {
|
|
|
|
+ val, err := os.ReadFile(filepath.Join(c.path, typeFile))
|
|
|
|
+ if err != nil {
|
|
|
|
+ return "", err
|
|
|
|
+ }
|
|
|
|
+ trimmed := strings.TrimSpace(string(val))
|
|
|
|
+ return CgroupType(trimmed), nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (c *Manager) SetType(cgType CgroupType) error {
|
|
|
|
+ // NOTE: We could abort if cgType != Threaded here as currently
|
|
|
|
+ // it's not possible to revert back to domain, but not sure
|
|
|
|
+ // it's worth being that opinionated, especially if that may
|
|
|
|
+ // ever change.
|
|
|
|
+ v := Value{
|
|
|
|
+ filename: typeFile,
|
|
|
|
+ value: string(cgType),
|
|
|
|
+ }
|
|
|
|
+ return writeValues(c.path, []Value{v})
|
|
|
|
+}
|
|
|
|
+
|
|
func (c *Manager) RootControllers() ([]string, error) {
|
|
func (c *Manager) RootControllers() ([]string, error) {
|
|
b, err := os.ReadFile(filepath.Join(c.unifiedMountpoint, controllersFile))
|
|
b, err := os.ReadFile(filepath.Join(c.unifiedMountpoint, controllersFile))
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -492,17 +520,15 @@ func (c *Manager) MoveTo(destination *Manager) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-var singleValueFiles = []string{
|
|
|
|
- "pids.current",
|
|
|
|
- "pids.max",
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
func (c *Manager) Stat() (*stats.Metrics, error) {
|
|
func (c *Manager) Stat() (*stats.Metrics, error) {
|
|
controllers, err := c.Controllers()
|
|
controllers, err := c.Controllers()
|
|
if err != nil {
|
|
if err != nil {
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
- out := make(map[string]interface{})
|
|
|
|
|
|
+ // Sizing this avoids an allocation to increase the map at runtime;
|
|
|
|
+ // currently the default bucket size is 8 and we put 40+ elements
|
|
|
|
+ // in it so we'd always end up allocating.
|
|
|
|
+ out := make(map[string]uint64, 50)
|
|
for _, controller := range controllers {
|
|
for _, controller := range controllers {
|
|
switch controller {
|
|
switch controller {
|
|
case "cpu", "memory":
|
|
case "cpu", "memory":
|
|
@@ -514,66 +540,58 @@ func (c *Manager) Stat() (*stats.Metrics, error) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- for _, name := range singleValueFiles {
|
|
|
|
- if err := readSingleFile(c.path, name, out); err != nil {
|
|
|
|
- if os.IsNotExist(err) {
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- memoryEvents := make(map[string]interface{})
|
|
|
|
|
|
+ memoryEvents := make(map[string]uint64)
|
|
if err := readKVStatsFile(c.path, "memory.events", memoryEvents); err != nil {
|
|
if err := readKVStatsFile(c.path, "memory.events", memoryEvents); err != nil {
|
|
if !os.IsNotExist(err) {
|
|
if !os.IsNotExist(err) {
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- var metrics stats.Metrics
|
|
|
|
|
|
|
|
|
|
+ var metrics stats.Metrics
|
|
metrics.Pids = &stats.PidsStat{
|
|
metrics.Pids = &stats.PidsStat{
|
|
- Current: getPidValue("pids.current", out),
|
|
|
|
- Limit: getPidValue("pids.max", out),
|
|
|
|
|
|
+ Current: getStatFileContentUint64(filepath.Join(c.path, "pids.current")),
|
|
|
|
+ Limit: getStatFileContentUint64(filepath.Join(c.path, "pids.max")),
|
|
}
|
|
}
|
|
metrics.CPU = &stats.CPUStat{
|
|
metrics.CPU = &stats.CPUStat{
|
|
- UsageUsec: getUint64Value("usage_usec", out),
|
|
|
|
- UserUsec: getUint64Value("user_usec", out),
|
|
|
|
- SystemUsec: getUint64Value("system_usec", out),
|
|
|
|
- NrPeriods: getUint64Value("nr_periods", out),
|
|
|
|
- NrThrottled: getUint64Value("nr_throttled", out),
|
|
|
|
- ThrottledUsec: getUint64Value("throttled_usec", out),
|
|
|
|
|
|
+ UsageUsec: out["usage_usec"],
|
|
|
|
+ UserUsec: out["user_usec"],
|
|
|
|
+ SystemUsec: out["system_usec"],
|
|
|
|
+ NrPeriods: out["nr_periods"],
|
|
|
|
+ NrThrottled: out["nr_throttled"],
|
|
|
|
+ ThrottledUsec: out["throttled_usec"],
|
|
}
|
|
}
|
|
metrics.Memory = &stats.MemoryStat{
|
|
metrics.Memory = &stats.MemoryStat{
|
|
- Anon: getUint64Value("anon", out),
|
|
|
|
- File: getUint64Value("file", out),
|
|
|
|
- KernelStack: getUint64Value("kernel_stack", out),
|
|
|
|
- Slab: getUint64Value("slab", out),
|
|
|
|
- Sock: getUint64Value("sock", out),
|
|
|
|
- Shmem: getUint64Value("shmem", out),
|
|
|
|
- FileMapped: getUint64Value("file_mapped", out),
|
|
|
|
- FileDirty: getUint64Value("file_dirty", out),
|
|
|
|
- FileWriteback: getUint64Value("file_writeback", out),
|
|
|
|
- AnonThp: getUint64Value("anon_thp", out),
|
|
|
|
- InactiveAnon: getUint64Value("inactive_anon", out),
|
|
|
|
- ActiveAnon: getUint64Value("active_anon", out),
|
|
|
|
- InactiveFile: getUint64Value("inactive_file", out),
|
|
|
|
- ActiveFile: getUint64Value("active_file", out),
|
|
|
|
- Unevictable: getUint64Value("unevictable", out),
|
|
|
|
- SlabReclaimable: getUint64Value("slab_reclaimable", out),
|
|
|
|
- SlabUnreclaimable: getUint64Value("slab_unreclaimable", out),
|
|
|
|
- Pgfault: getUint64Value("pgfault", out),
|
|
|
|
- Pgmajfault: getUint64Value("pgmajfault", out),
|
|
|
|
- WorkingsetRefault: getUint64Value("workingset_refault", out),
|
|
|
|
- WorkingsetActivate: getUint64Value("workingset_activate", out),
|
|
|
|
- WorkingsetNodereclaim: getUint64Value("workingset_nodereclaim", out),
|
|
|
|
- Pgrefill: getUint64Value("pgrefill", out),
|
|
|
|
- Pgscan: getUint64Value("pgscan", out),
|
|
|
|
- Pgsteal: getUint64Value("pgsteal", out),
|
|
|
|
- Pgactivate: getUint64Value("pgactivate", out),
|
|
|
|
- Pgdeactivate: getUint64Value("pgdeactivate", out),
|
|
|
|
- Pglazyfree: getUint64Value("pglazyfree", out),
|
|
|
|
- Pglazyfreed: getUint64Value("pglazyfreed", out),
|
|
|
|
- ThpFaultAlloc: getUint64Value("thp_fault_alloc", out),
|
|
|
|
- ThpCollapseAlloc: getUint64Value("thp_collapse_alloc", out),
|
|
|
|
|
|
+ Anon: out["anon"],
|
|
|
|
+ File: out["file"],
|
|
|
|
+ KernelStack: out["kernel_stack"],
|
|
|
|
+ Slab: out["slab"],
|
|
|
|
+ Sock: out["sock"],
|
|
|
|
+ Shmem: out["shmem"],
|
|
|
|
+ FileMapped: out["file_mapped"],
|
|
|
|
+ FileDirty: out["file_dirty"],
|
|
|
|
+ FileWriteback: out["file_writeback"],
|
|
|
|
+ AnonThp: out["anon_thp"],
|
|
|
|
+ InactiveAnon: out["inactive_anon"],
|
|
|
|
+ ActiveAnon: out["active_anon"],
|
|
|
|
+ InactiveFile: out["inactive_file"],
|
|
|
|
+ ActiveFile: out["active_file"],
|
|
|
|
+ Unevictable: out["unevictable"],
|
|
|
|
+ SlabReclaimable: out["slab_reclaimable"],
|
|
|
|
+ SlabUnreclaimable: out["slab_unreclaimable"],
|
|
|
|
+ Pgfault: out["pgfault"],
|
|
|
|
+ Pgmajfault: out["pgmajfault"],
|
|
|
|
+ WorkingsetRefault: out["workingset_refault"],
|
|
|
|
+ WorkingsetActivate: out["workingset_activate"],
|
|
|
|
+ WorkingsetNodereclaim: out["workingset_nodereclaim"],
|
|
|
|
+ Pgrefill: out["pgrefill"],
|
|
|
|
+ Pgscan: out["pgscan"],
|
|
|
|
+ Pgsteal: out["pgsteal"],
|
|
|
|
+ Pgactivate: out["pgactivate"],
|
|
|
|
+ Pgdeactivate: out["pgdeactivate"],
|
|
|
|
+ Pglazyfree: out["pglazyfree"],
|
|
|
|
+ Pglazyfreed: out["pglazyfreed"],
|
|
|
|
+ ThpFaultAlloc: out["thp_fault_alloc"],
|
|
|
|
+ ThpCollapseAlloc: out["thp_collapse_alloc"],
|
|
Usage: getStatFileContentUint64(filepath.Join(c.path, "memory.current")),
|
|
Usage: getStatFileContentUint64(filepath.Join(c.path, "memory.current")),
|
|
UsageLimit: getStatFileContentUint64(filepath.Join(c.path, "memory.max")),
|
|
UsageLimit: getStatFileContentUint64(filepath.Join(c.path, "memory.max")),
|
|
SwapUsage: getStatFileContentUint64(filepath.Join(c.path, "memory.swap.current")),
|
|
SwapUsage: getStatFileContentUint64(filepath.Join(c.path, "memory.swap.current")),
|
|
@@ -581,11 +599,11 @@ func (c *Manager) Stat() (*stats.Metrics, error) {
|
|
}
|
|
}
|
|
if len(memoryEvents) > 0 {
|
|
if len(memoryEvents) > 0 {
|
|
metrics.MemoryEvents = &stats.MemoryEvents{
|
|
metrics.MemoryEvents = &stats.MemoryEvents{
|
|
- Low: getUint64Value("low", memoryEvents),
|
|
|
|
- High: getUint64Value("high", memoryEvents),
|
|
|
|
- Max: getUint64Value("max", memoryEvents),
|
|
|
|
- Oom: getUint64Value("oom", memoryEvents),
|
|
|
|
- OomKill: getUint64Value("oom_kill", memoryEvents),
|
|
|
|
|
|
+ Low: memoryEvents["low"],
|
|
|
|
+ High: memoryEvents["high"],
|
|
|
|
+ Max: memoryEvents["max"],
|
|
|
|
+ Oom: memoryEvents["oom"],
|
|
|
|
+ OomKill: memoryEvents["oom_kill"],
|
|
}
|
|
}
|
|
}
|
|
}
|
|
metrics.Io = &stats.IOStat{Usage: readIoStats(c.path)}
|
|
metrics.Io = &stats.IOStat{Usage: readIoStats(c.path)}
|
|
@@ -598,56 +616,7 @@ func (c *Manager) Stat() (*stats.Metrics, error) {
|
|
return &metrics, nil
|
|
return &metrics, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func getUint64Value(key string, out map[string]interface{}) uint64 {
|
|
|
|
- v, ok := out[key]
|
|
|
|
- if !ok {
|
|
|
|
- return 0
|
|
|
|
- }
|
|
|
|
- switch t := v.(type) {
|
|
|
|
- case uint64:
|
|
|
|
- return t
|
|
|
|
- }
|
|
|
|
- return 0
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func getPidValue(key string, out map[string]interface{}) uint64 {
|
|
|
|
- v, ok := out[key]
|
|
|
|
- if !ok {
|
|
|
|
- return 0
|
|
|
|
- }
|
|
|
|
- switch t := v.(type) {
|
|
|
|
- case uint64:
|
|
|
|
- return t
|
|
|
|
- case string:
|
|
|
|
- if t == "max" {
|
|
|
|
- return math.MaxUint64
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return 0
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func readSingleFile(path string, file string, out map[string]interface{}) error {
|
|
|
|
- f, err := os.Open(filepath.Join(path, file))
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- defer f.Close()
|
|
|
|
- data, err := io.ReadAll(f)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- s := strings.TrimSpace(string(data))
|
|
|
|
- v, err := parseUint(s, 10, 64)
|
|
|
|
- if err != nil {
|
|
|
|
- // if we cannot parse as a uint, parse as a string
|
|
|
|
- out[file] = s
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
- out[file] = v
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func readKVStatsFile(path string, file string, out map[string]interface{}) error {
|
|
|
|
|
|
+func readKVStatsFile(path string, file string, out map[string]uint64) error {
|
|
f, err := os.Open(filepath.Join(path, file))
|
|
f, err := os.Open(filepath.Join(path, file))
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
@@ -692,16 +661,12 @@ func (c *Manager) freeze(path string, state State) error {
|
|
|
|
|
|
func (c *Manager) isCgroupEmpty() bool {
|
|
func (c *Manager) isCgroupEmpty() bool {
|
|
// In case of any error we return true so that we exit and don't leak resources
|
|
// In case of any error we return true so that we exit and don't leak resources
|
|
- out := make(map[string]interface{})
|
|
|
|
|
|
+ out := make(map[string]uint64)
|
|
if err := readKVStatsFile(c.path, "cgroup.events", out); err != nil {
|
|
if err := readKVStatsFile(c.path, "cgroup.events", out); err != nil {
|
|
return true
|
|
return true
|
|
}
|
|
}
|
|
if v, ok := out["populated"]; ok {
|
|
if v, ok := out["populated"]; ok {
|
|
- populated, ok := v.(uint64)
|
|
|
|
- if !ok {
|
|
|
|
- return true
|
|
|
|
- }
|
|
|
|
- return populated == 0
|
|
|
|
|
|
+ return v == 0
|
|
}
|
|
}
|
|
return true
|
|
return true
|
|
}
|
|
}
|
|
@@ -709,19 +674,19 @@ func (c *Manager) isCgroupEmpty() bool {
|
|
// MemoryEventFD returns inotify file descriptor and 'memory.events' inotify watch descriptor
|
|
// MemoryEventFD returns inotify file descriptor and 'memory.events' inotify watch descriptor
|
|
func (c *Manager) MemoryEventFD() (int, uint32, error) {
|
|
func (c *Manager) MemoryEventFD() (int, uint32, error) {
|
|
fpath := filepath.Join(c.path, "memory.events")
|
|
fpath := filepath.Join(c.path, "memory.events")
|
|
- fd, err := syscall.InotifyInit()
|
|
|
|
|
|
+ fd, err := unix.InotifyInit()
|
|
if err != nil {
|
|
if err != nil {
|
|
return 0, 0, errors.New("failed to create inotify fd")
|
|
return 0, 0, errors.New("failed to create inotify fd")
|
|
}
|
|
}
|
|
- wd, err := syscall.InotifyAddWatch(fd, fpath, unix.IN_MODIFY)
|
|
|
|
|
|
+ wd, err := unix.InotifyAddWatch(fd, fpath, unix.IN_MODIFY)
|
|
if err != nil {
|
|
if err != nil {
|
|
- syscall.Close(fd)
|
|
|
|
|
|
+ unix.Close(fd)
|
|
return 0, 0, fmt.Errorf("failed to add inotify watch for %q: %w", fpath, err)
|
|
return 0, 0, fmt.Errorf("failed to add inotify watch for %q: %w", fpath, err)
|
|
}
|
|
}
|
|
// monitor to detect process exit/cgroup deletion
|
|
// monitor to detect process exit/cgroup deletion
|
|
evpath := filepath.Join(c.path, "cgroup.events")
|
|
evpath := filepath.Join(c.path, "cgroup.events")
|
|
- if _, err = syscall.InotifyAddWatch(fd, evpath, unix.IN_MODIFY); err != nil {
|
|
|
|
- syscall.Close(fd)
|
|
|
|
|
|
+ if _, err = unix.InotifyAddWatch(fd, evpath, unix.IN_MODIFY); err != nil {
|
|
|
|
+ unix.Close(fd)
|
|
return 0, 0, fmt.Errorf("failed to add inotify watch for %q: %w", evpath, err)
|
|
return 0, 0, fmt.Errorf("failed to add inotify watch for %q: %w", evpath, err)
|
|
}
|
|
}
|
|
|
|
|
|
@@ -736,41 +701,6 @@ func (c *Manager) EventChan() (<-chan Event, <-chan error) {
|
|
return ec, errCh
|
|
return ec, errCh
|
|
}
|
|
}
|
|
|
|
|
|
-func parseMemoryEvents(out map[string]interface{}) (Event, error) {
|
|
|
|
- e := Event{}
|
|
|
|
- if v, ok := out["high"]; ok {
|
|
|
|
- e.High, ok = v.(uint64)
|
|
|
|
- if !ok {
|
|
|
|
- return Event{}, fmt.Errorf("cannot convert high to uint64: %+v", v)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if v, ok := out["low"]; ok {
|
|
|
|
- e.Low, ok = v.(uint64)
|
|
|
|
- if !ok {
|
|
|
|
- return Event{}, fmt.Errorf("cannot convert low to uint64: %+v", v)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if v, ok := out["max"]; ok {
|
|
|
|
- e.Max, ok = v.(uint64)
|
|
|
|
- if !ok {
|
|
|
|
- return Event{}, fmt.Errorf("cannot convert max to uint64: %+v", v)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if v, ok := out["oom"]; ok {
|
|
|
|
- e.OOM, ok = v.(uint64)
|
|
|
|
- if !ok {
|
|
|
|
- return Event{}, fmt.Errorf("cannot convert oom to uint64: %+v", v)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if v, ok := out["oom_kill"]; ok {
|
|
|
|
- e.OOMKill, ok = v.(uint64)
|
|
|
|
- if !ok {
|
|
|
|
- return Event{}, fmt.Errorf("cannot convert oom_kill to uint64: %+v", v)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return e, nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
func (c *Manager) waitForEvents(ec chan<- Event, errCh chan<- error) {
|
|
func (c *Manager) waitForEvents(ec chan<- Event, errCh chan<- error) {
|
|
defer close(errCh)
|
|
defer close(errCh)
|
|
|
|
|
|
@@ -779,17 +709,17 @@ func (c *Manager) waitForEvents(ec chan<- Event, errCh chan<- error) {
|
|
errCh <- err
|
|
errCh <- err
|
|
return
|
|
return
|
|
}
|
|
}
|
|
- defer syscall.Close(fd)
|
|
|
|
|
|
+ defer unix.Close(fd)
|
|
|
|
|
|
for {
|
|
for {
|
|
- buffer := make([]byte, syscall.SizeofInotifyEvent*10)
|
|
|
|
- bytesRead, err := syscall.Read(fd, buffer)
|
|
|
|
|
|
+ buffer := make([]byte, unix.SizeofInotifyEvent*10)
|
|
|
|
+ bytesRead, err := unix.Read(fd, buffer)
|
|
if err != nil {
|
|
if err != nil {
|
|
errCh <- err
|
|
errCh <- err
|
|
return
|
|
return
|
|
}
|
|
}
|
|
- if bytesRead >= syscall.SizeofInotifyEvent {
|
|
|
|
- out := make(map[string]interface{})
|
|
|
|
|
|
+ if bytesRead >= unix.SizeofInotifyEvent {
|
|
|
|
+ out := make(map[string]uint64)
|
|
if err := readKVStatsFile(c.path, "memory.events", out); err != nil {
|
|
if err := readKVStatsFile(c.path, "memory.events", out); err != nil {
|
|
// When cgroup is deleted read may return -ENODEV instead of -ENOENT from open.
|
|
// When cgroup is deleted read may return -ENODEV instead of -ENOENT from open.
|
|
if _, statErr := os.Lstat(filepath.Join(c.path, "memory.events")); !os.IsNotExist(statErr) {
|
|
if _, statErr := os.Lstat(filepath.Join(c.path, "memory.events")); !os.IsNotExist(statErr) {
|
|
@@ -797,12 +727,13 @@ func (c *Manager) waitForEvents(ec chan<- Event, errCh chan<- error) {
|
|
}
|
|
}
|
|
return
|
|
return
|
|
}
|
|
}
|
|
- e, err := parseMemoryEvents(out)
|
|
|
|
- if err != nil {
|
|
|
|
- errCh <- err
|
|
|
|
- return
|
|
|
|
|
|
+ ec <- Event{
|
|
|
|
+ Low: out["low"],
|
|
|
|
+ High: out["high"],
|
|
|
|
+ Max: out["max"],
|
|
|
|
+ OOM: out["oom"],
|
|
|
|
+ OOMKill: out["oom_kill"],
|
|
}
|
|
}
|
|
- ec <- e
|
|
|
|
if c.isCgroupEmpty() {
|
|
if c.isCgroupEmpty() {
|
|
return
|
|
return
|
|
}
|
|
}
|
|
@@ -818,7 +749,7 @@ func setDevices(path string, devices []specs.LinuxDeviceCgroup) error {
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
- dirFD, err := unix.Open(path, unix.O_DIRECTORY|unix.O_RDONLY|unix.O_CLOEXEC, 0600)
|
|
|
|
|
|
+ dirFD, err := unix.Open(path, unix.O_DIRECTORY|unix.O_RDONLY|unix.O_CLOEXEC, 0o600)
|
|
if err != nil {
|
|
if err != nil {
|
|
return fmt.Errorf("cannot get dir FD for %s", path)
|
|
return fmt.Errorf("cannot get dir FD for %s", path)
|
|
}
|
|
}
|