2014-06-12 16:15:24 +08:00
|
|
|
// +build linux
|
|
|
|
|
2015-01-27 20:54:19 +08:00
|
|
|
package libcontainer
|
2014-06-12 16:15:24 +08:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
2015-01-27 20:54:19 +08:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2014-06-12 16:15:24 +08:00
|
|
|
"syscall"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestNotifyOnOOM(t *testing.T) {
|
2015-01-27 20:54:19 +08:00
|
|
|
memoryPath, err := ioutil.TempDir("", "testnotifyoom-")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
oomPath := filepath.Join(memoryPath, "memory.oom_control")
|
|
|
|
eventPath := filepath.Join(memoryPath, "cgroup.event_control")
|
|
|
|
if err := ioutil.WriteFile(oomPath, []byte{}, 0700); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if err := ioutil.WriteFile(eventPath, []byte{}, 0700); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2014-06-12 16:15:24 +08:00
|
|
|
var eventFd, oomControlFd int
|
2015-02-07 13:12:27 +08:00
|
|
|
paths := map[string]string{
|
|
|
|
"memory": memoryPath,
|
2015-01-27 20:54:19 +08:00
|
|
|
}
|
2015-02-07 13:12:27 +08:00
|
|
|
ooms, err := NotifyOnOOM(paths)
|
2014-06-12 16:15:24 +08:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal("expected no error, got:", err)
|
|
|
|
}
|
|
|
|
|
2015-01-27 20:54:19 +08:00
|
|
|
data, err := ioutil.ReadFile(eventPath)
|
2014-06-12 16:15:24 +08:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal("couldn't read event control file:", err)
|
|
|
|
}
|
|
|
|
|
2015-01-27 20:54:19 +08:00
|
|
|
if _, err := fmt.Sscanf(string(data), "%d %d", &eventFd, &oomControlFd); err != nil {
|
2014-06-12 16:15:24 +08:00
|
|
|
t.Fatalf("invalid control data %q: %s", data, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// re-open the eventfd
|
|
|
|
efd, err := syscall.Dup(eventFd)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("unable to reopen eventfd:", err)
|
|
|
|
}
|
|
|
|
defer syscall.Close(efd)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("unable to dup event fd:", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := make([]byte, 8)
|
|
|
|
binary.LittleEndian.PutUint64(buf, 1)
|
|
|
|
|
|
|
|
if _, err := syscall.Write(efd, buf); err != nil {
|
|
|
|
t.Fatal("unable to write to eventfd:", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-ooms:
|
|
|
|
case <-time.After(100 * time.Millisecond):
|
|
|
|
t.Fatal("no notification on oom channel after 100ms")
|
|
|
|
}
|
|
|
|
|
|
|
|
// simulate what happens when a cgroup is destroyed by cleaning up and then
|
|
|
|
// writing to the eventfd.
|
2015-01-27 20:54:19 +08:00
|
|
|
if err := os.RemoveAll(memoryPath); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2014-06-12 16:15:24 +08:00
|
|
|
if _, err := syscall.Write(efd, buf); err != nil {
|
|
|
|
t.Fatal("unable to write to eventfd:", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// give things a moment to shut down
|
|
|
|
select {
|
|
|
|
case _, ok := <-ooms:
|
|
|
|
if ok {
|
|
|
|
t.Fatal("expected no oom to be triggered")
|
|
|
|
}
|
|
|
|
case <-time.After(100 * time.Millisecond):
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, _, err := syscall.Syscall(syscall.SYS_FCNTL, uintptr(oomControlFd), syscall.F_GETFD, 0); err != syscall.EBADF {
|
|
|
|
t.Error("expected oom control to be closed")
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, _, err := syscall.Syscall(syscall.SYS_FCNTL, uintptr(eventFd), syscall.F_GETFD, 0); err != syscall.EBADF {
|
|
|
|
t.Error("expected event fd to be closed")
|
|
|
|
}
|
|
|
|
}
|