From cc232c47073bfa0e9149a714edb857fe58937314 Mon Sep 17 00:00:00 2001 From: Vishnu Kannan Date: Wed, 26 Aug 2015 16:37:24 -0700 Subject: [PATCH] Adding oom_score_adj as a container config param. Signed-off-by: Vishnu Kannan --- libcontainer/configs/config.go | 6 ++++ libcontainer/init_linux.go | 7 ++++ libcontainer/integration/exec_test.go | 42 ++++++++++++++++++++++ libcontainer/integration/execin_test.go | 47 +++++++++++++++++++++++++ libcontainer/setns_init_linux.go | 3 ++ libcontainer/standard_init_linux.go | 4 +++ 6 files changed, 109 insertions(+) diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go index 42aafbf0..1179f63c 100644 --- a/libcontainer/configs/config.go +++ b/libcontainer/configs/config.go @@ -127,6 +127,12 @@ type Config struct { // If Rlimits are not set, the container will inherit rlimits from the parent process Rlimits []Rlimit `json:"rlimits"` + // OomScoreAdj specifies the adjustment to be made by the kernel when calculating oom scores + // for a process. Valid values are between the range [-1000, '1000'], where processes with + // higher scores are preferred for being killed. + // More information about kernel oom score calculation here: https://lwn.net/Articles/317814/ + OomScoreAdj int `json:"oom_score_adj"` + // AdditionalGroups specifies the gids that should be added to supplementary groups // in addition to those that the user belongs to. AdditionalGroups []string `json:"additional_groups"` diff --git a/libcontainer/init_linux.go b/libcontainer/init_linux.go index 16377ff6..6854a2d9 100644 --- a/libcontainer/init_linux.go +++ b/libcontainer/init_linux.go @@ -5,7 +5,9 @@ package libcontainer import ( "encoding/json" "fmt" + "io/ioutil" "os" + "strconv" "strings" "syscall" @@ -238,6 +240,11 @@ func setupRlimits(config *configs.Config) error { return nil } +func setOomScoreAdj(oomScoreAdj int) error { + path := "/proc/self/oom_score_adj" + return ioutil.WriteFile(path, []byte(strconv.Itoa(oomScoreAdj)), 0700) +} + // killCgroupProcesses freezes then iterates over all the processes inside the // manager's cgroups sending a SIGKILL to each process then waiting for them to // exit. diff --git a/libcontainer/integration/exec_test.go b/libcontainer/integration/exec_test.go index e61c32b2..26ce3a8a 100644 --- a/libcontainer/integration/exec_test.go +++ b/libcontainer/integration/exec_test.go @@ -878,3 +878,45 @@ func TestMountCgroupRW(t *testing.T) { } } } + +func TestOomScoreAdj(t *testing.T) { + if testing.Short() { + return + } + root, err := newTestRoot() + ok(t, err) + defer os.RemoveAll(root) + + rootfs, err := newRootfs() + ok(t, err) + defer remove(rootfs) + + config := newTemplateConfig(rootfs) + config.OomScoreAdj = 200 + + factory, err := libcontainer.New(root, libcontainer.Cgroupfs) + ok(t, err) + + container, err := factory.Create("test", config) + ok(t, err) + defer container.Destroy() + + var stdout bytes.Buffer + pconfig := libcontainer.Process{ + Args: []string{"sh", "-c", "cat /proc/self/oom_score_adj"}, + Env: standardEnvironment, + Stdin: nil, + Stdout: &stdout, + } + err = container.Start(&pconfig) + ok(t, err) + + // Wait for process + waitProcess(&pconfig, t) + outputOomScoreAdj := strings.TrimSpace(string(stdout.Bytes())) + + // Check that the oom_score_adj matches the value that was set as part of config. + if outputOomScoreAdj != strconv.Itoa(config.OomScoreAdj) { + t.Fatalf("Expected oom_score_adj %d; got %q", config.OomScoreAdj, outputOomScoreAdj) + } +} diff --git a/libcontainer/integration/execin_test.go b/libcontainer/integration/execin_test.go index d7f2348d..33d7b1cc 100644 --- a/libcontainer/integration/execin_test.go +++ b/libcontainer/integration/execin_test.go @@ -4,6 +4,7 @@ import ( "bytes" "io" "os" + "strconv" "strings" "testing" "time" @@ -332,3 +333,49 @@ func TestExecinPassExtraFiles(t *testing.T) { t.Fatalf("expected second pipe to receive '2', got '%s'", out2) } } + +func TestExecInOomScoreAdj(t *testing.T) { + if testing.Short() { + return + } + rootfs, err := newRootfs() + ok(t, err) + defer remove(rootfs) + config := newTemplateConfig(rootfs) + config.OomScoreAdj = 200 + container, err := newContainer(config) + ok(t, err) + defer container.Destroy() + + stdinR, stdinW, err := os.Pipe() + ok(t, err) + process := &libcontainer.Process{ + Args: []string{"cat"}, + Env: standardEnvironment, + Stdin: stdinR, + } + err = container.Start(process) + stdinR.Close() + defer stdinW.Close() + ok(t, err) + + buffers := newStdBuffers() + ps := &libcontainer.Process{ + Args: []string{"/bin/sh", "-c", "cat /proc/self/oom_score_adj"}, + Env: standardEnvironment, + Stdin: buffers.Stdin, + Stdout: buffers.Stdout, + Stderr: buffers.Stderr, + } + err = container.Start(ps) + ok(t, err) + waitProcess(ps, t) + + stdinW.Close() + waitProcess(process, t) + + out := buffers.Stdout.String() + if oomScoreAdj := strings.TrimSpace(out); oomScoreAdj != strconv.Itoa(config.OomScoreAdj) { + t.Fatalf("expected oomScoreAdj to be %d, got %s", config.OomScoreAdj, oomScoreAdj) + } +} diff --git a/libcontainer/setns_init_linux.go b/libcontainer/setns_init_linux.go index f40a5ac2..2bde44ff 100644 --- a/libcontainer/setns_init_linux.go +++ b/libcontainer/setns_init_linux.go @@ -21,6 +21,9 @@ func (l *linuxSetnsInit) Init() error { if err := setupRlimits(l.config.Config); err != nil { return err } + if err := setOomScoreAdj(l.config.Config.OomScoreAdj); err != nil { + return err + } if l.config.Config.Seccomp != nil { if err := seccomp.InitSeccomp(l.config.Config.Seccomp); err != nil { return err diff --git a/libcontainer/standard_init_linux.go b/libcontainer/standard_init_linux.go index 86f434d0..ec100578 100644 --- a/libcontainer/standard_init_linux.go +++ b/libcontainer/standard_init_linux.go @@ -47,6 +47,10 @@ func (l *linuxStandardInit) Init() error { if err := setupRlimits(l.config.Config); err != nil { return err } + if err := setOomScoreAdj(l.config.Config.OomScoreAdj); err != nil { + return err + } + label.Init() // InitializeMountNamespace() can be executed only for a new mount namespace if l.config.Config.Namespaces.Contains(configs.NEWNS) {