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