diff --git a/pkg/sentry/fsimpl/kernfs/kernfs.go b/pkg/sentry/fsimpl/kernfs/kernfs.go index 163fa1459..c1b6c7248 100644 --- a/pkg/sentry/fsimpl/kernfs/kernfs.go +++ b/pkg/sentry/fsimpl/kernfs/kernfs.go @@ -48,6 +48,8 @@ // Lock ordering: // // kernfs.Filesystem.mu +// kernel.TaskSet.mu +// kernel.Task.mu // kernfs.Dentry.dirMu // vfs.VirtualFilesystem.mountMu // vfs.Dentry.mu diff --git a/pkg/sentry/kernel/cgroup.go b/pkg/sentry/kernel/cgroup.go index ec6035e63..70880f708 100644 --- a/pkg/sentry/kernel/cgroup.go +++ b/pkg/sentry/kernel/cgroup.go @@ -91,6 +91,8 @@ type Cgroup struct { CgroupImpl } +// decRef drops a reference on the cgroup. This must happen outside a Task.mu +// critical section. func (c *Cgroup) decRef() { c.Dentry.DecRef(context.Background()) } diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go index dda7f46ca..5420e1584 100644 --- a/pkg/sentry/kernel/kernel.go +++ b/pkg/sentry/kernel/kernel.go @@ -1871,7 +1871,11 @@ func (k *Kernel) PopulateNewCgroupHierarchy(root Cgroup) { // hierarchy with the provided id. This is intended for use during hierarchy // teardown, as otherwise the tasks would be orphaned w.r.t to some controllers. func (k *Kernel) ReleaseCgroupHierarchy(hid uint32) { + var releasedCGs []Cgroup + k.tasks.mu.RLock() + // We'll have one cgroup per hierarchy per task. + releasedCGs = make([]Cgroup, 0, len(k.tasks.Root.tids)) k.tasks.forEachTaskLocked(func(t *Task) { if t.exitState != TaskExitNone { return @@ -1879,12 +1883,22 @@ func (k *Kernel) ReleaseCgroupHierarchy(hid uint32) { t.mu.Lock() for cg := range t.cgroups { if cg.HierarchyID() == hid { - t.leaveCgroupLocked(cg) + cg.Leave(t) + delete(t.cgroups, cg) + releasedCGs = append(releasedCGs, cg) + // A task can't be part of multiple cgroups from the same + // hierarchy, so we can skip checking the rest once we find a + // match. + break } } t.mu.Unlock() }) k.tasks.mu.RUnlock() + + for _, c := range releasedCGs { + c.decRef() + } } func (k *Kernel) ReplaceFSContextRoots(ctx context.Context, oldRoot vfs.VirtualDentry, newRoot vfs.VirtualDentry) { diff --git a/pkg/sentry/kernel/task_cgroup.go b/pkg/sentry/kernel/task_cgroup.go index 10c975dd4..efdad19f6 100644 --- a/pkg/sentry/kernel/task_cgroup.go +++ b/pkg/sentry/kernel/task_cgroup.go @@ -90,17 +90,15 @@ func (t *Task) enterCgroupIfNotYetLocked(c Cgroup) { // LeaveCgroups removes t out from all its cgroups. func (t *Task) LeaveCgroups() { t.mu.Lock() - defer t.mu.Unlock() - for c, _ := range t.cgroups { - t.leaveCgroupLocked(c) + cgs := t.cgroups + t.cgroups = nil + for c := range cgs { + c.Leave(t) + } + t.mu.Unlock() + for c := range cgs { + c.decRef() } -} - -// +checklocks:t.mu -func (t *Task) leaveCgroupLocked(c Cgroup) { - c.Leave(t) - delete(t.cgroups, c) - c.decRef() } // +checklocks:t.mu