|
@@ -2443,6 +2443,20 @@ out_release_tset:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * cgroup_may_migrate_to - verify whether a cgroup can be migration destination
|
|
|
+ * @dst_cgrp: destination cgroup to test
|
|
|
+ *
|
|
|
+ * On the default hierarchy, except for the root, subtree_control must be
|
|
|
+ * zero for migration destination cgroups with tasks so that child cgroups
|
|
|
+ * don't compete against tasks.
|
|
|
+ */
|
|
|
+static bool cgroup_may_migrate_to(struct cgroup *dst_cgrp)
|
|
|
+{
|
|
|
+ return !cgroup_on_dfl(dst_cgrp) || !cgroup_parent(dst_cgrp) ||
|
|
|
+ !dst_cgrp->subtree_control;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* cgroup_migrate_finish - cleanup after attach
|
|
|
* @preloaded_csets: list of preloaded css_sets
|
|
@@ -2529,14 +2543,6 @@ static int cgroup_migrate_prepare_dst(struct cgroup *dst_cgrp,
|
|
|
|
|
|
lockdep_assert_held(&cgroup_mutex);
|
|
|
|
|
|
- /*
|
|
|
- * Except for the root, subtree_control must be zero for a cgroup
|
|
|
- * with tasks so that child cgroups don't compete against tasks.
|
|
|
- */
|
|
|
- if (dst_cgrp && cgroup_on_dfl(dst_cgrp) && cgroup_parent(dst_cgrp) &&
|
|
|
- dst_cgrp->subtree_control)
|
|
|
- return -EBUSY;
|
|
|
-
|
|
|
/* look up the dst cset for each src cset and link it to src */
|
|
|
list_for_each_entry_safe(src_cset, tmp_cset, preloaded_csets, mg_preload_node) {
|
|
|
struct css_set *dst_cset;
|
|
@@ -2634,6 +2640,9 @@ static int cgroup_attach_task(struct cgroup *dst_cgrp,
|
|
|
struct task_struct *task;
|
|
|
int ret;
|
|
|
|
|
|
+ if (!cgroup_may_migrate_to(dst_cgrp))
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
/* look up all src csets */
|
|
|
spin_lock_bh(&css_set_lock);
|
|
|
rcu_read_lock();
|
|
@@ -4136,6 +4145,9 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
|
|
|
struct task_struct *task;
|
|
|
int ret;
|
|
|
|
|
|
+ if (!cgroup_may_migrate_to(to))
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
mutex_lock(&cgroup_mutex);
|
|
|
|
|
|
/* all tasks in @from are being moved, all csets are source */
|