Le 31/08/2022 à 14:26, Nicholas Piggin a écrit : > Allow PTE changes to avoid flushing the TLB when access permissions are > being relaxed, the dirty bit is being set, and the accessed bit is being > changed. > > Relaxing access permissions and setting dirty and accessed bits do not > require a flush because the MMU will re-load the PTE and notice the > updates (it may also cause a spurious fault). > > Clearing the accessed bit does not require a flush because of the > imprecise PTE accessed bit accounting that is already performed, as > documented in ptep_clear_flush_young(). > > This reduces TLB flushing for some mprotect(2) calls. > > Signed-off-by: Nicholas Piggin <npig...@gmail.com> > --- > > arch/powerpc/include/asm/book3s/64/pgtable.h | 3 ++ > arch/powerpc/include/asm/book3s/64/tlbflush.h | 53 +++++++++++++++++++ > arch/powerpc/mm/book3s64/hash_tlb.c | 1 + > arch/powerpc/mm/book3s64/pgtable.c | 1 + > 4 files changed, 58 insertions(+) > > diff --git a/arch/powerpc/include/asm/book3s/64/pgtable.h > b/arch/powerpc/include/asm/book3s/64/pgtable.h > index cb9d5fd39d7f..a5042bb9a30c 100644 > --- a/arch/powerpc/include/asm/book3s/64/pgtable.h > +++ b/arch/powerpc/include/asm/book3s/64/pgtable.h > @@ -411,6 +411,9 @@ static inline int __ptep_test_and_clear_young(struct > mm_struct *mm, > * event of it not getting flushed for a long time the delay > * shouldn't really matter because there's no real memory > * pressure for swapout to react to. ] > + * > + * Note: this optimisation also exists in pte_needs_flush() and > + * huge_pmd_needs_flush(). > */ > #define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH > #define ptep_clear_flush_young ptep_test_and_clear_young > diff --git a/arch/powerpc/include/asm/book3s/64/tlbflush.h > b/arch/powerpc/include/asm/book3s/64/tlbflush.h > index d2e80f178b6d..bcd7cb585d17 100644 > --- a/arch/powerpc/include/asm/book3s/64/tlbflush.h > +++ b/arch/powerpc/include/asm/book3s/64/tlbflush.h > @@ -143,6 +143,59 @@ static inline void flush_tlb_fix_spurious_fault(struct > vm_area_struct *vma, > flush_tlb_page(vma, address); > } > > +static inline bool __pte_flags_need_flush(unsigned long oldval, unsigned > long newval) > +{ > + unsigned long delta = oldval ^ newval; > + > + /* > + * The return value of this function doesn't matter for hash, > + * ptep_modify_prot_start() does a pte_update() which does or schedules > + * any necessary hash table update and flush. > + */ > + if (!radix_enabled()) > + return true; > + > + /* > + * In theory some changed software bits could be tolerated, in practice > + * those should rarely if ever matter. > + */ > + > + /* Don't deal with kernel mappings or non-PTEs or not-present PTEs */ > + VM_BUG_ON((oldval | newval) & _PAGE_PRIVILEGED); > + VM_BUG_ON(!((oldval | newval) & _PAGE_PTE)); > + VM_BUG_ON(!((oldval | newval) & _PAGE_PRESENT));
No new VM_BUG_ON() should be added, refer latests discussion on the subject at https://lore.kernel.org/all/CAHk-=wg40eazofo16eviaj7mfqdhz2gvebvfsmf6gyzsprj...@mail.gmail.com/ > + > + /* Must flush on any change except READ, WRITE, EXEC, DIRTY, ACCESSED */ > + if (delta & ~(_PAGE_RWX | _PAGE_DIRTY | _PAGE_ACCESSED)) > + return true; > + > + /* > + * If any of the above was present in old but cleared in new, flush. > + * With the exception of _PAGE_ACCESSED, don't worry about flushing > + * if that was cleared (see the comment in ptep_clear_flush_young()). > + */ > + if ((delta & ~_PAGE_ACCESSED) & oldval) > + return true; > + > + return false; > +} > + > +#ifndef pte_needs_flush Why that #ifndef ? I can't see it defined anywhere in powerpc at the time being. > +static inline bool pte_needs_flush(pte_t oldpte, pte_t newpte) > +{ > + return __pte_flags_need_flush(pte_val(oldpte), pte_val(newpte)); > +} > +#define pte_needs_flush pte_needs_flush > +#endif > + > +#ifndef huge_pmd_needs_flush Same > +static inline bool huge_pmd_needs_flush(pmd_t oldpmd, pmd_t newpmd) > +{ > + return __pte_flags_need_flush(pmd_val(oldpmd), pmd_val(newpmd)); > +} > +#define huge_pmd_needs_flush huge_pmd_needs_flush > +#endif > + > extern bool tlbie_capable; > extern bool tlbie_enabled; >