xref: /OK3568_Linux_fs/kernel/arch/mips/mm/context.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun #include <linux/atomic.h>
3*4882a593Smuzhiyun #include <linux/mmu_context.h>
4*4882a593Smuzhiyun #include <linux/percpu.h>
5*4882a593Smuzhiyun #include <linux/spinlock.h>
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun static DEFINE_RAW_SPINLOCK(cpu_mmid_lock);
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun static atomic64_t mmid_version;
10*4882a593Smuzhiyun static unsigned int num_mmids;
11*4882a593Smuzhiyun static unsigned long *mmid_map;
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun static DEFINE_PER_CPU(u64, reserved_mmids);
14*4882a593Smuzhiyun static cpumask_t tlb_flush_pending;
15*4882a593Smuzhiyun 
asid_versions_eq(int cpu,u64 a,u64 b)16*4882a593Smuzhiyun static bool asid_versions_eq(int cpu, u64 a, u64 b)
17*4882a593Smuzhiyun {
18*4882a593Smuzhiyun 	return ((a ^ b) & asid_version_mask(cpu)) == 0;
19*4882a593Smuzhiyun }
20*4882a593Smuzhiyun 
get_new_mmu_context(struct mm_struct * mm)21*4882a593Smuzhiyun void get_new_mmu_context(struct mm_struct *mm)
22*4882a593Smuzhiyun {
23*4882a593Smuzhiyun 	unsigned int cpu;
24*4882a593Smuzhiyun 	u64 asid;
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun 	/*
27*4882a593Smuzhiyun 	 * This function is specific to ASIDs, and should not be called when
28*4882a593Smuzhiyun 	 * MMIDs are in use.
29*4882a593Smuzhiyun 	 */
30*4882a593Smuzhiyun 	if (WARN_ON(IS_ENABLED(CONFIG_DEBUG_VM) && cpu_has_mmid))
31*4882a593Smuzhiyun 		return;
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun 	cpu = smp_processor_id();
34*4882a593Smuzhiyun 	asid = asid_cache(cpu);
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 	if (!((asid += cpu_asid_inc()) & cpu_asid_mask(&cpu_data[cpu]))) {
37*4882a593Smuzhiyun 		if (cpu_has_vtag_icache)
38*4882a593Smuzhiyun 			flush_icache_all();
39*4882a593Smuzhiyun 		local_flush_tlb_all();	/* start new asid cycle */
40*4882a593Smuzhiyun 	}
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun 	set_cpu_context(cpu, mm, asid);
43*4882a593Smuzhiyun 	asid_cache(cpu) = asid;
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(get_new_mmu_context);
46*4882a593Smuzhiyun 
check_mmu_context(struct mm_struct * mm)47*4882a593Smuzhiyun void check_mmu_context(struct mm_struct *mm)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun 	unsigned int cpu = smp_processor_id();
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	/*
52*4882a593Smuzhiyun 	 * This function is specific to ASIDs, and should not be called when
53*4882a593Smuzhiyun 	 * MMIDs are in use.
54*4882a593Smuzhiyun 	 */
55*4882a593Smuzhiyun 	if (WARN_ON(IS_ENABLED(CONFIG_DEBUG_VM) && cpu_has_mmid))
56*4882a593Smuzhiyun 		return;
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	/* Check if our ASID is of an older version and thus invalid */
59*4882a593Smuzhiyun 	if (!asid_versions_eq(cpu, cpu_context(cpu, mm), asid_cache(cpu)))
60*4882a593Smuzhiyun 		get_new_mmu_context(mm);
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(check_mmu_context);
63*4882a593Smuzhiyun 
flush_context(void)64*4882a593Smuzhiyun static void flush_context(void)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun 	u64 mmid;
67*4882a593Smuzhiyun 	int cpu;
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 	/* Update the list of reserved MMIDs and the MMID bitmap */
70*4882a593Smuzhiyun 	bitmap_clear(mmid_map, 0, num_mmids);
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	/* Reserve an MMID for kmap/wired entries */
73*4882a593Smuzhiyun 	__set_bit(MMID_KERNEL_WIRED, mmid_map);
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	for_each_possible_cpu(cpu) {
76*4882a593Smuzhiyun 		mmid = xchg_relaxed(&cpu_data[cpu].asid_cache, 0);
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 		/*
79*4882a593Smuzhiyun 		 * If this CPU has already been through a
80*4882a593Smuzhiyun 		 * rollover, but hasn't run another task in
81*4882a593Smuzhiyun 		 * the meantime, we must preserve its reserved
82*4882a593Smuzhiyun 		 * MMID, as this is the only trace we have of
83*4882a593Smuzhiyun 		 * the process it is still running.
84*4882a593Smuzhiyun 		 */
85*4882a593Smuzhiyun 		if (mmid == 0)
86*4882a593Smuzhiyun 			mmid = per_cpu(reserved_mmids, cpu);
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 		__set_bit(mmid & cpu_asid_mask(&cpu_data[cpu]), mmid_map);
89*4882a593Smuzhiyun 		per_cpu(reserved_mmids, cpu) = mmid;
90*4882a593Smuzhiyun 	}
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	/*
93*4882a593Smuzhiyun 	 * Queue a TLB invalidation for each CPU to perform on next
94*4882a593Smuzhiyun 	 * context-switch
95*4882a593Smuzhiyun 	 */
96*4882a593Smuzhiyun 	cpumask_setall(&tlb_flush_pending);
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun 
check_update_reserved_mmid(u64 mmid,u64 newmmid)99*4882a593Smuzhiyun static bool check_update_reserved_mmid(u64 mmid, u64 newmmid)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	bool hit;
102*4882a593Smuzhiyun 	int cpu;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	/*
105*4882a593Smuzhiyun 	 * Iterate over the set of reserved MMIDs looking for a match.
106*4882a593Smuzhiyun 	 * If we find one, then we can update our mm to use newmmid
107*4882a593Smuzhiyun 	 * (i.e. the same MMID in the current generation) but we can't
108*4882a593Smuzhiyun 	 * exit the loop early, since we need to ensure that all copies
109*4882a593Smuzhiyun 	 * of the old MMID are updated to reflect the mm. Failure to do
110*4882a593Smuzhiyun 	 * so could result in us missing the reserved MMID in a future
111*4882a593Smuzhiyun 	 * generation.
112*4882a593Smuzhiyun 	 */
113*4882a593Smuzhiyun 	hit = false;
114*4882a593Smuzhiyun 	for_each_possible_cpu(cpu) {
115*4882a593Smuzhiyun 		if (per_cpu(reserved_mmids, cpu) == mmid) {
116*4882a593Smuzhiyun 			hit = true;
117*4882a593Smuzhiyun 			per_cpu(reserved_mmids, cpu) = newmmid;
118*4882a593Smuzhiyun 		}
119*4882a593Smuzhiyun 	}
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	return hit;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun 
get_new_mmid(struct mm_struct * mm)124*4882a593Smuzhiyun static u64 get_new_mmid(struct mm_struct *mm)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun 	static u32 cur_idx = MMID_KERNEL_WIRED + 1;
127*4882a593Smuzhiyun 	u64 mmid, version, mmid_mask;
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	mmid = cpu_context(0, mm);
130*4882a593Smuzhiyun 	version = atomic64_read(&mmid_version);
131*4882a593Smuzhiyun 	mmid_mask = cpu_asid_mask(&boot_cpu_data);
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	if (!asid_versions_eq(0, mmid, 0)) {
134*4882a593Smuzhiyun 		u64 newmmid = version | (mmid & mmid_mask);
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 		/*
137*4882a593Smuzhiyun 		 * If our current MMID was active during a rollover, we
138*4882a593Smuzhiyun 		 * can continue to use it and this was just a false alarm.
139*4882a593Smuzhiyun 		 */
140*4882a593Smuzhiyun 		if (check_update_reserved_mmid(mmid, newmmid)) {
141*4882a593Smuzhiyun 			mmid = newmmid;
142*4882a593Smuzhiyun 			goto set_context;
143*4882a593Smuzhiyun 		}
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 		/*
146*4882a593Smuzhiyun 		 * We had a valid MMID in a previous life, so try to re-use
147*4882a593Smuzhiyun 		 * it if possible.
148*4882a593Smuzhiyun 		 */
149*4882a593Smuzhiyun 		if (!__test_and_set_bit(mmid & mmid_mask, mmid_map)) {
150*4882a593Smuzhiyun 			mmid = newmmid;
151*4882a593Smuzhiyun 			goto set_context;
152*4882a593Smuzhiyun 		}
153*4882a593Smuzhiyun 	}
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	/* Allocate a free MMID */
156*4882a593Smuzhiyun 	mmid = find_next_zero_bit(mmid_map, num_mmids, cur_idx);
157*4882a593Smuzhiyun 	if (mmid != num_mmids)
158*4882a593Smuzhiyun 		goto reserve_mmid;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	/* We're out of MMIDs, so increment the global version */
161*4882a593Smuzhiyun 	version = atomic64_add_return_relaxed(asid_first_version(0),
162*4882a593Smuzhiyun 					      &mmid_version);
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	/* Note currently active MMIDs & mark TLBs as requiring flushes */
165*4882a593Smuzhiyun 	flush_context();
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	/* We have more MMIDs than CPUs, so this will always succeed */
168*4882a593Smuzhiyun 	mmid = find_first_zero_bit(mmid_map, num_mmids);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun reserve_mmid:
171*4882a593Smuzhiyun 	__set_bit(mmid, mmid_map);
172*4882a593Smuzhiyun 	cur_idx = mmid;
173*4882a593Smuzhiyun 	mmid |= version;
174*4882a593Smuzhiyun set_context:
175*4882a593Smuzhiyun 	set_cpu_context(0, mm, mmid);
176*4882a593Smuzhiyun 	return mmid;
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun 
check_switch_mmu_context(struct mm_struct * mm)179*4882a593Smuzhiyun void check_switch_mmu_context(struct mm_struct *mm)
180*4882a593Smuzhiyun {
181*4882a593Smuzhiyun 	unsigned int cpu = smp_processor_id();
182*4882a593Smuzhiyun 	u64 ctx, old_active_mmid;
183*4882a593Smuzhiyun 	unsigned long flags;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	if (!cpu_has_mmid) {
186*4882a593Smuzhiyun 		check_mmu_context(mm);
187*4882a593Smuzhiyun 		write_c0_entryhi(cpu_asid(cpu, mm));
188*4882a593Smuzhiyun 		goto setup_pgd;
189*4882a593Smuzhiyun 	}
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	/*
192*4882a593Smuzhiyun 	 * MMID switch fast-path, to avoid acquiring cpu_mmid_lock when it's
193*4882a593Smuzhiyun 	 * unnecessary.
194*4882a593Smuzhiyun 	 *
195*4882a593Smuzhiyun 	 * The memory ordering here is subtle. If our active_mmids is non-zero
196*4882a593Smuzhiyun 	 * and the MMID matches the current version, then we update the CPU's
197*4882a593Smuzhiyun 	 * asid_cache with a relaxed cmpxchg. Racing with a concurrent rollover
198*4882a593Smuzhiyun 	 * means that either:
199*4882a593Smuzhiyun 	 *
200*4882a593Smuzhiyun 	 * - We get a zero back from the cmpxchg and end up waiting on
201*4882a593Smuzhiyun 	 *   cpu_mmid_lock in check_mmu_context(). Taking the lock synchronises
202*4882a593Smuzhiyun 	 *   with the rollover and so we are forced to see the updated
203*4882a593Smuzhiyun 	 *   generation.
204*4882a593Smuzhiyun 	 *
205*4882a593Smuzhiyun 	 * - We get a valid MMID back from the cmpxchg, which means the
206*4882a593Smuzhiyun 	 *   relaxed xchg in flush_context will treat us as reserved
207*4882a593Smuzhiyun 	 *   because atomic RmWs are totally ordered for a given location.
208*4882a593Smuzhiyun 	 */
209*4882a593Smuzhiyun 	ctx = cpu_context(cpu, mm);
210*4882a593Smuzhiyun 	old_active_mmid = READ_ONCE(cpu_data[cpu].asid_cache);
211*4882a593Smuzhiyun 	if (!old_active_mmid ||
212*4882a593Smuzhiyun 	    !asid_versions_eq(cpu, ctx, atomic64_read(&mmid_version)) ||
213*4882a593Smuzhiyun 	    !cmpxchg_relaxed(&cpu_data[cpu].asid_cache, old_active_mmid, ctx)) {
214*4882a593Smuzhiyun 		raw_spin_lock_irqsave(&cpu_mmid_lock, flags);
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 		ctx = cpu_context(cpu, mm);
217*4882a593Smuzhiyun 		if (!asid_versions_eq(cpu, ctx, atomic64_read(&mmid_version)))
218*4882a593Smuzhiyun 			ctx = get_new_mmid(mm);
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 		WRITE_ONCE(cpu_data[cpu].asid_cache, ctx);
221*4882a593Smuzhiyun 		raw_spin_unlock_irqrestore(&cpu_mmid_lock, flags);
222*4882a593Smuzhiyun 	}
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	/*
225*4882a593Smuzhiyun 	 * Invalidate the local TLB if needed. Note that we must only clear our
226*4882a593Smuzhiyun 	 * bit in tlb_flush_pending after this is complete, so that the
227*4882a593Smuzhiyun 	 * cpu_has_shared_ftlb_entries case below isn't misled.
228*4882a593Smuzhiyun 	 */
229*4882a593Smuzhiyun 	if (cpumask_test_cpu(cpu, &tlb_flush_pending)) {
230*4882a593Smuzhiyun 		if (cpu_has_vtag_icache)
231*4882a593Smuzhiyun 			flush_icache_all();
232*4882a593Smuzhiyun 		local_flush_tlb_all();
233*4882a593Smuzhiyun 		cpumask_clear_cpu(cpu, &tlb_flush_pending);
234*4882a593Smuzhiyun 	}
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	write_c0_memorymapid(ctx & cpu_asid_mask(&boot_cpu_data));
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	/*
239*4882a593Smuzhiyun 	 * If this CPU shares FTLB entries with its siblings and one or more of
240*4882a593Smuzhiyun 	 * those siblings hasn't yet invalidated its TLB following a version
241*4882a593Smuzhiyun 	 * increase then we need to invalidate any TLB entries for our MMID
242*4882a593Smuzhiyun 	 * that we might otherwise pick up from a sibling.
243*4882a593Smuzhiyun 	 *
244*4882a593Smuzhiyun 	 * We ifdef on CONFIG_SMP because cpu_sibling_map isn't defined in
245*4882a593Smuzhiyun 	 * CONFIG_SMP=n kernels.
246*4882a593Smuzhiyun 	 */
247*4882a593Smuzhiyun #ifdef CONFIG_SMP
248*4882a593Smuzhiyun 	if (cpu_has_shared_ftlb_entries &&
249*4882a593Smuzhiyun 	    cpumask_intersects(&tlb_flush_pending, &cpu_sibling_map[cpu])) {
250*4882a593Smuzhiyun 		/* Ensure we operate on the new MMID */
251*4882a593Smuzhiyun 		mtc0_tlbw_hazard();
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 		/*
254*4882a593Smuzhiyun 		 * Invalidate all TLB entries associated with the new
255*4882a593Smuzhiyun 		 * MMID, and wait for the invalidation to complete.
256*4882a593Smuzhiyun 		 */
257*4882a593Smuzhiyun 		ginvt_mmid();
258*4882a593Smuzhiyun 		sync_ginv();
259*4882a593Smuzhiyun 	}
260*4882a593Smuzhiyun #endif
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun setup_pgd:
263*4882a593Smuzhiyun 	TLBMISS_HANDLER_SETUP_PGD(mm->pgd);
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(check_switch_mmu_context);
266*4882a593Smuzhiyun 
mmid_init(void)267*4882a593Smuzhiyun static int mmid_init(void)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun 	if (!cpu_has_mmid)
270*4882a593Smuzhiyun 		return 0;
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	/*
273*4882a593Smuzhiyun 	 * Expect allocation after rollover to fail if we don't have at least
274*4882a593Smuzhiyun 	 * one more MMID than CPUs.
275*4882a593Smuzhiyun 	 */
276*4882a593Smuzhiyun 	num_mmids = asid_first_version(0);
277*4882a593Smuzhiyun 	WARN_ON(num_mmids <= num_possible_cpus());
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	atomic64_set(&mmid_version, asid_first_version(0));
280*4882a593Smuzhiyun 	mmid_map = kcalloc(BITS_TO_LONGS(num_mmids), sizeof(*mmid_map),
281*4882a593Smuzhiyun 			   GFP_KERNEL);
282*4882a593Smuzhiyun 	if (!mmid_map)
283*4882a593Smuzhiyun 		panic("Failed to allocate bitmap for %u MMIDs\n", num_mmids);
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	/* Reserve an MMID for kmap/wired entries */
286*4882a593Smuzhiyun 	__set_bit(MMID_KERNEL_WIRED, mmid_map);
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	pr_info("MMID allocator initialised with %u entries\n", num_mmids);
289*4882a593Smuzhiyun 	return 0;
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun early_initcall(mmid_init);
292