Skip to content
12 changes: 12 additions & 0 deletions arch/riscv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,18 @@ config RISCV_VECTOR_MISALIGNED
help
Enable detecting support for vector misaligned loads and stores.

config RISCV_LAZY_TLB_FLUSH
bool "Defer TLB Flush to context switch to avoid IPIs"
depends on MMU && SMP
def_bool n
help
This feature avoids unnecessary TLB Flush IPIs. After memory mapping
modifications on certain mm_struct, instead of sending IPIs, this feature
records the TLB Flush information on percpu buffer, defer the TLB Flush
to the moment when target CPUs really load this mm_struct.

If unsure what to do here, say N.

choice
prompt "Unaligned Accesses Support"
default RISCV_PROBE_UNALIGNED_ACCESS
Expand Down
4 changes: 4 additions & 0 deletions arch/riscv/include/asm/mmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ typedef struct {
#ifdef CONFIG_RISCV_ISA_SUPM
u8 pmlen;
#endif
#ifdef CONFIG_RISCV_LAZY_TLB_FLUSH
atomic_t lazy_tlb_cnt;
void *next;
#endif
} mm_context_t;

/* Lock the pointer masking mode because this mm is multithreaded */
Expand Down
5 changes: 5 additions & 0 deletions arch/riscv/include/asm/mmu_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
void switch_mm(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *task);

#ifdef CONFIG_RISCV_LAZY_TLB_FLUSH
#define arch_do_shoot_lazy_tlb arch_do_shoot_lazy_tlb
void arch_do_shoot_lazy_tlb(void *arg);
#endif

#define activate_mm activate_mm
static inline void activate_mm(struct mm_struct *prev,
struct mm_struct *next)
Expand Down
63 changes: 63 additions & 0 deletions arch/riscv/include/asm/tlbflush.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
#define FLUSH_TLB_NO_ASID ((unsigned long)-1)

#ifdef CONFIG_MMU
static inline unsigned long get_mm_asid(struct mm_struct *mm)
{
return mm ? cntx2asid(atomic_long_read(&mm->context.id)) : FLUSH_TLB_NO_ASID;
}

static inline void local_flush_tlb_all(void)
{
__asm__ __volatile__ ("sfence.vma" : : : "memory");
Expand Down Expand Up @@ -66,6 +71,64 @@ void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *batch,
void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch);

extern unsigned long tlb_flush_all_threshold;

#ifdef CONFIG_RISCV_LAZY_TLB_FLUSH

#define MAX_LOADED_MM 6
#define MAX_TLB_FLUSH_TASK 32
#define FLUSH_TLB_ALL_ASID 0x1

struct tlb_context {
struct mm_struct *mm;
unsigned int gen;
bool need_flush;
};

struct tlb_flush_task {
unsigned long start;
unsigned long size;
unsigned long stride;
};

struct tlb_flush_queue {
atomic_t len;
unsigned int flag;
struct tlb_flush_task tasks[MAX_TLB_FLUSH_TASK];
} ____cacheline_aligned_in_smp;

struct tlb_info {
rwlock_t rwlock;
struct mm_struct *active_mm;
unsigned int next_gen;
struct tlb_context contexts[MAX_LOADED_MM];
struct tlb_flush_queue *flush_queues;
};

DECLARE_PER_CPU_SHARED_ALIGNED(struct tlb_info, tlbinfo);

void local_load_tlb_mm(struct mm_struct *mm);
void local_flush_tlb_mm(struct mm_struct *mm);
void local_flush_tlb_all_mm(void);
void __init lazy_tlb_flush_init(void);

#else /* CONFIG_RISCV_LAZY_TLB_FLUSH */

static inline void local_load_tlb_mm(struct mm_struct *mm) {}

static inline void local_flush_tlb_mm(struct mm_struct *mm)
{
local_flush_tlb_all_asid(get_mm_asid(mm));
}

static inline void local_flush_tlb_all_mm(void)
{
local_flush_tlb_all();
}

static inline void lazy_tlb_flush_init(void) {}

#endif /* CONFIG_RISCV_LAZY_TLB_FLUSH */

#else /* CONFIG_MMU */
#define local_flush_tlb_all() do { } while (0)
#endif /* CONFIG_MMU */
Expand Down
24 changes: 23 additions & 1 deletion arch/riscv/mm/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ static void set_mm_asid(struct mm_struct *mm, unsigned int cpu)
satp_mode);

if (need_flush_tlb)
local_flush_tlb_all();
local_flush_tlb_all_mm();
}

static void set_mm_noasid(struct mm_struct *mm)
Expand All @@ -217,6 +217,7 @@ static inline void set_mm(struct mm_struct *prev,
*/
cpumask_set_cpu(cpu, mm_cpumask(next));
if (static_branch_unlikely(&use_asid_allocator)) {
local_load_tlb_mm(next);
set_mm_asid(next, cpu);
} else {
cpumask_clear_cpu(cpu, mm_cpumask(prev));
Expand Down Expand Up @@ -262,6 +263,8 @@ static int __init asids_init(void)

__set_bit(0, context_asid_map);

lazy_tlb_flush_init();

static_branch_enable(&use_asid_allocator);

pr_info("ASID allocator using %lu bits (%lu entries)\n",
Expand All @@ -273,6 +276,25 @@ static int __init asids_init(void)
return 0;
}
early_initcall(asids_init);

#ifdef CONFIG_RISCV_LAZY_TLB_FLUSH
void arch_do_shoot_lazy_tlb(void *arg)
{
struct mm_struct *mm = arg;

if (current->active_mm == mm) {
WARN_ON_ONCE(current->mm);
current->active_mm = &init_mm;
switch_mm(mm, &init_mm, current);
}

if (!static_branch_unlikely(&use_asid_allocator) || !mm)
return;

local_flush_tlb_mm(mm);
}
#endif /* CONFIG_RISCV_LAZY_TLB_FLUSH */

#else
static inline void set_mm(struct mm_struct *prev,
struct mm_struct *next, unsigned int cpu)
Expand Down
Loading
Loading