Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

asm-generic: pgalloc: provide generic pmd_alloc_one() and pmd_free_one()

For most architectures that support >2 levels of page tables,
pmd_alloc_one() is a wrapper for __get_free_pages(), sometimes with
__GFP_ZERO and sometimes followed by memset(0) instead.

More elaborate versions on arm64 and x86 account memory for the user page
tables and call to pgtable_pmd_page_ctor() as the part of PMD page
initialization.

Move the arm64 version to include/asm-generic/pgalloc.h and use the
generic version on several architectures.

The pgtable_pmd_page_ctor() is a NOP when ARCH_ENABLE_SPLIT_PMD_PTLOCK is
not enabled, so there is no functional change for most architectures
except of the addition of __GFP_ACCOUNT for allocation of user page
tables.

The pmd_free() is a wrapper for free_page() in all the cases, so no
functional change here.

Signed-off-by: Mike Rapoport <rppt@linux.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Pekka Enberg <penberg@kernel.org>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Abdul Haleem <abdhalee@linux.vnet.ibm.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Christophe Leroy <christophe.leroy@csgroup.eu>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Joerg Roedel <jroedel@suse.de>
Cc: Max Filippov <jcmvbkbc@gmail.com>
Cc: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Satheesh Rajendran <sathnaga@linux.vnet.ibm.com>
Cc: Stafford Horne <shorne@gmail.com>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Link: http://lkml.kernel.org/r/20200627143453.31835-5-rppt@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Mike Rapoport and committed by
Linus Torvalds
1355c31e 7278914c

+55 -135
+1 -14
arch/alpha/include/asm/pgalloc.h
··· 5 5 #include <linux/mm.h> 6 6 #include <linux/mmzone.h> 7 7 8 - #include <asm-generic/pgalloc.h> /* for pte_{alloc,free}_one */ 8 + #include <asm-generic/pgalloc.h> 9 9 10 10 /* 11 11 * Allocate and free page tables. The xxx_kernel() versions are ··· 38 38 pgd_free(struct mm_struct *mm, pgd_t *pgd) 39 39 { 40 40 free_page((unsigned long)pgd); 41 - } 42 - 43 - static inline pmd_t * 44 - pmd_alloc_one(struct mm_struct *mm, unsigned long address) 45 - { 46 - pmd_t *ret = (pmd_t *)__get_free_page(GFP_PGTABLE_USER); 47 - return ret; 48 - } 49 - 50 - static inline void 51 - pmd_free(struct mm_struct *mm, pmd_t *pmd) 52 - { 53 - free_page((unsigned long)pmd); 54 41 } 55 42 56 43 #endif /* _ALPHA_PGALLOC_H */
-11
arch/arm/include/asm/pgalloc.h
··· 22 22 23 23 #ifdef CONFIG_ARM_LPAE 24 24 25 - static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) 26 - { 27 - return (pmd_t *)get_zeroed_page(GFP_KERNEL); 28 - } 29 - 30 - static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) 31 - { 32 - BUG_ON((unsigned long)pmd & (PAGE_SIZE-1)); 33 - free_page((unsigned long)pmd); 34 - } 35 - 36 25 static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) 37 26 { 38 27 set_pud(pud, __pud(__pa(pmd) | PMD_TYPE_TABLE));
+1 -26
arch/arm64/include/asm/pgalloc.h
··· 13 13 #include <asm/cacheflush.h> 14 14 #include <asm/tlbflush.h> 15 15 16 - #include <asm-generic/pgalloc.h> /* for pte_{alloc,free}_one */ 16 + #include <asm-generic/pgalloc.h> 17 17 18 18 #define PGD_SIZE (PTRS_PER_PGD * sizeof(pgd_t)) 19 19 20 20 #if CONFIG_PGTABLE_LEVELS > 2 21 - 22 - static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) 23 - { 24 - gfp_t gfp = GFP_PGTABLE_USER; 25 - struct page *page; 26 - 27 - if (mm == &init_mm) 28 - gfp = GFP_PGTABLE_KERNEL; 29 - 30 - page = alloc_page(gfp); 31 - if (!page) 32 - return NULL; 33 - if (!pgtable_pmd_page_ctor(page)) { 34 - __free_page(page); 35 - return NULL; 36 - } 37 - return page_address(page); 38 - } 39 - 40 - static inline void pmd_free(struct mm_struct *mm, pmd_t *pmdp) 41 - { 42 - BUG_ON((unsigned long)pmdp & (PAGE_SIZE-1)); 43 - pgtable_pmd_page_dtor(virt_to_page(pmdp)); 44 - free_page((unsigned long)pmdp); 45 - } 46 21 47 22 static inline void __pud_populate(pud_t *pudp, phys_addr_t pmdp, pudval_t prot) 48 23 {
-10
arch/ia64/include/asm/pgalloc.h
··· 59 59 pud_val(*pud_entry) = __pa(pmd); 60 60 } 61 61 62 - static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) 63 - { 64 - return (pmd_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO); 65 - } 66 - 67 - static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) 68 - { 69 - free_page((unsigned long)pmd); 70 - } 71 - 72 62 #define __pmd_free_tlb(tlb, pmd, address) pmd_free((tlb)->mm, pmd) 73 63 74 64 static inline void
+2 -6
arch/mips/include/asm/pgalloc.h
··· 13 13 #include <linux/mm.h> 14 14 #include <linux/sched.h> 15 15 16 - #include <asm-generic/pgalloc.h> /* for pte_{alloc,free}_one */ 16 + #define __HAVE_ARCH_PMD_ALLOC_ONE 17 + #include <asm-generic/pgalloc.h> 17 18 18 19 static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, 19 20 pte_t *pte) ··· 69 68 if (pmd) 70 69 pmd_init((unsigned long)pmd, (unsigned long)invalid_pte_table); 71 70 return pmd; 72 - } 73 - 74 - static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) 75 - { 76 - free_pages((unsigned long)pmd, PMD_ORDER); 77 71 } 78 72 79 73 #define __pmd_free_tlb(tlb, x, addr) pmd_free((tlb)->mm, x)
+2 -9
arch/parisc/include/asm/pgalloc.h
··· 10 10 11 11 #include <asm/cache.h> 12 12 13 - #include <asm-generic/pgalloc.h> /* for pte_{alloc,free}_one */ 13 + #define __HAVE_ARCH_PMD_FREE 14 + #include <asm-generic/pgalloc.h> 14 15 15 16 /* Allocate the top level pgd (page directory) 16 17 * ··· 64 63 { 65 64 set_pud(pud, __pud((PxD_FLAG_PRESENT | PxD_FLAG_VALID) + 66 65 (__u32)(__pa((unsigned long)pmd) >> PxD_VALUE_SHIFT))); 67 - } 68 - 69 - static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address) 70 - { 71 - pmd_t *pmd = (pmd_t *)__get_free_pages(GFP_KERNEL, PMD_ORDER); 72 - if (pmd) 73 - memset(pmd, 0, PAGE_SIZE<<PMD_ORDER); 74 - return pmd; 75 66 } 76 67 77 68 static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
+1 -12
arch/riscv/include/asm/pgalloc.h
··· 11 11 #include <asm/tlb.h> 12 12 13 13 #ifdef CONFIG_MMU 14 - #include <asm-generic/pgalloc.h> /* for pte_{alloc,free}_one */ 14 + #include <asm-generic/pgalloc.h> 15 15 16 16 static inline void pmd_populate_kernel(struct mm_struct *mm, 17 17 pmd_t *pmd, pte_t *pte) ··· 61 61 } 62 62 63 63 #ifndef __PAGETABLE_PMD_FOLDED 64 - 65 - static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) 66 - { 67 - return (pmd_t *)__get_free_page( 68 - GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_ZERO); 69 - } 70 - 71 - static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) 72 - { 73 - free_page((unsigned long)pmd); 74 - } 75 64 76 65 #define __pmd_free_tlb(tlb, pmd, addr) pmd_free((tlb)->mm, pmd) 77 66
+3
arch/sh/include/asm/pgalloc.h
··· 3 3 #define __ASM_SH_PGALLOC_H 4 4 5 5 #include <asm/page.h> 6 + 7 + #define __HAVE_ARCH_PMD_ALLOC_ONE 8 + #define __HAVE_ARCH_PMD_FREE 6 9 #include <asm-generic/pgalloc.h> 7 10 8 11 extern pgd_t *pgd_alloc(struct mm_struct *);
+1 -7
arch/um/include/asm/pgalloc.h
··· 10 10 11 11 #include <linux/mm.h> 12 12 13 - #include <asm-generic/pgalloc.h> /* for pte_{alloc,free}_one */ 13 + #include <asm-generic/pgalloc.h> 14 14 15 15 #define pmd_populate_kernel(mm, pmd, pte) \ 16 16 set_pmd(pmd, __pmd(_PAGE_TABLE + (unsigned long) __pa(pte))) ··· 34 34 } while (0) 35 35 36 36 #ifdef CONFIG_3_LEVEL_PGTABLES 37 - 38 - static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) 39 - { 40 - free_page((unsigned long)pmd); 41 - } 42 - 43 37 #define __pmd_free_tlb(tlb,x, address) tlb_remove_page((tlb),virt_to_page(x)) 44 38 #endif 45 39
-3
arch/um/include/asm/pgtable-3level.h
··· 78 78 #define set_pmd(pmdptr, pmdval) (*(pmdptr) = (pmdval)) 79 79 #endif 80 80 81 - struct mm_struct; 82 - extern pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address); 83 - 84 81 static inline void pud_clear (pud_t *pud) 85 82 { 86 83 set_pud(pud, __pud(_PAGE_NEWPAGE));
-12
arch/um/kernel/mem.c
··· 201 201 free_page((unsigned long) pgd); 202 202 } 203 203 204 - #ifdef CONFIG_3_LEVEL_PGTABLES 205 - pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address) 206 - { 207 - pmd_t *pmd = (pmd_t *) __get_free_page(GFP_KERNEL); 208 - 209 - if (pmd) 210 - memset(pmd, 0, PAGE_SIZE); 211 - 212 - return pmd; 213 - } 214 - #endif 215 - 216 204 void *uml_kmalloc(int size, int flags) 217 205 { 218 206 return kmalloc(size, flags);
+1 -25
arch/x86/include/asm/pgalloc.h
··· 7 7 #include <linux/pagemap.h> 8 8 9 9 #define __HAVE_ARCH_PTE_ALLOC_ONE 10 - #include <asm-generic/pgalloc.h> /* for pte_{alloc,free}_one */ 10 + #include <asm-generic/pgalloc.h> 11 11 12 12 static inline int __paravirt_pgd_alloc(struct mm_struct *mm) { return 0; } 13 13 ··· 86 86 #define pmd_pgtable(pmd) pmd_page(pmd) 87 87 88 88 #if CONFIG_PGTABLE_LEVELS > 2 89 - static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) 90 - { 91 - struct page *page; 92 - gfp_t gfp = GFP_KERNEL_ACCOUNT | __GFP_ZERO; 93 - 94 - if (mm == &init_mm) 95 - gfp &= ~__GFP_ACCOUNT; 96 - page = alloc_pages(gfp, 0); 97 - if (!page) 98 - return NULL; 99 - if (!pgtable_pmd_page_ctor(page)) { 100 - __free_pages(page, 0); 101 - return NULL; 102 - } 103 - return (pmd_t *)page_address(page); 104 - } 105 - 106 - static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) 107 - { 108 - BUG_ON((unsigned long)pmd & (PAGE_SIZE-1)); 109 - pgtable_pmd_page_dtor(virt_to_page(pmd)); 110 - free_page((unsigned long)pmd); 111 - } 112 - 113 89 extern void ___pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd); 114 90 115 91 static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd,
+43
include/asm-generic/pgalloc.h
··· 102 102 __free_page(pte_page); 103 103 } 104 104 105 + 106 + #if CONFIG_PGTABLE_LEVELS > 2 107 + 108 + #ifndef __HAVE_ARCH_PMD_ALLOC_ONE 109 + /** 110 + * pmd_alloc_one - allocate a page for PMD-level page table 111 + * @mm: the mm_struct of the current context 112 + * 113 + * Allocates a page and runs the pgtable_pmd_page_ctor(). 114 + * Allocations use %GFP_PGTABLE_USER in user context and 115 + * %GFP_PGTABLE_KERNEL in kernel context. 116 + * 117 + * Return: pointer to the allocated memory or %NULL on error 118 + */ 119 + static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) 120 + { 121 + struct page *page; 122 + gfp_t gfp = GFP_PGTABLE_USER; 123 + 124 + if (mm == &init_mm) 125 + gfp = GFP_PGTABLE_KERNEL; 126 + page = alloc_pages(gfp, 0); 127 + if (!page) 128 + return NULL; 129 + if (!pgtable_pmd_page_ctor(page)) { 130 + __free_pages(page, 0); 131 + return NULL; 132 + } 133 + return (pmd_t *)page_address(page); 134 + } 135 + #endif 136 + 137 + #ifndef __HAVE_ARCH_PMD_FREE 138 + static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd) 139 + { 140 + BUG_ON((unsigned long)pmd & (PAGE_SIZE-1)); 141 + pgtable_pmd_page_dtor(virt_to_page(pmd)); 142 + free_page((unsigned long)pmd); 143 + } 144 + #endif 145 + 146 + #endif /* CONFIG_PGTABLE_LEVELS > 2 */ 147 + 105 148 #endif /* CONFIG_MMU */ 106 149 107 150 #endif /* __ASM_GENERIC_PGALLOC_H */