riscv: mm: notify remote harts about mmu cache updates
authorSergey Matyukevich <sergey.matyukevich@syntacore.com>
Mon, 29 Aug 2022 20:52:19 +0000 (23:52 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 12 Jan 2023 10:58:58 +0000 (11:58 +0100)
commit 4bd1d80efb5af640f99157f39b50fb11326ce641 upstream.

Current implementation of update_mmu_cache function performs local TLB
flush. It does not take into account ASID information. Besides, it does
not take into account other harts currently running the same mm context
or possible migration of the running context to other harts. Meanwhile
TLB flush is not performed for every context switch if ASID support
is enabled.

Patch [1] proposed to add ASID support to update_mmu_cache to avoid
flushing local TLB entirely. This patch takes into account other
harts currently running the same mm context as well as possible
migration of this context to other harts.

For this purpose the approach from flush_icache_mm is reused. Remote
harts currently running the same mm context are informed via SBI calls
that they need to flush their local TLBs. All the other harts are marked
as needing a deferred TLB flush when this mm context runs on them.

[1] https://lore.kernel.org/linux-riscv/20220821013926.8968-1-tjytimi@163.com/

Signed-off-by: Sergey Matyukevich <sergey.matyukevich@syntacore.com>
Fixes: 65d4b9c53017 ("RISC-V: Implement ASID allocator")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/linux-riscv/20220829205219.283543-1-geomatsi@gmail.com/#t
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/riscv/include/asm/mmu.h
arch/riscv/include/asm/pgtable.h
arch/riscv/include/asm/tlbflush.h
arch/riscv/mm/context.c
arch/riscv/mm/tlbflush.c

index 0099dc1161683ddd1e3c45460309e331b7e6b0a7..5ff1f19fd45c29b4fc7d2c8b44ac4984caf1e75c 100644 (file)
@@ -19,6 +19,8 @@ typedef struct {
 #ifdef CONFIG_SMP
        /* A local icache flush is needed before user execution can resume. */
        cpumask_t icache_stale_mask;
+       /* A local tlb flush is needed before user execution can resume. */
+       cpumask_t tlb_stale_mask;
 #endif
 } mm_context_t;
 
index 39b550310ec6456358021816144e0a4bb3d08b68..799c16e065253cdb89290eebf0fc6765c34a173e 100644 (file)
@@ -386,7 +386,7 @@ static inline void update_mmu_cache(struct vm_area_struct *vma,
         * Relying on flush_tlb_fix_spurious_fault would suffice, but
         * the extra traps reduce performance.  So, eagerly SFENCE.VMA.
         */
-       local_flush_tlb_page(address);
+       flush_tlb_page(vma, address);
 }
 
 static inline void update_mmu_cache_pmd(struct vm_area_struct *vma,
index 801019381dea3fec53f50fe443fb350c3ab92c43..907b9efd39a87dd3853c1f8f21f4baa2fdd1125c 100644 (file)
@@ -22,6 +22,24 @@ static inline void local_flush_tlb_page(unsigned long addr)
 {
        ALT_FLUSH_TLB_PAGE(__asm__ __volatile__ ("sfence.vma %0" : : "r" (addr) : "memory"));
 }
+
+static inline void local_flush_tlb_all_asid(unsigned long asid)
+{
+       __asm__ __volatile__ ("sfence.vma x0, %0"
+                       :
+                       : "r" (asid)
+                       : "memory");
+}
+
+static inline void local_flush_tlb_page_asid(unsigned long addr,
+               unsigned long asid)
+{
+       __asm__ __volatile__ ("sfence.vma %0, %1"
+                       :
+                       : "r" (addr), "r" (asid)
+                       : "memory");
+}
+
 #else /* CONFIG_MMU */
 #define local_flush_tlb_all()                  do { } while (0)
 #define local_flush_tlb_page(addr)             do { } while (0)
index ee3459cb6750b40f12ecc9e78aa9d72b4fdecba1..cc4a47bda82a0869aeb54f5065a75295ccf605b9 100644 (file)
@@ -196,6 +196,16 @@ switch_mm_fast:
 
        if (need_flush_tlb)
                local_flush_tlb_all();
+#ifdef CONFIG_SMP
+       else {
+               cpumask_t *mask = &mm->context.tlb_stale_mask;
+
+               if (cpumask_test_cpu(cpu, mask)) {
+                       cpumask_clear_cpu(cpu, mask);
+                       local_flush_tlb_all_asid(cntx & asid_mask);
+               }
+       }
+#endif
 }
 
 static void set_mm_noasid(struct mm_struct *mm)
index 64f8201237c24e736d18942d27b295b580efad61..efefc3986c48cd727b32d62c5bd9ffbaaf67f09b 100644 (file)
@@ -5,23 +5,7 @@
 #include <linux/sched.h>
 #include <asm/sbi.h>
 #include <asm/mmu_context.h>
-
-static inline void local_flush_tlb_all_asid(unsigned long asid)
-{
-       __asm__ __volatile__ ("sfence.vma x0, %0"
-                       :
-                       : "r" (asid)
-                       : "memory");
-}
-
-static inline void local_flush_tlb_page_asid(unsigned long addr,
-               unsigned long asid)
-{
-       __asm__ __volatile__ ("sfence.vma %0, %1"
-                       :
-                       : "r" (addr), "r" (asid)
-                       : "memory");
-}
+#include <asm/tlbflush.h>
 
 void flush_tlb_all(void)
 {
@@ -31,6 +15,7 @@ void flush_tlb_all(void)
 static void __sbi_tlb_flush_range(struct mm_struct *mm, unsigned long start,
                                  unsigned long size, unsigned long stride)
 {
+       struct cpumask *pmask = &mm->context.tlb_stale_mask;
        struct cpumask *cmask = mm_cpumask(mm);
        struct cpumask hmask;
        unsigned int cpuid;
@@ -45,6 +30,15 @@ static void __sbi_tlb_flush_range(struct mm_struct *mm, unsigned long start,
        if (static_branch_unlikely(&use_asid_allocator)) {
                unsigned long asid = atomic_long_read(&mm->context.id);
 
+               /*
+                * TLB will be immediately flushed on harts concurrently
+                * executing this MM context. TLB flush on other harts
+                * is deferred until this MM context migrates there.
+                */
+               cpumask_setall(pmask);
+               cpumask_clear_cpu(cpuid, pmask);
+               cpumask_andnot(pmask, pmask, cmask);
+
                if (broadcast) {
                        riscv_cpuid_to_hartid_mask(cmask, &hmask);
                        sbi_remote_sfence_vma_asid(cpumask_bits(&hmask),