The generic ptep_set_access_flags() calls set_pte_at() and then flush_tlb_fix_spurious_fault(). On sparc64 set_pte_at() already flushes the TSB entry and demaps the page on every CPU of the mm when the old PTE was accessible, and when it was not (a PTE made old by clearing _PAGE_VALID) the translation itself did not change, so there is nothing to flush. The next patch turns flush_tlb_fix_spurious_fault() from a no-op into that same flush. Without this patch it would then run twice, including the cross call to every CPU in mm_cpumask(), on every first write to a clean page. Provide an arch version without the second flush. Cc: stable@vger.kernel.org Link: https://github.com/sparclinux/issues/issues/108 Signed-off-by: Stian Halseth --- arch/sparc/include/asm/pgtable_64.h | 3 +++ arch/sparc/mm/tlb.c | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h --- a/arch/sparc/include/asm/pgtable_64.h +++ b/arch/sparc/include/asm/pgtable_64.h @@ -971,6 +971,9 @@ void mmu_info(struct seq_file *); struct vm_area_struct; +#define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS +int ptep_set_access_flags(struct vm_area_struct *vma, unsigned long address, + pte_t *ptep, pte_t entry, int dirty); void update_mmu_cache_range(struct vm_fault *, struct vm_area_struct *, unsigned long addr, pte_t *ptep, unsigned int nr); #define update_mmu_cache(vma, addr, ptep) \ diff --git a/arch/sparc/mm/tlb.c b/arch/sparc/mm/tlb.c --- a/arch/sparc/mm/tlb.c +++ b/arch/sparc/mm/tlb.c @@ -22,6 +22,16 @@ static DEFINE_PER_CPU(struct tlb_batch, tlb_batch); +int ptep_set_access_flags(struct vm_area_struct *vma, unsigned long address, + pte_t *ptep, pte_t entry, int dirty) +{ + int changed = !pte_same(ptep_get(ptep), entry); + + if (changed) + set_pte_at(vma->vm_mm, address, ptep, entry); + return changed; +} + void flush_tlb_pending(void) { struct tlb_batch *tb = &get_cpu_var(tlb_batch);