Skip to content

Commit ec28bb9

Browse files
Chintan Pandyawildea01
authored andcommitted
arm64: Implement page table free interfaces
arm64 requires break-before-make. Originally, before setting up new pmd/pud entry for huge mapping, in few cases, the modifying pmd/pud entry was still valid and pointing to next level page table as we only clear off leaf PTE in unmap leg. a) This was resulting into stale entry in TLBs (as few TLBs also cache intermediate mapping for performance reasons) b) Also, modifying pmd/pud was the only reference to next level page table and it was getting lost without freeing it. So, page leaks were happening. Implement pud_free_pmd_page() and pmd_free_pte_page() to enforce BBM and also free the leaking page tables. Implementation requires, 1) Clearing off the current pud/pmd entry 2) Invalidation of TLB 3) Freeing of the un-used next level page tables Reviewed-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Chintan Pandya <cpandya@codeaurora.org> Signed-off-by: Will Deacon <will.deacon@arm.com>
1 parent 05f2d2f commit ec28bb9

File tree

1 file changed

+44
-4
lines changed

1 file changed

+44
-4
lines changed

arch/arm64/mm/mmu.c

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include <asm/memblock.h>
4646
#include <asm/mmu_context.h>
4747
#include <asm/ptdump.h>
48+
#include <asm/tlbflush.h>
4849

4950
#define NO_BLOCK_MAPPINGS BIT(0)
5051
#define NO_CONT_MAPPINGS BIT(1)
@@ -977,12 +978,51 @@ int pmd_clear_huge(pmd_t *pmdp)
977978
return 1;
978979
}
979980

980-
int pud_free_pmd_page(pud_t *pud, unsigned long addr)
981+
int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
981982
{
982-
return pud_none(*pud);
983+
pte_t *table;
984+
pmd_t pmd;
985+
986+
pmd = READ_ONCE(*pmdp);
987+
988+
/* No-op for empty entry and WARN_ON for valid entry */
989+
if (!pmd_present(pmd) || !pmd_table(pmd)) {
990+
VM_WARN_ON(!pmd_table(pmd));
991+
return 1;
992+
}
993+
994+
table = pte_offset_kernel(pmdp, addr);
995+
pmd_clear(pmdp);
996+
__flush_tlb_kernel_pgtable(addr);
997+
pte_free_kernel(NULL, table);
998+
return 1;
983999
}
9841000

985-
int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)
1001+
int pud_free_pmd_page(pud_t *pudp, unsigned long addr)
9861002
{
987-
return pmd_none(*pmd);
1003+
pmd_t *table;
1004+
pmd_t *pmdp;
1005+
pud_t pud;
1006+
unsigned long next, end;
1007+
1008+
pud = READ_ONCE(*pudp);
1009+
1010+
/* No-op for empty entry and WARN_ON for valid entry */
1011+
if (!pud_present(pud) || !pud_table(pud)) {
1012+
VM_WARN_ON(!pud_table(pud));
1013+
return 1;
1014+
}
1015+
1016+
table = pmd_offset(pudp, addr);
1017+
pmdp = table;
1018+
next = addr;
1019+
end = addr + PUD_SIZE;
1020+
do {
1021+
pmd_free_pte_page(pmdp, next);
1022+
} while (pmdp++, next += PMD_SIZE, next != end);
1023+
1024+
pud_clear(pudp);
1025+
__flush_tlb_kernel_pgtable(addr);
1026+
pmd_free(NULL, table);
1027+
return 1;
9881028
}

0 commit comments

Comments
 (0)