Merge /spare/repo/linux-2.6/
[pandora-kernel.git] / mm / page_alloc.c
index 95cbd30..7ee675a 100644 (file)
@@ -68,14 +68,9 @@ EXPORT_SYMBOL(nr_swap_pages);
  * Used by page_zone() to look up the address of the struct zone whose
  * id is encoded in the upper bits of page->flags
  */
-struct zone *zone_table[1 << (ZONES_SHIFT + NODES_SHIFT)];
+struct zone *zone_table[1 << ZONETABLE_SHIFT];
 EXPORT_SYMBOL(zone_table);
 
-#ifdef CONFIG_NUMA
-static struct per_cpu_pageset
-       pageset_table[MAX_NR_ZONES*MAX_NUMNODES*NR_CPUS] __initdata;
-#endif
-
 static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" };
 int min_free_kbytes = 1024;
 
@@ -110,11 +105,13 @@ static void bad_page(const char *function, struct page *page)
        printk(KERN_EMERG "Backtrace:\n");
        dump_stack();
        printk(KERN_EMERG "Trying to fix it up, but a reboot is needed\n");
-       page->flags &= ~(1 << PG_private        |
+       page->flags &= ~(1 << PG_lru    |
+                       1 << PG_private |
                        1 << PG_locked  |
-                       1 << PG_lru     |
                        1 << PG_active  |
                        1 << PG_dirty   |
+                       1 << PG_reclaim |
+                       1 << PG_slab    |
                        1 << PG_swapcache |
                        1 << PG_writeback);
        set_page_count(page, 0);
@@ -445,14 +442,17 @@ void set_page_refs(struct page *page, int order)
  */
 static void prep_new_page(struct page *page, int order)
 {
-       if (page->mapping || page_mapcount(page) ||
-           (page->flags & (
+       if (    page_mapcount(page) ||
+               page->mapping != NULL ||
+               page_count(page) != 0 ||
+               (page->flags & (
+                       1 << PG_lru     |
                        1 << PG_private |
                        1 << PG_locked  |
-                       1 << PG_lru     |
                        1 << PG_active  |
                        1 << PG_dirty   |
                        1 << PG_reclaim |
+                       1 << PG_slab    |
                        1 << PG_swapcache |
                        1 << PG_writeback )))
                bad_page(__FUNCTION__, page);
@@ -516,6 +516,36 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
        return allocated;
 }
 
+#ifdef CONFIG_NUMA
+/* Called from the slab reaper to drain remote pagesets */
+void drain_remote_pages(void)
+{
+       struct zone *zone;
+       int i;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       for_each_zone(zone) {
+               struct per_cpu_pageset *pset;
+
+               /* Do not drain local pagesets */
+               if (zone->zone_pgdat->node_id == numa_node_id())
+                       continue;
+
+               pset = zone->pageset[smp_processor_id()];
+               for (i = 0; i < ARRAY_SIZE(pset->pcp); i++) {
+                       struct per_cpu_pages *pcp;
+
+                       pcp = &pset->pcp[i];
+                       if (pcp->count)
+                               pcp->count -= free_pages_bulk(zone, pcp->count,
+                                               &pcp->list, 0);
+               }
+       }
+       local_irq_restore(flags);
+}
+#endif
+
 #if defined(CONFIG_PM) || defined(CONFIG_HOTPLUG_CPU)
 static void __drain_pages(unsigned int cpu)
 {
@@ -622,10 +652,10 @@ static void fastcall free_hot_cold_page(struct page *page, int cold)
        free_pages_check(__FUNCTION__, page);
        pcp = &zone_pcp(zone, get_cpu())->pcp[cold];
        local_irq_save(flags);
-       if (pcp->count >= pcp->high)
-               pcp->count -= free_pages_bulk(zone, pcp->batch, &pcp->list, 0);
        list_add(&page->lru, &pcp->list);
        pcp->count++;
+       if (pcp->count >= pcp->high)
+               pcp->count -= free_pages_bulk(zone, pcp->batch, &pcp->list, 0);
        local_irq_restore(flags);
        put_cpu();
 }
@@ -859,7 +889,7 @@ rebalance:
        reclaim_state.reclaimed_slab = 0;
        p->reclaim_state = &reclaim_state;
 
-       did_some_progress = try_to_free_pages(zones, gfp_mask, order);
+       did_some_progress = try_to_free_pages(zones, gfp_mask);
 
        p->reclaim_state = NULL;
        p->flags &= ~PF_MEMALLOC;
@@ -935,6 +965,7 @@ nopage:
                        " order:%d, mode:0x%x\n",
                        p->comm, order, gfp_mask);
                dump_stack();
+               show_mem();
        }
        return NULL;
 got_pg:
@@ -1144,7 +1175,7 @@ void get_full_page_state(struct page_state *ret)
        __get_page_state(ret, sizeof(*ret) / sizeof(unsigned long));
 }
 
-unsigned long __read_page_state(unsigned offset)
+unsigned long __read_page_state(unsigned long offset)
 {
        unsigned long ret = 0;
        int cpu;
@@ -1158,7 +1189,7 @@ unsigned long __read_page_state(unsigned offset)
        return ret;
 }
 
-void __mod_page_state(unsigned offset, unsigned long delta)
+void __mod_page_state(unsigned long offset, unsigned long delta)
 {
        unsigned long flags;
        void* ptr;
@@ -1270,19 +1301,20 @@ void show_free_areas(void)
                        pageset = zone_pcp(zone, cpu);
 
                        for (temperature = 0; temperature < 2; temperature++)
-                               printk("cpu %d %s: low %d, high %d, batch %d\n",
+                               printk("cpu %d %s: low %d, high %d, batch %d used:%d\n",
                                        cpu,
                                        temperature ? "cold" : "hot",
                                        pageset->pcp[temperature].low,
                                        pageset->pcp[temperature].high,
-                                       pageset->pcp[temperature].batch);
+                                       pageset->pcp[temperature].batch,
+                                       pageset->pcp[temperature].count);
                }
        }
 
        get_page_state(&ps);
        get_zone_counts(&active, &inactive, &free);
 
-       printk("\nFree pages: %11ukB (%ukB HighMem)\n",
+       printk("Free pages: %11ukB (%ukB HighMem)\n",
                K(nr_free_pages()),
                K(nr_free_highpages()));
 
@@ -1617,11 +1649,17 @@ static void __init calculate_zone_totalpages(struct pglist_data *pgdat,
 void __init memmap_init_zone(unsigned long size, int nid, unsigned long zone,
                unsigned long start_pfn)
 {
-       struct page *start = pfn_to_page(start_pfn);
        struct page *page;
+       unsigned long end_pfn = start_pfn + size;
+       unsigned long pfn;
 
-       for (page = start; page < (start + size); page++) {
-               set_page_zone(page, NODEZONE(nid, zone));
+       for (pfn = start_pfn; pfn < end_pfn; pfn++, page++) {
+               if (!early_pfn_valid(pfn))
+                       continue;
+               if (!early_pfn_in_nid(pfn, nid))
+                       continue;
+               page = pfn_to_page(pfn);
+               set_page_links(page, zone, nid, pfn);
                set_page_count(page, 0);
                reset_page_mapcount(page);
                SetPageReserved(page);
@@ -1645,6 +1683,20 @@ void zone_init_free_lists(struct pglist_data *pgdat, struct zone *zone,
        }
 }
 
+#define ZONETABLE_INDEX(x, zone_nr)    ((x << ZONES_SHIFT) | zone_nr)
+void zonetable_add(struct zone *zone, int nid, int zid, unsigned long pfn,
+               unsigned long size)
+{
+       unsigned long snum = pfn_to_section_nr(pfn);
+       unsigned long end = pfn_to_section_nr(pfn + size);
+
+       if (FLAGS_HAS_NODE)
+               zone_table[ZONETABLE_INDEX(nid, zid)] = zone;
+       else
+               for (; snum <= end; snum++)
+                       zone_table[ZONETABLE_INDEX(snum, zid)] = zone;
+}
+
 #ifndef __HAVE_ARCH_MEMMAP_INIT
 #define memmap_init(size, nid, zone, start_pfn) \
        memmap_init_zone((size), (nid), (zone), (start_pfn))
@@ -1682,57 +1734,62 @@ static int __devinit zone_batchsize(struct zone *zone)
        return batch;
 }
 
+inline void setup_pageset(struct per_cpu_pageset *p, unsigned long batch)
+{
+       struct per_cpu_pages *pcp;
+
+       pcp = &p->pcp[0];               /* hot */
+       pcp->count = 0;
+       pcp->low = 2 * batch;
+       pcp->high = 6 * batch;
+       pcp->batch = max(1UL, 1 * batch);
+       INIT_LIST_HEAD(&pcp->list);
+
+       pcp = &p->pcp[1];               /* cold*/
+       pcp->count = 0;
+       pcp->low = 0;
+       pcp->high = 2 * batch;
+       pcp->batch = max(1UL, 1 * batch);
+       INIT_LIST_HEAD(&pcp->list);
+}
+
 #ifdef CONFIG_NUMA
 /*
- * Dynamicaly allocate memory for the
+ * Boot pageset table. One per cpu which is going to be used for all
+ * zones and all nodes. The parameters will be set in such a way
+ * that an item put on a list will immediately be handed over to
+ * the buddy list. This is safe since pageset manipulation is done
+ * with interrupts disabled.
+ *
+ * Some NUMA counter updates may also be caught by the boot pagesets.
+ *
+ * The boot_pagesets must be kept even after bootup is complete for
+ * unused processors and/or zones. They do play a role for bootstrapping
+ * hotplugged processors.
+ *
+ * zoneinfo_show() and maybe other functions do
+ * not check if the processor is online before following the pageset pointer.
+ * Other parts of the kernel may not check if the zone is available.
+ */
+static struct per_cpu_pageset
+       boot_pageset[NR_CPUS];
+
+/*
+ * Dynamically allocate memory for the
  * per cpu pageset array in struct zone.
  */
 static int __devinit process_zones(int cpu)
 {
        struct zone *zone, *dzone;
-       int i;
 
        for_each_zone(zone) {
-               struct per_cpu_pageset *npageset = NULL;
 
-               npageset = kmalloc_node(sizeof(struct per_cpu_pageset),
+               zone->pageset[cpu] = kmalloc_node(sizeof(struct per_cpu_pageset),
                                         GFP_KERNEL, cpu_to_node(cpu));
-               if (!npageset) {
-                       zone->pageset[cpu] = NULL;
+               if (!zone->pageset[cpu])
                        goto bad;
-               }
-
-               if (zone->pageset[cpu]) {
-                       memcpy(npageset, zone->pageset[cpu],
-                                       sizeof(struct per_cpu_pageset));
 
-                       /* Relocate lists */
-                       for (i = 0; i < 2; i++) {
-                               INIT_LIST_HEAD(&npageset->pcp[i].list);
-                               list_splice(&zone->pageset[cpu]->pcp[i].list,
-                                       &npageset->pcp[i].list);
-                       }
-               } else {
-                       struct per_cpu_pages *pcp;
-                       unsigned long batch;
-
-                       batch = zone_batchsize(zone);
-
-                       pcp = &npageset->pcp[0];                /* hot */
-                       pcp->count = 0;
-                       pcp->low = 2 * batch;
-                       pcp->high = 6 * batch;
-                       pcp->batch = 1 * batch;
-                       INIT_LIST_HEAD(&pcp->list);
-
-                       pcp = &npageset->pcp[1];                /* cold*/
-                       pcp->count = 0;
-                       pcp->low = 0;
-                       pcp->high = 2 * batch;
-                       pcp->batch = 1 * batch;
-                       INIT_LIST_HEAD(&pcp->list);
-               }
-               zone->pageset[cpu] = npageset;
+               setup_pageset(zone->pageset[cpu], zone_batchsize(zone));
        }
 
        return 0;
@@ -1824,7 +1881,6 @@ static void __init free_area_init_core(struct pglist_data *pgdat,
                unsigned long size, realsize;
                unsigned long batch;
 
-               zone_table[NODEZONE(nid, j)] = zone;
                realsize = size = zones_size[j];
                if (zholes_size)
                        realsize -= zholes_size[j];
@@ -1846,30 +1902,13 @@ static void __init free_area_init_core(struct pglist_data *pgdat,
                batch = zone_batchsize(zone);
 
                for (cpu = 0; cpu < NR_CPUS; cpu++) {
-                       struct per_cpu_pages *pcp;
 #ifdef CONFIG_NUMA
-                       struct per_cpu_pageset *pgset;
-                       pgset = &pageset_table[nid*MAX_NR_ZONES*NR_CPUS +
-                                       (j * NR_CPUS) + cpu];
-
-                       zone->pageset[cpu] = pgset;
+                       /* Early boot. Slab allocator not functional yet */
+                       zone->pageset[cpu] = &boot_pageset[cpu];
+                       setup_pageset(&boot_pageset[cpu],0);
 #else
-                       struct per_cpu_pageset *pgset = zone_pcp(zone, cpu);
+                       setup_pageset(zone_pcp(zone,cpu), batch);
 #endif
-
-                       pcp = &pgset->pcp[0];                   /* hot */
-                       pcp->count = 0;
-                       pcp->low = 2 * batch;
-                       pcp->high = 6 * batch;
-                       pcp->batch = 1 * batch;
-                       INIT_LIST_HEAD(&pcp->list);
-
-                       pcp = &pgset->pcp[1];                   /* cold */
-                       pcp->count = 0;
-                       pcp->low = 0;
-                       pcp->high = 2 * batch;
-                       pcp->batch = 1 * batch;
-                       INIT_LIST_HEAD(&pcp->list);
                }
                printk(KERN_DEBUG "  %s zone: %lu pages, LIFO batch:%lu\n",
                                zone_names[j], realsize, batch);
@@ -1907,6 +1946,8 @@ static void __init free_area_init_core(struct pglist_data *pgdat,
 
                memmap_init(size, nid, j, zone_start_pfn);
 
+               zonetable_add(zone, nid, j, zone_start_pfn, size);
+
                zone_start_pfn += size;
 
                zone_init_free_lists(pgdat, zone, zone->spanned_pages);
@@ -1915,24 +1956,30 @@ static void __init free_area_init_core(struct pglist_data *pgdat,
 
 static void __init alloc_node_mem_map(struct pglist_data *pgdat)
 {
-       unsigned long size;
-
        /* Skip empty nodes */
        if (!pgdat->node_spanned_pages)
                return;
 
+#ifdef CONFIG_FLAT_NODE_MEM_MAP
        /* ia64 gets its own node_mem_map, before this, without bootmem */
        if (!pgdat->node_mem_map) {
+               unsigned long size;
+               struct page *map;
+
                size = (pgdat->node_spanned_pages + 1) * sizeof(struct page);
-               pgdat->node_mem_map = alloc_bootmem_node(pgdat, size);
+               map = alloc_remap(pgdat->node_id, size);
+               if (!map)
+                       map = alloc_bootmem_node(pgdat, size);
+               pgdat->node_mem_map = map;
        }
-#ifndef CONFIG_DISCONTIGMEM
+#ifdef CONFIG_FLATMEM
        /*
         * With no DISCONTIG, the global mem_map is just set as node 0's
         */
        if (pgdat == NODE_DATA(0))
                mem_map = NODE_DATA(0)->node_mem_map;
 #endif
+#endif /* CONFIG_FLAT_NODE_MEM_MAP */
 }
 
 void __init free_area_init_node(int nid, struct pglist_data *pgdat,
@@ -1948,18 +1995,18 @@ void __init free_area_init_node(int nid, struct pglist_data *pgdat,
        free_area_init_core(pgdat, zones_size, zholes_size);
 }
 
-#ifndef CONFIG_DISCONTIGMEM
+#ifndef CONFIG_NEED_MULTIPLE_NODES
 static bootmem_data_t contig_bootmem_data;
 struct pglist_data contig_page_data = { .bdata = &contig_bootmem_data };
 
 EXPORT_SYMBOL(contig_page_data);
+#endif
 
 void __init free_area_init(unsigned long *zones_size)
 {
-       free_area_init_node(0, &contig_page_data, zones_size,
+       free_area_init_node(0, NODE_DATA(0), zones_size,
                        __pa(PAGE_OFFSET) >> PAGE_SHIFT, NULL);
 }
-#endif
 
 #ifdef CONFIG_PROC_FS