mm/demotion: build demotion targets based on explicit memory tiers
authorAneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Thu, 18 Aug 2022 13:10:37 +0000 (18:40 +0530)
committerAndrew Morton <akpm@linux-foundation.org>
Tue, 27 Sep 2022 02:46:12 +0000 (19:46 -0700)
This patch switch the demotion target building logic to use memory tiers
instead of NUMA distance.  All N_MEMORY NUMA nodes will be placed in the
default memory tier and additional memory tiers will be added by drivers
like dax kmem.

This patch builds the demotion target for a NUMA node by looking at all
memory tiers below the tier to which the NUMA node belongs.  The closest
node in the immediately following memory tier is used as a demotion
target.

Since we are now only building demotion target for N_MEMORY NUMA nodes the
CPU hotplug calls are removed in this patch.

Link: https://lkml.kernel.org/r/20220818131042.113280-6-aneesh.kumar@linux.ibm.com
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Reviewed-by: "Huang, Ying" <ying.huang@intel.com>
Acked-by: Wei Xu <weixugc@google.com>
Cc: Alistair Popple <apopple@nvidia.com>
Cc: Bharata B Rao <bharata@amd.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Hesham Almatary <hesham.almatary@huawei.com>
Cc: Jagdish Gediya <jvgediya.oss@gmail.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Tim Chen <tim.c.chen@intel.com>
Cc: Yang Shi <shy828301@gmail.com>
Cc: SeongJae Park <sj@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
include/linux/memory-tiers.h
include/linux/migrate.h
mm/memory-tiers.c
mm/migrate.c
mm/vmstat.c

index 49d281866dca9409b23b6362ac656a51446e76e9..ce9c6bac6725e61dff55eab6a52632ebf87c8f0b 100644 (file)
@@ -37,6 +37,14 @@ struct memory_dev_type *alloc_memory_type(int adistance);
 void destroy_memory_type(struct memory_dev_type *memtype);
 void init_node_memory_type(int node, struct memory_dev_type *default_type);
 void clear_node_memory_type(int node, struct memory_dev_type *memtype);
+#ifdef CONFIG_MIGRATION
+int next_demotion_node(int node);
+#else
+static inline int next_demotion_node(int node)
+{
+       return NUMA_NO_NODE;
+}
+#endif
 
 #else
 
@@ -63,5 +71,10 @@ static inline void clear_node_memory_type(int node, struct memory_dev_type *memt
 {
 
 }
+
+static inline int next_demotion_node(int node)
+{
+       return NUMA_NO_NODE;
+}
 #endif /* CONFIG_NUMA */
 #endif  /* _LINUX_MEMORY_TIERS_H */
index 96f8c84413fef1c23fb1afed91195e4151d5bfeb..704a04f5a0746eb5509917101dbacf3e2df2d538 100644 (file)
@@ -100,19 +100,6 @@ static inline int migrate_huge_page_move_mapping(struct address_space *mapping,
 
 #endif /* CONFIG_MIGRATION */
 
-#if defined(CONFIG_MIGRATION) && defined(CONFIG_NUMA)
-extern void set_migration_target_nodes(void);
-extern void migrate_on_reclaim_init(void);
-extern int next_demotion_node(int node);
-#else
-static inline void set_migration_target_nodes(void) {}
-static inline void migrate_on_reclaim_init(void) {}
-static inline int next_demotion_node(int node)
-{
-        return NUMA_NO_NODE;
-}
-#endif
-
 #ifdef CONFIG_COMPACTION
 bool PageMovable(struct page *page);
 void __SetPageMovable(struct page *page, const struct movable_operations *ops);
index babc10a5cc13d80b217a8cf15ca5eb8426e74758..ea57fc1f37acd499d486d4b4083fc4664d96f73e 100644 (file)
@@ -6,6 +6,8 @@
 #include <linux/memory.h>
 #include <linux/memory-tiers.h>
 
+#include "internal.h"
+
 struct memory_tier {
        /* hierarchy of memory tiers */
        struct list_head list;
@@ -19,6 +21,10 @@ struct memory_tier {
        int adistance_start;
 };
 
+struct demotion_nodes {
+       nodemask_t preferred;
+};
+
 struct node_memory_type_map {
        struct memory_dev_type *memtype;
        int map_count;
@@ -28,6 +34,66 @@ static DEFINE_MUTEX(memory_tier_lock);
 static LIST_HEAD(memory_tiers);
 static struct node_memory_type_map node_memory_types[MAX_NUMNODES];
 static struct memory_dev_type *default_dram_type;
+#ifdef CONFIG_MIGRATION
+/*
+ * node_demotion[] examples:
+ *
+ * Example 1:
+ *
+ * Node 0 & 1 are CPU + DRAM nodes, node 2 & 3 are PMEM nodes.
+ *
+ * node distances:
+ * node   0    1    2    3
+ *    0  10   20   30   40
+ *    1  20   10   40   30
+ *    2  30   40   10   40
+ *    3  40   30   40   10
+ *
+ * memory_tiers0 = 0-1
+ * memory_tiers1 = 2-3
+ *
+ * node_demotion[0].preferred = 2
+ * node_demotion[1].preferred = 3
+ * node_demotion[2].preferred = <empty>
+ * node_demotion[3].preferred = <empty>
+ *
+ * Example 2:
+ *
+ * Node 0 & 1 are CPU + DRAM nodes, node 2 is memory-only DRAM node.
+ *
+ * node distances:
+ * node   0    1    2
+ *    0  10   20   30
+ *    1  20   10   30
+ *    2  30   30   10
+ *
+ * memory_tiers0 = 0-2
+ *
+ * node_demotion[0].preferred = <empty>
+ * node_demotion[1].preferred = <empty>
+ * node_demotion[2].preferred = <empty>
+ *
+ * Example 3:
+ *
+ * Node 0 is CPU + DRAM nodes, Node 1 is HBM node, node 2 is PMEM node.
+ *
+ * node distances:
+ * node   0    1    2
+ *    0  10   20   30
+ *    1  20   10   40
+ *    2  30   40   10
+ *
+ * memory_tiers0 = 1
+ * memory_tiers1 = 0
+ * memory_tiers2 = 2
+ *
+ * node_demotion[0].preferred = 2
+ * node_demotion[1].preferred = 0
+ * node_demotion[2].preferred = <empty>
+ *
+ */
+static struct demotion_nodes *node_demotion __read_mostly;
+#endif /* CONFIG_MIGRATION */
 
 static struct memory_tier *find_create_memory_tier(struct memory_dev_type *memtype)
 {
@@ -73,6 +139,154 @@ static struct memory_tier *find_create_memory_tier(struct memory_dev_type *memty
        return new_memtier;
 }
 
+static struct memory_tier *__node_get_memory_tier(int node)
+{
+       struct memory_dev_type *memtype;
+
+       memtype = node_memory_types[node];
+       if (memtype && node_isset(node, memtype->nodes))
+               return memtype->memtier;
+       return NULL;
+}
+
+#ifdef CONFIG_MIGRATION
+/**
+ * next_demotion_node() - Get the next node in the demotion path
+ * @node: The starting node to lookup the next node
+ *
+ * Return: node id for next memory node in the demotion path hierarchy
+ * from @node; NUMA_NO_NODE if @node is terminal.  This does not keep
+ * @node online or guarantee that it *continues* to be the next demotion
+ * target.
+ */
+int next_demotion_node(int node)
+{
+       struct demotion_nodes *nd;
+       int target;
+
+       if (!node_demotion)
+               return NUMA_NO_NODE;
+
+       nd = &node_demotion[node];
+
+       /*
+        * node_demotion[] is updated without excluding this
+        * function from running.
+        *
+        * Make sure to use RCU over entire code blocks if
+        * node_demotion[] reads need to be consistent.
+        */
+       rcu_read_lock();
+       /*
+        * If there are multiple target nodes, just select one
+        * target node randomly.
+        *
+        * In addition, we can also use round-robin to select
+        * target node, but we should introduce another variable
+        * for node_demotion[] to record last selected target node,
+        * that may cause cache ping-pong due to the changing of
+        * last target node. Or introducing per-cpu data to avoid
+        * caching issue, which seems more complicated. So selecting
+        * target node randomly seems better until now.
+        */
+       target = node_random(&nd->preferred);
+       rcu_read_unlock();
+
+       return target;
+}
+
+static void disable_all_demotion_targets(void)
+{
+       int node;
+
+       for_each_node_state(node, N_MEMORY)
+               node_demotion[node].preferred = NODE_MASK_NONE;
+       /*
+        * Ensure that the "disable" is visible across the system.
+        * Readers will see either a combination of before+disable
+        * state or disable+after.  They will never see before and
+        * after state together.
+        */
+       synchronize_rcu();
+}
+
+static __always_inline nodemask_t get_memtier_nodemask(struct memory_tier *memtier)
+{
+       nodemask_t nodes = NODE_MASK_NONE;
+       struct memory_dev_type *memtype;
+
+       list_for_each_entry(memtype, &memtier->memory_types, tier_sibiling)
+               nodes_or(nodes, nodes, memtype->nodes);
+
+       return nodes;
+}
+
+/*
+ * Find an automatic demotion target for all memory
+ * nodes. Failing here is OK.  It might just indicate
+ * being at the end of a chain.
+ */
+static void establish_demotion_targets(void)
+{
+       struct memory_tier *memtier;
+       struct demotion_nodes *nd;
+       int target = NUMA_NO_NODE, node;
+       int distance, best_distance;
+       nodemask_t tier_nodes;
+
+       lockdep_assert_held_once(&memory_tier_lock);
+
+       if (!node_demotion || !IS_ENABLED(CONFIG_MIGRATION))
+               return;
+
+       disable_all_demotion_targets();
+
+       for_each_node_state(node, N_MEMORY) {
+               best_distance = -1;
+               nd = &node_demotion[node];
+
+               memtier = __node_get_memory_tier(node);
+               if (!memtier || list_is_last(&memtier->list, &memory_tiers))
+                       continue;
+               /*
+                * Get the lower memtier to find the  demotion node list.
+                */
+               memtier = list_next_entry(memtier, list);
+               tier_nodes = get_memtier_nodemask(memtier);
+               /*
+                * find_next_best_node, use 'used' nodemask as a skip list.
+                * Add all memory nodes except the selected memory tier
+                * nodelist to skip list so that we find the best node from the
+                * memtier nodelist.
+                */
+               nodes_andnot(tier_nodes, node_states[N_MEMORY], tier_nodes);
+
+               /*
+                * Find all the nodes in the memory tier node list of same best distance.
+                * add them to the preferred mask. We randomly select between nodes
+                * in the preferred mask when allocating pages during demotion.
+                */
+               do {
+                       target = find_next_best_node(node, &tier_nodes);
+                       if (target == NUMA_NO_NODE)
+                               break;
+
+                       distance = node_distance(node, target);
+                       if (distance == best_distance || best_distance == -1) {
+                               best_distance = distance;
+                               node_set(target, nd->preferred);
+                       } else {
+                               break;
+                       }
+               } while (1);
+       }
+}
+
+#else
+static inline void disable_all_demotion_targets(void) {}
+static inline void establish_demotion_targets(void) {}
+#endif /* CONFIG_MIGRATION */
+
 static inline void __init_node_memory_type(int node, struct memory_dev_type *memtype)
 {
        if (!node_memory_types[node].memtype)
@@ -109,16 +323,6 @@ static struct memory_tier *set_node_memory_tier(int node)
        return memtier;
 }
 
-static struct memory_tier *__node_get_memory_tier(int node)
-{
-       struct memory_dev_type *memtype;
-
-       memtype = node_memory_types[node];
-       if (memtype && node_isset(node, memtype->nodes))
-               return memtype->memtier;
-       return NULL;
-}
-
 static void destroy_memory_tier(struct memory_tier *memtier)
 {
        list_del(&memtier->list);
@@ -207,6 +411,7 @@ EXPORT_SYMBOL_GPL(clear_node_memory_type);
 static int __meminit memtier_hotplug_callback(struct notifier_block *self,
                                              unsigned long action, void *_arg)
 {
+       struct memory_tier *memtier;
        struct memory_notify *arg = _arg;
 
        /*
@@ -219,12 +424,15 @@ static int __meminit memtier_hotplug_callback(struct notifier_block *self,
        switch (action) {
        case MEM_OFFLINE:
                mutex_lock(&memory_tier_lock);
-               clear_node_memory_tier(arg->status_change_nid);
+               if (clear_node_memory_tier(arg->status_change_nid))
+                       establish_demotion_targets();
                mutex_unlock(&memory_tier_lock);
                break;
        case MEM_ONLINE:
                mutex_lock(&memory_tier_lock);
-               set_node_memory_tier(arg->status_change_nid);
+               memtier = set_node_memory_tier(arg->status_change_nid);
+               if (!IS_ERR(memtier))
+                       establish_demotion_targets();
                mutex_unlock(&memory_tier_lock);
                break;
        }
@@ -237,6 +445,11 @@ static int __init memory_tier_init(void)
        int node;
        struct memory_tier *memtier;
 
+#ifdef CONFIG_MIGRATION
+       node_demotion = kcalloc(nr_node_ids, sizeof(struct demotion_nodes),
+                               GFP_KERNEL);
+       WARN_ON(!node_demotion);
+#endif
        mutex_lock(&memory_tier_lock);
        /*
         * For now we can have 4 faster memory tiers with smaller adistance
@@ -259,6 +472,7 @@ static int __init memory_tier_init(void)
                         */
                        break;
        }
+       establish_demotion_targets();
        mutex_unlock(&memory_tier_lock);
 
        hotplug_memory_notifier(memtier_hotplug_callback, MEMTIER_HOTPLUG_PRIO);
index 30477cf4868dff09c3ddd9e8653d98b4070f008b..2a2329bf7c1a3f27a8f6bf0d81e281f11d45ee46 100644 (file)
@@ -2198,398 +2198,4 @@ out:
        return 0;
 }
 #endif /* CONFIG_NUMA_BALANCING */
-
-/*
- * node_demotion[] example:
- *
- * Consider a system with two sockets.  Each socket has
- * three classes of memory attached: fast, medium and slow.
- * Each memory class is placed in its own NUMA node.  The
- * CPUs are placed in the node with the "fast" memory.  The
- * 6 NUMA nodes (0-5) might be split among the sockets like
- * this:
- *
- *     Socket A: 0, 1, 2
- *     Socket B: 3, 4, 5
- *
- * When Node 0 fills up, its memory should be migrated to
- * Node 1.  When Node 1 fills up, it should be migrated to
- * Node 2.  The migration path start on the nodes with the
- * processors (since allocations default to this node) and
- * fast memory, progress through medium and end with the
- * slow memory:
- *
- *     0 -> 1 -> 2 -> stop
- *     3 -> 4 -> 5 -> stop
- *
- * This is represented in the node_demotion[] like this:
- *
- *     {  nr=1, nodes[0]=1 }, // Node 0 migrates to 1
- *     {  nr=1, nodes[0]=2 }, // Node 1 migrates to 2
- *     {  nr=0, nodes[0]=-1 }, // Node 2 does not migrate
- *     {  nr=1, nodes[0]=4 }, // Node 3 migrates to 4
- *     {  nr=1, nodes[0]=5 }, // Node 4 migrates to 5
- *     {  nr=0, nodes[0]=-1 }, // Node 5 does not migrate
- *
- * Moreover some systems may have multiple slow memory nodes.
- * Suppose a system has one socket with 3 memory nodes, node 0
- * is fast memory type, and node 1/2 both are slow memory
- * type, and the distance between fast memory node and slow
- * memory node is same. So the migration path should be:
- *
- *     0 -> 1/2 -> stop
- *
- * This is represented in the node_demotion[] like this:
- *     { nr=2, {nodes[0]=1, nodes[1]=2} }, // Node 0 migrates to node 1 and node 2
- *     { nr=0, nodes[0]=-1, }, // Node 1 dose not migrate
- *     { nr=0, nodes[0]=-1, }, // Node 2 does not migrate
- */
-
-/*
- * Writes to this array occur without locking.  Cycles are
- * not allowed: Node X demotes to Y which demotes to X...
- *
- * If multiple reads are performed, a single rcu_read_lock()
- * must be held over all reads to ensure that no cycles are
- * observed.
- */
-#define DEFAULT_DEMOTION_TARGET_NODES 15
-
-#if MAX_NUMNODES < DEFAULT_DEMOTION_TARGET_NODES
-#define DEMOTION_TARGET_NODES  (MAX_NUMNODES - 1)
-#else
-#define DEMOTION_TARGET_NODES  DEFAULT_DEMOTION_TARGET_NODES
-#endif
-
-struct demotion_nodes {
-       unsigned short nr;
-       short nodes[DEMOTION_TARGET_NODES];
-};
-
-static struct demotion_nodes *node_demotion __read_mostly;
-
-/**
- * next_demotion_node() - Get the next node in the demotion path
- * @node: The starting node to lookup the next node
- *
- * Return: node id for next memory node in the demotion path hierarchy
- * from @node; NUMA_NO_NODE if @node is terminal.  This does not keep
- * @node online or guarantee that it *continues* to be the next demotion
- * target.
- */
-int next_demotion_node(int node)
-{
-       struct demotion_nodes *nd;
-       unsigned short target_nr, index;
-       int target;
-
-       if (!node_demotion)
-               return NUMA_NO_NODE;
-
-       nd = &node_demotion[node];
-
-       /*
-        * node_demotion[] is updated without excluding this
-        * function from running.  RCU doesn't provide any
-        * compiler barriers, so the READ_ONCE() is required
-        * to avoid compiler reordering or read merging.
-        *
-        * Make sure to use RCU over entire code blocks if
-        * node_demotion[] reads need to be consistent.
-        */
-       rcu_read_lock();
-       target_nr = READ_ONCE(nd->nr);
-
-       switch (target_nr) {
-       case 0:
-               target = NUMA_NO_NODE;
-               goto out;
-       case 1:
-               index = 0;
-               break;
-       default:
-               /*
-                * If there are multiple target nodes, just select one
-                * target node randomly.
-                *
-                * In addition, we can also use round-robin to select
-                * target node, but we should introduce another variable
-                * for node_demotion[] to record last selected target node,
-                * that may cause cache ping-pong due to the changing of
-                * last target node. Or introducing per-cpu data to avoid
-                * caching issue, which seems more complicated. So selecting
-                * target node randomly seems better until now.
-                */
-               index = get_random_int() % target_nr;
-               break;
-       }
-
-       target = READ_ONCE(nd->nodes[index]);
-
-out:
-       rcu_read_unlock();
-       return target;
-}
-
-/* Disable reclaim-based migration. */
-static void __disable_all_migrate_targets(void)
-{
-       int node, i;
-
-       if (!node_demotion)
-               return;
-
-       for_each_online_node(node) {
-               node_demotion[node].nr = 0;
-               for (i = 0; i < DEMOTION_TARGET_NODES; i++)
-                       node_demotion[node].nodes[i] = NUMA_NO_NODE;
-       }
-}
-
-static void disable_all_migrate_targets(void)
-{
-       __disable_all_migrate_targets();
-
-       /*
-        * Ensure that the "disable" is visible across the system.
-        * Readers will see either a combination of before+disable
-        * state or disable+after.  They will never see before and
-        * after state together.
-        *
-        * The before+after state together might have cycles and
-        * could cause readers to do things like loop until this
-        * function finishes.  This ensures they can only see a
-        * single "bad" read and would, for instance, only loop
-        * once.
-        */
-       synchronize_rcu();
-}
-
-/*
- * Find an automatic demotion target for 'node'.
- * Failing here is OK.  It might just indicate
- * being at the end of a chain.
- */
-static int establish_migrate_target(int node, nodemask_t *used,
-                                   int best_distance)
-{
-       int migration_target, index, val;
-       struct demotion_nodes *nd;
-
-       if (!node_demotion)
-               return NUMA_NO_NODE;
-
-       nd = &node_demotion[node];
-
-       migration_target = find_next_best_node(node, used);
-       if (migration_target == NUMA_NO_NODE)
-               return NUMA_NO_NODE;
-
-       /*
-        * If the node has been set a migration target node before,
-        * which means it's the best distance between them. Still
-        * check if this node can be demoted to other target nodes
-        * if they have a same best distance.
-        */
-       if (best_distance != -1) {
-               val = node_distance(node, migration_target);
-               if (val > best_distance)
-                       goto out_clear;
-       }
-
-       index = nd->nr;
-       if (WARN_ONCE(index >= DEMOTION_TARGET_NODES,
-                     "Exceeds maximum demotion target nodes\n"))
-               goto out_clear;
-
-       nd->nodes[index] = migration_target;
-       nd->nr++;
-
-       return migration_target;
-out_clear:
-       node_clear(migration_target, *used);
-       return NUMA_NO_NODE;
-}
-
-/*
- * When memory fills up on a node, memory contents can be
- * automatically migrated to another node instead of
- * discarded at reclaim.
- *
- * Establish a "migration path" which will start at nodes
- * with CPUs and will follow the priorities used to build the
- * page allocator zonelists.
- *
- * The difference here is that cycles must be avoided.  If
- * node0 migrates to node1, then neither node1, nor anything
- * node1 migrates to can migrate to node0. Also one node can
- * be migrated to multiple nodes if the target nodes all have
- * a same best-distance against the source node.
- *
- * This function can run simultaneously with readers of
- * node_demotion[].  However, it can not run simultaneously
- * with itself.  Exclusion is provided by memory hotplug events
- * being single-threaded.
- */
-static void __set_migration_target_nodes(void)
-{
-       nodemask_t next_pass;
-       nodemask_t this_pass;
-       nodemask_t used_targets = NODE_MASK_NONE;
-       int node, best_distance;
-
-       /*
-        * Avoid any oddities like cycles that could occur
-        * from changes in the topology.  This will leave
-        * a momentary gap when migration is disabled.
-        */
-       disable_all_migrate_targets();
-
-       /*
-        * Allocations go close to CPUs, first.  Assume that
-        * the migration path starts at the nodes with CPUs.
-        */
-       next_pass = node_states[N_CPU];
-again:
-       this_pass = next_pass;
-       next_pass = NODE_MASK_NONE;
-       /*
-        * To avoid cycles in the migration "graph", ensure
-        * that migration sources are not future targets by
-        * setting them in 'used_targets'.  Do this only
-        * once per pass so that multiple source nodes can
-        * share a target node.
-        *
-        * 'used_targets' will become unavailable in future
-        * passes.  This limits some opportunities for
-        * multiple source nodes to share a destination.
-        */
-       nodes_or(used_targets, used_targets, this_pass);
-
-       for_each_node_mask(node, this_pass) {
-               best_distance = -1;
-
-               /*
-                * Try to set up the migration path for the node, and the target
-                * migration nodes can be multiple, so doing a loop to find all
-                * the target nodes if they all have a best node distance.
-                */
-               do {
-                       int target_node =
-                               establish_migrate_target(node, &used_targets,
-                                                        best_distance);
-
-                       if (target_node == NUMA_NO_NODE)
-                               break;
-
-                       if (best_distance == -1)
-                               best_distance = node_distance(node, target_node);
-
-                       /*
-                        * Visit targets from this pass in the next pass.
-                        * Eventually, every node will have been part of
-                        * a pass, and will become set in 'used_targets'.
-                        */
-                       node_set(target_node, next_pass);
-               } while (1);
-       }
-       /*
-        * 'next_pass' contains nodes which became migration
-        * targets in this pass.  Make additional passes until
-        * no more migrations targets are available.
-        */
-       if (!nodes_empty(next_pass))
-               goto again;
-}
-
-/*
- * For callers that do not hold get_online_mems() already.
- */
-void set_migration_target_nodes(void)
-{
-       get_online_mems();
-       __set_migration_target_nodes();
-       put_online_mems();
-}
-
-/*
- * This leaves migrate-on-reclaim transiently disabled between
- * the MEM_GOING_OFFLINE and MEM_OFFLINE events.  This runs
- * whether reclaim-based migration is enabled or not, which
- * ensures that the user can turn reclaim-based migration at
- * any time without needing to recalculate migration targets.
- *
- * These callbacks already hold get_online_mems().  That is why
- * __set_migration_target_nodes() can be used as opposed to
- * set_migration_target_nodes().
- */
-#ifdef CONFIG_MEMORY_HOTPLUG
-static int __meminit migrate_on_reclaim_callback(struct notifier_block *self,
-                                                unsigned long action, void *_arg)
-{
-       struct memory_notify *arg = _arg;
-
-       /*
-        * Only update the node migration order when a node is
-        * changing status, like online->offline.  This avoids
-        * the overhead of synchronize_rcu() in most cases.
-        */
-       if (arg->status_change_nid < 0)
-               return notifier_from_errno(0);
-
-       switch (action) {
-       case MEM_GOING_OFFLINE:
-               /*
-                * Make sure there are not transient states where
-                * an offline node is a migration target.  This
-                * will leave migration disabled until the offline
-                * completes and the MEM_OFFLINE case below runs.
-                */
-               disable_all_migrate_targets();
-               break;
-       case MEM_OFFLINE:
-       case MEM_ONLINE:
-               /*
-                * Recalculate the target nodes once the node
-                * reaches its final state (online or offline).
-                */
-               __set_migration_target_nodes();
-               break;
-       case MEM_CANCEL_OFFLINE:
-               /*
-                * MEM_GOING_OFFLINE disabled all the migration
-                * targets.  Reenable them.
-                */
-               __set_migration_target_nodes();
-               break;
-       case MEM_GOING_ONLINE:
-       case MEM_CANCEL_ONLINE:
-               break;
-       }
-
-       return notifier_from_errno(0);
-}
-#endif
-
-void __init migrate_on_reclaim_init(void)
-{
-       node_demotion = kcalloc(nr_node_ids,
-                               sizeof(struct demotion_nodes),
-                               GFP_KERNEL);
-       WARN_ON(!node_demotion);
-#ifdef CONFIG_MEMORY_HOTPLUG
-       hotplug_memory_notifier(migrate_on_reclaim_callback, 100);
-#endif
-       /*
-        * At this point, all numa nodes with memory/CPus have their state
-        * properly set, so we can build the demotion order now.
-        * Let us hold the cpu_hotplug lock just, as we could possibily have
-        * CPU hotplug events during boot.
-        */
-       cpus_read_lock();
-       set_migration_target_nodes();
-       cpus_read_unlock();
-}
 #endif /* CONFIG_NUMA */
-
-
index c109167a669c1ab9562bc416598fed27e1e606fa..779f1ea6e8ea6a8b5e62b73ef5b3327f29f51a12 100644 (file)
@@ -28,7 +28,6 @@
 #include <linux/mm_inline.h>
 #include <linux/page_ext.h>
 #include <linux/page_owner.h>
-#include <linux/migrate.h>
 
 #include "internal.h"
 
@@ -2068,7 +2067,6 @@ static int vmstat_cpu_online(unsigned int cpu)
 
        if (!node_state(cpu_to_node(cpu), N_CPU)) {
                node_set_state(cpu_to_node(cpu), N_CPU);
-               set_migration_target_nodes();
        }
 
        return 0;
@@ -2093,7 +2091,6 @@ static int vmstat_cpu_dead(unsigned int cpu)
                return 0;
 
        node_clear_state(node, N_CPU);
-       set_migration_target_nodes();
 
        return 0;
 }
@@ -2126,7 +2123,6 @@ void __init init_mm_internals(void)
 
        start_shepherd_timer();
 #endif
-       migrate_on_reclaim_init();
 #ifdef CONFIG_PROC_FS
        proc_create_seq("buddyinfo", 0444, NULL, &fragmentation_op);
        proc_create_seq("pagetypeinfo", 0400, NULL, &pagetypeinfo_op);