Merge branch 'for-4.6-ns' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup
[linux-2.6-block.git] / mm / memory_hotplug.c
index 979b18cbd343e3422d5b4ccb86d1fab2bb1090e2..aa34431c3f31769b05a43820aca0a75c6975e8a3 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/hugetlb.h>
 #include <linux/memblock.h>
 #include <linux/bootmem.h>
+#include <linux/compaction.h>
 
 #include <asm/tlbflush.h>
 
@@ -77,6 +78,9 @@ static struct {
 #define memhp_lock_acquire()      lock_map_acquire(&mem_hotplug.dep_map)
 #define memhp_lock_release()      lock_map_release(&mem_hotplug.dep_map)
 
+bool memhp_auto_online;
+EXPORT_SYMBOL_GPL(memhp_auto_online);
+
 void get_online_mems(void)
 {
        might_sleep();
@@ -163,7 +167,7 @@ void get_page_bootmem(unsigned long info,  struct page *page,
        page->lru.next = (struct list_head *) type;
        SetPagePrivate(page);
        set_page_private(page, info);
-       atomic_inc(&page->_count);
+       page_ref_inc(page);
 }
 
 void put_page_bootmem(struct page *page)
@@ -174,7 +178,7 @@ void put_page_bootmem(struct page *page)
        BUG_ON(type < MEMORY_HOTPLUG_MIN_BOOTMEM_TYPE ||
               type > MEMORY_HOTPLUG_MAX_BOOTMEM_TYPE);
 
-       if (atomic_dec_return(&page->_count) == 1) {
+       if (page_ref_dec_return(page) == 1) {
                ClearPagePrivate(page);
                set_page_private(page, 0);
                INIT_LIST_HEAD(&page->lru);
@@ -509,6 +513,8 @@ int __ref __add_pages(int nid, struct zone *zone, unsigned long phys_start_pfn,
        int start_sec, end_sec;
        struct vmem_altmap *altmap;
 
+       clear_zone_contiguous(zone);
+
        /* during initialize mem_map, align hot-added range to section */
        start_sec = pfn_to_section_nr(phys_start_pfn);
        end_sec = pfn_to_section_nr(phys_start_pfn + nr_pages - 1);
@@ -521,7 +527,8 @@ int __ref __add_pages(int nid, struct zone *zone, unsigned long phys_start_pfn,
                if (altmap->base_pfn != phys_start_pfn
                                || vmem_altmap_offset(altmap) > nr_pages) {
                        pr_warn_once("memory add fail, invalid altmap\n");
-                       return -EINVAL;
+                       err = -EINVAL;
+                       goto out;
                }
                altmap->alloc = 0;
        }
@@ -539,7 +546,8 @@ int __ref __add_pages(int nid, struct zone *zone, unsigned long phys_start_pfn,
                err = 0;
        }
        vmemmap_populate_print_last();
-
+out:
+       set_zone_contiguous(zone);
        return err;
 }
 EXPORT_SYMBOL_GPL(__add_pages);
@@ -811,6 +819,8 @@ int __remove_pages(struct zone *zone, unsigned long phys_start_pfn,
                }
        }
 
+       clear_zone_contiguous(zone);
+
        /*
         * We can only remove entire sections
         */
@@ -826,6 +836,9 @@ int __remove_pages(struct zone *zone, unsigned long phys_start_pfn,
                if (ret)
                        break;
        }
+
+       set_zone_contiguous(zone);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(__remove_pages);
@@ -1042,14 +1055,13 @@ 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 = pfn_to_nid(pfn);
+       nid = zone_to_nid(zone);
 
        ret = memory_notify(MEM_GOING_ONLINE, &arg);
        ret = notifier_to_errno(ret);
-       if (ret) {
-               memory_notify(MEM_CANCEL_ONLINE, &arg);
-               return ret;
-       }
+       if (ret)
+               goto failed_addition;
+
        /*
         * If this zone is not populated, then it is not in zonelist.
         * This means the page allocator ignores this zone.
@@ -1067,12 +1079,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
                if (need_zonelists_rebuild)
                        zone_pcp_reset(zone);
                mutex_unlock(&zonelists_mutex);
-               printk(KERN_DEBUG "online_pages [mem %#010llx-%#010llx] failed\n",
-                      (unsigned long long) pfn << PAGE_SHIFT,
-                      (((unsigned long long) pfn + nr_pages)
-                           << PAGE_SHIFT) - 1);
-               memory_notify(MEM_CANCEL_ONLINE, &arg);
-               return ret;
+               goto failed_addition;
        }
 
        zone->present_pages += onlined_pages;
@@ -1082,7 +1089,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
        pgdat_resize_unlock(zone->zone_pgdat, &flags);
 
        if (onlined_pages) {
-               node_states_set_node(zone_to_nid(zone), &arg);
+               node_states_set_node(nid, &arg);
                if (need_zonelists_rebuild)
                        build_all_zonelists(NULL, NULL);
                else
@@ -1093,8 +1100,10 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
 
        init_per_zone_wmark_min();
 
-       if (onlined_pages)
-               kswapd_run(zone_to_nid(zone));
+       if (onlined_pages) {
+               kswapd_run(nid);
+               kcompactd_run(nid);
+       }
 
        vm_total_pages = nr_free_pagecache_pages();
 
@@ -1103,6 +1112,13 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
        if (onlined_pages)
                memory_notify(MEM_ONLINE, &arg);
        return 0;
+
+failed_addition:
+       pr_debug("online_pages [mem %#010llx-%#010llx] failed\n",
+                (unsigned long long) pfn << PAGE_SHIFT,
+                (((unsigned long long) pfn + nr_pages) << PAGE_SHIFT) - 1);
+       memory_notify(MEM_CANCEL_ONLINE, &arg);
+       return ret;
 }
 #endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
 
@@ -1261,8 +1277,13 @@ int zone_for_memory(int nid, u64 start, u64 size, int zone_default,
        return zone_default;
 }
 
+static int online_memory_block(struct memory_block *mem, void *arg)
+{
+       return memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE);
+}
+
 /* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */
-int __ref add_memory_resource(int nid, struct resource *res)
+int __ref add_memory_resource(int nid, struct resource *res, bool online)
 {
        u64 start, size;
        pg_data_t *pgdat = NULL;
@@ -1322,6 +1343,11 @@ int __ref add_memory_resource(int nid, struct resource *res)
        /* create new memmap entry */
        firmware_map_add_hotplug(start, start + size, "System RAM");
 
+       /* online pages if requested */
+       if (online)
+               walk_memory_range(PFN_DOWN(start), PFN_UP(start + size - 1),
+                                 NULL, online_memory_block);
+
        goto out;
 
 error:
@@ -1345,7 +1371,7 @@ int __ref add_memory(int nid, u64 start, u64 size)
        if (IS_ERR(res))
                return PTR_ERR(res);
 
-       ret = add_memory_resource(nid, res);
+       ret = add_memory_resource(nid, res, memhp_auto_online);
        if (ret < 0)
                release_memory_resource(res);
        return ret;
@@ -1504,8 +1530,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
 
                } else {
 #ifdef CONFIG_DEBUG_VM
-                       printk(KERN_ALERT "removing pfn %lx from LRU failed\n",
-                              pfn);
+                       pr_alert("removing pfn %lx from LRU failed\n", pfn);
                        dump_page(page, "failed to remove from LRU");
 #endif
                        put_page(page);
@@ -1833,7 +1858,7 @@ repeat:
                ret = -EBUSY;
                goto failed_removal;
        }
-       printk(KERN_INFO "Offlined Pages %ld\n", offlined_pages);
+       pr_info("Offlined Pages %ld\n", offlined_pages);
        /* Ok, all of our target is isolated.
           We cannot do rollback at this point. */
        offline_isolated_pages(start_pfn, end_pfn);
@@ -1858,8 +1883,10 @@ repeat:
                zone_pcp_update(zone);
 
        node_states_clear_node(node, &arg);
-       if (arg.status_change_nid >= 0)
+       if (arg.status_change_nid >= 0) {
                kswapd_stop(node);
+               kcompactd_stop(node);
+       }
 
        vm_total_pages = nr_free_pagecache_pages();
        writeback_set_ratelimit();
@@ -1868,9 +1895,9 @@ repeat:
        return 0;
 
 failed_removal:
-       printk(KERN_INFO "memory offlining [mem %#010llx-%#010llx] failed\n",
-              (unsigned long long) start_pfn << PAGE_SHIFT,
-              ((unsigned long long) end_pfn << PAGE_SHIFT) - 1);
+       pr_debug("memory offlining [mem %#010llx-%#010llx] failed\n",
+                (unsigned long long) start_pfn << PAGE_SHIFT,
+                ((unsigned long long) end_pfn << PAGE_SHIFT) - 1);
        memory_notify(MEM_CANCEL_OFFLINE, &arg);
        /* pushback to free area */
        undo_isolate_page_range(start_pfn, end_pfn, MIGRATE_MOVABLE);
@@ -1943,8 +1970,7 @@ static int check_memblock_offlined_cb(struct memory_block *mem, void *arg)
 
                beginpa = PFN_PHYS(section_nr_to_pfn(mem->start_section_nr));
                endpa = PFN_PHYS(section_nr_to_pfn(mem->end_section_nr + 1))-1;
-               pr_warn("removing memory fails, because memory "
-                       "[%pa-%pa] is onlined\n",
+               pr_warn("removing memory fails, because memory [%pa-%pa] is onlined\n",
                        &beginpa, &endpa);
        }