github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/notify_linux.go (about) 1 package libcontainer 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "path/filepath" 8 9 "golang.org/x/sys/unix" 10 ) 11 12 type PressureLevel uint 13 14 const ( 15 LowPressure PressureLevel = iota 16 MediumPressure 17 CriticalPressure 18 ) 19 20 func registerMemoryEvent(cgDir string, evName string, arg string) (<-chan struct{}, error) { 21 evFile, err := os.Open(filepath.Join(cgDir, evName)) 22 if err != nil { 23 return nil, err 24 } 25 fd, err := unix.Eventfd(0, unix.EFD_CLOEXEC) 26 if err != nil { 27 evFile.Close() 28 return nil, err 29 } 30 31 eventfd := os.NewFile(uintptr(fd), "eventfd") 32 33 eventControlPath := filepath.Join(cgDir, "cgroup.event_control") 34 data := fmt.Sprintf("%d %d %s", eventfd.Fd(), evFile.Fd(), arg) 35 if err := os.WriteFile(eventControlPath, []byte(data), 0o700); err != nil { 36 eventfd.Close() 37 evFile.Close() 38 return nil, err 39 } 40 ch := make(chan struct{}) 41 go func() { 42 defer func() { 43 eventfd.Close() 44 evFile.Close() 45 close(ch) 46 }() 47 buf := make([]byte, 8) 48 for { 49 if _, err := eventfd.Read(buf); err != nil { 50 return 51 } 52 // When a cgroup is destroyed, an event is sent to eventfd. 53 // So if the control path is gone, return instead of notifying. 54 if _, err := os.Lstat(eventControlPath); os.IsNotExist(err) { 55 return 56 } 57 ch <- struct{}{} 58 } 59 }() 60 return ch, nil 61 } 62 63 // notifyOnOOM returns channel on which you can expect event about OOM, 64 // if process died without OOM this channel will be closed. 65 func notifyOnOOM(dir string) (<-chan struct{}, error) { 66 if dir == "" { 67 return nil, errors.New("memory controller missing") 68 } 69 70 return registerMemoryEvent(dir, "memory.oom_control", "") 71 } 72 73 func notifyMemoryPressure(dir string, level PressureLevel) (<-chan struct{}, error) { 74 if dir == "" { 75 return nil, errors.New("memory controller missing") 76 } 77 78 if level > CriticalPressure { 79 return nil, fmt.Errorf("invalid pressure level %d", level) 80 } 81 82 levelStr := []string{"low", "medium", "critical"}[level] 83 return registerMemoryEvent(dir, "memory.pressure_level", levelStr) 84 }