Skip to content

Fixes set memory to unlimited#1127

Merged
hqhq merged 1 commit intoopencontainers:masterfrom
boynux:fix-set-mem-to-unlimited
Feb 16, 2017
Merged

Fixes set memory to unlimited#1127
hqhq merged 1 commit intoopencontainers:masterfrom
boynux:fix-set-mem-to-unlimited

Conversation

@boynux
Copy link
Contributor

@boynux boynux commented Oct 19, 2016

Should set Swap to '-1' (unlimited) if memory is set to unlimited. Otherwise kernel validation fails.
these changes required to fix this issue in Docker project: moby/moby#22578

// Memeory set to '-1' which is unlimited. In this situation we need to update swap to -1
// to avoid kernel validation failures.
if cgroup.Resources.Memory == -1 {
if err := writeFile(path, "memory.memsw.limit_in_bytes", strconv.FormatInt(-1, 10)); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Should we make sure memory.memsw.limit_in_bytes exists first or ignore the ErrNotExist error? I think if the system does not enable memory swap, this file does not exist

Copy link
Contributor Author

@boynux boynux Oct 20, 2016

Choose a reason for hiding this comment

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

Good point, I just followed the convention (see the exiting code), I can add checks if you think it's required. However the getMemoryData is suspicious to fail too.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is also not libcontainer's concern, a value is assigned, libcontainer set it, simple as that. Otherwise we'll end up with another huge validation done by Docker and other high level tools.

// Memeory set to '-1' which is unlimited. In this situation we need to update swap to -1
// to avoid kernel validation failures.
if cgroup.Resources.Memory == -1 {
if err := writeFile(path, "memory.memsw.limit_in_bytes", strconv.FormatInt(-1, 10)); 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.

libcontainer as a low level library usually don't do this kind of thing, it only does what it's told to do.

So if docker want to set memory as -1, it should pass memorySwap as -1 as well, libcontainer only adapt the writing sequence to make the logic right.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree that aggressive validation may add unnecessary overhead the the low level libraries like RunC.

Copy link
Member

@cyphar cyphar Oct 20, 2016

Choose a reason for hiding this comment

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

It's not "unnecessary overhead" that @hqhq is referring to (the overhead of doing a single write / check is not important here). He's talking about libcontainer doing things it wasn't told to do explicitly. libcontainer is meant to be stupid (though clever enough to know when to be clever about it), specifically this means that we in general don't take initiative past what the user has told us to do -- it's not libcontainer's job to change the settings that a user asked for.

If you wanted to idiot-proof this then you could put it inside libcontainer/specconv so that runC makes this easier for users. But then again, I'm not even sure if we should be doing that to be honest.

@boynux
Copy link
Contributor Author

boynux commented Oct 20, 2016

@hqhq @cyphar @coolljt0725 Shall I update the PR or these changes are sufficient?

@hqhq
Copy link
Contributor

hqhq commented Oct 21, 2016

@boynux You should update your PR.

I think a change like this would be sufficient,

diff --git a/libcontainer/cgroups/fs/memory.go b/libcontainer/cgroups/fs/memory.go
index 2a3ccf0..d3ba108 100644
--- a/libcontainer/cgroups/fs/memory.go
+++ b/libcontainer/cgroups/fs/memory.go
@@ -98,7 +98,7 @@ func setKernelMemory(path string, kernelMemoryLimit int64) error {
 func setMemoryAndSwap(path string, cgroup *configs.Cgroup) error {
        // When memory and swap memory are both set, we need to handle the cases
        // for updating container.
-       if cgroup.Resources.Memory != 0 && cgroup.Resources.MemorySwap > 0 {
+       if cgroup.Resources.Memory != 0 && cgroup.Resources.MemorySwap != 0 {
                memoryUsage, err := getMemoryData(path, "")
                if err != nil {
                        return err
@@ -108,6 +108,7 @@ func setMemoryAndSwap(path string, cgroup *configs.Cgroup) error {
                // for memory and swap memory, so it won't fail because the new
                // value and the old value don't fit kernel's validation.
                if memoryUsage.Limit < uint64(cgroup.Resources.MemorySwap) {
+                       // Here includes the case that `Limit == -1 && MemorySwap == -1`
                        if err := writeFile(path, "memory.memsw.limit_in_bytes", strconv.FormatInt(cgroup.Resources.MemorySwap, 10)); err != nil {
                                return err
                        }

@boynux
Copy link
Contributor Author

boynux commented Oct 21, 2016

@hqhq That's actually the first thing I've tried, RunC assumption is that SwapMemory == -1 means skip Swap update! And the proposed changes will break the existing tests. (Unless we want to change the behaviour with this PR) Since it's a known implementation right now I'm not sure about the impact on other depending projects.

The other thing is should I actually check for SwapMemory == -1 at all. Since if user requests for unlimited memory we can implicitly set SwapMemory to '-1' as it will be rejected by Kernel otherwise.

@hqhq
Copy link
Contributor

hqhq commented Oct 22, 2016

@boynux There's no such assumption in RunC AFAIK and there shouldn't be. If only test case assumes that, you can change it. That might break Docker and that's something you should fix in moby/moby#22578.

Check for SwapMemory == -1 or not doesn't matter in this case, I'm ok with either. Usually RunC shouldn't do much validations if kernel can do the same thing, unless error message from kernel is vague and we want it to be clear. If SwapMemory is negative and it's not -1, I don't think there are much valuable information we can give than Invalid argument which is given by kernel.

@boynux boynux force-pushed the fix-set-mem-to-unlimited branch 2 times, most recently from 1e7b56a to f4d035a Compare October 23, 2016 08:02
@boynux
Copy link
Contributor Author

boynux commented Oct 23, 2016

@hqhq Now it should work as expected!

  • If memory and swap both set to '-1' will be set both to unlimited.
  • If wrong configuration provided (for example unlimited memory and limited swap) will fail with kernel error.
  • '0' means no update (was '-1' for swap before)

For Docker there should not be any issue, but I'm not sure about other clients that may rely on previous behavior!

@LK4D4
Copy link
Contributor

LK4D4 commented Nov 10, 2016

@hqhq is this okay for you?

const (
memoryBefore = 314572800 // 300M
memoryswapBefore = 629145600 // 600M
memoryAfter = 0 // Unlimited
Copy link
Contributor

Choose a reason for hiding this comment

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

Why 0 presents unlimited? Doesn't sound reasonable. Note that unit test didn't use real cgroup fs for test, so you better add an integration test to cover this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@hqhq Ok, I'll try to add integration test to cover this use case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@hqhq Ok I managed to write the test there is only one issue and that is how to get host memory. I checked bats and could not find a clean way (except global vars) to get the hosts total memory. If global values are find I can push the changes, otherwise please give me a direction.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for your reply.
Correct, my problem was the host machine total amount of RAM :)
I found a clean solution. Will push my changes when I did some clean ups.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@hqhq I'm done with integration tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems memory swap is not enabled in build server kernel. I'm going to put a check there.

@boynux boynux force-pushed the fix-set-mem-to-unlimited branch 7 times, most recently from 69b4300 to bca1765 Compare December 1, 2016 14:22
@boynux
Copy link
Contributor Author

boynux commented Dec 9, 2016

@hqhq Any updates here?

@hqhq
Copy link
Contributor

hqhq commented Dec 10, 2016

Super busy these days, will take a look next week, sorry for the late.

update.go Outdated
dest *uint64
}{
{"memory", r.Memory.Limit},
{"memory-swap", r.Memory.Swap},
Copy link
Contributor

Choose a reason for hiding this comment

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

The rule that '-1' means reset applies to all *.limit_in_bytes, so you can do this for all memory related limitations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

By that do you mean kernel-memory and kernel-memory-tcp and memory-reservation also?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep.

sed -i "s/\(\"resources\": {\)/\1\n${DATA}/" ${BUSYBOX_BUNDLE}/config.json

# run a detached busybox to work with
runc run -d --console /dev/pts/ptmx test_cgroups_unlimited_mem
Copy link
Contributor

Choose a reason for hiding this comment

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

Needs update to use --console-socket.

@@ -0,0 +1,73 @@
#!/usr/bin/env bats
Copy link
Contributor

Choose a reason for hiding this comment

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

You can add this case in update.bats, no need to create a new file.

memoryAfter = 524288000 // 500M
memoryswapBefore = 629145600 // 600M
memoryswapAfter = 629145600 // 600M
memoryswapAfter = 0 // Unlimited
Copy link
Contributor

Choose a reason for hiding this comment

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

We took negative value means not change in unit test, and that's the point of this unit test, since that's not true after your PR, you can remove that unit test, and no need to add another.

Copy link
Contributor

Choose a reason for hiding this comment

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

@boynux Can you check my comment here and amend your unit test changes?

[ "$status" -eq 0 ]

# Get System memory limit
SYSTEM_MEMORY=$(cat "${CGROUP_SYSTEM_MEMORY}/memory.limit_in_bytes")
Copy link
Contributor

Choose a reason for hiding this comment

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

You can do the test at line 125 so you don't have to reset to initial test value again.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@hqhq I updated the tests please have a look if everything if fine I'll merge the commits and do a master rebase. Thank you

@boynux boynux force-pushed the fix-set-mem-to-unlimited branch 3 times, most recently from cbec8e1 to d2999da Compare February 9, 2017 17:23
// -1 means unlimited memory
if cgroup.Resources.Memory == -1 {
// Only set swap if it's enbled in kernel
if _, err := readFile(path, "memory.memsw.limit_in_bytes"); 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.

You can use os.Stat, otherwise looks good to me.

@hqhq
Copy link
Contributor

hqhq commented Feb 10, 2017

Please squash your commits.

@boynux boynux force-pushed the fix-set-mem-to-unlimited branch from 6b6e69c to b218edc Compare February 10, 2017 09:11
@boynux
Copy link
Contributor Author

boynux commented Feb 10, 2017

Done!

@boynux
Copy link
Contributor Author

boynux commented Feb 10, 2017

@hqhq Should I rebase my changes with master?

Note: I just did it on my machine and managed to pass all the tests. If you want I can push it now.

@hqhq
Copy link
Contributor

hqhq commented Feb 11, 2017

@boynux You don't have to as long as there's no conflict with master.

LGTM

Thanks for your patience.

Approved with PullApprove

@boynux
Copy link
Contributor Author

boynux commented Feb 13, 2017

How should we get the second approval?

const cgroupKernelMemoryLimit = "memory.kmem.limit_in_bytes"
const (
cgroupKernelMemoryLimit = "memory.kmem.limit_in_bytes"
cgroupMemorySwap = "memory.memsw.limit_in_bytes"
Copy link
Contributor

Choose a reason for hiding this comment

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

this should also be cgroupMemorySwapLimit just for consistency ?

@@ -108,28 +121,28 @@ func setMemoryAndSwap(path string, cgroup *configs.Cgroup) error {
// for memory and swap memory, so it won't fail because the new
// value and the old value don't fit kernel's validation.
if memoryUsage.Limit < uint64(cgroup.Resources.MemorySwap) {
Copy link
Contributor

Choose a reason for hiding this comment

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

when reset memory swap to -1 while the container still have memory.limit and memory.memsw.limit, this will fail and we will set memory limit to -1 while memsw is still in place. I think that will cause an error.
We should check if we want to reset memory / memoryswap here and change the order accordingly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we set memsw to -1 without changing memlimit I don't think it fails, since memory will be '0' (means no change) and it just sets memsw to -1 (which means unlimited)

Did I miss something here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

runc update test_update --memory-swap -1
 [ "$status" -eq 0 ]

This test should cover what you just said, doesn't it? I just added it the test suite and it passed!

Copy link
Contributor Author

@boynux boynux Feb 13, 2017

Choose a reason for hiding this comment

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

@dqminh Changed const name to be more "consistent" and also added that test just in case ;)

Copy link
Contributor

Choose a reason for hiding this comment

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

ah i see what's happening here. currentmemLimit < uint64(memSwap) with memSwap = -1 is practically always true because uint64(-1) is basically MaxUint64.

That being said, do you think its clearer to just add if cgroup.Resources.MemorySwap == -1 || memoryUsage.Limit < uint64(cgroup.Resources.MemorySwap) so we dont depend on this uint64(-1) casting. I dont really like it.

@boynux boynux force-pushed the fix-set-mem-to-unlimited branch 2 times, most recently from 9a30595 to 61c5f4e Compare February 13, 2017 21:09
@boynux
Copy link
Contributor Author

boynux commented Feb 14, 2017

@dqminh ping!

@@ -108,28 +121,28 @@ func setMemoryAndSwap(path string, cgroup *configs.Cgroup) error {
// for memory and swap memory, so it won't fail because the new
// value and the old value don't fit kernel's validation.
if memoryUsage.Limit < uint64(cgroup.Resources.MemorySwap) {
Copy link
Contributor

Choose a reason for hiding this comment

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

ah i see what's happening here. currentmemLimit < uint64(memSwap) with memSwap = -1 is practically always true because uint64(-1) is basically MaxUint64.

That being said, do you think its clearer to just add if cgroup.Resources.MemorySwap == -1 || memoryUsage.Limit < uint64(cgroup.Resources.MemorySwap) so we dont depend on this uint64(-1) casting. I dont really like it.

check_cgroup_value $CGROUP_MEMORY "memory.memsw.limit_in_bytes" 96468992
fi

if [ -f "$CGROUP_MEMORY/memory.memsw.limit_in_bytes" ]; then
Copy link
Contributor

Choose a reason for hiding this comment

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

this test can be merge into the conditional above right, make it clearer to see that we have an existing memory-swap limit too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll merge them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For casting -1 to uint64 I think that's a defined behavior and pretty explanatory, but since you missed it on the first try it says that can be more intuitive. being said that I'll also add a check explicitly for -1.

Kernel validation fails if memory set to -1 which is unlimited but
swap is not set so.

Signed-off-by: Mohammad Arab <[email protected]>
@boynux boynux force-pushed the fix-set-mem-to-unlimited branch from 61c5f4e to 18ebc51 Compare February 15, 2017 07:12
@boynux
Copy link
Contributor Author

boynux commented Feb 15, 2017

Ping @hqhq @dqminh

Changes are done, please check again.

@dqminh
Copy link
Contributor

dqminh commented Feb 15, 2017

LGTM

Approved with PullApprove

@boynux
Copy link
Contributor Author

boynux commented Feb 15, 2017

@hqhq sorry for the trouble but we need your approval again.

@hqhq
Copy link
Contributor

hqhq commented Feb 16, 2017

LGTM

Approved with PullApprove

@hqhq hqhq merged commit 6b1d0e7 into opencontainers:master Feb 16, 2017
kolyshkin added a commit to kolyshkin/runc that referenced this pull request Apr 2, 2020
In cgroupv1, command `runc update $ID --memory -1` sets both
memory and memory+swap to -1 (aka unlimited). This was introduced
by commit 18ebc51 (Reset Swap when memory is set to unlimited,
Oct 19 2016).

This is not the case for cgroupv2. Fix it.

References:
 - opencontainers#1127
 - moby/moby#22578

Signed-off-by: Kir Kolyshkin <[email protected]>
kolyshkin added a commit to kolyshkin/runc that referenced this pull request Apr 2, 2020
In cgroupv1, command `runc update $ID --memory -1` sets both
memory and memory+swap to -1 (aka unlimited). This was introduced
by commit 18ebc51 (Reset Swap when memory is set to unlimited,
Oct 19 2016).

This is not the case for cgroupv2. Fix it.

References:
 - opencontainers#1127
 - moby/moby#22578

Signed-off-by: Kir Kolyshkin <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants