Skip to content

Commit

Permalink
fix runc events error in cgroup v2
Browse files Browse the repository at this point in the history
Signed-off-by: lifubang <[email protected]>
  • Loading branch information
lifubang committed Apr 25, 2020
1 parent dbe44cb commit d326aad
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 0 deletions.
7 changes: 7 additions & 0 deletions libcontainer/container_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,13 @@ func (c *linuxContainer) NotifyOOM() (<-chan struct{}, error) {
if c.config.RootlessCgroups {
logrus.Warn("getting OOM notifications may fail if you don't have the full access to cgroups")
}
if cgroups.IsCgroup2UnifiedMode() {
path, err := c.cgroupManager.GetUnifiedPath()
if err != nil {
return nil, err
}
return notifyOnOOMV2(path)
}
return notifyOnOOM(c.cgroupManager.GetPaths())
}

Expand Down
101 changes: 101 additions & 0 deletions libcontainer/notify_linux_v2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// +build linux

package libcontainer

import (
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"unsafe"

"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)

func getOOMCount(path string) (int, error) {
// When a cgroup is destroyed, an event is sent to eventfd.
// So if the control path is gone, return instead of notifying.
if _, err := os.Lstat(path); err != nil {
return 0, err
}

content, err := ioutil.ReadFile(path)
if err != nil {
return 0, err
}

oomCount := 0
lines := strings.Split(string(content), "\n")
for _, line := range lines {
arr := strings.Fields(line)
if len(arr) == 2 && (arr[0] == "oom" || arr[0] == "oom_kill") {
count, err := strconv.Atoi(arr[1])
if err == nil && count > oomCount {
oomCount = count
}
}
}
return oomCount, nil
}

func registerMemoryEventV2(cgDir string, evName string) (<-chan struct{}, error) {
eventControlPath := filepath.Join(cgDir, evName)
fd, err := unix.InotifyInit()
if err != nil {
return nil, err
}
_, err = unix.InotifyAddWatch(fd, eventControlPath, unix.IN_MODIFY|unix.IN_DELETE|unix.IN_DELETE_SELF)
if err != nil {
unix.Close(fd)
return nil, err
}
ch := make(chan struct{})
go func() {
var (
buf [unix.SizeofInotifyEvent * 10]byte
n int
offset uint32
)

defer func() {
unix.Close(fd)
close(ch)
}()

for {
n, err = unix.Read(fd, buf[:])
if err != nil {
logrus.Warn(err)
return
}
offset = 0
for offset <= uint32(n-unix.SizeofInotifyEvent) {
raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
mask := uint32(raw.Mask)
nameLen := uint32(raw.Len)
offset += unix.SizeofInotifyEvent + nameLen
if mask&unix.IN_MODIFY > 0 {
oomCount, err := getOOMCount(eventControlPath)
if err != nil {
return
}
if oomCount > 0 {
ch <- struct{}{}
return
}
} else if mask&unix.IN_DELETE > 0 || mask&unix.IN_DELETE_SELF > 0 {
return
}
}
}
}()
return ch, nil
}

// notifyOnOOMV2 returns channel on which you can expect event about OOM,
// if process died without OOM this channel will be closed.
func notifyOnOOMV2(path string) (<-chan struct{}, error) {
return registerMemoryEventV2(path, "memory.events")
}

0 comments on commit d326aad

Please sign in to comment.