mm: remove offlining arg to migrate_pages
[deliverable/linux.git] / mm / memory_hotplug.c
index 3f792375f3267f53fafdecf5b2c0426f9d181a3d..dda1ca695a08be1bda136e811b4d64fca24a6683 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/suspend.h>
 #include <linux/mm_inline.h>
 #include <linux/firmware-map.h>
+#include <linux/stop_machine.h>
 
 #include <asm/tlbflush.h>
 
@@ -123,6 +124,7 @@ void __ref put_page_bootmem(struct page *page)
                mutex_lock(&ppb_lock);
                __free_pages_bootmem(page, 0);
                mutex_unlock(&ppb_lock);
+               totalram_pages++;
        }
 
 }
@@ -1016,11 +1018,14 @@ static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
        unsigned long zholes_size[MAX_NR_ZONES] = {0};
        unsigned long start_pfn = start >> PAGE_SHIFT;
 
-       pgdat = arch_alloc_nodedata(nid);
-       if (!pgdat)
-               return NULL;
+       pgdat = NODE_DATA(nid);
+       if (!pgdat) {
+               pgdat = arch_alloc_nodedata(nid);
+               if (!pgdat)
+                       return NULL;
 
-       arch_refresh_nodedata(nid, pgdat);
+               arch_refresh_nodedata(nid, pgdat);
+       }
 
        /* we can use NODE_DATA(nid) from here */
 
@@ -1073,7 +1078,8 @@ out:
 int __ref add_memory(int nid, u64 start, u64 size)
 {
        pg_data_t *pgdat = NULL;
-       int new_pgdat = 0;
+       bool new_pgdat;
+       bool new_node;
        struct resource *res;
        int ret;
 
@@ -1084,12 +1090,16 @@ int __ref add_memory(int nid, u64 start, u64 size)
        if (!res)
                goto out;
 
-       if (!node_online(nid)) {
+       {       /* Stupid hack to suppress address-never-null warning */
+               void *p = NODE_DATA(nid);
+               new_pgdat = !p;
+       }
+       new_node = !node_online(nid);
+       if (new_node) {
                pgdat = hotadd_new_pgdat(nid, start);
                ret = -ENOMEM;
                if (!pgdat)
                        goto error;
-               new_pgdat = 1;
        }
 
        /* call arch's memory hotadd */
@@ -1101,7 +1111,7 @@ int __ref add_memory(int nid, u64 start, u64 size)
        /* we online node here. we can't roll back from here. */
        node_set_online(nid);
 
-       if (new_pgdat) {
+       if (new_node) {
                ret = register_one_node(nid);
                /*
                 * If sysfs file of new node can't create, cpu on the node
@@ -1276,8 +1286,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
                 * migrate_pages returns # of failed pages.
                 */
                ret = migrate_pages(&source, alloc_migrate_target, 0,
-                                                       true, MIGRATE_SYNC,
-                                                       MR_MEMORY_HOTPLUG);
+                                       MIGRATE_SYNC, MR_MEMORY_HOTPLUG);
                if (ret)
                        putback_lru_pages(&source);
        }
@@ -1679,7 +1688,111 @@ static int is_memblock_offlined_cb(struct memory_block *mem, void *arg)
        return ret;
 }
 
-int __ref remove_memory(u64 start, u64 size)
+static int check_cpu_on_node(void *data)
+{
+       struct pglist_data *pgdat = data;
+       int cpu;
+
+       for_each_present_cpu(cpu) {
+               if (cpu_to_node(cpu) == pgdat->node_id)
+                       /*
+                        * the cpu on this node isn't removed, and we can't
+                        * offline this node.
+                        */
+                       return -EBUSY;
+       }
+
+       return 0;
+}
+
+static void unmap_cpu_on_node(void *data)
+{
+#ifdef CONFIG_ACPI_NUMA
+       struct pglist_data *pgdat = data;
+       int cpu;
+
+       for_each_possible_cpu(cpu)
+               if (cpu_to_node(cpu) == pgdat->node_id)
+                       numa_clear_node(cpu);
+#endif
+}
+
+static int check_and_unmap_cpu_on_node(void *data)
+{
+       int ret = check_cpu_on_node(data);
+
+       if (ret)
+               return ret;
+
+       /*
+        * the node will be offlined when we come here, so we can clear
+        * the cpu_to_node() now.
+        */
+
+       unmap_cpu_on_node(data);
+       return 0;
+}
+
+/* offline the node if all memory sections of this node are removed */
+void try_offline_node(int nid)
+{
+       pg_data_t *pgdat = NODE_DATA(nid);
+       unsigned long start_pfn = pgdat->node_start_pfn;
+       unsigned long end_pfn = start_pfn + pgdat->node_spanned_pages;
+       unsigned long pfn;
+       struct page *pgdat_page = virt_to_page(pgdat);
+       int i;
+
+       for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
+               unsigned long section_nr = pfn_to_section_nr(pfn);
+
+               if (!present_section_nr(section_nr))
+                       continue;
+
+               if (pfn_to_nid(pfn) != nid)
+                       continue;
+
+               /*
+                * some memory sections of this node are not removed, and we
+                * can't offline node now.
+                */
+               return;
+       }
+
+       if (stop_machine(check_and_unmap_cpu_on_node, pgdat, NULL))
+               return;
+
+       /*
+        * all memory/cpu of this node are removed, we can offline this
+        * node now.
+        */
+       node_set_offline(nid);
+       unregister_one_node(nid);
+
+       if (!PageSlab(pgdat_page) && !PageCompound(pgdat_page))
+               /* node data is allocated from boot memory */
+               return;
+
+       /* free waittable in each zone */
+       for (i = 0; i < MAX_NR_ZONES; i++) {
+               struct zone *zone = pgdat->node_zones + i;
+
+               if (zone->wait_table)
+                       vfree(zone->wait_table);
+       }
+
+       /*
+        * Since there is no way to guarentee the address of pgdat/zone is not
+        * on stack of any kernel threads or used by other kernel objects
+        * without reference counting or other symchronizing method, do not
+        * reset node_data and free pgdat here. Just reset it to 0 and reuse
+        * the memory when the node is online again.
+        */
+       memset(pgdat, 0, sizeof(*pgdat));
+}
+EXPORT_SYMBOL(try_offline_node);
+
+int __ref remove_memory(int nid, u64 start, u64 size)
 {
        unsigned long start_pfn, end_pfn;
        int ret = 0;
@@ -1734,6 +1847,8 @@ repeat:
 
        arch_remove_memory(start, size);
 
+       try_offline_node(nid);
+
        unlock_memory_hotplug();
 
        return 0;
@@ -1743,7 +1858,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages)
 {
        return -EINVAL;
 }
-int remove_memory(u64 start, u64 size)
+int remove_memory(int nid, u64 start, u64 size)
 {
        return -EINVAL;
 }
This page took 0.025987 seconds and 5 git commands to generate.