Commit 8dfcc9ba authored by Nick Piggin's avatar Nick Piggin Committed by Linus Torvalds

[PATCH] mm: split highorder pages

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: default avatarNick Piggin <npiggin@suse.de>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: David Howells <dhowells@redhat.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Chris Zankel <chris@zankel.net>
Signed-off-by: default avatarYoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 8e7a9aae
...@@ -223,6 +223,8 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, ...@@ -223,6 +223,8 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
pte = consistent_pte[idx] + off; pte = consistent_pte[idx] + off;
c->vm_pages = page; c->vm_pages = page;
split_page(page, order);
/* /*
* Set the "dma handle" * Set the "dma handle"
*/ */
...@@ -231,7 +233,6 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, ...@@ -231,7 +233,6 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
do { do {
BUG_ON(!pte_none(*pte)); BUG_ON(!pte_none(*pte));
set_page_count(page, 1);
/* /*
* x86 does not mark the pages reserved... * x86 does not mark the pages reserved...
*/ */
...@@ -250,7 +251,6 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, ...@@ -250,7 +251,6 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
* Free the otherwise unused pages. * Free the otherwise unused pages.
*/ */
while (page < end) { while (page < end) {
set_page_count(page, 1);
__free_page(page); __free_page(page);
page++; page++;
} }
......
...@@ -115,9 +115,7 @@ void *consistent_alloc(gfp_t gfp, size_t size, dma_addr_t *dma_handle) ...@@ -115,9 +115,7 @@ void *consistent_alloc(gfp_t gfp, size_t size, dma_addr_t *dma_handle)
*/ */
if (order > 0) { if (order > 0) {
struct page *rpage = virt_to_page(page); struct page *rpage = virt_to_page(page);
split_page(rpage, order);
for (i = 1; i < (1 << order); i++)
set_page_count(rpage + i, 1);
} }
err = 0; err = 0;
......
...@@ -54,7 +54,8 @@ unsigned long empty_zero_page, zero_page_mask; ...@@ -54,7 +54,8 @@ unsigned long empty_zero_page, zero_page_mask;
*/ */
unsigned long setup_zero_pages(void) unsigned long setup_zero_pages(void)
{ {
unsigned long order, size; unsigned int order;
unsigned long size;
struct page *page; struct page *page;
if (cpu_has_vce) if (cpu_has_vce)
...@@ -67,9 +68,9 @@ unsigned long setup_zero_pages(void) ...@@ -67,9 +68,9 @@ unsigned long setup_zero_pages(void)
panic("Oh boy, that early out of memory?"); panic("Oh boy, that early out of memory?");
page = virt_to_page(empty_zero_page); page = virt_to_page(empty_zero_page);
split_page(page, order);
while (page < virt_to_page(empty_zero_page + (PAGE_SIZE << order))) { while (page < virt_to_page(empty_zero_page + (PAGE_SIZE << order))) {
SetPageReserved(page); SetPageReserved(page);
set_page_count(page, 1);
page++; page++;
} }
......
...@@ -223,6 +223,8 @@ __dma_alloc_coherent(size_t size, dma_addr_t *handle, gfp_t gfp) ...@@ -223,6 +223,8 @@ __dma_alloc_coherent(size_t size, dma_addr_t *handle, gfp_t gfp)
pte_t *pte = consistent_pte + CONSISTENT_OFFSET(vaddr); pte_t *pte = consistent_pte + CONSISTENT_OFFSET(vaddr);
struct page *end = page + (1 << order); struct page *end = page + (1 << order);
split_page(page, order);
/* /*
* Set the "dma handle" * Set the "dma handle"
*/ */
...@@ -231,7 +233,6 @@ __dma_alloc_coherent(size_t size, dma_addr_t *handle, gfp_t gfp) ...@@ -231,7 +233,6 @@ __dma_alloc_coherent(size_t size, dma_addr_t *handle, gfp_t gfp)
do { do {
BUG_ON(!pte_none(*pte)); BUG_ON(!pte_none(*pte));
set_page_count(page, 1);
SetPageReserved(page); SetPageReserved(page);
set_pte_at(&init_mm, vaddr, set_pte_at(&init_mm, vaddr,
pte, mk_pte(page, pgprot_noncached(PAGE_KERNEL))); pte, mk_pte(page, pgprot_noncached(PAGE_KERNEL)));
...@@ -244,7 +245,6 @@ __dma_alloc_coherent(size_t size, dma_addr_t *handle, gfp_t gfp) ...@@ -244,7 +245,6 @@ __dma_alloc_coherent(size_t size, dma_addr_t *handle, gfp_t gfp)
* Free the otherwise unused pages. * Free the otherwise unused pages.
*/ */
while (page < end) { while (page < end) {
set_page_count(page, 1);
__free_page(page); __free_page(page);
page++; page++;
} }
......
...@@ -23,6 +23,7 @@ void *consistent_alloc(gfp_t gfp, size_t size, dma_addr_t *handle) ...@@ -23,6 +23,7 @@ void *consistent_alloc(gfp_t gfp, size_t size, dma_addr_t *handle)
page = alloc_pages(gfp, order); page = alloc_pages(gfp, order);
if (!page) if (!page)
return NULL; return NULL;
split_page(page, order);
ret = page_address(page); ret = page_address(page);
*handle = virt_to_phys(ret); *handle = virt_to_phys(ret);
...@@ -37,8 +38,6 @@ void *consistent_alloc(gfp_t gfp, size_t size, dma_addr_t *handle) ...@@ -37,8 +38,6 @@ void *consistent_alloc(gfp_t gfp, size_t size, dma_addr_t *handle)
end = page + (1 << order); end = page + (1 << order);
while (++page < end) { while (++page < end) {
set_page_count(page, 1);
/* Free any unused pages */ /* Free any unused pages */
if (page >= free) { if (page >= free) {
__free_page(page); __free_page(page);
......
...@@ -21,13 +21,9 @@ pte_t* pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) ...@@ -21,13 +21,9 @@ pte_t* pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
p = (pte_t*) __get_free_pages(GFP_KERNEL|__GFP_REPEAT, COLOR_ORDER); p = (pte_t*) __get_free_pages(GFP_KERNEL|__GFP_REPEAT, COLOR_ORDER);
if (likely(p)) { if (likely(p)) {
struct page *page; split_page(virt_to_page(p), COLOR_ORDER);
for (i = 0; i < COLOR_SIZE; i++) { for (i = 0; i < COLOR_SIZE; i++) {
page = virt_to_page(p);
set_page_count(page, 1);
if (ADDR_COLOR(p) == color) if (ADDR_COLOR(p) == color)
pte = p; pte = p;
else else
...@@ -55,9 +51,9 @@ struct page* pte_alloc_one(struct mm_struct *mm, unsigned long address) ...@@ -55,9 +51,9 @@ struct page* pte_alloc_one(struct mm_struct *mm, unsigned long address)
p = alloc_pages(GFP_KERNEL | __GFP_REPEAT, PTE_ORDER); p = alloc_pages(GFP_KERNEL | __GFP_REPEAT, PTE_ORDER);
if (likely(p)) { if (likely(p)) {
for (i = 0; i < PAGE_ORDER; i++) { split_page(p, COLOR_ORDER);
set_page_count(p, 1);
for (i = 0; i < PAGE_ORDER; i++) {
if (PADDR_COLOR(page_address(p)) == color) if (PADDR_COLOR(page_address(p)) == color)
page = p; page = p;
else else
......
...@@ -328,6 +328,12 @@ static inline void get_page(struct page *page) ...@@ -328,6 +328,12 @@ static inline void get_page(struct page *page)
void put_page(struct page *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 * Multiple processes may "see" the same page. E.g. for untouched
* mappings of /dev/null, all processes see the same page full of * mappings of /dev/null, all processes see the same page full of
......
...@@ -1221,9 +1221,7 @@ static int insert_page(struct mm_struct *mm, unsigned long addr, struct page *pa ...@@ -1221,9 +1221,7 @@ static int insert_page(struct mm_struct *mm, unsigned long addr, struct page *pa
* The page has to be a nice clean _individual_ kernel allocation. * The page has to be a nice clean _individual_ kernel allocation.
* If you allocate a compound page, you need to have marked it as * If you allocate a compound page, you need to have marked it as
* such (__GFP_COMP), or manually just split the page up yourself * such (__GFP_COMP), or manually just split the page up yourself
* (which is mainly an issue of doing "set_page_count(page, 1)" for * (see split_page()).
* each sub-page, and then freeing them one by one when you free
* them rather than freeing it as a compound page).
* *
* NOTE! Traditionally this was done with "remap_pfn_range()" which * NOTE! Traditionally this was done with "remap_pfn_range()" which
* took an arbitrary page protection parameter. This doesn't allow * took an arbitrary page protection parameter. This doesn't allow
......
...@@ -752,6 +752,28 @@ static inline void prep_zero_page(struct page *page, int order, gfp_t gfp_flags) ...@@ -752,6 +752,28 @@ static inline void prep_zero_page(struct page *page, int order, gfp_t gfp_flags)
clear_highpage(page + i); clear_highpage(page + i);
} }
#ifdef CONFIG_MMU
/*
* split_page takes a non-compound higher-order page, and splits it into
* n (1<<order) sub-pages: page[0..n]
* Each sub-page must be freed individually.
*
* Note: this is probably too low level an operation for use in drivers.
* Please consult with lkml before using this in your driver.
*/
void split_page(struct page *page, unsigned int order)
{
int i;
BUG_ON(PageCompound(page));
BUG_ON(!page_count(page));
for (i = 1; i < (1 << order); i++) {
BUG_ON(page_count(page + i));
set_page_count(page + i, 1);
}
}
#endif
/* /*
* Really, prep_compound_page() should be called from __rmqueue_bulk(). But * Really, prep_compound_page() should be called from __rmqueue_bulk(). But
* we cheat by calling it from here, in the order > 0 path. Saves a branch * we cheat by calling it from here, in the order > 0 path. Saves a branch
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment