Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove kmem Initialization check while setting memory configuration #962

Merged
merged 1 commit into from
Aug 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 47 additions & 37 deletions libcontainer/cgroups/fs/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ package fs
import (
"bufio"
"fmt"
"math"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"syscall"

"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/system"
)

const (
cgroupKernelMemoryLimit = "memory.kmem.limit_in_bytes"
)

type MemoryGroup struct {
Expand All @@ -34,9 +38,7 @@ func (s *MemoryGroup) Apply(d *cgroupData) (err error) {
return err
}
}
// We have to set kernel memory here, as we can't change it once
// processes have been attached to the cgroup.
if err := s.SetKernelMemory(path, d.config); err != nil {
if err := EnableKernelMemoryAccounting(path); err != nil {
return err
}
}
Expand All @@ -55,38 +57,43 @@ func (s *MemoryGroup) Apply(d *cgroupData) (err error) {
return nil
}
Copy link
Member

@cyphar cyphar Jul 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I say again, we need to update the systemd manager as well. Right now the systemd manager just does this to "set the kernel memory":

func setKernelMemory(c *configs.Cgroup) error {
    path, err := getSubsystemPath(c, "memory")
    if err != nil && !cgroups.IsNotFound(err) {
        return err
    }

    return os.MkdirAll(path, 0755)
}

This code used to call this function (apply_raw.go:SetKernelMemory) directly. It no longer does this, meaning that we're now maintaining two versions of different hacks to enable accounting for cgroups. Can we please fix this (combine the two), or at least verify that it works as intended?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, we should fix up systemd cgroup manager as well.

Copy link
Author

@dubstack dubstack Jul 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cyphar @mrunalp IIUC, currently on systemd we are just creating the memory cgroup and not setting any kmem limit on them during cgroup creation. And it doesn't look like the intended behaviour.

I will update my patch to have the same logic as cgroup fs in the systemd implementation.

The call to SetKernelMemory was removed in the following PR : #790. Maybe @mlaventure would have more idea as to why that change was made.


func (s *MemoryGroup) SetKernelMemory(path string, cgroup *configs.Cgroup) error {
// This has to be done separately because it has special
// constraints (it can only be initialized before setting up a
// hierarchy or adding a task to the cgroups. However, if
// sucessfully initialized, it can be updated anytime afterwards)
if cgroup.Resources.KernelMemory != 0 {
kmemInitialized := false
// Is kmem.limit_in_bytes already set?
kmemValue, err := getCgroupParamUint(path, "memory.kmem.limit_in_bytes")
if err != nil {
return err
}
switch system.GetLongBit() {
case 32:
kmemInitialized = uint32(kmemValue) != uint32(math.MaxUint32)
case 64:
kmemInitialized = kmemValue != uint64(math.MaxUint64)
}
if !kmemInitialized {
// If there's already tasks in the cgroup, we can't change the limit either
tasks, err := getCgroupParamString(path, "tasks")
if err != nil {
return err
}
if tasks != "" {
return fmt.Errorf("cannot set kmem.limit_in_bytes after task have joined this cgroup")
}
}
func EnableKernelMemoryAccounting(path string) error {
// Check if kernel memory is enabled
// We have to limit the kernel memory here as it won't be accounted at all
// until a limit is set on the cgroup and limit cannot be set once the
// cgroup has children, or if there are already tasks in the cgroup.
kernelMemoryLimit := int64(1)
if err := setKernelMemory(path, kernelMemoryLimit); err != nil {
return err
}
kernelMemoryLimit = int64(-1)
if err := setKernelMemory(path, kernelMemoryLimit); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the only consumer of GetLongBit, so you can remove this function in libcontainer/system/sysconfig.go in this PR.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SG. Thanks.

return err
}
return nil
}

if err := writeFile(path, "memory.kmem.limit_in_bytes", strconv.FormatInt(cgroup.Resources.KernelMemory, 10)); err != nil {
return err
func setKernelMemory(path string, kernelMemoryLimit int64) error {
if path == "" {
return fmt.Errorf("no such directory for %s", cgroupKernelMemoryLimit)
}
if !cgroups.PathExists(filepath.Join(path, cgroupKernelMemoryLimit)) {
// kernel memory is not enabled on the system so we should do nothing
return nil
}
if err := ioutil.WriteFile(filepath.Join(path, cgroupKernelMemoryLimit), []byte(strconv.FormatInt(kernelMemoryLimit, 10)), 0700); err != nil {
// Check if the error number returned by the syscall is "EBUSY"
// The EBUSY signal is returned on attempts to write to the
// memory.kmem.limit_in_bytes file if the cgroup has children or
// once tasks have been attached to the cgroup
if pathErr, ok := err.(*os.PathError); ok {
if errNo, ok := pathErr.Err.(syscall.Errno); ok {
if errNo == syscall.EBUSY {
return fmt.Errorf("failed to set %s, because either tasks have already joined this cgroup or it has children", cgroupKernelMemoryLimit)
}
}
}
return fmt.Errorf("failed to write %v to %v: %v", kernelMemoryLimit, cgroupKernelMemoryLimit, err)
}
return nil
}
Expand Down Expand Up @@ -139,15 +146,18 @@ func (s *MemoryGroup) Set(path string, cgroup *configs.Cgroup) error {
return err
}

if err := s.SetKernelMemory(path, cgroup); err != nil {
return err
if cgroup.Resources.KernelMemory != 0 {
if err := setKernelMemory(path, cgroup.Resources.KernelMemory); err != nil {
return err
}
}

if cgroup.Resources.MemoryReservation != 0 {
if err := writeFile(path, "memory.soft_limit_in_bytes", strconv.FormatInt(cgroup.Resources.MemoryReservation, 10)); err != nil {
return err
}
}

if cgroup.Resources.KernelMemoryTCP != 0 {
if err := writeFile(path, "memory.kmem.tcp.limit_in_bytes", strconv.FormatInt(cgroup.Resources.KernelMemoryTCP, 10)); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion libcontainer/cgroups/fs/memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func TestMemorySetKernelMemory(t *testing.T) {

helper.CgroupData.config.Resources.KernelMemory = kernelMemoryAfter
memory := &MemoryGroup{}
if err := memory.SetKernelMemory(helper.CgroupPath, helper.CgroupData.config); err != nil {
if err := memory.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
t.Fatal(err)
}

Expand Down
5 changes: 4 additions & 1 deletion libcontainer/cgroups/systemd/apply_systemd.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,5 +490,8 @@ func setKernelMemory(c *configs.Cgroup) error {
return err
}

return os.MkdirAll(path, 0755)
if err := os.MkdirAll(path, 0755); err != nil {
return err
}
return fs.EnableKernelMemoryAccounting(path)
}
4 changes: 0 additions & 4 deletions libcontainer/system/sysconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,3 @@ import "C"
func GetClockTicks() int {
return int(C.sysconf(C._SC_CLK_TCK))
}

func GetLongBit() int {
return int(C.GetLongBit())
}