Whenever memory node mask is changed, there are 4 places where the node
mask has to be updated or used.
 1) task's node mask via cpuset_change_task_nodemask()
 2) memory policy binding via mpol_rebind_mm()
 3) if memory migration is enabled, migrate from old_mems_allowed to
    the new node mask via cpuset_migrate_mm().
 4) setting old_mems_allowed

These memory actions are done in cpuset_update_tasks_nodemask() and
cpuset_attach(). However there are inconsistencies in what node masks
are being used in these 2 functions.

In cpuset_update_tasks_nodemask(),
 - cpuset_change_task_nodemask(): guarantee_online_mems()
 - mpol_rebind_mm(): mems_allowed
 - cpuset_migrate_mm(): guarantee_online_mems()
 - old_mems_allowed: guarantee_online_mems()

In cpuset_attach(),
 - cpuset_change_task_nodemask(): guarantee_online_mems()
 - mpol_rebind_mm(): effective_mems
 - cpuset_migrate_mm(): effective_mems
 - old_mems_allowed: effective_mems

These inconsistencies dates back to quite a long time ago and it is
hard to say what should be the correct values.

The guarantee_online_mems() function returns a node mask from current or
an ancestor cpuset that is a subset of node_states[N_MEMORY]. Nodes in
node_states[N_MEMORY] are all online, i.e. in node_states[N_ONLINE].
However, node in node_states[N_ONLINE] may not have memory. So
node_states[N_MEMORY] should be a subset of node_states[N_ONLINE].

The guarantee_online_mems() function should mostly be useful for v1
where mems_allowed is the same as effective_mems. With v2, the memory
nodes in effective_mems should be a subset of node_states[N_MEMORY]
except when a memory hot-unplug operation is in progress and a memory
node is removed from node_states[N_MEMORY] but not yet reflected in
the effective_mems's as cpuset_handle_hotplug() has not been called
from cpuset_track_online_nodes().

Let use the following setup for both of them and make them consistent.
 - cpuset_change_task_nodemask(): guarantee_online_mems()
 - mpol_rebind_mm(): effective_mems
 - cpuset_migrate_mm(): guarantee_online_mems()
 - old_mems_allowed: guarantee_online_mems()

So for v2, it is effectively all effective_mems most of the time. For
v1, mpol_rebind_mm() uses mems_allowed which may differ from what
guarantee_online_mems() returns, but it conforms to what the cpuset v1
documentation says with respect to setting memory policy.

Signed-off-by: Waiman Long <[email protected]>
Reviewed-by: Ridong Chen <[email protected]>
Reviewed-by: Gregory Price <[email protected]>
---
 kernel/cgroup/cpuset.c | 30 ++++++++++++++++++------------
 1 file changed, 18 insertions(+), 12 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 49d8564d1a48..d108c2083e86 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -489,7 +489,10 @@ static void guarantee_active_cpus(struct task_struct *tsk,
  * Return in *pmask the portion of a cpusets's mems_allowed that
  * are online, with memory.  If none are online with memory, walk
  * up the cpuset hierarchy until we find one that does have some
- * online mems.  The top cpuset always has some mems online.
+ * online mems.  The top cpuset always has some mems online. With v2,
+ * effective_mems should always contain online memory nodes except
+ * during the transition period where a memory node hotunplug operation
+ * is in progress.
  *
  * One way or another, we guarantee to return some non-empty subset
  * of node_states[N_MEMORY].
@@ -2633,6 +2636,14 @@ static void *cpuset_being_rebound;
  * Iterate through each task of @cs updating its mems_allowed to the
  * effective cpuset's.  As this function is called with cpuset_mutex held,
  * cpuset membership stays stable.
+ *
+ * - cpuset_change_task_nodemask(): guarantee_online_mems()
+ * - mpol_rebind_mm(): effective_mems
+ * - cpuset_migrate_mm(): guarantee_online_mems()
+ * - old_mems_allowed: guarantee_online_mems()
+ *
+ * For v2, guarantee_online_mems() should return a node mask that is the same
+ * as the effective_mems of current cpuset.
  */
 void cpuset_update_tasks_nodemask(struct cpuset *cs)
 {
@@ -2641,7 +2652,6 @@ void cpuset_update_tasks_nodemask(struct cpuset *cs)
        struct task_struct *task;
 
        cpuset_being_rebound = cs;              /* causes mpol_dup() rebind */
-
        guarantee_online_mems(cs, &newmems);
 
        /*
@@ -3159,19 +3169,16 @@ static void cpuset_attach(struct cgroup_taskset *tset)
        cpus_updated = !cpumask_equal(cs->effective_cpus,
                                      oldcs->effective_cpus);
        mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems);
+       guarantee_online_mems(cs, &cpuset_attach_nodemask_to);
 
        /*
         * In the default hierarchy, enabling cpuset in the child cgroups
-        * will trigger a number of cpuset_attach() calls with no change
-        * in effective cpus and mems. In that case, we can optimize out
-        * by skipping the task iteration and update.
+        * will trigger a cpuset_attach() call with no change in effective cpus
+        * and mems. In that case, we can optimize out by skipping the task
+        * iteration and update.
         */
-       if (cpuset_v2() && !cpus_updated && !mems_updated) {
-               cpuset_attach_nodemask_to = cs->effective_mems;
+       if (cpuset_v2() && !cpus_updated && !mems_updated)
                goto out;
-       }
-
-       guarantee_online_mems(cs, &cpuset_attach_nodemask_to);
 
        cgroup_taskset_for_each(task, css, tset)
                cpuset_attach_task(cs, task);
@@ -3182,7 +3189,6 @@ static void cpuset_attach(struct cgroup_taskset *tset)
         * if there is no change in effective_mems and CS_MEMORY_MIGRATE is
         * not set.
         */
-       cpuset_attach_nodemask_to = cs->effective_mems;
        if (!is_memory_migrate(cs) && !mems_updated)
                goto out;
 
@@ -3190,7 +3196,7 @@ static void cpuset_attach(struct cgroup_taskset *tset)
                struct mm_struct *mm = get_task_mm(leader);
 
                if (mm) {
-                       mpol_rebind_mm(mm, &cpuset_attach_nodemask_to);
+                       mpol_rebind_mm(mm, &cs->effective_mems);
 
                        /*
                         * old_mems_allowed is the same with mems_allowed
-- 
2.54.0


Reply via email to