Skip to content

Commit 9b19700

Browse files
ardbiesheuvelwilldeacon
authored andcommitted
arm64: fpsimd: Drop unneeded 'busy' flag
Kernel mode NEON will preserve the user mode FPSIMD state by saving it into the task struct before clobbering the registers. In order to avoid the need for preserving kernel mode state too, we disallow nested use of kernel mode NEON, i..e, use in softirq context while the interrupted task context was using kernel mode NEON too. Originally, this policy was implemented using a per-CPU flag which was exposed via may_use_simd(), requiring the users of the kernel mode NEON to deal with the possibility that it might return false, and having NEON and non-NEON code paths. This policy was changed by commit 1315014 ("arm64: fpsimd: run kernel mode NEON with softirqs disabled"), and now, softirq processing is disabled entirely instead, and so may_use_simd() can never fail when called from task or softirq context. This means we can drop the fpsimd_context_busy flag entirely, and instead, ensure that we disable softirq processing in places where we formerly relied on the flag for preventing races in the FPSIMD preserve routines. Signed-off-by: Ard Biesheuvel <[email protected]> Reviewed-by: Mark Brown <[email protected]> Tested-by: Geert Uytterhoeven <[email protected]> Link: https://lore.kernel.org/r/[email protected] [will: Folded in fix from CAMj1kXFhzbJRyWHELCivQW1yJaF=p07LLtbuyXYX3G1WtsdyQg@mail.gmail.com] Signed-off-by: Will Deacon <[email protected]>
1 parent 2cc14f5 commit 9b19700

File tree

2 files changed

+15
-51
lines changed

2 files changed

+15
-51
lines changed

arch/arm64/include/asm/simd.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
#include <linux/preempt.h>
1313
#include <linux/types.h>
1414

15-
DECLARE_PER_CPU(bool, fpsimd_context_busy);
16-
1715
#ifdef CONFIG_KERNEL_MODE_NEON
1816

1917
/*
@@ -28,17 +26,10 @@ static __must_check inline bool may_use_simd(void)
2826
/*
2927
* We must make sure that the SVE has been initialized properly
3028
* before using the SIMD in kernel.
31-
* fpsimd_context_busy is only set while preemption is disabled,
32-
* and is clear whenever preemption is enabled. Since
33-
* this_cpu_read() is atomic w.r.t. preemption, fpsimd_context_busy
34-
* cannot change under our feet -- if it's set we cannot be
35-
* migrated, and if it's clear we cannot be migrated to a CPU
36-
* where it is set.
3729
*/
3830
return !WARN_ON(!system_capabilities_finalized()) &&
3931
system_supports_fpsimd() &&
40-
!in_hardirq() && !irqs_disabled() && !in_nmi() &&
41-
!this_cpu_read(fpsimd_context_busy);
32+
!in_hardirq() && !irqs_disabled() && !in_nmi();
4233
}
4334

4435
#else /* ! CONFIG_KERNEL_MODE_NEON */

arch/arm64/kernel/fpsimd.c

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,13 @@
8585
* softirq kicks in. Upon vcpu_put(), KVM will save the vcpu FP state and
8686
* flag the register state as invalid.
8787
*
88-
* In order to allow softirq handlers to use FPSIMD, kernel_neon_begin() may
89-
* save the task's FPSIMD context back to task_struct from softirq context.
90-
* To prevent this from racing with the manipulation of the task's FPSIMD state
91-
* from task context and thereby corrupting the state, it is necessary to
92-
* protect any manipulation of a task's fpsimd_state or TIF_FOREIGN_FPSTATE
93-
* flag with {, __}get_cpu_fpsimd_context(). This will still allow softirqs to
94-
* run but prevent them to use FPSIMD.
88+
* In order to allow softirq handlers to use FPSIMD, kernel_neon_begin() may be
89+
* called from softirq context, which will save the task's FPSIMD context back
90+
* to task_struct. To prevent this from racing with the manipulation of the
91+
* task's FPSIMD state from task context and thereby corrupting the state, it
92+
* is necessary to protect any manipulation of a task's fpsimd_state or
93+
* TIF_FOREIGN_FPSTATE flag with get_cpu_fpsimd_context(), which will suspend
94+
* softirq servicing entirely until put_cpu_fpsimd_context() is called.
9595
*
9696
* For a certain task, the sequence may look something like this:
9797
* - the task gets scheduled in; if both the task's fpsimd_cpu field
@@ -209,27 +209,14 @@ static inline void sme_free(struct task_struct *t) { }
209209

210210
#endif
211211

212-
DEFINE_PER_CPU(bool, fpsimd_context_busy);
213-
EXPORT_PER_CPU_SYMBOL(fpsimd_context_busy);
214-
215212
static void fpsimd_bind_task_to_cpu(void);
216213

217-
static void __get_cpu_fpsimd_context(void)
218-
{
219-
bool busy = __this_cpu_xchg(fpsimd_context_busy, true);
220-
221-
WARN_ON(busy);
222-
}
223-
224214
/*
225215
* Claim ownership of the CPU FPSIMD context for use by the calling context.
226216
*
227217
* The caller may freely manipulate the FPSIMD context metadata until
228218
* put_cpu_fpsimd_context() is called.
229219
*
230-
* The double-underscore version must only be called if you know the task
231-
* can't be preempted.
232-
*
233220
* On RT kernels local_bh_disable() is not sufficient because it only
234221
* serializes soft interrupt related sections via a local lock, but stays
235222
* preemptible. Disabling preemption is the right choice here as bottom
@@ -242,14 +229,6 @@ static void get_cpu_fpsimd_context(void)
242229
local_bh_disable();
243230
else
244231
preempt_disable();
245-
__get_cpu_fpsimd_context();
246-
}
247-
248-
static void __put_cpu_fpsimd_context(void)
249-
{
250-
bool busy = __this_cpu_xchg(fpsimd_context_busy, false);
251-
252-
WARN_ON(!busy); /* No matching get_cpu_fpsimd_context()? */
253232
}
254233

255234
/*
@@ -261,18 +240,12 @@ static void __put_cpu_fpsimd_context(void)
261240
*/
262241
static void put_cpu_fpsimd_context(void)
263242
{
264-
__put_cpu_fpsimd_context();
265243
if (!IS_ENABLED(CONFIG_PREEMPT_RT))
266244
local_bh_enable();
267245
else
268246
preempt_enable();
269247
}
270248

271-
static bool have_cpu_fpsimd_context(void)
272-
{
273-
return !preemptible() && __this_cpu_read(fpsimd_context_busy);
274-
}
275-
276249
unsigned int task_get_vl(const struct task_struct *task, enum vec_type type)
277250
{
278251
return task->thread.vl[type];
@@ -383,7 +356,7 @@ static void task_fpsimd_load(void)
383356
bool restore_ffr;
384357

385358
WARN_ON(!system_supports_fpsimd());
386-
WARN_ON(!have_cpu_fpsimd_context());
359+
WARN_ON(preemptible());
387360

388361
if (system_supports_sve() || system_supports_sme()) {
389362
switch (current->thread.fp_type) {
@@ -467,7 +440,7 @@ static void fpsimd_save(void)
467440
unsigned int vl;
468441

469442
WARN_ON(!system_supports_fpsimd());
470-
WARN_ON(!have_cpu_fpsimd_context());
443+
WARN_ON(preemptible());
471444

472445
if (test_thread_flag(TIF_FOREIGN_FPSTATE))
473446
return;
@@ -1507,7 +1480,7 @@ void fpsimd_thread_switch(struct task_struct *next)
15071480
if (!system_supports_fpsimd())
15081481
return;
15091482

1510-
__get_cpu_fpsimd_context();
1483+
WARN_ON_ONCE(!irqs_disabled());
15111484

15121485
/* Save unsaved fpsimd state, if any: */
15131486
fpsimd_save();
@@ -1523,8 +1496,6 @@ void fpsimd_thread_switch(struct task_struct *next)
15231496

15241497
update_tsk_thread_flag(next, TIF_FOREIGN_FPSTATE,
15251498
wrong_task || wrong_cpu);
1526-
1527-
__put_cpu_fpsimd_context();
15281499
}
15291500

15301501
static void fpsimd_flush_thread_vl(enum vec_type type)
@@ -1826,13 +1797,15 @@ static void fpsimd_flush_cpu_state(void)
18261797
*/
18271798
void fpsimd_save_and_flush_cpu_state(void)
18281799
{
1800+
unsigned long flags;
1801+
18291802
if (!system_supports_fpsimd())
18301803
return;
18311804
WARN_ON(preemptible());
1832-
__get_cpu_fpsimd_context();
1805+
local_irq_save(flags);
18331806
fpsimd_save();
18341807
fpsimd_flush_cpu_state();
1835-
__put_cpu_fpsimd_context();
1808+
local_irq_restore(flags);
18361809
}
18371810

18381811
#ifdef CONFIG_KERNEL_MODE_NEON

0 commit comments

Comments
 (0)