summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Morton <akpm@osdl.org>2004-04-11 23:15:46 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-04-11 23:15:46 -0700
commit3c7011b3e90508f2f3adb895d712d36b1cfdcfd2 (patch)
tree7bc0c30404997ed7c4e631f9c947bbf8918509de
parent60af4464478e1511998ad82d7cba3fbfb6c258dd (diff)
[PATCH] use compound pages for hugetlb pages only
The compound page logic is a little fragile - it relies on additional metadata in the pageframes which some other kernel code likes to stomp on (xfs was doing this). Also, because we're treating all higher-order pages as compound pages it is no longer possible to free individual lower-order pages from the middle of higher-order pages. At least one ARM driver insists on doing this. We only really need the compound page logic for higher-order pages which can be mapped into user pagetables and placed under direct-io. This covers hugetlb pages and, conceivably, soundcard DMA buffers which were allcoated with a higher-order allocation but which weren't marked PageReserved. The patch arranges for the hugetlb implications to allocate their pages with compound page metadata, and all other higher-order allocations go back to the old way. (Andrea supplied the GFP_LEVEL_MASK fix)
-rw-r--r--arch/i386/mm/hugetlbpage.c3
-rw-r--r--arch/ia64/mm/hugetlbpage.c3
-rw-r--r--arch/ppc64/mm/hugetlbpage.c3
-rw-r--r--arch/sh/mm/hugetlbpage.c3
-rw-r--r--arch/sparc64/mm/hugetlbpage.c3
-rw-r--r--include/linux/gfp.h6
-rw-r--r--include/linux/mm.h4
-rw-r--r--include/linux/slab.h4
-rw-r--r--mm/page_alloc.c22
9 files changed, 30 insertions, 21 deletions
diff --git a/arch/i386/mm/hugetlbpage.c b/arch/i386/mm/hugetlbpage.c
index 0c73f414b5b1..7224ddcb6a11 100644
--- a/arch/i386/mm/hugetlbpage.c
+++ b/arch/i386/mm/hugetlbpage.c
@@ -54,7 +54,8 @@ static struct page *alloc_fresh_huge_page(void)
{
static int nid = 0;
struct page *page;
- page = alloc_pages_node(nid, GFP_HIGHUSER, HUGETLB_PAGE_ORDER);
+ page = alloc_pages_node(nid, GFP_HIGHUSER|__GFP_COMP,
+ HUGETLB_PAGE_ORDER);
nid = (nid + 1) % numnodes;
return page;
}
diff --git a/arch/ia64/mm/hugetlbpage.c b/arch/ia64/mm/hugetlbpage.c
index aa2a1945d2c2..3dec8e2f4056 100644
--- a/arch/ia64/mm/hugetlbpage.c
+++ b/arch/ia64/mm/hugetlbpage.c
@@ -58,7 +58,8 @@ static struct page *alloc_fresh_huge_page(void)
{
static int nid = 0;
struct page *page;
- page = alloc_pages_node(nid, GFP_HIGHUSER, HUGETLB_PAGE_ORDER);
+ page = alloc_pages_node(nid, GFP_HIGHUSER|__GFP_COMP,
+ HUGETLB_PAGE_ORDER);
nid = (nid + 1) % numnodes;
return page;
}
diff --git a/arch/ppc64/mm/hugetlbpage.c b/arch/ppc64/mm/hugetlbpage.c
index 032a1c9c5766..a7b2c63c700f 100644
--- a/arch/ppc64/mm/hugetlbpage.c
+++ b/arch/ppc64/mm/hugetlbpage.c
@@ -78,7 +78,8 @@ static struct page *alloc_fresh_huge_page(void)
static int nid = 0;
struct page *page;
- page = alloc_pages_node(nid, GFP_HIGHUSER, HUGETLB_PAGE_ORDER);
+ page = alloc_pages_node(nid, GFP_HIGHUSER|__GFP_COMP,
+ HUGETLB_PAGE_ORDER);
if (!page)
return NULL;
diff --git a/arch/sh/mm/hugetlbpage.c b/arch/sh/mm/hugetlbpage.c
index f458eb2d0e6e..6f72d865e8d2 100644
--- a/arch/sh/mm/hugetlbpage.c
+++ b/arch/sh/mm/hugetlbpage.c
@@ -60,7 +60,8 @@ static struct page *alloc_fresh_huge_page(void)
{
static int nid = 0;
struct page *page;
- page = alloc_pages_node(nid, GFP_HIGHUSER, HUGETLB_PAGE_ORDER);
+ page = alloc_pages_node(nid, GFP_HIGHUSER|__GFP_COMP,
+ HUGETLB_PAGE_ORDER);
nid = (nid + 1) % numnodes;
return page;
}
diff --git a/arch/sparc64/mm/hugetlbpage.c b/arch/sparc64/mm/hugetlbpage.c
index dd2a7549caef..5a674bbd5796 100644
--- a/arch/sparc64/mm/hugetlbpage.c
+++ b/arch/sparc64/mm/hugetlbpage.c
@@ -56,7 +56,8 @@ static struct page *alloc_fresh_huge_page(void)
{
static int nid = 0;
struct page *page;
- page = alloc_pages_node(nid, GFP_HIGHUSER, HUGETLB_PAGE_ORDER);
+ page = alloc_pages_node(nid, GFP_HIGHUSER|__GFP_COMP,
+ HUGETLB_PAGE_ORDER);
nid = (nid + 1) % numnodes;
return page;
}
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index c9695427a435..679fc963f842 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -32,10 +32,16 @@
#define __GFP_NOFAIL 0x800 /* Retry for ever. Cannot fail */
#define __GFP_NORETRY 0x1000 /* Do not retry. Might fail */
#define __GFP_NO_GROW 0x2000 /* Slab internal usage */
+#define __GFP_COMP 0x4000 /* Add compound page metadata */
#define __GFP_BITS_SHIFT 16 /* Room for 16 __GFP_FOO bits */
#define __GFP_BITS_MASK ((1 << __GFP_BITS_SHIFT) - 1)
+/* if you forget to add the bitmask here kernel will crash, period */
+#define GFP_LEVEL_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_FS| \
+ __GFP_COLD|__GFP_NOWARN|__GFP_REPEAT| \
+ __GFP_NOFAIL|__GFP_NORETRY|__GFP_NO_GROW|__GFP_COMP)
+
#define GFP_ATOMIC (__GFP_HIGH)
#define GFP_NOIO (__GFP_WAIT)
#define GFP_NOFS (__GFP_WAIT | __GFP_IO)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 2ba5ab34cbdd..f827be900157 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -247,14 +247,14 @@ static inline int page_count(struct page *p)
static inline void get_page(struct page *page)
{
- if (PageCompound(page))
+ if (unlikely(PageCompound(page)))
page = (struct page *)page->private;
atomic_inc(&page->count);
}
static inline void put_page(struct page *page)
{
- if (PageCompound(page)) {
+ if (unlikely(PageCompound(page))) {
page = (struct page *)page->private;
if (put_page_testzero(page)) {
if (page[1].mapping) { /* destructor? */
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 69be5b308a11..806cc52abd3a 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -25,9 +25,7 @@ typedef struct kmem_cache_s kmem_cache_t;
#define SLAB_KERNEL GFP_KERNEL
#define SLAB_DMA GFP_DMA
-#define SLAB_LEVEL_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_FS|\
- __GFP_COLD|__GFP_NOWARN|__GFP_REPEAT|\
- __GFP_NOFAIL|__GFP_NORETRY)
+#define SLAB_LEVEL_MASK GFP_LEVEL_MASK
#define SLAB_NO_GROW __GFP_NO_GROW /* don't grow a cache */
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 96fb97866a28..4148e94eee13 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -130,6 +130,9 @@ static void destroy_compound_page(struct page *page, unsigned long order)
int i;
int nr_pages = 1 << order;
+ if (!PageCompound(page))
+ return;
+
if (page[1].index != order)
bad_page(__FUNCTION__, page);
@@ -487,10 +490,12 @@ void fastcall free_cold_page(struct page *page)
* or two.
*/
-static struct page *buffered_rmqueue(struct zone *zone, int order, int cold)
+static struct page *
+buffered_rmqueue(struct zone *zone, int order, int gfp_flags)
{
unsigned long flags;
struct page *page = NULL;
+ int cold = !!(gfp_flags & __GFP_COLD);
if (order == 0) {
struct per_cpu_pages *pcp;
@@ -519,7 +524,7 @@ static struct page *buffered_rmqueue(struct zone *zone, int order, int cold)
BUG_ON(bad_range(zone, page));
mod_page_state_zone(zone, pgalloc, 1 << order);
prep_new_page(page, order);
- if (order)
+ if (order && (gfp_flags & __GFP_COMP))
prep_compound_page(page, order);
}
return page;
@@ -552,16 +557,11 @@ __alloc_pages(unsigned int gfp_mask, unsigned int order,
struct reclaim_state reclaim_state;
struct task_struct *p = current;
int i;
- int cold;
int alloc_type;
int do_retry;
might_sleep_if(wait);
- cold = 0;
- if (gfp_mask & __GFP_COLD)
- cold = 1;
-
zones = zonelist->zones; /* the list of zones suitable for gfp_mask */
if (zones[0] == NULL) /* no zones in the zonelist */
return NULL;
@@ -583,7 +583,7 @@ __alloc_pages(unsigned int gfp_mask, unsigned int order,
if (z->free_pages >= min ||
(!wait && z->free_pages >= z->pages_high)) {
- page = buffered_rmqueue(z, order, cold);
+ page = buffered_rmqueue(z, order, gfp_mask);
if (page)
goto got_pg;
}
@@ -606,7 +606,7 @@ __alloc_pages(unsigned int gfp_mask, unsigned int order,
if (z->free_pages >= min ||
(!wait && z->free_pages >= z->pages_high)) {
- page = buffered_rmqueue(z, order, cold);
+ page = buffered_rmqueue(z, order, gfp_mask);
if (page)
goto got_pg;
}
@@ -620,7 +620,7 @@ rebalance:
for (i = 0; zones[i] != NULL; i++) {
struct zone *z = zones[i];
- page = buffered_rmqueue(z, order, cold);
+ page = buffered_rmqueue(z, order, gfp_mask);
if (page)
goto got_pg;
}
@@ -648,7 +648,7 @@ rebalance:
if (z->free_pages >= min ||
(!wait && z->free_pages >= z->pages_high)) {
- page = buffered_rmqueue(z, order, cold);
+ page = buffered_rmqueue(z, order, gfp_mask);
if (page)
goto got_pg;
}