Skip to content

Commit f89dae9

Browse files
committed
fix runc events error in cgroup v2
Signed-off-by: lifubang <[email protected]>
1 parent b19f9ce commit f89dae9

File tree

3 files changed

+142
-8
lines changed

3 files changed

+142
-8
lines changed

libcontainer/container_linux.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,13 @@ func (c *linuxContainer) NotifyOOM() (<-chan struct{}, error) {
648648
if c.config.RootlessCgroups {
649649
logrus.Warn("getting OOM notifications may fail if you don't have the full access to cgroups")
650650
}
651+
if cgroups.IsCgroup2UnifiedMode() {
652+
path, err := c.cgroupManager.GetUnifiedPath()
653+
if err != nil {
654+
return nil, err
655+
}
656+
return notifyOnOOMV2(path)
657+
}
651658
return notifyOnOOM(c.cgroupManager.GetPaths())
652659
}
653660

libcontainer/notify_linux_v2.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// +build linux
2+
3+
package libcontainer
4+
5+
import (
6+
"io/ioutil"
7+
"os"
8+
"path/filepath"
9+
"strconv"
10+
"strings"
11+
"unsafe"
12+
13+
"github.com/sirupsen/logrus"
14+
"golang.org/x/sys/unix"
15+
)
16+
17+
func getOOMCount(path string) (int, error) {
18+
if _, err := os.Lstat(path); err != nil {
19+
return 0, err
20+
}
21+
22+
content, err := ioutil.ReadFile(path)
23+
if err != nil {
24+
return 0, err
25+
}
26+
27+
oomCount := 0
28+
lines := strings.Split(string(content), "\n")
29+
for _, line := range lines {
30+
arr := strings.Split(line, " ")
31+
if len(arr) == 2 && (arr[0] == "oom" || arr[0] == "oom_kill") {
32+
count, err := strconv.Atoi(arr[1])
33+
if err == nil && count > oomCount {
34+
oomCount = count
35+
}
36+
}
37+
}
38+
return oomCount, nil
39+
}
40+
41+
func getPopulated(path string) (int, error) {
42+
if _, err := os.Lstat(path); err != nil {
43+
return 0, err
44+
}
45+
46+
content, err := ioutil.ReadFile(path)
47+
if err != nil {
48+
return 0, err
49+
}
50+
lines := strings.Split(string(content), "\n")
51+
for _, line := range lines {
52+
arr := strings.Split(line, " ")
53+
if len(arr) == 2 && arr[0] == "populated" {
54+
return strconv.Atoi(arr[1])
55+
}
56+
}
57+
return 0, nil
58+
}
59+
60+
func registerMemoryEventV2(cgDir string, evName string, cgEvName string) (<-chan struct{}, error) {
61+
eventControlPath := filepath.Join(cgDir, evName)
62+
cgEvPath := filepath.Join(cgDir, cgEvName)
63+
fd, err := unix.InotifyInit()
64+
if err != nil {
65+
return nil, err
66+
}
67+
// watching oom kill
68+
_, err = unix.InotifyAddWatch(fd, eventControlPath, unix.IN_MODIFY)
69+
if err != nil {
70+
unix.Close(fd)
71+
return nil, err
72+
}
73+
// Because no `unix.IN_DELETE|unix.IN_DELETE_SELF` event for cgroup file system, so watching all process exited
74+
_, err = unix.InotifyAddWatch(fd, cgEvPath, unix.IN_MODIFY)
75+
if err != nil {
76+
unix.Close(fd)
77+
return nil, err
78+
}
79+
ch := make(chan struct{})
80+
go func() {
81+
var (
82+
buffer [unix.SizeofInotifyEvent + unix.PathMax]byte
83+
offset uint32
84+
)
85+
defer func() {
86+
unix.Close(fd)
87+
close(ch)
88+
}()
89+
90+
for {
91+
n, err := unix.Read(fd, buffer[:])
92+
if err != nil {
93+
logrus.Warn(err)
94+
return
95+
}
96+
offset = 0
97+
for offset <= uint32(n-unix.SizeofInotifyEvent) {
98+
rawEvent := (*unix.InotifyEvent)(unsafe.Pointer(&buffer[offset]))
99+
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buffer[unix.SizeofInotifyEvent]))
100+
eventName := strings.TrimRight(string(bytes[0:rawEvent.Len]), "\000")
101+
offset += unix.SizeofInotifyEvent + uint32(rawEvent.Len)
102+
if rawEvent.Mask&unix.IN_MODIFY == unix.IN_MODIFY {
103+
switch eventName {
104+
case evName:
105+
oom, err := getOOMCount(eventControlPath)
106+
if err != nil || oom > 0 {
107+
ch <- struct{}{}
108+
return
109+
}
110+
case cgEvName:
111+
pids, err := getPopulated(cgEvPath)
112+
if err != nil || pids == 0 {
113+
return
114+
}
115+
}
116+
}
117+
}
118+
}
119+
}()
120+
return ch, nil
121+
}
122+
123+
// notifyOnOOMV2 returns channel on which you can expect event about OOM,
124+
// if process died without OOM this channel will be closed.
125+
func notifyOnOOMV2(path string) (<-chan struct{}, error) {
126+
return registerMemoryEventV2(path, "memory.events", "cgroup.events")
127+
}

tests/integration/events.bats

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ function teardown() {
1313

1414
@test "events --stats" {
1515
# XXX: currently cgroups require root containers.
16-
# TODO: support cgroup v2 memory.events
17-
requires root cgroups_v1
16+
requires root
17+
init_cgroup_paths
1818

1919
# run busybox detached
2020
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
@@ -29,8 +29,8 @@ function teardown() {
2929

3030
@test "events --interval default " {
3131
# XXX: currently cgroups require root containers.
32-
# TODO: support cgroup v2 memory.events
33-
requires root cgroups_v1
32+
requires root
33+
init_cgroup_paths
3434

3535
# run busybox detached
3636
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
@@ -57,8 +57,8 @@ function teardown() {
5757

5858
@test "events --interval 1s " {
5959
# XXX: currently cgroups require root containers.
60-
# TODO: support cgroup v2 memory.events
61-
requires root cgroups_v1
60+
requires root
61+
init_cgroup_paths
6262

6363
# run busybox detached
6464
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
@@ -84,8 +84,8 @@ function teardown() {
8484

8585
@test "events --interval 100ms " {
8686
# XXX: currently cgroups require root containers.
87-
# TODO: support cgroup v2 memory.events
88-
requires root cgroups_v1
87+
requires root
88+
init_cgroup_paths
8989

9090
# run busybox detached
9191
runc run -d --console-socket $CONSOLE_SOCKET test_busybox

0 commit comments

Comments
 (0)