Skip to content

Commit

Permalink
PREEMPT_RT: mm: x86, arm64: add arch_has_hw_pte_young()
Browse files Browse the repository at this point in the history
Some architectures automatically set the accessed bit in PTEs, e.g.,
x86 and arm64 v8.2. On architectures that do not have this capability,
clearing the accessed bit in a PTE triggers a page fault following the
TLB miss of this PTE.

Being aware of this capability can help make better decisions, i.e.,
whether to limit the size of each batch of PTEs and the burst of
batches when clearing the accessed bit.

Signed-off-by: Yu Zhao <yuzhao@google.com>
Tested-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
Rebased-by: Alexandre Frade <kernel@xanmod.org>
Signed-off-by: Alexandre Frade <kernel@xanmod.org>
  • Loading branch information
yuzhaogoogle authored and xanmod committed Dec 15, 2021
1 parent 4f4b15b commit 62ecc3f
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 21 deletions.
5 changes: 5 additions & 0 deletions arch/arm64/include/asm/cpufeature.h
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,11 @@ static inline bool system_supports_tlb_range(void)
cpus_have_const_cap(ARM64_HAS_TLB_RANGE);
}

static inline bool system_has_hw_af(void)
{
return IS_ENABLED(CONFIG_ARM64_HW_AFDBM) && cpus_have_const_cap(ARM64_HW_AF);
}

extern int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt);

static inline u32 id_aa64mmfr0_parange_to_phys_shift(int parange)
Expand Down
13 changes: 8 additions & 5 deletions arch/arm64/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -999,21 +999,24 @@ static inline void update_mmu_cache(struct vm_area_struct *vma,
* page after fork() + CoW for pfn mappings. We don't always have a
* hardware-managed access flag on arm64.
*/
static inline bool arch_faults_on_old_pte(void)
static inline bool arch_has_hw_pte_young(bool local)
{
WARN_ON(is_migratable());
if (local) {
WARN_ON(is_migratable());
return cpu_has_hw_af();
}

return !cpu_has_hw_af();
return system_has_hw_af();
}
#define arch_faults_on_old_pte arch_faults_on_old_pte
#define arch_has_hw_pte_young arch_has_hw_pte_young

/*
* Experimentally, it's cheap to set the access flag in hardware and we
* benefit from prefaulting mappings as 'old' to start with.
*/
static inline bool arch_wants_old_prefaulted_pte(void)
{
return !arch_faults_on_old_pte();
return arch_has_hw_pte_young(true);
}
#define arch_wants_old_prefaulted_pte arch_wants_old_prefaulted_pte

Expand Down
10 changes: 10 additions & 0 deletions arch/arm64/kernel/cpufeature.c
Original file line number Diff line number Diff line change
Expand Up @@ -2161,6 +2161,16 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
.matches = has_hw_dbm,
.cpu_enable = cpu_enable_hw_dbm,
},
{
.desc = "Hardware update of the Access flag",
.type = ARM64_CPUCAP_SYSTEM_FEATURE,
.capability = ARM64_HW_AF,
.sys_reg = SYS_ID_AA64MMFR1_EL1,
.sign = FTR_UNSIGNED,
.field_pos = ID_AA64MMFR1_HADBS_SHIFT,
.min_field_value = 1,
.matches = has_cpuid_feature,
},
#endif
{
.desc = "CRC32 instructions",
Expand Down
1 change: 1 addition & 0 deletions arch/arm64/tools/cpucaps
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ HAS_STAGE2_FWB
HAS_SYSREG_GIC_CPUIF
HAS_TLB_RANGE
HAS_VIRT_HOST_EXTN
HW_AF
HW_DBM
KVM_PROTECTED_MODE
MISMATCHED_CACHE_TYPE
Expand Down
6 changes: 3 additions & 3 deletions arch/x86/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -1398,10 +1398,10 @@ static inline bool arch_has_pfn_modify_check(void)
return boot_cpu_has_bug(X86_BUG_L1TF);
}

#define arch_faults_on_old_pte arch_faults_on_old_pte
static inline bool arch_faults_on_old_pte(void)
#define arch_has_hw_pte_young arch_has_hw_pte_young
static inline bool arch_has_hw_pte_young(bool local)
{
return false;
return true;
}

#endif /* __ASSEMBLY__ */
Expand Down
13 changes: 13 additions & 0 deletions include/linux/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,19 @@ static inline int pmdp_clear_flush_young(struct vm_area_struct *vma,
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
#endif

#ifndef arch_has_hw_pte_young
/*
* Return whether the accessed bit is supported by the local CPU or all CPUs.
*
* Those arches which have hw access flag feature need to implement their own
* helper. By default, "false" means pagefault will be hit on old pte.
*/
static inline bool arch_has_hw_pte_young(bool local)
{
return false;
}
#endif

#ifndef __HAVE_ARCH_PTEP_GET_AND_CLEAR
static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
unsigned long address,
Expand Down
14 changes: 1 addition & 13 deletions mm/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,18 +121,6 @@ int randomize_va_space __read_mostly =
2;
#endif

#ifndef arch_faults_on_old_pte
static inline bool arch_faults_on_old_pte(void)
{
/*
* Those arches which don't have hw access flag feature need to
* implement their own helper. By default, "true" means pagefault
* will be hit on old pte.
*/
return true;
}
#endif

#ifndef arch_wants_old_prefaulted_pte
static inline bool arch_wants_old_prefaulted_pte(void)
{
Expand Down Expand Up @@ -2770,7 +2758,7 @@ static inline bool cow_user_page(struct page *dst, struct page *src,
* On architectures with software "accessed" bits, we would
* take a double page fault, so mark it accessed here.
*/
if (arch_faults_on_old_pte() && !pte_young(vmf->orig_pte)) {
if (!arch_has_hw_pte_young(true) && !pte_young(vmf->orig_pte)) {
pte_t entry;

vmf->pte = pte_offset_map_lock(mm, vmf->pmd, addr, &vmf->ptl);
Expand Down

0 comments on commit 62ecc3f

Please sign in to comment.