notify_linux.go 1.9 KB

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