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  }