📄 page_alloc.c
字号:
/* * linux/mm/page_alloc.c * * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds * Swap reorganised 29.12.95, Stephen Tweedie * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 * 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 */#include <linux/config.h>#include <linux/mm.h>#include <linux/swap.h>#include <linux/swapctl.h>#include <linux/interrupt.h>#include <linux/pagemap.h>#include <linux/bootmem.h>#include <linux/slab.h>#include <linux/compiler.h>int nr_swap_pages;int nr_active_pages;int nr_inactive_pages;struct list_head inactive_list;struct list_head active_list;pg_data_t *pgdat_list;static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" };static int zone_balance_ratio[MAX_NR_ZONES] __initdata = { 128, 128, 128, };static int zone_balance_min[MAX_NR_ZONES] __initdata = { 20 , 20, 20, };static int zone_balance_max[MAX_NR_ZONES] __initdata = { 255 , 255, 255, };/* * Free_page() adds the page to the free lists. This is optimized for * fast normal cases (no error jumps taken normally). * * The way to optimize jumps for gcc-2.2.2 is to: * - select the "normal" case and put it inside the if () { XXX } * - no else-statements if you can avoid them * * With the above two rules, you get a straight-line execution path * for the normal case, giving better asm-code. */#define memlist_init(x) INIT_LIST_HEAD(x)#define memlist_add_head list_add#define memlist_add_tail list_add_tail#define memlist_del list_del#define memlist_entry list_entry#define memlist_next(x) ((x)->next)#define memlist_prev(x) ((x)->prev)/* * Temporary debugging check. */#define BAD_RANGE(zone,x) (((zone) != (x)->zone) || (((x)-mem_map) < (zone)->zone_start_mapnr) || (((x)-mem_map) >= (zone)->zone_start_mapnr+(zone)->size))/* * Buddy system. Hairy. You really aren't expected to understand this * * Hint: -mask = 1+~mask */static void FASTCALL(__free_pages_ok (struct page *page, unsigned int order));static void __free_pages_ok (struct page *page, unsigned int order){ unsigned long index, page_idx, mask, flags; free_area_t *area; struct page *base; zone_t *zone; /* Yes, think what happens when other parts of the kernel take * a reference to a page in order to pin it for io. -ben */ if (PageLRU(page)) lru_cache_del(page); if (page->buffers) BUG(); if (page->mapping) BUG(); if (!VALID_PAGE(page)) BUG(); if (PageSwapCache(page)) BUG(); if (PageLocked(page)) BUG(); if (PageLRU(page)) BUG(); if (PageActive(page)) BUG(); page->flags &= ~((1<<PG_referenced) | (1<<PG_dirty)); if (current->flags & PF_FREE_PAGES) goto local_freelist; back_local_freelist: zone = page->zone; mask = (~0UL) << order; base = zone->zone_mem_map; page_idx = page - base; if (page_idx & ~mask) BUG(); index = page_idx >> (1 + order); area = zone->free_area + order; spin_lock_irqsave(&zone->lock, flags); zone->free_pages -= mask; while (mask + (1 << (MAX_ORDER-1))) { struct page *buddy1, *buddy2; if (area >= zone->free_area + MAX_ORDER) BUG(); if (!__test_and_change_bit(index, area->map)) /* * the buddy page is still allocated. */ break; /* * Move the buddy up one level. */ buddy1 = base + (page_idx ^ -mask); buddy2 = base + page_idx; if (BAD_RANGE(zone,buddy1)) BUG(); if (BAD_RANGE(zone,buddy2)) BUG(); memlist_del(&buddy1->list); mask <<= 1; area++; index >>= 1; page_idx &= mask; } memlist_add_head(&(base + page_idx)->list, &area->free_list); spin_unlock_irqrestore(&zone->lock, flags); return; local_freelist: if (current->nr_local_pages) goto back_local_freelist; if (in_interrupt()) goto back_local_freelist; list_add(&page->list, ¤t->local_pages); page->index = order; current->nr_local_pages++;}#define MARK_USED(index, order, area) \ __change_bit((index) >> (1+(order)), (area)->map)static inline struct page * expand (zone_t *zone, struct page *page, unsigned long index, int low, int high, free_area_t * area){ unsigned long size = 1 << high; while (high > low) { if (BAD_RANGE(zone,page)) BUG(); area--; high--; size >>= 1; memlist_add_head(&(page)->list, &(area)->free_list); MARK_USED(index, high, area); index += size; page += size; } if (BAD_RANGE(zone,page)) BUG(); return page;}static FASTCALL(struct page * rmqueue(zone_t *zone, unsigned int order));static struct page * rmqueue(zone_t *zone, unsigned int order){ free_area_t * area = zone->free_area + order; unsigned int curr_order = order; struct list_head *head, *curr; unsigned long flags; struct page *page; spin_lock_irqsave(&zone->lock, flags); do { head = &area->free_list; curr = memlist_next(head); if (curr != head) { unsigned int index; page = memlist_entry(curr, struct page, list); if (BAD_RANGE(zone,page)) BUG(); memlist_del(curr); index = page - zone->zone_mem_map; if (curr_order != MAX_ORDER-1) MARK_USED(index, curr_order, area); zone->free_pages -= 1UL << order; page = expand(zone, page, index, order, curr_order, area); spin_unlock_irqrestore(&zone->lock, flags); set_page_count(page, 1); if (BAD_RANGE(zone,page)) BUG(); if (PageLRU(page)) BUG(); if (PageActive(page)) BUG(); return page; } curr_order++; area++; } while (curr_order < MAX_ORDER); spin_unlock_irqrestore(&zone->lock, flags); return NULL;}#ifndef CONFIG_DISCONTIGMEMstruct page *_alloc_pages(unsigned int gfp_mask, unsigned int order){ return __alloc_pages(gfp_mask, order, contig_page_data.node_zonelists+(gfp_mask & GFP_ZONEMASK));}#endifstatic struct page * FASTCALL(balance_classzone(zone_t *, unsigned int, unsigned int, int *));static struct page * balance_classzone(zone_t * classzone, unsigned int gfp_mask, unsigned int order, int * freed){ struct page * page = NULL; int __freed = 0; if (!(gfp_mask & __GFP_WAIT)) goto out; if (in_interrupt()) BUG(); current->allocation_order = order; current->flags |= PF_MEMALLOC | PF_FREE_PAGES; __freed = try_to_free_pages(classzone, gfp_mask, order); current->flags &= ~(PF_MEMALLOC | PF_FREE_PAGES); if (current->nr_local_pages) { struct list_head * entry, * local_pages; struct page * tmp; int nr_pages; local_pages = ¤t->local_pages; if (likely(__freed)) { /* pick from the last inserted so we're lifo */ entry = local_pages->next; do { tmp = list_entry(entry, struct page, list); if (tmp->index == order && memclass(tmp->zone, classzone)) { list_del(entry); current->nr_local_pages--; set_page_count(tmp, 1); page = tmp; if (page->buffers) BUG(); if (page->mapping) BUG(); if (!VALID_PAGE(page)) BUG(); if (PageSwapCache(page)) BUG(); if (PageLocked(page)) BUG(); if (PageLRU(page)) BUG(); if (PageActive(page)) BUG(); if (PageDirty(page)) BUG(); break; } } while ((entry = entry->next) != local_pages); } nr_pages = current->nr_local_pages; /* free in reverse order so that the global order will be lifo */ while ((entry = local_pages->prev) != local_pages) { list_del(entry); tmp = list_entry(entry, struct page, list); __free_pages_ok(tmp, tmp->index); if (!nr_pages--) BUG(); } current->nr_local_pages = 0; } out: *freed = __freed; return page;}/* * This is the 'heart' of the zoned buddy allocator: */struct page * __alloc_pages(unsigned int gfp_mask, unsigned int order, zonelist_t *zonelist){ unsigned long min; zone_t **zone, * classzone; struct page * page; int freed; zone = zonelist->zones; classzone = *zone; min = 1UL << order; for (;;) { zone_t *z = *(zone++); if (!z) break; min += z->pages_low; if (z->free_pages > min) { page = rmqueue(z, order); if (page) return page; } } classzone->need_balance = 1; mb(); if (waitqueue_active(&kswapd_wait)) wake_up_interruptible(&kswapd_wait); zone = zonelist->zones; min = 1UL << order; for (;;) { unsigned long local_min; zone_t *z = *(zone++); if (!z) break; local_min = z->pages_min; if (!(gfp_mask & __GFP_WAIT)) local_min >>= 2; min += local_min; if (z->free_pages > min) { page = rmqueue(z, order); if (page) return page; } } /* here we're in the low on memory slow path */rebalance: if (current->flags & (PF_MEMALLOC | PF_MEMDIE)) { zone = zonelist->zones; for (;;) { zone_t *z = *(zone++); if (!z) break; page = rmqueue(z, order); if (page) return page; } return NULL; } /* Atomic allocations - we can't balance anything */ if (!(gfp_mask & __GFP_WAIT)) return NULL; page = balance_classzone(classzone, gfp_mask, order, &freed); if (page) return page; zone = zonelist->zones; min = 1UL << order; for (;;) { zone_t *z = *(zone++); if (!z) break; min += z->pages_min; if (z->free_pages > min) { page = rmqueue(z, order); if (page) return page; } } /* Don't let big-order allocations loop */ if (order > 3)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -