Skip to content

Commit 0e9df1c

Browse files
Ryan Robertswilldeacon
authored andcommitted
arm64: mm: Don't remap pgtables for allocate vs populate
During linear map pgtable creation, each pgtable is fixmapped / fixunmapped twice; once during allocation to zero the memory, and a again during population to write the entries. This means each table has 2 TLB invalidations issued against it. Let's fix this so that each table is only fixmapped/fixunmapped once, halving the number of TLBIs, and improving performance. Achieve this by separating allocation and initialization (zeroing) of the page. The allocated page is now fixmapped directly by the walker and initialized, before being populated and finally fixunmapped. This approach keeps the change small, but has the side effect that late allocations (using __get_free_page()) must also go through the generic memory clearing routine. So let's tell __get_free_page() not to zero the memory to avoid duplication. Additionally this approach means that fixmap/fixunmap is still used for late pgtable modifications. That's not technically needed since the memory is all mapped in the linear map by that point. That's left as a possible future optimization if found to be needed. Execution time of map_mem(), which creates the kernel linear map page tables, was measured on different machines with different RAM configs: | Apple M2 VM | Ampere Altra| Ampere Altra| Ampere Altra | VM, 16G | VM, 64G | VM, 256G | Metal, 512G ---------------|-------------|-------------|-------------|------------- | ms (%) | ms (%) | ms (%) | ms (%) ---------------|-------------|-------------|-------------|------------- before | 11 (0%) | 161 (0%) | 656 (0%) | 1654 (0%) after | 10 (-11%) | 104 (-35%) | 438 (-33%) | 1223 (-26%) Signed-off-by: Ryan Roberts <[email protected]> Suggested-by: Mark Rutland <[email protected]> Tested-by: Itaru Kitayama <[email protected]> Tested-by: Eric Chanudet <[email protected]> Reviewed-by: Mark Rutland <[email protected]> Reviewed-by: Ard Biesheuvel <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent 1fcb7ce commit 0e9df1c

File tree

2 files changed

+37
-32
lines changed

2 files changed

+37
-32
lines changed

arch/arm64/include/asm/pgtable.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,8 @@ static inline p4d_t *p4d_offset_kimg(pgd_t *pgdp, u64 addr)
10101010

10111011
static inline bool pgtable_l5_enabled(void) { return false; }
10121012

1013+
#define p4d_index(addr) (((addr) >> P4D_SHIFT) & (PTRS_PER_P4D - 1))
1014+
10131015
/* Match p4d_offset folding in <asm/generic/pgtable-nop4d.h> */
10141016
#define p4d_set_fixmap(addr) NULL
10151017
#define p4d_set_fixmap_offset(p4dp, addr) ((p4d_t *)p4dp)

arch/arm64/mm/mmu.c

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -109,28 +109,12 @@ EXPORT_SYMBOL(phys_mem_access_prot);
109109
static phys_addr_t __init early_pgtable_alloc(int shift)
110110
{
111111
phys_addr_t phys;
112-
void *ptr;
113112

114113
phys = memblock_phys_alloc_range(PAGE_SIZE, PAGE_SIZE, 0,
115114
MEMBLOCK_ALLOC_NOLEAKTRACE);
116115
if (!phys)
117116
panic("Failed to allocate page table page\n");
118117

119-
/*
120-
* The FIX_{PGD,PUD,PMD} slots may be in active use, but the FIX_PTE
121-
* slot will be free, so we can (ab)use the FIX_PTE slot to initialise
122-
* any level of table.
123-
*/
124-
ptr = pte_set_fixmap(phys);
125-
126-
memset(ptr, 0, PAGE_SIZE);
127-
128-
/*
129-
* Implicit barriers also ensure the zeroed page is visible to the page
130-
* table walker
131-
*/
132-
pte_clear_fixmap();
133-
134118
return phys;
135119
}
136120

@@ -172,6 +156,14 @@ bool pgattr_change_is_safe(u64 old, u64 new)
172156
return ((old ^ new) & ~mask) == 0;
173157
}
174158

159+
static void init_clear_pgtable(void *table)
160+
{
161+
clear_page(table);
162+
163+
/* Ensure the zeroing is observed by page table walks. */
164+
dsb(ishst);
165+
}
166+
175167
static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
176168
phys_addr_t phys, pgprot_t prot)
177169
{
@@ -214,12 +206,15 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
214206
pmdval |= PMD_TABLE_PXN;
215207
BUG_ON(!pgtable_alloc);
216208
pte_phys = pgtable_alloc(PAGE_SHIFT);
209+
ptep = pte_set_fixmap(pte_phys);
210+
init_clear_pgtable(ptep);
211+
ptep += pte_index(addr);
217212
__pmd_populate(pmdp, pte_phys, pmdval);
218-
pmd = READ_ONCE(*pmdp);
213+
} else {
214+
BUG_ON(pmd_bad(pmd));
215+
ptep = pte_set_fixmap_offset(pmdp, addr);
219216
}
220-
BUG_ON(pmd_bad(pmd));
221217

222-
ptep = pte_set_fixmap_offset(pmdp, addr);
223218
do {
224219
pgprot_t __prot = prot;
225220

@@ -298,12 +293,15 @@ static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
298293
pudval |= PUD_TABLE_PXN;
299294
BUG_ON(!pgtable_alloc);
300295
pmd_phys = pgtable_alloc(PMD_SHIFT);
296+
pmdp = pmd_set_fixmap(pmd_phys);
297+
init_clear_pgtable(pmdp);
298+
pmdp += pmd_index(addr);
301299
__pud_populate(pudp, pmd_phys, pudval);
302-
pud = READ_ONCE(*pudp);
300+
} else {
301+
BUG_ON(pud_bad(pud));
302+
pmdp = pmd_set_fixmap_offset(pudp, addr);
303303
}
304-
BUG_ON(pud_bad(pud));
305304

306-
pmdp = pmd_set_fixmap_offset(pudp, addr);
307305
do {
308306
pgprot_t __prot = prot;
309307

@@ -340,12 +338,15 @@ static void alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end,
340338
p4dval |= P4D_TABLE_PXN;
341339
BUG_ON(!pgtable_alloc);
342340
pud_phys = pgtable_alloc(PUD_SHIFT);
341+
pudp = pud_set_fixmap(pud_phys);
342+
init_clear_pgtable(pudp);
343+
pudp += pud_index(addr);
343344
__p4d_populate(p4dp, pud_phys, p4dval);
344-
p4d = READ_ONCE(*p4dp);
345+
} else {
346+
BUG_ON(p4d_bad(p4d));
347+
pudp = pud_set_fixmap_offset(p4dp, addr);
345348
}
346-
BUG_ON(p4d_bad(p4d));
347349

348-
pudp = pud_set_fixmap_offset(p4dp, addr);
349350
do {
350351
pud_t old_pud = READ_ONCE(*pudp);
351352

@@ -395,12 +396,15 @@ static void alloc_init_p4d(pgd_t *pgdp, unsigned long addr, unsigned long end,
395396
pgdval |= PGD_TABLE_PXN;
396397
BUG_ON(!pgtable_alloc);
397398
p4d_phys = pgtable_alloc(P4D_SHIFT);
399+
p4dp = p4d_set_fixmap(p4d_phys);
400+
init_clear_pgtable(p4dp);
401+
p4dp += p4d_index(addr);
398402
__pgd_populate(pgdp, p4d_phys, pgdval);
399-
pgd = READ_ONCE(*pgdp);
403+
} else {
404+
BUG_ON(pgd_bad(pgd));
405+
p4dp = p4d_set_fixmap_offset(pgdp, addr);
400406
}
401-
BUG_ON(pgd_bad(pgd));
402407

403-
p4dp = p4d_set_fixmap_offset(pgdp, addr);
404408
do {
405409
p4d_t old_p4d = READ_ONCE(*p4dp);
406410

@@ -467,11 +471,10 @@ void create_kpti_ng_temp_pgd(pgd_t *pgdir, phys_addr_t phys, unsigned long virt,
467471

468472
static phys_addr_t __pgd_pgtable_alloc(int shift)
469473
{
470-
void *ptr = (void *)__get_free_page(GFP_PGTABLE_KERNEL);
471-
BUG_ON(!ptr);
474+
/* Page is zeroed by init_clear_pgtable() so don't duplicate effort. */
475+
void *ptr = (void *)__get_free_page(GFP_PGTABLE_KERNEL & ~__GFP_ZERO);
472476

473-
/* Ensure the zeroed page is visible to the page table walker */
474-
dsb(ishst);
477+
BUG_ON(!ptr);
475478
return __pa(ptr);
476479
}
477480

0 commit comments

Comments
 (0)