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