[Devel] [PATCH RHEL7 COMMIT] ub: sync exec_ub on fork

Konstantin Khorenko khorenko at virtuozzo.com
Wed Jun 17 02:38:37 PDT 2015


The commit is pushed to "branch-rh7-3.10.0-123.1.2-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-123.1.2.vz7.5.14
------>
commit e23fda2e7065a8fb143e88bf2c0888a062ca5d67
Author: Vladimir Davydov <vdavydov at parallels.com>
Date:   Wed Jun 17 13:38:37 2015 +0400

    ub: sync exec_ub on fork
    
    Since we want to be able to get/set current's exec_ub, when attaching a
    task to a ub cgroup we change its exec_ub in a task_work (i.e. from its
    own context, when it returns to userspace). This design works perfectly
    well, but we have to be careful with forking tasks while currently we
    are not.
    
    The problem is that if a forking task is moved between cgroups, the
    child will have exec_ub set to the source cgroup while being attached to
    the destination cgroup. To avoid this discrepancy, we have to
    synchronize the child's exec_ub with its cgroup on cgroup_post_fork.
    This patch does the trick. It is safe to change exec_ub there, because
    the task is not allowed to run yet and therefore cannot get/set its
    exec_ub.
    
    Reported-by: Kirill Tkhai <ktkhai at odin.com>
    Signed-off-by: Vladimir Davydov <vdavydov at parallels.com>
---
 kernel/bc/beancounter.c | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/kernel/bc/beancounter.c b/kernel/bc/beancounter.c
index 02ff31f..5f1affa 100644
--- a/kernel/bc/beancounter.c
+++ b/kernel/bc/beancounter.c
@@ -557,20 +557,27 @@ static void ub_cgroup_css_free(struct cgroup *cg)
 	free_ub(ub);
 }
 
-static void ub_cgroup_attach_work_fn(struct callback_head *ch)
+static void __ub_cgroup_attach(struct task_struct *tsk)
 {
-	struct task_struct *tsk = current;
 	struct user_beancounter *ub;
 
 	rcu_read_lock();
 	do {
 		ub = cgroup_ub(task_cgroup(current, ub_subsys_id));
+		if (tsk->task_bc.exec_ub == ub)
+			goto out;
 	} while (!get_beancounter_rcu(ub));
 	put_beancounter(tsk->task_bc.exec_ub);
 	tsk->task_bc.exec_ub = ub;
+out:
 	rcu_read_unlock();
 }
 
+static void ub_cgroup_attach_work_fn(struct callback_head *ch)
+{
+	__ub_cgroup_attach(current);
+}
+
 static void ub_cgroup_attach(struct cgroup *cg, struct cgroup_taskset *tset)
 {
 	struct task_struct *p;
@@ -594,6 +601,20 @@ static void ub_cgroup_attach(struct cgroup *cg, struct cgroup_taskset *tset)
 	}
 }
 
+static void ub_cgroup_fork(struct task_struct *tsk)
+{
+	/*
+	 * If a forking task is moved between cgroups, the child will have
+	 * exec_ub set to the source cgroup while being attached to the
+	 * destination cgroup, because the parent's exec_ub will only change
+	 * when it returns to userspace (see ub_cgroup_attach). To avoid this
+	 * discrepancy, here we synchronize the child's exec_ub with its
+	 * cgroup. It is safe, because the task is not allowed to run yet and
+	 * therefore cannot get/set its exec_ub.
+	 */
+	__ub_cgroup_attach(tsk);
+}
+
 static ssize_t ub_cgroup_read(struct cgroup *cg, struct cftype *cft,
 			      struct file *file, char __user *buf,
 			      size_t nbytes, loff_t *ppos)
@@ -818,6 +839,7 @@ struct cgroup_subsys ub_subsys = {
 	.css_offline = ub_cgroup_css_offline,
 	.css_free = ub_cgroup_css_free,
 	.attach = ub_cgroup_attach,
+	.fork = ub_cgroup_fork,
 	.base_cftypes = ub_cgroup_files,
 	.use_id = true,
 };



More information about the Devel mailing list