batched freeing of anonymous pages
Subsystems (7)
PATCH 7/21 batched freeing of anonymous pages
The VMA teardown code is currently removing pages from the LRU one-at-a-time. And it is freeing those pages one-at-a-time.
The patch batches that up to sixteen-at-a-time by passing an on-stack pagevec around and freeing all the pages whenever the pagevec fills up.
include/asm-arm/tlb.h | 2 +- include/asm-generic/tlb.h | 12 +++++++++--- include/asm-i386/pgalloc.h | 2 +- include/asm-ia64/pgalloc.h | 2 +- include/asm-m68k/motorola_pgalloc.h | 3 ++- include/asm-m68k/sun3_pgalloc.h | 5 +++-- include/asm-ppc/pgalloc.h | 2 +- include/asm-ppc64/pgalloc.h | 4 ++-- include/asm-s390/pgalloc.h | 2 +- include/asm-s390x/pgalloc.h | 2 +- include/asm-sparc64/tlb.h | 2 +- include/asm-x86_64/pgalloc.h | 2 +- include/linux/pagevec.h | 3 +++ include/linux/swap.h | 3 ++- mm/memory.c | 21 +++++++++++++++------ mm/mmap.c | 1 + mm/page_alloc.c | 16 +++++++++++++--- mm/swap_state.c | 6 ++++-- 18 files changed, 62 insertions(+), 28 deletions(-)
mm/swap_state.c renamed from mm/swap_state.c~anon-pagevec +4 -2
@@ -14,6 +14,7 @@#include <linux/pagemap.h> #include <linux/smp_lock.h> #include <linux/buffer_head.h> /* block_sync_page() */ +#include <linux/pagevec.h> #include <asm/pgtable.h>
@@ -295,7 +296,7 @@ int move_from_swap_cache(struct page *pa* 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) +void free_page_and_swap_cache (struct pagevec *pvec,struct page *page) {/* * If we are the only user, then try to free up the swap cache.
@@ -309,7 +310,8 @@ void free_page_and_swap_cache(struct pagremove_exclusive_swap_page (page);unlock_page (page); } -page_cache_release (page); +if (!pagevec_add (pvec, page)) +__pagevec_release (pvec); }/*
include/asm-generic/tlb.h renamed from include/asm-generic/tlb.h~anon-pagevec +9 -3
@@ -14,6 +14,7 @@#define _ASM_GENERIC__TLB_H#include <linux/config.h> +#include <linux/pagevec.h> #include <asm/tlbflush.h> /*
@@ -70,9 +71,13 @@ static inline void tlb_flush_mmu(mmu_gat nr = tlb->nr;if (!tlb_fast_mode (tlb)) {unsigned long i; +struct pagevec pvec; + tlb->nr =0 ; +pagevec_init (&pvec);for (i=0 ; i < nr; i++) -free_page_and_swap_cache (tlb->pages[i]); +free_page_and_swap_cache (&pvec, tlb->pages[i]); +pagevec_release (&pvec); } }
@@ -101,10 +106,11 @@ static inline void tlb_finish_mmu(mmu_ga* handling the additional races in SMP caused by other CPUs caching valid * mappings in their TLBs. */ -static inline void tlb_remove_page (mmu_gather_t *tlb,struct page *page) +static inline void +tlb_remove_page (mmu_gather_t *tlb,struct pagevec *pvec,struct page *page) {if (tlb_fast_mode (tlb)) { -free_page_and_swap_cache (page); +free_page_and_swap_cache (pvec, page);return ; } tlb->pages[tlb->nr++] = page;
include/linux/swap.h renamed from include/linux/swap.h~anon-pagevec +2 -1
@@ -140,6 +140,7 @@ struct vm_area_struct;struct sysinfo ;struct address_space ;struct zone_t ; +struct pagevec ;/* linux/mm/rmap.c */ extern int FASTCALL (page_referenced (struct page *));
@@ -183,7 +184,7 @@ extern void delete_from_swap_cache(strucextern int move_to_swap_cache (struct page *page,swp_entry_t entry);extern int move_from_swap_cache (struct page *page,unsigned long index,struct address_space *mapping); -extern void free_page_and_swap_cache (struct page *page); +extern void free_page_and_swap_cache (struct pagevec *pvec,struct page *page);extern struct page *lookup_swap_cache (swp_entry_t );extern struct page *read_swap_cache_async (swp_entry_t );
mm/memory.c renamed from mm/memory.c~anon-pagevec +15 -6
@@ -44,6 +44,7 @@#include <linux/iobuf.h> #include <linux/highmem.h> #include <linux/pagemap.h> +#include <linux/pagevec.h> #include <asm/pgalloc.h> #include <asm/rmap.h>
@@ -78,7 +79,8 @@ struct page *mem_map;* Note: this doesn't free the actual pages themselves. That * has been handled earlier when unmapping all the memory regions. */ -static inline void free_one_pmd (mmu_gather_t *tlb,pmd_t * dir) +static inline void +free_one_pmd (mmu_gather_t *tlb,struct pagevec *pvec,pmd_t * dir) {struct page *page;
@@ -92,10 +94,11 @@ static inline void free_one_pmd(mmu_gath page =pmd_page (*dir);pmd_clear (dir);pgtable_remove_rmap (page); -pte_free_tlb (tlb, page); +pte_free_tlb (tlb, pvec, page); } -static inline void free_one_pgd (mmu_gather_t *tlb,pgd_t * dir) +static inline void +free_one_pgd (mmu_gather_t *tlb,struct pagevec *pvec,pgd_t *dir) {int j;pmd_t * pmd;
@@ -111,7 +114,7 @@ static inline void free_one_pgd(mmu_gathpgd_clear (dir);for (j =0 ; j <PTRS_PER_PMD ; j++) {prefetchw (pmd+j+(PREFETCH_STRIDE /16 )); -free_one_pmd (tlb, pmd+j); +free_one_pmd (tlb, pvec, pmd+j); }pmd_free_tlb (tlb, pmd); }
@@ -125,12 +128,15 @@ static inline void free_one_pgd(mmu_gathvoid clear_page_tables (mmu_gather_t *tlb,unsigned long first,int nr) {pgd_t * page_dir = tlb->mm->pgd; +struct pagevec pvec; +pagevec_init (&pvec); page_dir += first;do { -free_one_pgd (tlb, page_dir); +free_one_pgd (tlb, &pvec, page_dir); page_dir++; }while (--nr); +pagevec_release (&pvec); }pte_t *pte_alloc_map (struct mm_struct *mm,pmd_t *pmd,unsigned long address)
@@ -322,6 +328,7 @@ static void zap_pte_range(mmu_gather_t * {unsigned long offset;pte_t *ptep; +struct pagevec pvec;if (pmd_none (*pmd))return ;
@@ -335,6 +342,7 @@ static void zap_pte_range(mmu_gather_t *if (offset + size >PMD_SIZE ) size =PMD_SIZE - offset; size &=PAGE_MASK ; +pagevec_init (&pvec);for (offset=0 ; offset < size; ptep++, offset +=PAGE_SIZE ) {pte_t pte = *ptep;if (pte_none (pte))
@@ -351,7 +359,7 @@ static void zap_pte_range(mmu_gather_t *set_page_dirty (page); tlb->freed++;page_remove_rmap (page, ptep); -tlb_remove_page (tlb, page); +tlb_remove_page (tlb, &pvec, page); } } }else {
@@ -360,6 +368,7 @@ static void zap_pte_range(mmu_gather_t * } }pte_unmap (ptep-1 ); +pagevec_release (&pvec); }static void zap_pmd_range (mmu_gather_t *tlb,pgd_t * dir,unsigned long address,unsigned long size)
mm/page_alloc.c renamed from mm/page_alloc.c~anon-pagevec +13 -3
@@ -452,10 +452,20 @@ unsigned long get_zeroed_page(unsigned ivoid page_cache_release (struct page *page) { +/* +* FIXME: this PageReserved test is really expensive +*/ if (!PageReserved (page) &&put_page_testzero (page)) { -if (PageLRU (page)) -lru_cache_del (page); -__free_pages_ok (page,0 ); +/* +* This path almost never happens - pages are normally freed +* via pagevecs. +*/ +struct pagevec pvec; + +page_cache_get (page); + pvec.nr =1 ; + pvec.pages[0 ] = page; +__pagevec_release (&pvec); } }
mm/mmap.c renamed from mm/mmap.c~anon-pagevec +1 -0
@@ -16,6 +16,7 @@#include <linux/file.h> #include <linux/fs.h> #include <linux/personality.h> +#include <linux/pagevec.h> #include <linux/security.h> #include <asm/uaccess.h>
include/asm-m68k/sun3_pgalloc.h renamed from include/asm-m68k/sun3_pgalloc.h~anon-pagevec +3 -2
@@ -31,9 +31,10 @@ static inline void pte_free(struct page__free_page (page); } -static inline void pte_free_tlb (mmu_gather_t *tlb,struct page *page) +static inline void +pte_free_tlb (mmu_gather_t *tlb,struct pagevec *pvec,struct page *page) { -tlb_remove_page (tlb, page); +tlb_remove_page (tlb, pvec, page); } static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
include/asm-i386/pgalloc.h renamed from include/asm-i386/pgalloc.h~anon-pagevec +1 -1
@@ -37,7 +37,7 @@ static inline void pte_free(struct page } -#define pte_free_tlb (tlb,pte) tlb_remove_page((tlb),(pte)) +#define pte_free_tlb (tlb,pvec,pte) tlb_remove_page((tlb),(pvec),(pte))/* * allocating and freeing a pmd is trivial: the 1-entry pmd is
include/asm-ia64/pgalloc.h renamed from include/asm-ia64/pgalloc.h~anon-pagevec +1 -1
@@ -153,7 +153,7 @@ pte_free_kernel (pte_t *pte)free_page ((unsigned long ) pte); } -#define pte_free_tlb (tlb, pte) tlb_remove_page((tlb), (pte)) +#define pte_free_tlb (tlb, pvec, pte) tlb_remove_page((tlb), (pvec), (pte))extern void check_pgt_cache (void );
include/asm-s390/pgalloc.h renamed from include/asm-s390/pgalloc.h~anon-pagevec +1 -1
@@ -107,7 +107,7 @@ static inline void pte_free(struct page__free_page (pte); } -#define pte_free_tlb (tlb,pte) tlb_remove_page((tlb),(pte)) +#define pte_free_tlb (tlb,pvec,pte) tlb_remove_page((tlb),(pvec),(pte))/* * This establishes kernel virtual mappings (e.g., as a result of a
include/asm-s390x/pgalloc.h renamed from include/asm-s390x/pgalloc.h~anon-pagevec +1 -1
@@ -123,7 +123,7 @@ static inline void pte_free(struct page__free_page (pte); } -#define pte_free_tlb (tlb,pte) tlb_remove_page((tlb),(pte)) +#define pte_free_tlb (tlb,pvec,pte) tlb_remove_page((tlb),(pvec),(pte))/* * This establishes kernel virtual mappings (e.g., as a result of a
include/asm-x86_64/pgalloc.h renamed from include/asm-x86_64/pgalloc.h~anon-pagevec +1 -1
@@ -75,7 +75,7 @@ extern inline void pte_free(struct page __free_page(pte); } -#define pte_free_tlb (tlb,pte) tlb_remove_page((tlb),(pte)) +#define pte_free_tlb (tlb,pvec,pte) tlb_remove_page((tlb),(pvec),(pte))#define pmd_free_tlb (tlb,x) do { } while (0)#endif /* _X86_64_PGALLOC_H */
include/asm-arm/tlb.h renamed from include/asm-arm/tlb.h~anon-pagevec +1 -1
@@ -16,6 +16,6 @@#include <asm-generic/tlb.h> #define pmd_free_tlb (tlb, pmd) pmd_free(pmd) -#define pte_free_tlb (tlb, pte) pte_free(pte) +#define pte_free_tlb (tlb, pvec, pte) pte_free(pte)#endif
include/asm-m68k/motorola_pgalloc.h renamed from include/asm-m68k/motorola_pgalloc.h~anon-pagevec +2 -1
@@ -55,7 +55,8 @@ static inline void pte_free(struct page__free_page (page); } -static inline void pte_free_tlb (mmu_gather_t *tlb,struct page *page) +static inline void +pte_free_tlb (mmu_gather_t *tlb,struct pagevec *pvec,struct page *page) {cache_page (kmap (page));kunmap (page);
include/asm-ppc/pgalloc.h renamed from include/asm-ppc/pgalloc.h~anon-pagevec +1 -1
@@ -33,7 +33,7 @@ extern struct page *pte_alloc_one(structextern void pte_free_kernel (pte_t *pte);extern void pte_free (struct page *pte); -#define pte_free_tlb (tlb, pte) pte_free((pte)) +#define pte_free_tlb (tlb, pvec, pte) pte_free((pte))#define check_pgt_cache () do { } while (0)
include/asm-sparc64/tlb.h renamed from include/asm-sparc64/tlb.h~anon-pagevec +1 -1
@@ -22,6 +22,6 @@ do { if (!(tlb)->fullmm) \#include <asm-generic/tlb.h> #define pmd_free_tlb (tlb, pmd) pmd_free(pmd) -#define pte_free_tlb (tlb, pte) pte_free(pte) +#define pte_free_tlb (tlb, pvec, pte) pte_free(pte)#endif /* _SPARC64_TLB_H */
include/asm-ppc64/pgalloc.h renamed from include/asm-ppc64/pgalloc.h~anon-pagevec +2 -2
@@ -87,8 +87,8 @@ pte_free_kernel(pte_t *pte)free_page ((unsigned long )pte); } -#define pte_free (pte_page) pte_free_kernel(page_address(pte_page)) -#define pte_free_tlb (tlb, pte) pte_free(pte) +#define pte_free (pte_page) pte_free_kernel(page_address(pte_page)) +#define pte_free_tlb (tlb, pvec, pte) pte_free(pte)#define check_pgt_cache () do { } while (0)
include/linux/pagevec.h renamed from include/linux/pagevec.h~anon-pagevec +3 -0
@@ -1,3 +1,5 @@ +#ifndef _LINUX_PAGEVEC_H +#define _LINUX_PAGEVEC_H/* * include/linux/pagevec.h *
@@ -74,3 +76,4 @@ static inline void pagevec_lru_del(strucif (pagevec_count (pvec)) __pagevec_lru_del(pvec); } +#endif _LINUX_PAGEVEC_H
.
Quoted 3 lines · Andrew Morton
The VMA teardown code is currently removing pages from the LRU one-at-a-time. And it is freeing those pages one-at-a-time.
This patch is wrong.
We already _have_ the pagevec for page table teardown, and by making it a per-CPU static structure instead of allocating it on the stack it can be made (and is) quite a lot bigger than a pvec.
If you want batching here, then the right approach is to just remove the "fast" code entirely, and batch it properly at the TLB struct level (since we _have_ to batch it there anyway, to fix the the thread unmapping TLB race condition)
This is what "tlbgather" is all about.
Linus