

> I have a feeling that we can now combine buddy_idx and page_idx and get
> rid of base completely, but nothing has come to me yet.  

Yeah, I had that feeling too.  Attached is a patch which stops using
base to calculate the buddy and combined page pointers.  I use the
fact that the mem_map array is guarenteed to be contigious for the
surrounding (1 << MAX_ORDER) pages.  I use the relative positions of
the pages in the physical address space to provide the alignement;
which conicidentally fixed the issue where zones are not aligned
at MAX_ORDER.  There is a very comprehensive comment in the new
code explaining the mathematical relationship between a page an
its buddy so I won't reproduce it here.

Obviously if this was to go in, we should also remove the base
parameter and modify all callers.  This has been lightly boot tested
on a numa i386 box.

Comments?

-apw

=== 8< ===
We have a number of difference memory models which use different
layouts for their mem_map arrays.  free_pages_bulk needs to be
independant of this layout.  By using the assumption that mem_map
is at least contigious in MAX_ORDER stretches and that memory
allocations are expected to be maximally aligned we can remove
this dependance.  This has a nice side effect of guarenteeing the
alignment guarentees regardless of the alignment of the zone edges,
which on some systems could trigger crashes.

Revision: $Rev$

Signed-off-by: Andy Whitcroft <apw@shadowen.org>

Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
---

 memhotplug-dave/mm/page_alloc.c |   51 +++++++++++++++++++++++++++++++---------
 1 files changed, 40 insertions(+), 11 deletions(-)

diff -puN mm/page_alloc.c~C0-free-pages-no-base mm/page_alloc.c
--- memhotplug/mm/page_alloc.c~C0-free-pages-no-base	2005-01-04 13:49:31.000000000 -0800
+++ memhotplug-dave/mm/page_alloc.c	2005-01-04 13:49:31.000000000 -0800
@@ -184,6 +184,36 @@ static inline void rmv_page_order(struct
 }
 
 /*
+ * Locate the struct page for both the matching buddy in our
+ * pair (buddy1) and the combined O(n+1) page they form (page).
+ *
+ * 1) Any buddy B1 will have an order O twin B2 which satisfies
+ * the following equasion:
+ *     B2 = B1 ^ (1 << O)
+ * For example, if the starting buddy (buddy2) is #8 its order
+ * 1 buddy is #10:
+ *     B2 = 8 ^ (1 << 1) = 8 ^ 2 = 10
+ *
+ * 2) Any buddy B will have an order O+1 parent P which
+ * satisfies the following equasion:
+ *     P = B & ~(1 << O)
+ *
+ * Assumption: *_mem_map is contigious at least up to MAX_ORDER
+ */
+static inline struct page *__page_find_buddy(struct page *page, unsigned long page_idx, unsigned int order)
+{
+        unsigned long buddy_idx = page_idx ^ (1 << order);
+
+	return page + (buddy_idx - page_idx);
+}
+
+static inline unsigned long __find_combined_index(unsigned long page_idx, unsigned int order)
+{
+	return (page_idx & ~(1 << order));
+}
+
+
+/*
  * This function checks whether a page is free && is the buddy
  * we can do coalesce a page and its buddy if
  * (a) the buddy is free &&
@@ -230,40 +260,39 @@ static inline void __free_pages_bulk (st
 		struct zone *zone, unsigned int order)
 {
 	unsigned long page_idx;
-	struct page *coalesced;
 	int order_size = 1 << order;
 
 	if (unlikely(order))
 		destroy_compound_page(page, order);
 
-	page_idx = page - base;
+	page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);
 
 	BUG_ON(page_idx & (order_size - 1));
 	BUG_ON(bad_range(zone, page));
 
 	zone->free_pages += order_size;
 	while (order < MAX_ORDER-1) {
+		unsigned long combined_idx;
 		struct free_area *area;
 		struct page *buddy;
-		int buddy_idx;
 
-		buddy_idx = (page_idx ^ (1 << order));
-		buddy = base + buddy_idx;
+		combined_idx = __find_combined_index(page_idx, order);
+		buddy = __page_find_buddy(page, page_idx, order);
+
 		if (bad_range(zone, buddy))
 			break;
 		if (!page_is_buddy(buddy, order))
-			break;
-		/* Move the buddy up one level. */
+			break;		/* Move the buddy up one level. */
 		list_del(&buddy->lru);
 		area = zone->free_area + order;
 		area->nr_free--;
 		rmv_page_order(buddy);
-		page_idx &= buddy_idx;
+		page = page + (combined_idx - page_idx);
+		page_idx = combined_idx;
 		order++;
 	}
-	coalesced = base + page_idx;
-	set_page_order(coalesced, order);
-	list_add(&coalesced->lru, &zone->free_area[order].free_list);
+	set_page_order(page, order);
+	list_add(&page->lru, &zone->free_area[order].free_list);
 	zone->free_area[order].nr_free++;
 }
 
_
