Skip to content

Commit b7d9d2e

Browse files
ryanhrobctmarinas
authored andcommitted
arm64: mm: __ptep_set_access_flags must hint correct TTL
It has been reported that since commit 752a0d1 ("arm64: mm: Provide level hint for flush_tlb_page()"), the arm64 check_hugetlb_options selftest has been locking up while running "Check child hugetlb memory with private mapping, sync error mode and mmap memory". This is due to hugetlb (and THP) helpers casting their PMD/PUD entries to PTE and calling __ptep_set_access_flags(), which issues a __flush_tlb_page(). Now that this is hinted for level 3, in this case, the TLB entry does not get evicted and we end up in a spurious fault loop. Fix this by creating a __ptep_set_access_flags_anysz() function which takes the pgsize of the entry. It can then add the appropriate hint. The "_anysz" approach is the established pattern for problems of this class. Reported-by: Aishwarya TCV <[email protected]> Fixes: 752a0d1 ("arm64: mm: Provide level hint for flush_tlb_page()") Signed-off-by: Ryan Roberts <[email protected]> Signed-off-by: Catalin Marinas <[email protected]>
1 parent 752a0d1 commit b7d9d2e

3 files changed

Lines changed: 42 additions & 13 deletions

File tree

arch/arm64/include/asm/pgtable.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,18 +1248,27 @@ static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
12481248
return pte_pmd(pte_modify(pmd_pte(pmd), newprot));
12491249
}
12501250

1251-
extern int __ptep_set_access_flags(struct vm_area_struct *vma,
1252-
unsigned long address, pte_t *ptep,
1253-
pte_t entry, int dirty);
1251+
extern int __ptep_set_access_flags_anysz(struct vm_area_struct *vma,
1252+
unsigned long address, pte_t *ptep,
1253+
pte_t entry, int dirty,
1254+
unsigned long pgsize);
1255+
1256+
static inline int __ptep_set_access_flags(struct vm_area_struct *vma,
1257+
unsigned long address, pte_t *ptep,
1258+
pte_t entry, int dirty)
1259+
{
1260+
return __ptep_set_access_flags_anysz(vma, address, ptep, entry, dirty,
1261+
PAGE_SIZE);
1262+
}
12541263

12551264
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
12561265
#define __HAVE_ARCH_PMDP_SET_ACCESS_FLAGS
12571266
static inline int pmdp_set_access_flags(struct vm_area_struct *vma,
12581267
unsigned long address, pmd_t *pmdp,
12591268
pmd_t entry, int dirty)
12601269
{
1261-
return __ptep_set_access_flags(vma, address, (pte_t *)pmdp,
1262-
pmd_pte(entry), dirty);
1270+
return __ptep_set_access_flags_anysz(vma, address, (pte_t *)pmdp,
1271+
pmd_pte(entry), dirty, PMD_SIZE);
12631272
}
12641273
#endif
12651274

arch/arm64/mm/fault.c

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,13 @@ static void show_pte(unsigned long addr)
204204
*
205205
* Returns whether or not the PTE actually changed.
206206
*/
207-
int __ptep_set_access_flags(struct vm_area_struct *vma,
208-
unsigned long address, pte_t *ptep,
209-
pte_t entry, int dirty)
207+
int __ptep_set_access_flags_anysz(struct vm_area_struct *vma,
208+
unsigned long address, pte_t *ptep,
209+
pte_t entry, int dirty, unsigned long pgsize)
210210
{
211211
pteval_t old_pteval, pteval;
212212
pte_t pte = __ptep_get(ptep);
213+
int level;
213214

214215
if (pte_same(pte, entry))
215216
return 0;
@@ -238,8 +239,27 @@ int __ptep_set_access_flags(struct vm_area_struct *vma,
238239
* may still cause page faults and be invalidated via
239240
* flush_tlb_fix_spurious_fault().
240241
*/
241-
if (dirty)
242-
__flush_tlb_page(vma, address, TLBF_NOBROADCAST);
242+
if (dirty) {
243+
switch (pgsize) {
244+
case PAGE_SIZE:
245+
level = 3;
246+
break;
247+
case PMD_SIZE:
248+
level = 2;
249+
break;
250+
#ifndef __PAGETABLE_PMD_FOLDED
251+
case PUD_SIZE:
252+
level = 1;
253+
break;
254+
#endif
255+
default:
256+
level = TLBI_TTL_UNKNOWN;
257+
WARN_ON(1);
258+
}
259+
260+
__flush_tlb_range(vma, address, address + pgsize, pgsize, level,
261+
TLBF_NOWALKCACHE | TLBF_NOBROADCAST);
262+
}
243263
return 1;
244264
}
245265

arch/arm64/mm/hugetlbpage.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -427,11 +427,11 @@ int huge_ptep_set_access_flags(struct vm_area_struct *vma,
427427
pte_t orig_pte;
428428

429429
VM_WARN_ON(!pte_present(pte));
430+
ncontig = num_contig_ptes(huge_page_size(hstate_vma(vma)), &pgsize);
430431

431432
if (!pte_cont(pte))
432-
return __ptep_set_access_flags(vma, addr, ptep, pte, dirty);
433-
434-
ncontig = num_contig_ptes(huge_page_size(hstate_vma(vma)), &pgsize);
433+
return __ptep_set_access_flags_anysz(vma, addr, ptep, pte,
434+
dirty, pgsize);
435435

436436
if (!__cont_access_flags_changed(ptep, pte, ncontig))
437437
return 0;

0 commit comments

Comments
 (0)