Merge branch 'slab/next' of git://git.kernel.org/pub/scm/linux/kernel/git/penberg...
[platform/adaptation/renesas_rcar/renesas_kernel.git] / mm / memory_hotplug.c
index ed85fe3..489f235 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/firmware-map.h>
 #include <linux/stop_machine.h>
 #include <linux/hugetlb.h>
+#include <linux/memblock.h>
 
 #include <asm/tlbflush.h>
 
@@ -365,8 +366,7 @@ out_fail:
 static void grow_pgdat_span(struct pglist_data *pgdat, unsigned long start_pfn,
                            unsigned long end_pfn)
 {
-       unsigned long old_pgdat_end_pfn =
-               pgdat->node_start_pfn + pgdat->node_spanned_pages;
+       unsigned long old_pgdat_end_pfn = pgdat_end_pfn(pgdat);
 
        if (!pgdat->node_spanned_pages || start_pfn < pgdat->node_start_pfn)
                pgdat->node_start_pfn = start_pfn;
@@ -402,13 +402,12 @@ static int __meminit __add_zone(struct zone *zone, unsigned long phys_start_pfn)
 static int __meminit __add_section(int nid, struct zone *zone,
                                        unsigned long phys_start_pfn)
 {
-       int nr_pages = PAGES_PER_SECTION;
        int ret;
 
        if (pfn_valid(phys_start_pfn))
                return -EEXIST;
 
-       ret = sparse_add_one_section(zone, phys_start_pfn, nr_pages);
+       ret = sparse_add_one_section(zone, phys_start_pfn);
 
        if (ret < 0)
                return ret;
@@ -579,9 +578,9 @@ static void shrink_zone_span(struct zone *zone, unsigned long start_pfn,
 static void shrink_pgdat_span(struct pglist_data *pgdat,
                              unsigned long start_pfn, unsigned long end_pfn)
 {
-       unsigned long pgdat_start_pfn =  pgdat->node_start_pfn;
-       unsigned long pgdat_end_pfn =
-               pgdat->node_start_pfn + pgdat->node_spanned_pages;
+       unsigned long pgdat_start_pfn = pgdat->node_start_pfn;
+       unsigned long p = pgdat_end_pfn(pgdat); /* pgdat_end_pfn namespace clash */
+       unsigned long pgdat_end_pfn = p;
        unsigned long pfn;
        struct mem_section *ms;
        int nid = pgdat->node_id;
@@ -935,7 +934,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
        arg.nr_pages = nr_pages;
        node_states_check_changes_online(nr_pages, zone, &arg);
 
-       nid = page_to_nid(pfn_to_page(pfn));
+       nid = pfn_to_nid(pfn);
 
        ret = memory_notify(MEM_GOING_ONLINE, &arg);
        ret = notifier_to_errno(ret);
@@ -1044,17 +1043,23 @@ static void rollback_node_hotadd(int nid, pg_data_t *pgdat)
 }
 
 
-/*
+/**
+ * try_online_node - online a node if offlined
+ *
  * called by cpu_up() to online a node without onlined memory.
  */
-int mem_online_node(int nid)
+int try_online_node(int nid)
 {
        pg_data_t       *pgdat;
        int     ret;
 
+       if (node_online(nid))
+               return 0;
+
        lock_memory_hotplug();
        pgdat = hotadd_new_pgdat(nid, 0);
        if (!pgdat) {
+               pr_err("Cannot online node %d due to NULL pgdat\n", nid);
                ret = -ENOMEM;
                goto out;
        }
@@ -1062,6 +1067,12 @@ int mem_online_node(int nid)
        ret = register_one_node(nid);
        BUG_ON(ret);
 
+       if (pgdat->node_zonelists->_zonerefs->zone == NULL) {
+               mutex_lock(&zonelists_mutex);
+               build_all_zonelists(NULL, NULL);
+               mutex_unlock(&zonelists_mutex);
+       }
+
 out:
        unlock_memory_hotplug();
        return ret;
@@ -1412,6 +1423,36 @@ static bool can_offline_normal(struct zone *zone, unsigned long nr_pages)
 }
 #endif /* CONFIG_MOVABLE_NODE */
 
+static int __init cmdline_parse_movable_node(char *p)
+{
+#ifdef CONFIG_MOVABLE_NODE
+       /*
+        * Memory used by the kernel cannot be hot-removed because Linux
+        * cannot migrate the kernel pages. When memory hotplug is
+        * enabled, we should prevent memblock from allocating memory
+        * for the kernel.
+        *
+        * ACPI SRAT records all hotpluggable memory ranges. But before
+        * SRAT is parsed, we don't know about it.
+        *
+        * The kernel image is loaded into memory at very early time. We
+        * cannot prevent this anyway. So on NUMA system, we set any
+        * node the kernel resides in as un-hotpluggable.
+        *
+        * Since on modern servers, one node could have double-digit
+        * gigabytes memory, we can assume the memory around the kernel
+        * image is also un-hotpluggable. So before SRAT is parsed, just
+        * allocate memory near the kernel image to try the best to keep
+        * the kernel away from hotpluggable memory.
+        */
+       memblock_set_bottom_up(true);
+#else
+       pr_warn("movable_node option not supported\n");
+#endif
+       return 0;
+}
+early_param("movable_node", cmdline_parse_movable_node);
+
 /* check which state of node_states will be changed when offline memory */
 static void node_states_check_changes_offline(unsigned long nr_pages,
                struct zone *zone, struct memory_notify *arg)
@@ -1702,7 +1743,7 @@ int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn,
 }
 
 #ifdef CONFIG_MEMORY_HOTREMOVE
-static int is_memblock_offlined_cb(struct memory_block *mem, void *arg)
+static int check_memblock_offlined_cb(struct memory_block *mem, void *arg)
 {
        int ret = !is_memblock_offlined(mem);
 
@@ -1854,7 +1895,7 @@ void __ref remove_memory(int nid, u64 start, u64 size)
         * if this is not the case.
         */
        ret = walk_memory_range(PFN_DOWN(start), PFN_UP(start + size - 1), NULL,
-                               is_memblock_offlined_cb);
+                               check_memblock_offlined_cb);
        if (ret) {
                unlock_memory_hotplug();
                BUG();