From: Andy Whitcroft Currently the buddy allocator requires that zone boundaries be at MAX_ORDER boundaries. This may not always be desirable or possible. Add a config option to allow these boundaries to be arbitrary. Signed-off-by: Andy Whitcroft Signed-off-by: Andrew Morton --- include/linux/mm.h | 7 +++++-- include/linux/mmzone.h | 4 ++++ mm/page_alloc.c | 21 ++++++++++++++------- 3 files changed, 23 insertions(+), 9 deletions(-) diff -puN include/linux/mm.h~zone-allow-unaligned-zone-boundaries include/linux/mm.h --- devel/include/linux/mm.h~zone-allow-unaligned-zone-boundaries 2006-05-19 16:00:24.000000000 -0700 +++ devel-akpm/include/linux/mm.h 2006-05-19 16:00:24.000000000 -0700 @@ -473,10 +473,13 @@ static inline unsigned long page_zonenum struct zone; extern struct zone *zone_table[]; +static inline int page_zone_id(struct page *page) +{ + return (page->flags >> ZONETABLE_PGSHIFT) & ZONETABLE_MASK; +} static inline struct zone *page_zone(struct page *page) { - return zone_table[(page->flags >> ZONETABLE_PGSHIFT) & - ZONETABLE_MASK]; + return zone_table[page_zone_id(page)]; } static inline unsigned long page_to_nid(struct page *page) diff -puN include/linux/mmzone.h~zone-allow-unaligned-zone-boundaries include/linux/mmzone.h --- devel/include/linux/mmzone.h~zone-allow-unaligned-zone-boundaries 2006-05-19 16:00:24.000000000 -0700 +++ devel-akpm/include/linux/mmzone.h 2006-05-19 16:00:24.000000000 -0700 @@ -389,7 +389,11 @@ static inline int is_dma(struct zone *zo static inline unsigned long zone_boundary_align_pfn(unsigned long pfn) { +#ifdef CONFIG_UNALIGNED_ZONE_BOUNDRIES + return pfn; +#else return pfn & ~((1 << MAX_ORDER) - 1); +#endif } /* These two functions are used to setup the per zone pages min values */ diff -puN mm/page_alloc.c~zone-allow-unaligned-zone-boundaries mm/page_alloc.c --- devel/mm/page_alloc.c~zone-allow-unaligned-zone-boundaries 2006-05-19 16:00:24.000000000 -0700 +++ devel-akpm/mm/page_alloc.c 2006-05-19 16:00:24.000000000 -0700 @@ -285,22 +285,28 @@ __find_combined_index(unsigned long page * we can do coalesce a page and its buddy if * (a) the buddy is not in a hole && * (b) the buddy is in the buddy system && - * (c) a page and its buddy have the same order. + * (c) a page and its buddy have the same order && + * (d) a page and its buddy are in the same zone. * * For recording whether a page is in the buddy system, we use PG_buddy. * Setting, clearing, and testing PG_buddy is serialized by zone->lock. * * For recording page's order, we use page_private(page). */ -static inline int page_is_buddy(struct page *page, int order) +static inline int page_is_buddy(struct page *page, struct page *buddy, + int order) { #ifdef CONFIG_HOLES_IN_ZONE - if (!pfn_valid(page_to_pfn(page))) + if (!pfn_valid(page_to_pfn(buddy))) + return 0; +#endif +#ifdef CONFIG_UNALIGNED_ZONE_BOUNDRIES + if (page_zone_id(page) != page_zone_id(buddy)) return 0; #endif - if (PageBuddy(page) && page_order(page) == order) { - BUG_ON(page_count(page) != 0); + if (PageBuddy(buddy) && page_order(buddy) == order) { + BUG_ON(page_count(buddy) != 0); return 1; } return 0; @@ -351,7 +357,7 @@ static inline void __free_one_page(struc struct page *buddy; buddy = __page_find_buddy(page, page_idx, order); - if (!page_is_buddy(buddy, order)) + if (!page_is_buddy(page, buddy, order)) break; /* Move the buddy up one level. */ list_del(&buddy->lru); @@ -2090,7 +2096,8 @@ static void __init free_area_init_core(s if (zone_boundary_align_pfn(zone_start_pfn) != zone_start_pfn && j != 0 && size != 0) printk(KERN_CRIT "node %d zone %s misaligned " - "start pfn\n", nid, zone_names[j]); + "start pfn, enable UNALIGNED_ZONE_BOUNDRIES\n", + nid, zone_names[j]); zone->spanned_pages = size; zone->present_pages = realsize; _