This stack check implementation leverages the compiler's profiling (gcc -p)
[linux-2.6.git] / mm / page_alloc.c
index 4824cc4..675b061 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/cpu.h>
 #include <linux/vs_base.h>
 #include <linux/vs_limit.h>
+#include <linux/ckrm_mem_inline.h>
 
 #include <asm/tlbflush.h>
 
@@ -40,13 +41,18 @@ DECLARE_BITMAP(node_online_map, MAX_NUMNODES);
 struct pglist_data *pgdat_list;
 unsigned long totalram_pages;
 unsigned long totalhigh_pages;
-int nr_swap_pages;
+long nr_swap_pages;
 int numnodes = 1;
 int sysctl_lower_zone_protection = 0;
 
 EXPORT_SYMBOL(totalram_pages);
 EXPORT_SYMBOL(nr_swap_pages);
 
+#ifdef CONFIG_CRASH_DUMP_MODULE
+/* This symbol has to be exported to use 'for_each_pgdat' macro by modules. */
+EXPORT_SYMBOL(pgdat_list);
+#endif
+
 /*
  * Used by page_zone() to look up the address of the struct zone whose
  * id is encoded in the upper bits of page->flags
@@ -57,6 +63,9 @@ EXPORT_SYMBOL(zone_table);
 static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" };
 int min_free_kbytes = 1024;
 
+static unsigned long __initdata nr_kernel_pages;
+static unsigned long __initdata nr_all_pages;
+
 /*
  * Temporary debugging check for pages not lying within a given zone.
  */
@@ -95,7 +104,8 @@ static void bad_page(const char *function, struct page *page)
        page->mapcount = 0;
 }
 
-#ifndef CONFIG_HUGETLB_PAGE
+#if !defined(CONFIG_HUGETLB_PAGE) && !defined(CONFIG_CRASH_DUMP) \
+       && !defined(CONFIG_CRASH_DUMP_MODULE)
 #define prep_compound_page(page, order) do { } while (0)
 #define destroy_compound_page(page, order) do { } while (0)
 #else
@@ -121,7 +131,7 @@ static void prep_compound_page(struct page *page, unsigned long order)
        int i;
        int nr_pages = 1 << order;
 
-       page[1].mapping = 0;
+       page[1].mapping = NULL;
        page[1].index = order;
        for (i = 0; i < nr_pages; i++) {
                struct page *p = page + i;
@@ -178,20 +188,20 @@ static void destroy_compound_page(struct page *page, unsigned long order)
  */
 
 static inline void __free_pages_bulk (struct page *page, struct page *base,
-               struct zone *zone, struct free_area *area, unsigned long mask,
-               unsigned int order)
+               struct zone *zone, struct free_area *area, unsigned int order)
 {
-       unsigned long page_idx, index;
+       unsigned long page_idx, index, mask;
 
        if (order)
                destroy_compound_page(page, order);
+       mask = (~0UL) << order;
        page_idx = page - base;
        if (page_idx & ~mask)
                BUG();
        index = page_idx >> (1 + order);
 
-       zone->free_pages -= mask;
-       while (mask + (1 << (MAX_ORDER-1))) {
+       zone->free_pages += 1 << order;
+       while (order < MAX_ORDER-1) {
                struct page *buddy1, *buddy2;
 
                BUG_ON(area >= zone->free_area + MAX_ORDER);
@@ -200,17 +210,15 @@ static inline void __free_pages_bulk (struct page *page, struct page *base,
                         * the buddy page is still allocated.
                         */
                        break;
-               /*
-                * Move the buddy up one level.
-                * This code is taking advantage of the identity:
-                *      -mask = 1+~mask
-                */
-               buddy1 = base + (page_idx ^ -mask);
+
+               /* Move the buddy up one level. */
+               buddy1 = base + (page_idx ^ (1 << order));
                buddy2 = base + page_idx;
                BUG_ON(bad_range(zone, buddy1));
                BUG_ON(bad_range(zone, buddy2));
                list_del(&buddy1->lru);
                mask <<= 1;
+               order++;
                area++;
                index >>= 1;
                page_idx &= mask;
@@ -254,12 +262,11 @@ static int
 free_pages_bulk(struct zone *zone, int count,
                struct list_head *list, unsigned int order)
 {
-       unsigned long mask, flags;
+       unsigned long flags;
        struct free_area *area;
        struct page *base, *page = NULL;
        int ret = 0;
 
-       mask = (~0UL) << order;
        base = zone->zone_mem_map;
        area = zone->free_area + order;
        spin_lock_irqsave(&zone->lock, flags);
@@ -269,7 +276,8 @@ free_pages_bulk(struct zone *zone, int count,
                page = list_entry(list->prev, struct page, lru);
                /* have to delete it as __free_pages_bulk list manipulates */
                list_del(&page->lru);
-               __free_pages_bulk(page, base, zone, area, mask, order);
+               __free_pages_bulk(page, base, zone, area, order);
+               ckrm_clear_page_class(page);
                ret++;
        }
        spin_unlock_irqrestore(&zone->lock, flags);
@@ -294,6 +302,20 @@ void __free_pages_ok(struct page *page, unsigned int order)
 #define MARK_USED(index, order, area) \
        __change_bit((index) >> (1+(order)), (area)->map)
 
+/*
+ * The order of subdivision here is critical for the IO subsystem.
+ * Please do not alter this order without good reasons and regression
+ * testing. Specifically, as large blocks of memory are subdivided,
+ * the order in which smaller blocks are delivered depends on the order
+ * they're subdivided in this function. This is the primary factor
+ * influencing the order in which pages are delivered to the IO
+ * subsystem according to empirical testing, and this is also justified
+ * by considering the behavior of a buddy system containing a single
+ * large block of memory acted on by a series of small allocations.
+ * This behavior is a critical factor in sglist merging's success.
+ *
+ * -- wli
+ */
 static inline struct page *
 expand(struct zone *zone, struct page *page,
         unsigned long index, int low, int high, struct free_area *area)
@@ -301,14 +323,12 @@ expand(struct zone *zone, struct page *page,
        unsigned long size = 1 << high;
 
        while (high > low) {
-               BUG_ON(bad_range(zone, page));
                area--;
                high--;
                size >>= 1;
-               list_add(&page->lru, &area->free_list);
-               MARK_USED(index, high, area);
-               index += size;
-               page += size;
+               BUG_ON(bad_range(zone, &page[size]));
+               list_add(&page[size].lru, &area->free_list);
+               MARK_USED(index + size, high, area);
        }
        return page;
 }
@@ -604,6 +624,10 @@ __alloc_pages(unsigned int gfp_mask, unsigned int order,
 
        might_sleep_if(wait);
 
+       if (!ckrm_class_limit_ok((GET_MEM_CLASS(current)))) {
+               return NULL;
+       }
+
        zones = zonelist->zones;  /* the list of zones suitable for gfp_mask */
        if (zones[0] == NULL)     /* no zones in the zonelist */
                return NULL;
@@ -733,6 +757,7 @@ nopage:
        return NULL;
 got_pg:
        kernel_map_pages(page, 1 << order, 1);
+       ckrm_set_pages_class(page, 1 << order, GET_MEM_CLASS(current));
        return page;
 }
 
@@ -819,17 +844,6 @@ unsigned int nr_free_pages(void)
 
 EXPORT_SYMBOL(nr_free_pages);
 
-unsigned int nr_used_zone_pages(void)
-{
-       unsigned int pages = 0;
-       struct zone *zone;
-
-       for_each_zone(zone)
-               pages += zone->nr_active + zone->nr_inactive;
-
-       return pages;
-}
-
 #ifdef CONFIG_NUMA
 unsigned int nr_free_pages_pgdat(pg_data_t *pgdat)
 {
@@ -1231,7 +1245,7 @@ static void __init build_zonelists(pg_data_t *pgdat)
        DECLARE_BITMAP(used_mask, MAX_NUMNODES);
 
        /* initialize zonelists */
-       for (i = 0; i < MAX_NR_ZONES; i++) {
+       for (i = 0; i < GFP_ZONETYPES; i++) {
                zonelist = pgdat->node_zonelists + i;
                memset(zonelist, 0, sizeof(*zonelist));
                zonelist->zones[0] = NULL;
@@ -1253,7 +1267,7 @@ static void __init build_zonelists(pg_data_t *pgdat)
                        node_load[node] += load;
                prev_node = node;
                load--;
-               for (i = 0; i < MAX_NR_ZONES; i++) {
+               for (i = 0; i < GFP_ZONETYPES; i++) {
                        zonelist = pgdat->node_zonelists + i;
                        for (j = 0; zonelist->zones[j] != NULL; j++);
 
@@ -1276,7 +1290,7 @@ static void __init build_zonelists(pg_data_t *pgdat)
        int i, j, k, node, local_node;
 
        local_node = pgdat->node_id;
-       for (i = 0; i < MAX_NR_ZONES; i++) {
+       for (i = 0; i < GFP_ZONETYPES; i++) {
                struct zonelist *zonelist;
 
                zonelist = pgdat->node_zonelists + i;
@@ -1377,7 +1391,7 @@ static void __init calculate_zone_totalpages(struct pglist_data *pgdat,
                for (i = 0; i < MAX_NR_ZONES; i++)
                        realtotalpages -= zholes_size[i];
        pgdat->node_present_pages = realtotalpages;
-       printk("On node %d totalpages: %lu\n", pgdat->node_id, realtotalpages);
+       printk(KERN_DEBUG "On node %d totalpages: %lu\n", pgdat->node_id, realtotalpages);
 }
 
 
@@ -1398,7 +1412,7 @@ void __init memmap_init_zone(struct page *start, unsigned long size, int nid,
                INIT_LIST_HEAD(&page->lru);
 #ifdef WANT_PAGE_VIRTUAL
                /* The shift won't overflow because ZONE_NORMAL is below 4G. */
-               if (zone != ZONE_HIGHMEM)
+               if (!is_highmem_idx(zone))
                        set_page_address(page, __va(start_pfn << PAGE_SHIFT));
 #endif
                start_pfn++;
@@ -1438,6 +1452,10 @@ static void __init free_area_init_core(struct pglist_data *pgdat,
                if (zholes_size)
                        realsize -= zholes_size[j];
 
+               if (j == ZONE_DMA || j == ZONE_NORMAL)
+                       nr_kernel_pages += realsize;
+               nr_all_pages += realsize;
+
                zone->spanned_pages = size;
                zone->present_pages = realsize;
                zone->name = zone_names[j];
@@ -1479,12 +1497,12 @@ static void __init free_area_init_core(struct pglist_data *pgdat,
                        pcp->batch = 1 * batch;
                        INIT_LIST_HEAD(&pcp->list);
                }
-               printk("  %s zone: %lu pages, LIFO batch:%lu\n",
+               printk(KERN_DEBUG "  %s zone: %lu pages, LIFO batch:%lu\n",
                                zone_names[j], realsize, batch);
                INIT_LIST_HEAD(&zone->active_list);
                INIT_LIST_HEAD(&zone->inactive_list);
-               atomic_set(&zone->nr_scan_active, 0);
-               atomic_set(&zone->nr_scan_inactive, 0);
+               zone->nr_scan_active = 0;
+               zone->nr_scan_inactive = 0;
                zone->nr_active = 0;
                zone->nr_inactive = 0;
                if (!size)
@@ -1832,7 +1850,7 @@ static void setup_per_zone_protection(void)
                 * For each of the different allocation types:
                 * GFP_DMA -> GFP_KERNEL -> GFP_HIGHMEM
                 */
-               for (i = 0; i < MAX_NR_ZONES; i++) {
+               for (i = 0; i < GFP_ZONETYPES; i++) {
                        /*
                         * For each of the zones:
                         * ZONE_HIGHMEM -> ZONE_NORMAL -> ZONE_DMA
@@ -1958,9 +1976,9 @@ module_init(init_per_zone_pages_min)
  *     changes.
  */
 int min_free_kbytes_sysctl_handler(ctl_table *table, int write, 
-               struct file *file, void __user *buffer, size_t *length)
+               struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
 {
-       proc_dointvec(table, write, file, buffer, length);
+       proc_dointvec(table, write, file, buffer, length, ppos);
        setup_per_zone_pages_min();
        setup_per_zone_protection();
        return 0;
@@ -1972,9 +1990,75 @@ int min_free_kbytes_sysctl_handler(ctl_table *table, int write,
  *     whenever sysctl_lower_zone_protection changes.
  */
 int lower_zone_protection_sysctl_handler(ctl_table *table, int write,
-                struct file *file, void __user *buffer, size_t *length)
+                struct file *file, void __user *buffer, size_t *length, loff_t *ppos)
 {
-       proc_dointvec_minmax(table, write, file, buffer, length);
+       proc_dointvec_minmax(table, write, file, buffer, length, ppos);
        setup_per_zone_protection();
        return 0;
 }
+
+/*
+ * allocate a large system hash table from bootmem
+ * - it is assumed that the hash table must contain an exact power-of-2
+ *   quantity of entries
+ */
+void *__init alloc_large_system_hash(const char *tablename,
+                                    unsigned long bucketsize,
+                                    unsigned long numentries,
+                                    int scale,
+                                    int consider_highmem,
+                                    unsigned int *_hash_shift,
+                                    unsigned int *_hash_mask)
+{
+       unsigned long mem, max, log2qty, size;
+       void *table;
+
+       /* round applicable memory size up to nearest megabyte */
+       mem = consider_highmem ? nr_all_pages : nr_kernel_pages;
+       mem += (1UL << (20 - PAGE_SHIFT)) - 1;
+       mem >>= 20 - PAGE_SHIFT;
+       mem <<= 20 - PAGE_SHIFT;
+
+       /* limit to 1 bucket per 2^scale bytes of low memory (rounded up to
+        * nearest power of 2 in size) */
+       if (scale > PAGE_SHIFT)
+               mem >>= (scale - PAGE_SHIFT);
+       else
+               mem <<= (PAGE_SHIFT - scale);
+
+       mem = 1UL << (long_log2(mem) + 1);
+
+       /* limit allocation size */
+       max = (1UL << (PAGE_SHIFT + MAX_SYS_HASH_TABLE_ORDER)) / bucketsize;
+       if (max > mem)
+               max = mem;
+
+       /* allow the kernel cmdline to have a say */
+       if (!numentries || numentries > max)
+               numentries = max;
+
+       log2qty = long_log2(numentries);
+
+       do {
+               size = bucketsize << log2qty;
+
+               table = (void *) alloc_bootmem(size);
+
+       } while (!table && size > PAGE_SIZE);
+
+       if (!table)
+               panic("Failed to allocate %s hash table\n", tablename);
+
+       printk("%s hash table entries: %d (order: %d, %lu bytes)\n",
+              tablename,
+              (1U << log2qty),
+              long_log2(size) - PAGE_SHIFT,
+              size);
+
+       if (_hash_shift)
+               *_hash_shift = log2qty;
+       if (_hash_mask)
+               *_hash_mask = (1 << log2qty) - 1;
+
+       return table;
+}