Skip to content

remove cgroup path recursively in cgroup v2#2412

Merged
mrunalp merged 1 commit into
opencontainers:masterfrom
lifubang:removecgpath
May 27, 2020
Merged

remove cgroup path recursively in cgroup v2#2412
mrunalp merged 1 commit into
opencontainers:masterfrom
lifubang:removecgpath

Conversation

@lifubang
Copy link
Copy Markdown
Member

fix #2403

If we have subcgroups in a cgroup path, we should remove it recursively.

Signed-off-by: lifubang lifubang@acmcoder.com

Comment thread libcontainer/cgroups/fs2/fs2.go Outdated
@kolyshkin
Copy link
Copy Markdown
Contributor

We need a test case -- create sub-cgroups, check they are actually removed.

@lifubang
Copy link
Copy Markdown
Member Author

We need a test case -- create sub-cgroups, check they are actually removed.

I add a test case in delete.bats, it test success in my computer(Fedora 31).

root@acm:/opt/mygo/src/github.com/opencontainers/runc# make localintegration TESTPATH=/delete.bats
go build "-mod=vendor" -buildmode=pie  -tags "seccomp selinux apparmor" -ldflags "-X main.gitCommit="15ec27f390f377780e1c332d24588e7e2ba7d3d3-dirty" -X main.version=1.0.0-rc10+dev " -o runc .
go build "-mod=vendor" -buildmode=pie  -tags "seccomp selinux apparmor" -ldflags "-X main.gitCommit="15ec27f390f377780e1c332d24588e7e2ba7d3d3-dirty" -X main.version=1.0.0-rc10+dev " -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
bats -t tests/integration/delete.bats
1..4
ok 1 runc delete
ok 2 runc delete --force
ok 3 runc delete --force with subcgroups in v2
ok 4 runc delete --force ignore not exist
root@acm:/opt/mygo/src/github.com/opencontainers/runc#

But failed in CI:

not ok 24 runc delete --force with subcgroups in v2
# (in test file tests/integration/delete.bats, line 74)
#   `[ "$status" -eq 0 ]' failed

the line 72-74 is:

# run busybox detached
  runc run -d --console-socket $CONSOLE_SOCKET test_busybox
  [ "$status" -eq 0 ]

So, we can't create rw cgroup mount in CI?
@kolyshkin PTAL, thanks.

Comment thread tests/integration/delete.bats Outdated
Comment thread tests/integration/delete.bats Outdated
Comment thread tests/integration/delete.bats Outdated
Comment thread tests/integration/delete.bats Outdated
@lifubang lifubang force-pushed the removecgpath branch 2 times, most recently from 9f06248 to 2b1da44 Compare May 20, 2020 02:49
@lifubang lifubang marked this pull request as ready for review May 20, 2020 03:23
Comment thread libcontainer/cgroups/utils.go Outdated
@AkihiroSuda
Copy link
Copy Markdown
Member

AkihiroSuda commented May 20, 2020

LGTM, though I expect the function name to be more descriptive

Approved with PullApprove

@AkihiroSuda
Copy link
Copy Markdown
Member

AkihiroSuda commented May 20, 2020

LGTM

Approved with PullApprove

Comment thread libcontainer/cgroups/utils.go Outdated
@AkihiroSuda
Copy link
Copy Markdown
Member

AkihiroSuda commented May 20, 2020

LGTM

Approved with PullApprove

Comment thread libcontainer/cgroups/utils.go Outdated
// Because there may be subcgroups in it.
func RemovePathUnified(path string) error {
infos, err := ioutil.ReadDir(path)
if err == nil {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 nit:

if err != nil {
   if os.IsNotExist(err) {
      err = nil
   }
   return err
}

so

  1. the rest of function will be less indented
  2. we won't return ENOENT.

Comment thread libcontainer/cgroups/utils.go Outdated
for _, info := range infos {
if info.IsDir() {
// We should remove subcgroups dir first
if err = RemovePathUnified(filepath.Join(path, info.Name())); err != nil && !os.IsNotExist(err) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

with the above suggestion, we won't have to check for IsNotExist here.

Comment thread libcontainer/cgroups/utils.go Outdated
}
}
}
if err == nil || os.IsNotExist(err) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

...and here

Comment thread libcontainer/cgroups/utils.go Outdated
}
}
if err == nil || os.IsNotExist(err) {
return os.Remove(path)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should avoid returning ENOENT from here as well (similar to what os.RemoveAll() does.

Comment thread libcontainer/cgroups/utils.go Outdated
// RemovePathUnified aims to remove cgroup path recursively
// Because there may be subcgroups in it.
func RemovePathUnified(path string) error {
infos, err := ioutil.ReadDir(path)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

OK, I think we can optimize things for the common case here:

err := os.Remove(path)
if err == nil || os.IsNotExist(err) {
  return nil
}
// remove subdirectories first
infos, err :=  ioutil.ReadDir(path)
....

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I still think it's a pretty good optimization. Will follow up.

Comment thread libcontainer/cgroups/utils.go Outdated

// RemovePathUnified aims to remove cgroup path recursively
// Because there may be subcgroups in it.
func RemovePathUnified(path string) error {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since we're only using this function from fs2, why don't we move it to fs2 and make it private.

Maybe rename to removeCgroupPath or so.

@kolyshkin
Copy link
Copy Markdown
Contributor

left an optimization suggestion for RemovePathUnified, and a few nits.

Found another problem as well -- the test case added works even without the changes. I think we need to make sure the test case fails, then make it work, otherwise it's not clear what are we fixing here. Maybe the sub-cgroup need to have some limits set? IDK.

$ git fetch lifubang 
...
$ git checkout removecgpath
Branch 'removecgpath' set up to track remote branch 'removecgpath' from 'lifubang'.
Switched to a new branch 'removecgpath'

$ git show libcontainer/ | patch -p1 -R # revert the "fixing" part
patching file libcontainer/cgroups/fs2/fs2.go
patching file libcontainer/cgroups/utils.go

$ sudo -s
# RUNC_USE_SYSTEMD=yes make localintegration TESTPATH=/delete.bats
go build "-mod=vendor" -buildmode=pie  -tags "seccomp selinux apparmor" -ldflags "-X main.gitCommit="430a1e88c359974ba441cf75b619626cece7c261-dirty" -X main.version=1.0.0-rc10+dev " -o runc .
go build "-mod=vendor" -buildmode=pie  -tags "seccomp selinux apparmor" -ldflags "-X main.gitCommit="430a1e88c359974ba441cf75b619626cece7c261-dirty" -X main.version=1.0.0-rc10+dev " -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
bats -t tests/integration/delete.bats
1..4
ok 1 runc delete
ok 2 runc delete --force
ok 3 runc delete --force ignore not exist
ok 4 runc delete --force in cgroupv2 with subcgroups

Signed-off-by: lifubang <lifubang@acmcoder.com>
@AkihiroSuda
Copy link
Copy Markdown
Member

AkihiroSuda commented May 26, 2020

LGTM

Approved with PullApprove

@mrunalp
Copy link
Copy Markdown
Contributor

mrunalp commented May 27, 2020

LGTM

Approved with PullApprove

@mrunalp mrunalp merged commit 4f0bdaf into opencontainers:master May 27, 2020
@lifubang lifubang deleted the removecgpath branch May 28, 2020 01:31
# create subcgroups
cat <<EOF > nest.sh
cd /sys/fs/cgroup
for f in \$(cat cgroup.controllers); do echo +\$f > cgroup.subtree_control; done
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This gives an error on my system when trying to enable io controller.

Most probably just +pids is enough (and it won't error).


# create a sub process
__runc exec -d test_busybox sleep 1d
[ "$status" -eq 0 ]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

__runc does not set $status, so this check is checking and older status.

[ "$status" -eq 0 ]

# find the pid of sleep
pid=$(__runc exec test_busybox ps -a | grep 1d | awk '{print $1}')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

busybox ps does not have -a, but fortunately it is ignored.

cat cgroup.threads
EOF
cat nest.sh | runc exec test_busybox sh
[[ ${output} =~ [0-9]+ ]]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

you don't check the exit status


# create subcgroups
cat <<EOF > nest.sh
cd /sys/fs/cgroup
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

makes sense to add set -x -u -e


# check cgroup.threads' value
runc exec test_busybox cat /sys/fs/cgroup/foo/cgroup.threads
[[ ${output} =~ [0-9]+ ]]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We have already checked it above (the output of nest.sh script), looks like this check is redundant.

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.

cgroup2: Manager.Destroy() should recursively delete subgroups

4 participants