--- linux/mm/page_alloc.c.orig Tue Mar 13 12:48:51 2001 +++ linux/mm/page_alloc.c Tue Mar 13 12:48:51 2001 @@ -7,6 +7,7 @@ * Reshaped it to be a zoned allocator, Ingo Molnar, Red Hat, 1999 * Discontiguous memory support, Kanoj Sarcar, SGI, Nov 1999 * Zone balancing, Kanoj Sarcar, SGI, Jan 2000 + * Per-CPU page pool, Ingo Molnar, Red Hat, 2001 */ #include @@ -67,8 +68,16 @@ unsigned long index, page_idx, mask, flags; free_area_t *area; struct page *base; + per_cpu_t *per_cpu; zone_t *zone; + /* + * This late check is safe because reserved pages do not + * have a valid page->count. This trick avoids overhead + * in __free_pages(). + */ + if (PageReserved(page)) + return; if (page->buffers) BUG(); if (page->mapping) @@ -102,7 +111,18 @@ area = zone->free_area + order; - spin_lock_irqsave(&zone->lock, flags); + per_cpu = zone->cpu_pages + smp_processor_id(); + + __save_flags(flags); + __cli(); + if (!order && (per_cpu->nr_pages < per_cpu->max_nr_pages)) { + list_add(&page->list, &per_cpu->head); + per_cpu->nr_pages++; + __restore_flags(flags); + return; + } + + spin_lock(&zone->lock); zone->free_pages -= mask; @@ -169,16 +189,33 @@ return page; } + static FASTCALL(struct page * rmqueue(zone_t *zone, unsigned long order)); static struct page * rmqueue(zone_t *zone, unsigned long order) { + per_cpu_t *per_cpu = zone->cpu_pages + smp_processor_id(); free_area_t * area = zone->free_area + order; unsigned long curr_order = order; struct list_head *head, *curr; unsigned long flags; struct page *page; - spin_lock_irqsave(&zone->lock, flags); + __save_flags(flags); + __cli(); + + if (!order && per_cpu->nr_pages) { + if (list_empty(&per_cpu->head)) + BUG(); + page = list_entry(per_cpu->head.next, struct page, list); + list_del(&page->list); + per_cpu->nr_pages--; + __restore_flags(flags); + + set_page_count(page, 1); + return page; + } + + spin_lock(&zone->lock); do { head = &area->free_list; curr = memlist_next(head); @@ -527,7 +564,7 @@ void __free_pages(struct page *page, unsigned long order) { - if (!PageReserved(page) && put_page_testzero(page)) + if (put_page_testzero(page)) __free_pages_ok(page, order); } @@ -835,6 +872,7 @@ offset = lmem_map - mem_map; for (j = 0; j < MAX_NR_ZONES; j++) { + int k; zone_t *zone = pgdat->node_zones + j; unsigned long mask; unsigned long size, realsize; @@ -846,6 +884,18 @@ printk("zone(%lu): %lu pages.\n", j, size); zone->size = size; zone->name = zone_names[j]; + + for (k = 0; k < NR_CPUS; k++) { + per_cpu_t *per_cpu = zone->cpu_pages + i; + + INIT_LIST_HEAD(&per_cpu->head); + per_cpu->max_nr_pages = realsize / smp_num_cpus / 128; + if (per_cpu->max_nr_pages > MAX_PER_CPU_PAGES) + per_cpu->max_nr_pages = MAX_PER_CPU_PAGES; + else + if (!per_cpu->max_nr_pages) + per_cpu->max_nr_pages = 1; + } zone->lock = SPIN_LOCK_UNLOCKED; zone->zone_pgdat = pgdat; zone->free_pages = 0; --- linux/include/linux/mmzone.h.orig Tue Mar 13 12:48:44 2001 +++ linux/include/linux/mmzone.h Tue Mar 13 12:48:51 2001 @@ -7,12 +7,13 @@ #include #include #include +#include /* * Free memory management - zoned buddy allocator. */ -#define MAX_ORDER 10 +#define MAX_ORDER 11 typedef struct free_area_struct { struct list_head free_list; @@ -21,6 +22,14 @@ struct pglist_data; +#define MAX_PER_CPU_PAGES 1024 + +typedef struct per_cpu_pages_s { + int nr_pages; + int max_nr_pages; + struct list_head head; +} __attribute__((aligned(L1_CACHE_BYTES))) per_cpu_t; + /* * On machines where it is needed (eg PCs) we divide physical memory * into multiple physical zones. On a PC we have 3 zones: @@ -33,6 +42,7 @@ /* * Commonly accessed fields: */ + per_cpu_t cpu_pages [NR_CPUS]; spinlock_t lock; unsigned long offset; unsigned long free_pages;