notify_linux.go 2.1 KB

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