]> err.no Git - linux-2.6/blobdiff - mm/page_alloc.c
Merge git://git.linux-nfs.org/pub/linux/nfs-2.6
[linux-2.6] / mm / page_alloc.c
index cdad3249cf7fc101892ee9d451c2f4726f3601ca..fd47494cb9890b887c55dbd76831819b1c5c645c 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/memory_hotplug.h>
 #include <linux/nodemask.h>
 #include <linux/vmalloc.h>
+#include <linux/mempolicy.h>
 
 #include <asm/tlbflush.h>
 #include "internal.h"
@@ -132,16 +133,16 @@ static inline int bad_range(struct zone *zone, struct page *page)
 }
 #endif
 
-static void bad_page(const char *function, struct page *page)
+static void bad_page(struct page *page)
 {
-       printk(KERN_EMERG "Bad page state at %s (in process '%s', page %p)\n",
-               function, current->comm, page);
-       printk(KERN_EMERG "flags:0x%0*lx mapping:%p mapcount:%d count:%d\n",
-               (int)(2*sizeof(unsigned long)), (unsigned long)page->flags,
-               page->mapping, page_mapcount(page), page_count(page));
-       printk(KERN_EMERG "Backtrace:\n");
+       printk(KERN_EMERG "Bad page state in process '%s'\n"
+               "page:%p flags:0x%0*lx mapping:%p mapcount:%d count:%d\n"
+               "Trying to fix it up, but a reboot is needed\n"
+               "Backtrace:\n",
+               current->comm, page, (int)(2*sizeof(unsigned long)),
+               (unsigned long)page->flags, page->mapping,
+               page_mapcount(page), page_count(page));
        dump_stack();
-       printk(KERN_EMERG "Trying to fix it up, but a reboot is needed\n");
        page->flags &= ~(1 << PG_lru    |
                        1 << PG_private |
                        1 << PG_locked  |
@@ -194,19 +195,15 @@ static void destroy_compound_page(struct page *page, unsigned long order)
        int i;
        int nr_pages = 1 << order;
 
-       if (!PageCompound(page))
-               return;
-
-       if (page[1].index != order)
-               bad_page(__FUNCTION__, page);
+       if (unlikely(page[1].index != order))
+               bad_page(page);
 
        for (i = 0; i < nr_pages; i++) {
                struct page *p = page + i;
 
-               if (!PageCompound(p))
-                       bad_page(__FUNCTION__, page);
-               if (page_private(p) != (unsigned long)page)
-                       bad_page(__FUNCTION__, page);
+               if (unlikely(!PageCompound(p) |
+                               (page_private(p) != (unsigned long)page)))
+                       bad_page(page);
                ClearPageCompound(p);
        }
 }
@@ -316,7 +313,7 @@ static inline void __free_pages_bulk (struct page *page,
        unsigned long page_idx;
        int order_size = 1 << order;
 
-       if (unlikely(order))
+       if (unlikely(PageCompound(page)))
                destroy_compound_page(page, order);
 
        page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);
@@ -348,7 +345,7 @@ static inline void __free_pages_bulk (struct page *page,
        zone->free_area[order].nr_free++;
 }
 
-static inline int free_pages_check(const char *function, struct page *page)
+static inline int free_pages_check(struct page *page)
 {
        if (unlikely(page_mapcount(page) |
                (page->mapping != NULL)  |
@@ -363,7 +360,7 @@ static inline int free_pages_check(const char *function, struct page *page)
                        1 << PG_swapcache |
                        1 << PG_writeback |
                        1 << PG_reserved ))))
-               bad_page(function, page);
+               bad_page(page);
        if (PageDirty(page))
                __ClearPageDirty(page);
        /*
@@ -422,14 +419,14 @@ void __free_pages_ok(struct page *page, unsigned int order)
 #endif
 
        for (i = 0 ; i < (1 << order) ; ++i)
-               reserved += free_pages_check(__FUNCTION__, page + i);
+               reserved += free_pages_check(page + i);
        if (reserved)
                return;
 
        list_add(&page->lru, &list);
-       mod_page_state(pgfree, 1 << order);
        kernel_map_pages(page, 1<<order, 0);
        local_irq_save(flags);
+       __mod_page_state(pgfree, 1 << order);
        free_pages_bulk(page_zone(page), 1, &list, order);
        local_irq_restore(flags);
 }
@@ -517,7 +514,7 @@ static int prep_new_page(struct page *page, int order)
                        1 << PG_swapcache |
                        1 << PG_writeback |
                        1 << PG_reserved ))))
-               bad_page(__FUNCTION__, page);
+               bad_page(page);
 
        /*
         * For now, we report if PG_reserved was found set, but do not
@@ -677,18 +674,14 @@ void drain_local_pages(void)
 }
 #endif /* CONFIG_PM */
 
-static void zone_statistics(struct zonelist *zonelist, struct zone *z)
+static void zone_statistics(struct zonelist *zonelist, struct zone *z, int cpu)
 {
 #ifdef CONFIG_NUMA
-       unsigned long flags;
-       int cpu;
        pg_data_t *pg = z->zone_pgdat;
        pg_data_t *orig = zonelist->zones[0]->zone_pgdat;
        struct per_cpu_pageset *p;
 
-       local_irq_save(flags);
-       cpu = smp_processor_id();
-       p = zone_pcp(z,cpu);
+       p = zone_pcp(z, cpu);
        if (pg == orig) {
                p->numa_hit++;
        } else {
@@ -699,7 +692,6 @@ static void zone_statistics(struct zonelist *zonelist, struct zone *z)
                p->local_node++;
        else
                p->other_node++;
-       local_irq_restore(flags);
 #endif
 }
 
@@ -716,14 +708,14 @@ static void fastcall free_hot_cold_page(struct page *page, int cold)
 
        if (PageAnon(page))
                page->mapping = NULL;
-       if (free_pages_check(__FUNCTION__, page))
+       if (free_pages_check(page))
                return;
 
-       inc_page_state(pgfree);
        kernel_map_pages(page, 1, 0);
 
        pcp = &zone_pcp(zone, get_cpu())->pcp[cold];
        local_irq_save(flags);
+       __inc_page_state(pgfree);
        list_add(&page->lru, &pcp->list);
        pcp->count++;
        if (pcp->count >= pcp->high)
@@ -756,49 +748,58 @@ static inline void prep_zero_page(struct page *page, int order, gfp_t gfp_flags)
  * we cheat by calling it from here, in the order > 0 path.  Saves a branch
  * or two.
  */
-static struct page *
-buffered_rmqueue(struct zone *zone, int order, gfp_t gfp_flags)
+static struct page *buffered_rmqueue(struct zonelist *zonelist,
+                       struct zone *zone, int order, gfp_t gfp_flags)
 {
        unsigned long flags;
        struct page *page;
        int cold = !!(gfp_flags & __GFP_COLD);
+       int cpu;
 
 again:
+       cpu  = get_cpu();
        if (order == 0) {
                struct per_cpu_pages *pcp;
 
-               page = NULL;
-               pcp = &zone_pcp(zone, get_cpu())->pcp[cold];
+               pcp = &zone_pcp(zone, cpu)->pcp[cold];
                local_irq_save(flags);
-               if (!pcp->count)
+               if (!pcp->count) {
                        pcp->count += rmqueue_bulk(zone, 0,
                                                pcp->batch, &pcp->list);
-               if (likely(pcp->count)) {
-                       page = list_entry(pcp->list.next, struct page, lru);
-                       list_del(&page->lru);
-                       pcp->count--;
+                       if (unlikely(!pcp->count))
+                               goto failed;
                }
-               local_irq_restore(flags);
-               put_cpu();
+               page = list_entry(pcp->list.next, struct page, lru);
+               list_del(&page->lru);
+               pcp->count--;
        } else {
                spin_lock_irqsave(&zone->lock, flags);
                page = __rmqueue(zone, order);
-               spin_unlock_irqrestore(&zone->lock, flags);
+               spin_unlock(&zone->lock);
+               if (!page)
+                       goto failed;
        }
 
-       if (page != NULL) {
-               BUG_ON(bad_range(zone, page));
-               mod_page_state_zone(zone, pgalloc, 1 << order);
-               if (prep_new_page(page, order))
-                       goto again;
+       __mod_page_state_zone(zone, pgalloc, 1 << order);
+       zone_statistics(zonelist, zone, cpu);
+       local_irq_restore(flags);
+       put_cpu();
 
-               if (gfp_flags & __GFP_ZERO)
-                       prep_zero_page(page, order, gfp_flags);
+       BUG_ON(bad_range(zone, page));
+       if (prep_new_page(page, order))
+               goto again;
 
-               if (order && (gfp_flags & __GFP_COMP))
-                       prep_compound_page(page, order);
-       }
+       if (gfp_flags & __GFP_ZERO)
+               prep_zero_page(page, order, gfp_flags);
+
+       if (order && (gfp_flags & __GFP_COMP))
+               prep_compound_page(page, order);
        return page;
+
+failed:
+       local_irq_restore(flags);
+       put_cpu();
+       return NULL;
 }
 
 #define ALLOC_NO_WATERMARKS    0x01 /* don't check watermarks at all */
@@ -874,9 +875,8 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order,
                                continue;
                }
 
-               page = buffered_rmqueue(*z, order, gfp_mask);
+               page = buffered_rmqueue(zonelist, *z, order, gfp_mask);
                if (page) {
-                       zone_statistics(zonelist, *z);
                        break;
                }
        } while (*(++z) != NULL);
@@ -1251,7 +1251,7 @@ void get_full_page_state(struct page_state *ret)
        __get_page_state(ret, sizeof(*ret) / sizeof(unsigned long), &mask);
 }
 
-unsigned long __read_page_state(unsigned long offset)
+unsigned long read_page_state_offset(unsigned long offset)
 {
        unsigned long ret = 0;
        int cpu;
@@ -1265,18 +1265,26 @@ unsigned long __read_page_state(unsigned long offset)
        return ret;
 }
 
-void __mod_page_state(unsigned long offset, unsigned long delta)
+void __mod_page_state_offset(unsigned long offset, unsigned long delta)
+{
+       void *ptr;
+
+       ptr = &__get_cpu_var(page_states);
+       *(unsigned long *)(ptr + offset) += delta;
+}
+EXPORT_SYMBOL(__mod_page_state_offset);
+
+void mod_page_state_offset(unsigned long offset, unsigned long delta)
 {
        unsigned long flags;
-       voidptr;
+       void *ptr;
 
        local_irq_save(flags);
        ptr = &__get_cpu_var(page_states);
-       *(unsigned long*)(ptr + offset) += delta;
+       *(unsigned long *)(ptr + offset) += delta;
        local_irq_restore(flags);
 }
-
-EXPORT_SYMBOL(__mod_page_state);
+EXPORT_SYMBOL(mod_page_state_offset);
 
 void __get_zone_counts(unsigned long *active, unsigned long *inactive,
                        unsigned long *free, struct pglist_data *pgdat)
@@ -1362,7 +1370,7 @@ void show_free_areas(void)
                show_node(zone);
                printk("%s per-cpu:", zone->name);
 
-               if (!zone->present_pages) {
+               if (!populated_zone(zone)) {
                        printk(" empty\n");
                        continue;
                } else
@@ -1439,7 +1447,7 @@ void show_free_areas(void)
 
                show_node(zone);
                printk("%s: ", zone->name);
-               if (!zone->present_pages) {
+               if (!populated_zone(zone)) {
                        printk("empty\n");
                        continue;
                }
@@ -1459,36 +1467,29 @@ void show_free_areas(void)
 
 /*
  * Builds allocation fallback zone lists.
+ *
+ * Add all populated zones of a node to the zonelist.
  */
-static int __init build_zonelists_node(pg_data_t *pgdat, struct zonelist *zonelist, int j, int k)
-{
-       switch (k) {
-               struct zone *zone;
-       default:
-               BUG();
-       case ZONE_HIGHMEM:
-               zone = pgdat->node_zones + ZONE_HIGHMEM;
-               if (zone->present_pages) {
+static int __init build_zonelists_node(pg_data_t *pgdat,
+                       struct zonelist *zonelist, int nr_zones, int zone_type)
+{
+       struct zone *zone;
+
+       BUG_ON(zone_type > ZONE_HIGHMEM);
+
+       do {
+               zone = pgdat->node_zones + zone_type;
+               if (populated_zone(zone)) {
 #ifndef CONFIG_HIGHMEM
-                       BUG();
+                       BUG_ON(zone_type > ZONE_NORMAL);
 #endif
-                       zonelist->zones[j++] = zone;
+                       zonelist->zones[nr_zones++] = zone;
+                       check_highest_zone(zone_type);
                }
-       case ZONE_NORMAL:
-               zone = pgdat->node_zones + ZONE_NORMAL;
-               if (zone->present_pages)
-                       zonelist->zones[j++] = zone;
-       case ZONE_DMA32:
-               zone = pgdat->node_zones + ZONE_DMA32;
-               if (zone->present_pages)
-                       zonelist->zones[j++] = zone;
-       case ZONE_DMA:
-               zone = pgdat->node_zones + ZONE_DMA;
-               if (zone->present_pages)
-                       zonelist->zones[j++] = zone;
-       }
+               zone_type--;
 
-       return j;
+       } while (zone_type >= 0);
+       return nr_zones;
 }
 
 static inline int highest_zone(int zone_bits)
@@ -2138,7 +2139,7 @@ static int frag_show(struct seq_file *m, void *arg)
        int order;
 
        for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
-               if (!zone->present_pages)
+               if (!populated_zone(zone))
                        continue;
 
                spin_lock_irqsave(&zone->lock, flags);
@@ -2171,7 +2172,7 @@ static int zoneinfo_show(struct seq_file *m, void *arg)
        for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; zone++) {
                int i;
 
-               if (!zone->present_pages)
+               if (!populated_zone(zone))
                        continue;
 
                spin_lock_irqsave(&zone->lock, flags);
@@ -2277,32 +2278,40 @@ static char *vmstat_text[] = {
        "pgpgout",
        "pswpin",
        "pswpout",
-       "pgalloc_high",
 
+       "pgalloc_high",
        "pgalloc_normal",
+       "pgalloc_dma32",
        "pgalloc_dma",
+
        "pgfree",
        "pgactivate",
        "pgdeactivate",
 
        "pgfault",
        "pgmajfault",
+
        "pgrefill_high",
        "pgrefill_normal",
+       "pgrefill_dma32",
        "pgrefill_dma",
 
        "pgsteal_high",
        "pgsteal_normal",
+       "pgsteal_dma32",
        "pgsteal_dma",
+
        "pgscan_kswapd_high",
        "pgscan_kswapd_normal",
-
+       "pgscan_kswapd_dma32",
        "pgscan_kswapd_dma",
+
        "pgscan_direct_high",
        "pgscan_direct_normal",
+       "pgscan_direct_dma32",
        "pgscan_direct_dma",
-       "pginodesteal",
 
+       "pginodesteal",
        "slabs_scanned",
        "kswapd_steal",
        "kswapd_inodesteal",