From: Nick Piggin Have an explicit mm call to split higher order pages into individual pages. Should help to avoid bugs and be more explicit about the code's intention. Signed-off-by: Nick Piggin Cc: Russell King Cc: David Howells Cc: Ralf Baechle Cc: Benjamin Herrenschmidt Cc: Paul Mundt Cc: "David S. Miller" Cc: Chris Zankel Signed-off-by: Andrew Morton --- arch/arm/mm/consistent.c | 4 ++-- arch/frv/mm/dma-alloc.c | 4 +--- arch/mips/mm/init.c | 2 +- arch/ppc/kernel/dma-mapping.c | 4 ++-- arch/sh/mm/consistent.c | 3 +-- arch/sparc64/mm/init.c | 6 +----- arch/xtensa/mm/pgtable.c | 10 +++------- include/linux/mm.h | 6 ++++++ mm/memory.c | 4 +--- mm/page_alloc.c | 22 ++++++++++++++++++++++ 10 files changed, 40 insertions(+), 25 deletions(-) diff -puN arch/arm/mm/consistent.c~mm-split-highorder-pages arch/arm/mm/consistent.c --- devel/arch/arm/mm/consistent.c~mm-split-highorder-pages 2006-01-25 00:49:39.000000000 -0800 +++ devel-akpm/arch/arm/mm/consistent.c 2006-01-25 00:49:39.000000000 -0800 @@ -223,6 +223,8 @@ __dma_alloc(struct device *dev, size_t s pte = consistent_pte[idx] + off; c->vm_pages = page; + split_page(page, order); + /* * Set the "dma handle" */ @@ -231,7 +233,6 @@ __dma_alloc(struct device *dev, size_t s do { BUG_ON(!pte_none(*pte)); - set_page_count(page, 1); /* * x86 does not mark the pages reserved... */ @@ -250,7 +251,6 @@ __dma_alloc(struct device *dev, size_t s * Free the otherwise unused pages. */ while (page < end) { - set_page_count(page, 1); __free_page(page); page++; } diff -puN arch/frv/mm/dma-alloc.c~mm-split-highorder-pages arch/frv/mm/dma-alloc.c --- devel/arch/frv/mm/dma-alloc.c~mm-split-highorder-pages 2006-01-25 00:49:39.000000000 -0800 +++ devel-akpm/arch/frv/mm/dma-alloc.c 2006-01-25 00:49:39.000000000 -0800 @@ -115,9 +115,7 @@ void *consistent_alloc(gfp_t gfp, size_t */ if (order > 0) { struct page *rpage = virt_to_page(page); - - for (i = 1; i < (1 << order); i++) - set_page_count(rpage + i, 1); + split_page(rpage, order); } err = 0; diff -puN arch/mips/mm/init.c~mm-split-highorder-pages arch/mips/mm/init.c --- devel/arch/mips/mm/init.c~mm-split-highorder-pages 2006-01-25 00:49:39.000000000 -0800 +++ devel-akpm/arch/mips/mm/init.c 2006-01-25 11:10:15.000000000 -0800 @@ -66,9 +66,9 @@ unsigned long setup_zero_pages(void) panic("Oh boy, that early out of memory?"); page = virt_to_page(empty_zero_page); + split_page(page); while (page < virt_to_page(empty_zero_page + (PAGE_SIZE << order))) { SetPageReserved(page); - set_page_count(page, 1); page++; } diff -puN arch/ppc/kernel/dma-mapping.c~mm-split-highorder-pages arch/ppc/kernel/dma-mapping.c --- devel/arch/ppc/kernel/dma-mapping.c~mm-split-highorder-pages 2006-01-25 00:49:39.000000000 -0800 +++ devel-akpm/arch/ppc/kernel/dma-mapping.c 2006-01-25 00:49:39.000000000 -0800 @@ -223,6 +223,8 @@ __dma_alloc_coherent(size_t size, dma_ad pte_t *pte = consistent_pte + CONSISTENT_OFFSET(vaddr); struct page *end = page + (1 << order); + split_page(page, order); + /* * Set the "dma handle" */ @@ -231,7 +233,6 @@ __dma_alloc_coherent(size_t size, dma_ad do { BUG_ON(!pte_none(*pte)); - set_page_count(page, 1); SetPageReserved(page); set_pte_at(&init_mm, vaddr, pte, mk_pte(page, pgprot_noncached(PAGE_KERNEL))); @@ -244,7 +245,6 @@ __dma_alloc_coherent(size_t size, dma_ad * Free the otherwise unused pages. */ while (page < end) { - set_page_count(page, 1); __free_page(page); page++; } diff -puN arch/sh/mm/consistent.c~mm-split-highorder-pages arch/sh/mm/consistent.c --- devel/arch/sh/mm/consistent.c~mm-split-highorder-pages 2006-01-25 00:49:39.000000000 -0800 +++ devel-akpm/arch/sh/mm/consistent.c 2006-01-25 00:49:39.000000000 -0800 @@ -23,6 +23,7 @@ void *consistent_alloc(gfp_t gfp, size_t page = alloc_pages(gfp, order); if (!page) return NULL; + split_page(page, order); ret = page_address(page); *handle = virt_to_phys(ret); @@ -37,8 +38,6 @@ void *consistent_alloc(gfp_t gfp, size_t end = page + (1 << order); while (++page < end) { - set_page_count(page, 1); - /* Free any unused pages */ if (page >= free) { __free_page(page); diff -puN arch/sparc64/mm/init.c~mm-split-highorder-pages arch/sparc64/mm/init.c --- devel/arch/sparc64/mm/init.c~mm-split-highorder-pages 2006-01-25 00:49:39.000000000 -0800 +++ devel-akpm/arch/sparc64/mm/init.c 2006-01-25 11:11:32.000000000 -0800 @@ -1076,11 +1076,7 @@ pte_t *pte_alloc_one_kernel(struct mm_st pte_t *pte; #ifdef DCACHE_ALIASING_POSSIBLE - set_page_count(page, 1); - ClearPageCompound(page); - - set_page_count((page + 1), 1); - ClearPageCompound(page + 1); + split_page(page, DC_ALIAS_SHIFT); #endif paddr = (unsigned long) page_address(page); memset((char *)paddr, 0, (PAGE_SIZE << DC_ALIAS_SHIFT)); diff -puN arch/xtensa/mm/pgtable.c~mm-split-highorder-pages arch/xtensa/mm/pgtable.c --- devel/arch/xtensa/mm/pgtable.c~mm-split-highorder-pages 2006-01-25 00:49:39.000000000 -0800 +++ devel-akpm/arch/xtensa/mm/pgtable.c 2006-01-25 00:49:39.000000000 -0800 @@ -21,13 +21,9 @@ pte_t* pte_alloc_one_kernel(struct mm_st p = (pte_t*) __get_free_pages(GFP_KERNEL|__GFP_REPEAT, COLOR_ORDER); if (likely(p)) { - struct page *page; + split_page(virt_to_page(p), COLOR_ORDER); for (i = 0; i < COLOR_SIZE; i++) { - page = virt_to_page(p); - - set_page_count(page, 1); - if (ADDR_COLOR(p) == color) pte = p; else @@ -55,9 +51,9 @@ struct page* pte_alloc_one(struct mm_str p = alloc_pages(GFP_KERNEL | __GFP_REPEAT, PTE_ORDER); if (likely(p)) { - for (i = 0; i < PAGE_ORDER; i++) { - set_page_count(p, 1); + split_page(p, COLOR_ORDER); + for (i = 0; i < PAGE_ORDER; i++) { if (PADDR_COLOR(page_address(p)) == color) page = p; else diff -puN include/linux/mm.h~mm-split-highorder-pages include/linux/mm.h --- devel/include/linux/mm.h~mm-split-highorder-pages 2006-01-25 00:49:39.000000000 -0800 +++ devel-akpm/include/linux/mm.h 2006-01-25 00:49:39.000000000 -0800 @@ -328,6 +328,12 @@ static inline void get_page(struct page void put_page(struct page *page); +#ifdef CONFIG_MMU +void split_page(struct page *page, unsigned int order); +#else +static inline void split_page(struct page *page, unsigned int order) {} +#endif + /* * Multiple processes may "see" the same page. E.g. for untouched * mappings of /dev/null, all processes see the same page full of diff -puN mm/memory.c~mm-split-highorder-pages mm/memory.c --- devel/mm/memory.c~mm-split-highorder-pages 2006-01-25 00:49:39.000000000 -0800 +++ devel-akpm/mm/memory.c 2006-01-25 00:49:39.000000000 -0800 @@ -1210,9 +1210,7 @@ out: * The page has to be a nice clean _individual_ kernel allocation. * If you allocate a compound page, you need to have marked it as * such (__GFP_COMP), or manually just split the page up yourself - * (which is mainly an issue of doing "set_page_count(page, 1)" for - * each sub-page, and then freeing them one by one when you free - * them rather than freeing it as a compound page). + * (see split_page()). * * NOTE! Traditionally this was done with "remap_pfn_range()" which * took an arbitrary page protection parameter. This doesn't allow diff -puN mm/page_alloc.c~mm-split-highorder-pages mm/page_alloc.c --- devel/mm/page_alloc.c~mm-split-highorder-pages 2006-01-25 00:49:39.000000000 -0800 +++ devel-akpm/mm/page_alloc.c 2006-01-25 11:11:22.000000000 -0800 @@ -749,6 +749,28 @@ static inline void prep_zero_page(struct clear_highpage(page + i); } +#ifdef CONFIG_MMU +/* + * split_page takes a non-compound higher-order page, and splits it into + * n (1< 0 path. Saves a branch _