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

LoongArch: Set initial pte entry with PAGE_GLOBAL for kernel space

There are two pages in one TLB entry on LoongArch system. For kernel
space, it requires both two pte entries (buddies) with PAGE_GLOBAL bit
set, otherwise HW treats it as non-global tlb, there will be potential
problems if tlb entry for kernel space is not global. Such as fail to
flush kernel tlb with the function local_flush_tlb_kernel_range() which
supposed only flush tlb with global bit.

Kernel address space areas include percpu, vmalloc, vmemmap, fixmap and
kasan areas. For these areas both two consecutive page table entries
should be enabled with PAGE_GLOBAL bit. So with function set_pte() and
pte_clear(), pte buddy entry is checked and set besides its own pte
entry. However it is not atomic operation to set both two pte entries,
there is problem with test_vmalloc test case.

So function kernel_pte_init() is added to init a pte table when it is
created for kernel address space, and the default initial pte value is
PAGE_GLOBAL rather than zero at beginning. Then only its own pte entry
need update with function set_pte() and pte_clear(), nothing to do with
the pte buddy entry.

Signed-off-by: Bibo Mao <maobibo@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>

authored by

Bibo Mao and committed by
Huacai Chen
d2f86710 134475a9

+54 -30
+11
arch/loongarch/include/asm/pgalloc.h
··· 10 10 11 11 #define __HAVE_ARCH_PMD_ALLOC_ONE 12 12 #define __HAVE_ARCH_PUD_ALLOC_ONE 13 + #define __HAVE_ARCH_PTE_ALLOC_ONE_KERNEL 13 14 #include <asm-generic/pgalloc.h> 14 15 15 16 static inline void pmd_populate_kernel(struct mm_struct *mm, ··· 44 43 extern void pagetable_init(void); 45 44 46 45 extern pgd_t *pgd_alloc(struct mm_struct *mm); 46 + 47 + static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm) 48 + { 49 + pte_t *pte = __pte_alloc_one_kernel(mm); 50 + 51 + if (pte) 52 + kernel_pte_init(pte); 53 + 54 + return pte; 55 + } 47 56 48 57 #define __pte_free_tlb(tlb, pte, address) \ 49 58 do { \
+7 -28
arch/loongarch/include/asm/pgtable.h
··· 269 269 extern void pgd_init(void *addr); 270 270 extern void pud_init(void *addr); 271 271 extern void pmd_init(void *addr); 272 + extern void kernel_pte_init(void *addr); 272 273 273 274 /* 274 275 * Encode/decode swap entries and swap PTEs. Swap PTEs are all PTEs that ··· 326 325 { 327 326 WRITE_ONCE(*ptep, pteval); 328 327 329 - if (pte_val(pteval) & _PAGE_GLOBAL) { 330 - pte_t *buddy = ptep_buddy(ptep); 331 - /* 332 - * Make sure the buddy is global too (if it's !none, 333 - * it better already be global) 334 - */ 335 - if (pte_none(ptep_get(buddy))) { 336 328 #ifdef CONFIG_SMP 337 - /* 338 - * For SMP, multiple CPUs can race, so we need 339 - * to do this atomically. 340 - */ 341 - __asm__ __volatile__( 342 - __AMOR "$zero, %[global], %[buddy] \n" 343 - : [buddy] "+ZB" (buddy->pte) 344 - : [global] "r" (_PAGE_GLOBAL) 345 - : "memory"); 346 - 347 - DBAR(0b11000); /* o_wrw = 0b11000 */ 348 - #else /* !CONFIG_SMP */ 349 - WRITE_ONCE(*buddy, __pte(pte_val(ptep_get(buddy)) | _PAGE_GLOBAL)); 350 - #endif /* CONFIG_SMP */ 351 - } 352 - } 329 + if (pte_val(pteval) & _PAGE_GLOBAL) 330 + DBAR(0b11000); /* o_wrw = 0b11000 */ 331 + #endif 353 332 } 354 333 355 334 static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) 356 335 { 357 - /* Preserve global status for the pair */ 358 - if (pte_val(ptep_get(ptep_buddy(ptep))) & _PAGE_GLOBAL) 359 - set_pte(ptep, __pte(_PAGE_GLOBAL)); 360 - else 361 - set_pte(ptep, __pte(0)); 336 + pte_t pte = ptep_get(ptep); 337 + pte_val(pte) &= _PAGE_GLOBAL; 338 + set_pte(ptep, pte); 362 339 } 363 340 364 341 #define PGD_T_LOG2 (__builtin_ffs(sizeof(pgd_t)) - 1)
+2
arch/loongarch/mm/init.c
··· 201 201 pte = memblock_alloc(PAGE_SIZE, PAGE_SIZE); 202 202 if (!pte) 203 203 panic("%s: Failed to allocate memory\n", __func__); 204 + 204 205 pmd_populate_kernel(&init_mm, pmd, pte); 206 + kernel_pte_init(pte); 205 207 } 206 208 207 209 return pte_offset_kernel(pmd, addr);
+20
arch/loongarch/mm/pgtable.c
··· 116 116 EXPORT_SYMBOL_GPL(pud_init); 117 117 #endif 118 118 119 + void kernel_pte_init(void *addr) 120 + { 121 + unsigned long *p, *end; 122 + 123 + p = (unsigned long *)addr; 124 + end = p + PTRS_PER_PTE; 125 + 126 + do { 127 + p[0] = _PAGE_GLOBAL; 128 + p[1] = _PAGE_GLOBAL; 129 + p[2] = _PAGE_GLOBAL; 130 + p[3] = _PAGE_GLOBAL; 131 + p[4] = _PAGE_GLOBAL; 132 + p += 8; 133 + p[-3] = _PAGE_GLOBAL; 134 + p[-2] = _PAGE_GLOBAL; 135 + p[-1] = _PAGE_GLOBAL; 136 + } while (p != end); 137 + } 138 + 119 139 pmd_t mk_pmd(struct page *page, pgprot_t prot) 120 140 { 121 141 pmd_t pmd;
+2 -1
include/linux/mm.h
··· 3818 3818 struct page * __populate_section_memmap(unsigned long pfn, 3819 3819 unsigned long nr_pages, int nid, struct vmem_altmap *altmap, 3820 3820 struct dev_pagemap *pgmap); 3821 - void pmd_init(void *addr); 3822 3821 void pud_init(void *addr); 3822 + void pmd_init(void *addr); 3823 + void kernel_pte_init(void *addr); 3823 3824 pgd_t *vmemmap_pgd_populate(unsigned long addr, int node); 3824 3825 p4d_t *vmemmap_p4d_populate(pgd_t *pgd, unsigned long addr, int node); 3825 3826 pud_t *vmemmap_pud_populate(p4d_t *p4d, unsigned long addr, int node);
+7 -1
mm/kasan/init.c
··· 106 106 } 107 107 } 108 108 109 + void __weak __meminit kernel_pte_init(void *addr) 110 + { 111 + } 112 + 109 113 static int __ref zero_pmd_populate(pud_t *pud, unsigned long addr, 110 114 unsigned long end) 111 115 { ··· 130 126 131 127 if (slab_is_available()) 132 128 p = pte_alloc_one_kernel(&init_mm); 133 - else 129 + else { 134 130 p = early_alloc(PAGE_SIZE, NUMA_NO_NODE); 131 + kernel_pte_init(p); 132 + } 135 133 if (!p) 136 134 return -ENOMEM; 137 135
+5
mm/sparse-vmemmap.c
··· 184 184 return p; 185 185 } 186 186 187 + void __weak __meminit kernel_pte_init(void *addr) 188 + { 189 + } 190 + 187 191 pmd_t * __meminit vmemmap_pmd_populate(pud_t *pud, unsigned long addr, int node) 188 192 { 189 193 pmd_t *pmd = pmd_offset(pud, addr); ··· 195 191 void *p = vmemmap_alloc_block_zero(PAGE_SIZE, node); 196 192 if (!p) 197 193 return NULL; 194 + kernel_pte_init(p); 198 195 pmd_populate_kernel(&init_mm, pmd, p); 199 196 } 200 197 return pmd;