aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Whitcroft <apw@shadowen.org>2006-06-23 05:03:01 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-23 10:42:45 -0400
commitcb2b95e1c6b56e3d2369d3a5f4bc97f4fa180683 (patch)
tree121d3fcd539f7d62887949840307ac26aadaa9e4
parent6f0419e06a3b151ab616a31accdabef41dc2d1b0 (diff)
[PATCH] zone handle unaligned zone boundaries
The buddy allocator has a requirement that boundaries between contigious zones occur aligned with the the MAX_ORDER ranges. Where they do not we will incorrectly merge pages cross zone boundaries. This can lead to pages from the wrong zone being handed out. Originally the buddy allocator would check that buddies were in the same zone by referencing the zone start and end page frame numbers. This was removed as it became very expensive and the buddy allocator already made the assumption that zones boundaries were aligned. It is clear that not all configurations and architectures are honouring this alignment requirement. Therefore it seems safest to reintroduce support for non-aligned zone boundaries. This patch introduces a new check when considering a page a buddy it compares the zone_table index for the two pages and refuses to merge the pages where they do not match. The zone_table index is unique for each node/zone combination when FLATMEM/DISCONTIGMEM is enabled and for each section/zone combination when SPARSEMEM is enabled (a SPARSEMEM section is at least a MAX_ORDER size). Signed-off-by: Andy Whitcroft <apw@shadowen.org> Cc: Dave Hansen <haveblue@us.ibm.com> Cc: Mel Gorman <mel@csn.ul.ie> Cc: Yasunori Goto <y-goto@jp.fujitsu.com> Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--include/linux/mm.h7
-rw-r--r--mm/page_alloc.c17
2 files changed, 16 insertions, 8 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h
index e2fa375e478e..697c6bf248c2 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -465,10 +465,13 @@ static inline unsigned long page_zonenum(struct page *page)
465struct zone; 465struct zone;
466extern struct zone *zone_table[]; 466extern struct zone *zone_table[];
467 467
468static inline int page_zone_id(struct page *page)
469{
470 return (page->flags >> ZONETABLE_PGSHIFT) & ZONETABLE_MASK;
471}
468static inline struct zone *page_zone(struct page *page) 472static inline struct zone *page_zone(struct page *page)
469{ 473{
470 return zone_table[(page->flags >> ZONETABLE_PGSHIFT) & 474 return zone_table[page_zone_id(page)];
471 ZONETABLE_MASK];
472} 475}
473 476
474static inline unsigned long page_to_nid(struct page *page) 477static inline unsigned long page_to_nid(struct page *page)
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 253a450c400d..fd631c2536a5 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -286,22 +286,27 @@ __find_combined_index(unsigned long page_idx, unsigned int order)
286 * we can do coalesce a page and its buddy if 286 * we can do coalesce a page and its buddy if
287 * (a) the buddy is not in a hole && 287 * (a) the buddy is not in a hole &&
288 * (b) the buddy is in the buddy system && 288 * (b) the buddy is in the buddy system &&
289 * (c) a page and its buddy have the same order. 289 * (c) a page and its buddy have the same order &&
290 * (d) a page and its buddy are in the same zone.
290 * 291 *
291 * For recording whether a page is in the buddy system, we use PG_buddy. 292 * For recording whether a page is in the buddy system, we use PG_buddy.
292 * Setting, clearing, and testing PG_buddy is serialized by zone->lock. 293 * Setting, clearing, and testing PG_buddy is serialized by zone->lock.
293 * 294 *
294 * For recording page's order, we use page_private(page). 295 * For recording page's order, we use page_private(page).
295 */ 296 */
296static inline int page_is_buddy(struct page *page, int order) 297static inline int page_is_buddy(struct page *page, struct page *buddy,
298 int order)
297{ 299{
298#ifdef CONFIG_HOLES_IN_ZONE 300#ifdef CONFIG_HOLES_IN_ZONE
299 if (!pfn_valid(page_to_pfn(page))) 301 if (!pfn_valid(page_to_pfn(buddy)))
300 return 0; 302 return 0;
301#endif 303#endif
302 304
303 if (PageBuddy(page) && page_order(page) == order) { 305 if (page_zone_id(page) != page_zone_id(buddy))
304 BUG_ON(page_count(page) != 0); 306 return 0;
307
308 if (PageBuddy(buddy) && page_order(buddy) == order) {
309 BUG_ON(page_count(buddy) != 0);
305 return 1; 310 return 1;
306 } 311 }
307 return 0; 312 return 0;
@@ -352,7 +357,7 @@ static inline void __free_one_page(struct page *page,
352 struct page *buddy; 357 struct page *buddy;
353 358
354 buddy = __page_find_buddy(page, page_idx, order); 359 buddy = __page_find_buddy(page, page_idx, order);
355 if (!page_is_buddy(buddy, order)) 360 if (!page_is_buddy(page, buddy, order))
356 break; /* Move the buddy up one level. */ 361 break; /* Move the buddy up one level. */
357 362
358 list_del(&buddy->lru); 363 list_del(&buddy->lru);