summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/cgroup/cgroup.c28
1 files changed, 28 insertions, 0 deletions
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index f534c0473a46..faccc793325e 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -6880,6 +6880,31 @@ static struct cgroup *cgroup_get_from_file(struct file *f)
return cgrp;
}
+/*
+ * A new threadgroup gets its own cgroup_threadgroup_rwsem. Once its PID is
+ * visible, a cgroup.procs writer can find the child and lock that rwsem even
+ * though cgroup_post_fork() has not linked the child to its css_set yet.
+ *
+ * cgroup_can_fork() already holds the global read lock, which keeps
+ * cgroup_enable_per_threadgroup_rwsem stable. Lock the child's rwsem as a
+ * nested instance until cgroup_post_fork() finishes. Threads share their
+ * parent's signal_struct and are covered by the existing read lock.
+ */
+static void cgroup_fork_child_lock(struct task_struct *child)
+{
+ if (cgroup_enable_per_threadgroup_rwsem &&
+ child->signal != current->signal)
+ down_read_nested(&child->signal->cgroup_threadgroup_rwsem,
+ SINGLE_DEPTH_NESTING);
+}
+
+static void cgroup_fork_child_unlock(struct task_struct *child)
+{
+ if (cgroup_enable_per_threadgroup_rwsem &&
+ child->signal != current->signal)
+ up_read(&child->signal->cgroup_threadgroup_rwsem);
+}
+
/**
* cgroup_css_set_fork - find or create a css_set for a child process
* @kargs: the arguments passed to create the child process
@@ -7045,6 +7070,7 @@ int cgroup_can_fork(struct task_struct *child, struct kernel_clone_args *kargs)
goto out_revert;
} while_each_subsys_mask();
+ cgroup_fork_child_lock(child);
return 0;
out_revert:
@@ -7079,6 +7105,7 @@ void cgroup_cancel_fork(struct task_struct *child,
if (ss->cancel_fork)
ss->cancel_fork(child, kargs->cset);
+ cgroup_fork_child_unlock(child);
cgroup_css_set_put_fork(kargs);
}
@@ -7176,6 +7203,7 @@ void cgroup_post_fork(struct task_struct *child,
if (unlikely(kill))
do_send_sig_info(SIGKILL, SEND_SIG_NOINFO, child, PIDTYPE_TGID);
+ cgroup_fork_child_unlock(child);
cgroup_css_set_put_fork(kargs);
}