mirror of
https://github.com/adulau/aha.git
synced 2024-12-29 12:16:20 +00:00
[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: Nick 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: Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
8e7a9aae91
commit
8dfcc9ba27
9 changed files with 41 additions and 21 deletions
|
@ -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,
|
||||||
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,
|
||||||
* 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)
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
* 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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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 @@ out:
|
||||||
* 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)
|
||||||
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
|
||||||
|
|
Loading…
Reference in a new issue