[Devel] [PATCH rh7 v3] ve: Attach a ve cgroup from task_work

Kirill Tkhai ktkhai at odin.com
Tue Nov 17 04:53:55 PST 2015


There are many places where get_exec_env() is used.
Since a task may be attached to a ve not only by
itself, this may lead to a problem, when dereferenced
ve has changed, but a code is operating with the old
ve.

This patch makes task_ve to be changed using task_work,
i.e. in a place where it definitelly is not using
get_exec_env().

Suggested-by: Vladimir Davydov <vdavydov at odin.com>
Suggested-by: Pavel Emelyanov <xemul at odin.com>
Signed-off-by: Kirill Tkhai <ktkhai at odin.com>
---
 include/linux/ve.h |    4 ++++
 kernel/ve/ve.c     |   38 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/include/linux/ve.h b/include/linux/ve.h
index 2554337..f4c139a 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -53,6 +53,10 @@ struct ve_struct {
 	/* see vzcalluser.h for VE_FEATURE_XXX definitions */
 	__u64			features;
 
+	struct callback_head	task_attach_work;
+	struct callback_head	task_wait_work;
+	struct completion	task_completion;
+
 	struct task_struct	*ve_kthread_task;
 	struct kthread_worker	ve_kthread_worker;
 
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index 8f5f905..8b6c12b 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -42,6 +42,7 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/ctype.h>
+#include <linux/task_work.h>
 
 #include <uapi/linux/vzcalluser.h>
 #include <linux/venet.h>
@@ -61,8 +62,12 @@ struct kmapset_set ve_sysfs_perms;
 
 static DEFINE_PER_CPU(struct kstat_lat_pcpu_snap_struct, ve0_lat_stats);
 
+static void task_attach_work(struct callback_head *head);
+
 struct ve_struct ve0 = {
 	.ve_name		= "0",
+	.task_attach_work.func	= task_attach_work,
+	.task_wait_work.func	= NULL,
 	.start_jiffies		= INITIAL_JIFFIES,
 	.jiffies_fixup		= 0,
 	RCU_POINTER_INITIALIZER(ve_ns, &init_nsproxy),
@@ -656,6 +661,8 @@ static struct cgroup_subsys_state *ve_create(struct cgroup *cg)
 	INIT_LIST_HEAD(&ve->devmnt_list);
 	mutex_init(&ve->devmnt_mutex);
 	kmapset_init_key(&ve->ve_sysfs_perms);
+	init_task_work(&ve->task_attach_work, task_attach_work);
+	init_task_work(&ve->task_wait_work, NULL);
 
 #ifdef CONFIG_AIO
 	spin_lock_init(&ve->aio_nr_lock);
@@ -745,6 +752,11 @@ static int ve_can_attach(struct cgroup *cg, struct cgroup_taskset *tset)
 	} else
 		return -EINVAL;
 
+	/* Attaching in process? */
+	if (ve->task_wait_work.func != NULL)
+		return -EBUSY;
+	smp_mb(); /* Pairs with smp_mb() in task_wait_work() */
+
 	return 0;
 }
 
@@ -753,6 +765,23 @@ static void ve_update_cpuid_faulting(void *dummy)
 	set_cpuid_faulting(!ve_is_super(get_exec_env()));
 }
 
+static void task_attach_work(struct callback_head *head)
+{
+	struct ve_struct *ve = container_of(head, struct ve_struct, task_attach_work);
+
+	current->task_ve = ve;
+	complete(&ve->task_completion);
+}
+
+static void task_wait_work(struct callback_head *head)
+{
+	struct ve_struct *ve = container_of(head, struct ve_struct, task_wait_work);
+
+	wait_for_completion(&ve->task_completion);
+	smp_mb(); /* Pairs with smp_mb() in ve_can_attach() */
+	init_task_work(&ve->task_wait_work, NULL);
+}
+
 static void ve_attach(struct cgroup *cg, struct cgroup_taskset *tset)
 {
 	struct ve_struct *ve = cgroup_ve(cg);
@@ -770,7 +799,14 @@ static void ve_attach(struct cgroup *cg, struct cgroup_taskset *tset)
 		/* Leave parent exec domain */
 		task->parent_exec_id--;
 
-		task->task_ve = ve;
+		if (cgroup_taskset_first(tset) == current) {
+			task->task_ve = ve;
+		} else {
+			init_completion(&ve->task_completion);
+			task_work_add(task, &ve->task_attach_work, true);
+			init_task_work(&ve->task_wait_work, task_wait_work);
+			task_work_add(current, &ve->task_wait_work, false);
+		}
 	}
 
 	/* Adjust cpuid faulting */



More information about the Devel mailing list