xref: /kernel/linux/linux-6.6/arch/riscv/mm/context.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2012 Regents of the University of California
462306a36Sopenharmony_ci * Copyright (C) 2017 SiFive
562306a36Sopenharmony_ci * Copyright (C) 2021 Western Digital Corporation or its affiliates.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/bitops.h>
962306a36Sopenharmony_ci#include <linux/cpumask.h>
1062306a36Sopenharmony_ci#include <linux/mm.h>
1162306a36Sopenharmony_ci#include <linux/percpu.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/spinlock.h>
1462306a36Sopenharmony_ci#include <linux/static_key.h>
1562306a36Sopenharmony_ci#include <asm/tlbflush.h>
1662306a36Sopenharmony_ci#include <asm/cacheflush.h>
1762306a36Sopenharmony_ci#include <asm/mmu_context.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#ifdef CONFIG_MMU
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(use_asid_allocator);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic unsigned long asid_bits;
2462306a36Sopenharmony_cistatic unsigned long num_asids;
2562306a36Sopenharmony_ciunsigned long asid_mask;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic atomic_long_t current_version;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(context_lock);
3062306a36Sopenharmony_cistatic cpumask_t context_tlb_flush_pending;
3162306a36Sopenharmony_cistatic unsigned long *context_asid_map;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic DEFINE_PER_CPU(atomic_long_t, active_context);
3462306a36Sopenharmony_cistatic DEFINE_PER_CPU(unsigned long, reserved_context);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic bool check_update_reserved_context(unsigned long cntx,
3762306a36Sopenharmony_ci					  unsigned long newcntx)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	int cpu;
4062306a36Sopenharmony_ci	bool hit = false;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/*
4362306a36Sopenharmony_ci	 * Iterate over the set of reserved CONTEXT looking for a match.
4462306a36Sopenharmony_ci	 * If we find one, then we can update our mm to use new CONTEXT
4562306a36Sopenharmony_ci	 * (i.e. the same CONTEXT in the current_version) but we can't
4662306a36Sopenharmony_ci	 * exit the loop early, since we need to ensure that all copies
4762306a36Sopenharmony_ci	 * of the old CONTEXT are updated to reflect the mm. Failure to do
4862306a36Sopenharmony_ci	 * so could result in us missing the reserved CONTEXT in a future
4962306a36Sopenharmony_ci	 * version.
5062306a36Sopenharmony_ci	 */
5162306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
5262306a36Sopenharmony_ci		if (per_cpu(reserved_context, cpu) == cntx) {
5362306a36Sopenharmony_ci			hit = true;
5462306a36Sopenharmony_ci			per_cpu(reserved_context, cpu) = newcntx;
5562306a36Sopenharmony_ci		}
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return hit;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic void __flush_context(void)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	int i;
6462306a36Sopenharmony_ci	unsigned long cntx;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	/* Must be called with context_lock held */
6762306a36Sopenharmony_ci	lockdep_assert_held(&context_lock);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/* Update the list of reserved ASIDs and the ASID bitmap. */
7062306a36Sopenharmony_ci	bitmap_zero(context_asid_map, num_asids);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* Mark already active ASIDs as used */
7362306a36Sopenharmony_ci	for_each_possible_cpu(i) {
7462306a36Sopenharmony_ci		cntx = atomic_long_xchg_relaxed(&per_cpu(active_context, i), 0);
7562306a36Sopenharmony_ci		/*
7662306a36Sopenharmony_ci		 * If this CPU has already been through a rollover, but
7762306a36Sopenharmony_ci		 * hasn't run another task in the meantime, we must preserve
7862306a36Sopenharmony_ci		 * its reserved CONTEXT, as this is the only trace we have of
7962306a36Sopenharmony_ci		 * the process it is still running.
8062306a36Sopenharmony_ci		 */
8162306a36Sopenharmony_ci		if (cntx == 0)
8262306a36Sopenharmony_ci			cntx = per_cpu(reserved_context, i);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci		__set_bit(cntx & asid_mask, context_asid_map);
8562306a36Sopenharmony_ci		per_cpu(reserved_context, i) = cntx;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Mark ASID #0 as used because it is used at boot-time */
8962306a36Sopenharmony_ci	__set_bit(0, context_asid_map);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* Queue a TLB invalidation for each CPU on next context-switch */
9262306a36Sopenharmony_ci	cpumask_setall(&context_tlb_flush_pending);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic unsigned long __new_context(struct mm_struct *mm)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	static u32 cur_idx = 1;
9862306a36Sopenharmony_ci	unsigned long cntx = atomic_long_read(&mm->context.id);
9962306a36Sopenharmony_ci	unsigned long asid, ver = atomic_long_read(&current_version);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/* Must be called with context_lock held */
10262306a36Sopenharmony_ci	lockdep_assert_held(&context_lock);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (cntx != 0) {
10562306a36Sopenharmony_ci		unsigned long newcntx = ver | (cntx & asid_mask);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		/*
10862306a36Sopenharmony_ci		 * If our current CONTEXT was active during a rollover, we
10962306a36Sopenharmony_ci		 * can continue to use it and this was just a false alarm.
11062306a36Sopenharmony_ci		 */
11162306a36Sopenharmony_ci		if (check_update_reserved_context(cntx, newcntx))
11262306a36Sopenharmony_ci			return newcntx;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		/*
11562306a36Sopenharmony_ci		 * We had a valid CONTEXT in a previous life, so try to
11662306a36Sopenharmony_ci		 * re-use it if possible.
11762306a36Sopenharmony_ci		 */
11862306a36Sopenharmony_ci		if (!__test_and_set_bit(cntx & asid_mask, context_asid_map))
11962306a36Sopenharmony_ci			return newcntx;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/*
12362306a36Sopenharmony_ci	 * Allocate a free ASID. If we can't find one then increment
12462306a36Sopenharmony_ci	 * current_version and flush all ASIDs.
12562306a36Sopenharmony_ci	 */
12662306a36Sopenharmony_ci	asid = find_next_zero_bit(context_asid_map, num_asids, cur_idx);
12762306a36Sopenharmony_ci	if (asid != num_asids)
12862306a36Sopenharmony_ci		goto set_asid;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* We're out of ASIDs, so increment current_version */
13162306a36Sopenharmony_ci	ver = atomic_long_add_return_relaxed(num_asids, &current_version);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* Flush everything  */
13462306a36Sopenharmony_ci	__flush_context();
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* We have more ASIDs than CPUs, so this will always succeed */
13762306a36Sopenharmony_ci	asid = find_next_zero_bit(context_asid_map, num_asids, 1);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ciset_asid:
14062306a36Sopenharmony_ci	__set_bit(asid, context_asid_map);
14162306a36Sopenharmony_ci	cur_idx = asid;
14262306a36Sopenharmony_ci	return asid | ver;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic void set_mm_asid(struct mm_struct *mm, unsigned int cpu)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	unsigned long flags;
14862306a36Sopenharmony_ci	bool need_flush_tlb = false;
14962306a36Sopenharmony_ci	unsigned long cntx, old_active_cntx;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	cntx = atomic_long_read(&mm->context.id);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/*
15462306a36Sopenharmony_ci	 * If our active_context is non-zero and the context matches the
15562306a36Sopenharmony_ci	 * current_version, then we update the active_context entry with a
15662306a36Sopenharmony_ci	 * relaxed cmpxchg.
15762306a36Sopenharmony_ci	 *
15862306a36Sopenharmony_ci	 * Following is how we handle racing with a concurrent rollover:
15962306a36Sopenharmony_ci	 *
16062306a36Sopenharmony_ci	 * - We get a zero back from the cmpxchg and end up waiting on the
16162306a36Sopenharmony_ci	 *   lock. Taking the lock synchronises with the rollover and so
16262306a36Sopenharmony_ci	 *   we are forced to see the updated verion.
16362306a36Sopenharmony_ci	 *
16462306a36Sopenharmony_ci	 * - We get a valid context back from the cmpxchg then we continue
16562306a36Sopenharmony_ci	 *   using old ASID because __flush_context() would have marked ASID
16662306a36Sopenharmony_ci	 *   of active_context as used and next context switch we will
16762306a36Sopenharmony_ci	 *   allocate new context.
16862306a36Sopenharmony_ci	 */
16962306a36Sopenharmony_ci	old_active_cntx = atomic_long_read(&per_cpu(active_context, cpu));
17062306a36Sopenharmony_ci	if (old_active_cntx &&
17162306a36Sopenharmony_ci	    ((cntx & ~asid_mask) == atomic_long_read(&current_version)) &&
17262306a36Sopenharmony_ci	    atomic_long_cmpxchg_relaxed(&per_cpu(active_context, cpu),
17362306a36Sopenharmony_ci					old_active_cntx, cntx))
17462306a36Sopenharmony_ci		goto switch_mm_fast;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	raw_spin_lock_irqsave(&context_lock, flags);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* Check that our ASID belongs to the current_version. */
17962306a36Sopenharmony_ci	cntx = atomic_long_read(&mm->context.id);
18062306a36Sopenharmony_ci	if ((cntx & ~asid_mask) != atomic_long_read(&current_version)) {
18162306a36Sopenharmony_ci		cntx = __new_context(mm);
18262306a36Sopenharmony_ci		atomic_long_set(&mm->context.id, cntx);
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (cpumask_test_and_clear_cpu(cpu, &context_tlb_flush_pending))
18662306a36Sopenharmony_ci		need_flush_tlb = true;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	atomic_long_set(&per_cpu(active_context, cpu), cntx);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&context_lock, flags);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ciswitch_mm_fast:
19362306a36Sopenharmony_ci	csr_write(CSR_SATP, virt_to_pfn(mm->pgd) |
19462306a36Sopenharmony_ci		  ((cntx & asid_mask) << SATP_ASID_SHIFT) |
19562306a36Sopenharmony_ci		  satp_mode);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (need_flush_tlb)
19862306a36Sopenharmony_ci		local_flush_tlb_all();
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void set_mm_noasid(struct mm_struct *mm)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	/* Switch the page table and blindly nuke entire local TLB */
20462306a36Sopenharmony_ci	csr_write(CSR_SATP, virt_to_pfn(mm->pgd) | satp_mode);
20562306a36Sopenharmony_ci	local_flush_tlb_all();
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic inline void set_mm(struct mm_struct *prev,
20962306a36Sopenharmony_ci			  struct mm_struct *next, unsigned int cpu)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	/*
21262306a36Sopenharmony_ci	 * The mm_cpumask indicates which harts' TLBs contain the virtual
21362306a36Sopenharmony_ci	 * address mapping of the mm. Compared to noasid, using asid
21462306a36Sopenharmony_ci	 * can't guarantee that stale TLB entries are invalidated because
21562306a36Sopenharmony_ci	 * the asid mechanism wouldn't flush TLB for every switch_mm for
21662306a36Sopenharmony_ci	 * performance. So when using asid, keep all CPUs footmarks in
21762306a36Sopenharmony_ci	 * cpumask() until mm reset.
21862306a36Sopenharmony_ci	 */
21962306a36Sopenharmony_ci	cpumask_set_cpu(cpu, mm_cpumask(next));
22062306a36Sopenharmony_ci	if (static_branch_unlikely(&use_asid_allocator)) {
22162306a36Sopenharmony_ci		set_mm_asid(next, cpu);
22262306a36Sopenharmony_ci	} else {
22362306a36Sopenharmony_ci		cpumask_clear_cpu(cpu, mm_cpumask(prev));
22462306a36Sopenharmony_ci		set_mm_noasid(next);
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int __init asids_init(void)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	unsigned long old;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* Figure-out number of ASID bits in HW */
23362306a36Sopenharmony_ci	old = csr_read(CSR_SATP);
23462306a36Sopenharmony_ci	asid_bits = old | (SATP_ASID_MASK << SATP_ASID_SHIFT);
23562306a36Sopenharmony_ci	csr_write(CSR_SATP, asid_bits);
23662306a36Sopenharmony_ci	asid_bits = (csr_read(CSR_SATP) >> SATP_ASID_SHIFT)  & SATP_ASID_MASK;
23762306a36Sopenharmony_ci	asid_bits = fls_long(asid_bits);
23862306a36Sopenharmony_ci	csr_write(CSR_SATP, old);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/*
24162306a36Sopenharmony_ci	 * In the process of determining number of ASID bits (above)
24262306a36Sopenharmony_ci	 * we polluted the TLB of current HART so let's do TLB flushed
24362306a36Sopenharmony_ci	 * to remove unwanted TLB enteries.
24462306a36Sopenharmony_ci	 */
24562306a36Sopenharmony_ci	local_flush_tlb_all();
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* Pre-compute ASID details */
24862306a36Sopenharmony_ci	if (asid_bits) {
24962306a36Sopenharmony_ci		num_asids = 1 << asid_bits;
25062306a36Sopenharmony_ci		asid_mask = num_asids - 1;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/*
25462306a36Sopenharmony_ci	 * Use ASID allocator only if number of HW ASIDs are
25562306a36Sopenharmony_ci	 * at-least twice more than CPUs
25662306a36Sopenharmony_ci	 */
25762306a36Sopenharmony_ci	if (num_asids > (2 * num_possible_cpus())) {
25862306a36Sopenharmony_ci		atomic_long_set(&current_version, num_asids);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		context_asid_map = bitmap_zalloc(num_asids, GFP_KERNEL);
26162306a36Sopenharmony_ci		if (!context_asid_map)
26262306a36Sopenharmony_ci			panic("Failed to allocate bitmap for %lu ASIDs\n",
26362306a36Sopenharmony_ci			      num_asids);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci		__set_bit(0, context_asid_map);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		static_branch_enable(&use_asid_allocator);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		pr_info("ASID allocator using %lu bits (%lu entries)\n",
27062306a36Sopenharmony_ci			asid_bits, num_asids);
27162306a36Sopenharmony_ci	} else {
27262306a36Sopenharmony_ci		pr_info("ASID allocator disabled (%lu bits)\n", asid_bits);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ciearly_initcall(asids_init);
27862306a36Sopenharmony_ci#else
27962306a36Sopenharmony_cistatic inline void set_mm(struct mm_struct *prev,
28062306a36Sopenharmony_ci			  struct mm_struct *next, unsigned int cpu)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	/* Nothing to do here when there is no MMU */
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci#endif
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci/*
28762306a36Sopenharmony_ci * When necessary, performs a deferred icache flush for the given MM context,
28862306a36Sopenharmony_ci * on the local CPU.  RISC-V has no direct mechanism for instruction cache
28962306a36Sopenharmony_ci * shoot downs, so instead we send an IPI that informs the remote harts they
29062306a36Sopenharmony_ci * need to flush their local instruction caches.  To avoid pathologically slow
29162306a36Sopenharmony_ci * behavior in a common case (a bunch of single-hart processes on a many-hart
29262306a36Sopenharmony_ci * machine, ie 'make -j') we avoid the IPIs for harts that are not currently
29362306a36Sopenharmony_ci * executing a MM context and instead schedule a deferred local instruction
29462306a36Sopenharmony_ci * cache flush to be performed before execution resumes on each hart.  This
29562306a36Sopenharmony_ci * actually performs that local instruction cache flush, which implicitly only
29662306a36Sopenharmony_ci * refers to the current hart.
29762306a36Sopenharmony_ci *
29862306a36Sopenharmony_ci * The "cpu" argument must be the current local CPU number.
29962306a36Sopenharmony_ci */
30062306a36Sopenharmony_cistatic inline void flush_icache_deferred(struct mm_struct *mm, unsigned int cpu)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci#ifdef CONFIG_SMP
30362306a36Sopenharmony_ci	cpumask_t *mask = &mm->context.icache_stale_mask;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (cpumask_test_cpu(cpu, mask)) {
30662306a36Sopenharmony_ci		cpumask_clear_cpu(cpu, mask);
30762306a36Sopenharmony_ci		/*
30862306a36Sopenharmony_ci		 * Ensure the remote hart's writes are visible to this hart.
30962306a36Sopenharmony_ci		 * This pairs with a barrier in flush_icache_mm.
31062306a36Sopenharmony_ci		 */
31162306a36Sopenharmony_ci		smp_mb();
31262306a36Sopenharmony_ci		local_flush_icache_all();
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci#endif
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_civoid switch_mm(struct mm_struct *prev, struct mm_struct *next,
31962306a36Sopenharmony_ci	struct task_struct *task)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	unsigned int cpu;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (unlikely(prev == next))
32462306a36Sopenharmony_ci		return;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	/*
32762306a36Sopenharmony_ci	 * Mark the current MM context as inactive, and the next as
32862306a36Sopenharmony_ci	 * active.  This is at least used by the icache flushing
32962306a36Sopenharmony_ci	 * routines in order to determine who should be flushed.
33062306a36Sopenharmony_ci	 */
33162306a36Sopenharmony_ci	cpu = smp_processor_id();
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	set_mm(prev, next, cpu);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	flush_icache_deferred(next, cpu);
33662306a36Sopenharmony_ci}
337