Commit 8fd3d458 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] batched freeing of anon pages

A reworked version of the batched page freeing and lock amortisation
for VMA teardown.

It walks the existing 507-page list in the mmu_gather_t in 16-page
chunks, drops their refcounts in 16-page chunks, and de-LRUs and
frees any resulting zero-count pages in up-to-16 page chunks.
parent 2b341443
...@@ -79,10 +79,8 @@ static inline void tlb_flush_mmu(mmu_gather_t *tlb, unsigned long start, unsigne ...@@ -79,10 +79,8 @@ static inline void tlb_flush_mmu(mmu_gather_t *tlb, unsigned long start, unsigne
tlb_flush(tlb); tlb_flush(tlb);
nr = tlb->nr; nr = tlb->nr;
if (!tlb_fast_mode(tlb)) { if (!tlb_fast_mode(tlb)) {
unsigned long i; free_pages_and_swap_cache(tlb->pages, tlb->nr);
tlb->nr = 0; tlb->nr = 0;
for (i=0; i < nr; i++)
free_page_and_swap_cache(tlb->pages[i]);
} }
} }
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#define page_cache_get(page) get_page(page) #define page_cache_get(page) get_page(page)
#define page_cache_release(page) put_page(page) #define page_cache_release(page) put_page(page)
void release_pages(struct page **pages, int nr);
static inline struct page *page_cache_alloc(struct address_space *x) static inline struct page *page_cache_alloc(struct address_space *x)
{ {
......
...@@ -182,6 +182,7 @@ extern int move_to_swap_cache(struct page *page, swp_entry_t entry); ...@@ -182,6 +182,7 @@ extern int move_to_swap_cache(struct page *page, swp_entry_t entry);
extern int move_from_swap_cache(struct page *page, unsigned long index, extern int move_from_swap_cache(struct page *page, unsigned long index,
struct address_space *mapping); struct address_space *mapping);
extern void free_page_and_swap_cache(struct page *page); extern void free_page_and_swap_cache(struct page *page);
extern void free_pages_and_swap_cache(struct page **pages, int nr);
extern struct page * lookup_swap_cache(swp_entry_t); extern struct page * lookup_swap_cache(swp_entry_t);
extern struct page * read_swap_cache_async(swp_entry_t); extern struct page * read_swap_cache_async(swp_entry_t);
......
...@@ -87,7 +87,7 @@ void __page_cache_release(struct page *page) ...@@ -87,7 +87,7 @@ void __page_cache_release(struct page *page)
/* /*
* Batched page_cache_release(). Decrement the reference count on all the * Batched page_cache_release(). Decrement the reference count on all the
* pagevec's pages. If it fell to zero then remove the page from the LRU and * passed pages. If it fell to zero then remove the page from the LRU and
* free it. * free it.
* *
* Avoid taking zone->lru_lock if possible, but if it is taken, retain it * Avoid taking zone->lru_lock if possible, but if it is taken, retain it
...@@ -96,18 +96,16 @@ void __page_cache_release(struct page *page) ...@@ -96,18 +96,16 @@ void __page_cache_release(struct page *page)
* The locking in this function is against shrink_cache(): we recheck the * The locking in this function is against shrink_cache(): we recheck the
* page count inside the lock to see whether shrink_cache grabbed the page * page count inside the lock to see whether shrink_cache grabbed the page
* via the LRU. If it did, give up: shrink_cache will free it. * via the LRU. If it did, give up: shrink_cache will free it.
*
* This function reinitialises the caller's pagevec.
*/ */
void __pagevec_release(struct pagevec *pvec) void release_pages(struct page **pages, int nr)
{ {
int i; int i;
struct pagevec pages_to_free; struct pagevec pages_to_free;
struct zone *zone = NULL; struct zone *zone = NULL;
pagevec_init(&pages_to_free); pagevec_init(&pages_to_free);
for (i = 0; i < pagevec_count(pvec); i++) { for (i = 0; i < nr; i++) {
struct page *page = pvec->pages[i]; struct page *page = pages[i];
struct zone *pagezone; struct zone *pagezone;
if (PageReserved(page) || !put_page_testzero(page)) if (PageReserved(page) || !put_page_testzero(page))
...@@ -122,13 +120,24 @@ void __pagevec_release(struct pagevec *pvec) ...@@ -122,13 +120,24 @@ void __pagevec_release(struct pagevec *pvec)
} }
if (TestClearPageLRU(page)) if (TestClearPageLRU(page))
del_page_from_lru(zone, page); del_page_from_lru(zone, page);
if (page_count(page) == 0) if (page_count(page) == 0) {
pagevec_add(&pages_to_free, page); if (!pagevec_add(&pages_to_free, page)) {
spin_unlock_irq(&zone->lru_lock);
pagevec_free(&pages_to_free);
pagevec_init(&pages_to_free);
spin_lock_irq(&zone->lru_lock);
}
}
} }
if (zone) if (zone)
spin_unlock_irq(&zone->lru_lock); spin_unlock_irq(&zone->lru_lock);
pagevec_free(&pages_to_free); pagevec_free(&pages_to_free);
}
void __pagevec_release(struct pagevec *pvec)
{
release_pages(pvec->pages, pagevec_count(pvec));
pagevec_init(pvec); pagevec_init(pvec);
} }
......
...@@ -292,28 +292,55 @@ int move_from_swap_cache(struct page *page, unsigned long index, ...@@ -292,28 +292,55 @@ int move_from_swap_cache(struct page *page, unsigned long index,
return err; return err;
} }
/* /*
* Perform a free_page(), also freeing any swap cache associated with * If we are the only user, then try to free up the swap cache.
* this page if it is the last user of the page. Can not do a lock_page, *
* as we are holding the page_table_lock spinlock. * Its ok to check for PageSwapCache without the page lock
* here because we are going to recheck again inside
* exclusive_swap_page() _with_ the lock.
* - Marcelo
*/ */
void free_page_and_swap_cache(struct page *page) static inline void free_swap_cache(struct page *page)
{ {
/*
* If we are the only user, then try to free up the swap cache.
*
* Its ok to check for PageSwapCache without the page lock
* here because we are going to recheck again inside
* exclusive_swap_page() _with_ the lock.
* - Marcelo
*/
if (PageSwapCache(page) && !TestSetPageLocked(page)) { if (PageSwapCache(page) && !TestSetPageLocked(page)) {
remove_exclusive_swap_page(page); remove_exclusive_swap_page(page);
unlock_page(page); unlock_page(page);
} }
}
/*
* Perform a free_page(), also freeing any swap cache associated with
* this page if it is the last user of the page. Can not do a lock_page,
* as we are holding the page_table_lock spinlock.
*/
void free_page_and_swap_cache(struct page *page)
{
free_swap_cache(page);
page_cache_release(page); page_cache_release(page);
} }
/*
* Passed an array of pages, drop them all from swapcache and then release
* them. They are removed from the LRU and freed if this is their last use.
*/
void free_pages_and_swap_cache(struct page **pages, int nr)
{
const int chunk = 16;
struct page **pagep = pages;
while (nr) {
int todo = min(chunk, nr);
int i;
for (i = 0; i < todo; i++)
free_swap_cache(pagep[i]);
release_pages(pagep, todo);
pagep += todo;
nr -= todo;
}
}
/* /*
* Lookup a swap entry in the swap cache. A found page will be returned * Lookup a swap entry in the swap cache. A found page will be returned
* unlocked and with its refcount incremented - we rely on the kernel * unlocked and with its refcount incremented - we rely on the kernel
......
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