Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / mm / memory_hotplug.c
index 2c4edc4..6629faf 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/pagemap.h>
 #include <linux/bootmem.h>
 #include <linux/compiler.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/pagevec.h>
 #include <linux/writeback.h>
 #include <linux/slab.h>
 
 #include "internal.h"
 
+/*
+ * online_page_callback contains pointer to current page onlining function.
+ * Initially it is generic_online_page(). If it is required it could be
+ * changed by calling set_online_page_callback() for callback registration
+ * and restore_online_page_callback() for generic callback restore.
+ */
+
+static void generic_online_page(struct page *page);
+
+static online_page_callback_t online_page_callback = generic_online_page;
+
 DEFINE_MUTEX(mem_hotplug_mutex);
 
 void lock_memory_hotplug(void)
@@ -361,27 +372,74 @@ int __remove_pages(struct zone *zone, unsigned long phys_start_pfn,
 }
 EXPORT_SYMBOL_GPL(__remove_pages);
 
-void online_page(struct page *page)
+int set_online_page_callback(online_page_callback_t callback)
+{
+       int rc = -EINVAL;
+
+       lock_memory_hotplug();
+
+       if (online_page_callback == generic_online_page) {
+               online_page_callback = callback;
+               rc = 0;
+       }
+
+       unlock_memory_hotplug();
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(set_online_page_callback);
+
+int restore_online_page_callback(online_page_callback_t callback)
+{
+       int rc = -EINVAL;
+
+       lock_memory_hotplug();
+
+       if (online_page_callback == callback) {
+               online_page_callback = generic_online_page;
+               rc = 0;
+       }
+
+       unlock_memory_hotplug();
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(restore_online_page_callback);
+
+void __online_page_set_limits(struct page *page)
 {
        unsigned long pfn = page_to_pfn(page);
 
-       totalram_pages++;
        if (pfn >= num_physpages)
                num_physpages = pfn + 1;
+}
+EXPORT_SYMBOL_GPL(__online_page_set_limits);
+
+void __online_page_increment_counters(struct page *page)
+{
+       totalram_pages++;
 
 #ifdef CONFIG_HIGHMEM
        if (PageHighMem(page))
                totalhigh_pages++;
 #endif
+}
+EXPORT_SYMBOL_GPL(__online_page_increment_counters);
 
-#ifdef CONFIG_FLATMEM
-       max_mapnr = max(pfn, max_mapnr);
-#endif
-
+void __online_page_free(struct page *page)
+{
        ClearPageReserved(page);
        init_page_count(page);
        __free_page(page);
 }
+EXPORT_SYMBOL_GPL(__online_page_free);
+
+static void generic_online_page(struct page *page)
+{
+       __online_page_set_limits(page);
+       __online_page_increment_counters(page);
+       __online_page_free(page);
+}
 
 static int online_pages_range(unsigned long start_pfn, unsigned long nr_pages,
                        void *arg)
@@ -392,7 +450,7 @@ static int online_pages_range(unsigned long start_pfn, unsigned long nr_pages,
        if (PageReserved(pfn_to_page(start_pfn)))
                for (i = 0; i < nr_pages; i++) {
                        page = pfn_to_page(start_pfn + i);
-                       online_page(page);
+                       (*online_page_callback)(page);
                        onlined_pages++;
                }
        *(unsigned long *)arg = onlined_pages;
@@ -459,8 +517,9 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages)
                zone_pcp_update(zone);
 
        mutex_unlock(&zonelists_mutex);
-       setup_per_zone_wmarks();
-       calculate_zone_inactive_ratio(zone);
+
+       init_per_zone_wmark_min();
+
        if (onlined_pages) {
                kswapd_run(zone_to_nid(zone));
                node_set_state(zone_to_nid(zone), N_HIGH_MEMORY);
@@ -497,6 +556,14 @@ static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
        /* init node's zones as empty zones, we don't have any present pages.*/
        free_area_init_node(nid, zones_size, start_pfn, zholes_size);
 
+       /*
+        * The node we allocated has no zone fallback lists. For avoiding
+        * to access not-initialized zonelist, build here.
+        */
+       mutex_lock(&zonelists_mutex);
+       build_all_zonelists(NULL);
+       mutex_unlock(&zonelists_mutex);
+
        return pgdat;
 }
 
@@ -518,7 +585,7 @@ int mem_online_node(int nid)
 
        lock_memory_hotplug();
        pgdat = hotadd_new_pgdat(nid, 0);
-       if (pgdat) {
+       if (!pgdat) {
                ret = -ENOMEM;
                goto out;
        }
@@ -705,7 +772,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
                if (!pfn_valid(pfn))
                        continue;
                page = pfn_to_page(pfn);
-               if (!page_count(page))
+               if (!get_page_unless_zero(page))
                        continue;
                /*
                 * We can skip free pages. And we can only deal with pages on
@@ -713,6 +780,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
                 */
                ret = isolate_lru_page(page);
                if (!ret) { /* Success */
+                       put_page(page);
                        list_add_tail(&page->lru, &source);
                        move_pages--;
                        inc_zone_page_state(page, NR_ISOLATED_ANON +
@@ -724,6 +792,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
                               pfn);
                        dump_page(page);
 #endif
+                       put_page(page);
                        /* Because we don't have big zone->lock. we should
                           check this again here. */
                        if (page_count(page)) {
@@ -740,7 +809,7 @@ do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
                }
                /* this function returns # of failed pages */
                ret = migrate_pages(&source, hotremove_migrate_alloc, 0,
-                                                               true, true);
+                                                       true, MIGRATE_SYNC);
                if (ret)
                        putback_lru_pages(&source);
        }
@@ -893,8 +962,8 @@ repeat:
        zone->zone_pgdat->node_present_pages -= offlined_pages;
        totalram_pages -= offlined_pages;
 
-       setup_per_zone_wmarks();
-       calculate_zone_inactive_ratio(zone);
+       init_per_zone_wmark_min();
+
        if (!node_present_pages(node)) {
                node_clear_state(node, N_HIGH_MEMORY);
                kswapd_stop(node);