Skip to content

Commit 62b95a7

Browse files
ardbiesheuvelRussell King (Oracle)
authored andcommitted
ARM: 9282/1: vfp: Manipulate task VFP state with softirqs disabled
In a subsequent patch, we will relax the kernel mode NEON policy, and permit kernel mode NEON to be used not only from task context, as is permitted today, but also from softirq context. Given that softirqs may trigger over the back of any IRQ unless they are explicitly disabled, we need to address the resulting races in the VFP state handling, by disabling softirq processing in two distinct but related cases: - kernel mode NEON will leave the FPU disabled after it completes, so any kernel code sequence that enables the FPU and subsequently accesses its registers needs to disable softirqs until it completes; - kernel_neon_begin() will preserve the userland VFP state in memory, and if it interrupts the ordinary VFP state preserve sequence, the latter will resume execution with the VFP registers corrupted, and happily continue saving them to memory. Given that disabling softirqs also disables preemption, we can replace the existing preempt_disable/enable occurrences in the VFP state handling asm code with new macros that dis/enable softirqs instead. In the VFP state handling C code, add local_bh_disable/enable() calls in those places where the VFP state is preserved. One thing to keep in mind is that, once we allow NEON use in softirq context, the result of any such interruption is that the FPEXC_EN bit in the FPEXC register will be cleared, and vfp_current_hw_state[cpu] will be NULL. This means that any sequence that [conditionally] clears FPEXC_EN and/or sets vfp_current_hw_state[cpu] to NULL does not need to run with softirqs disabled, as the result will be the same. Furthermore, the handling of THREAD_NOTIFY_SWITCH is guaranteed to run with IRQs disabled, and so it does not need protection from softirq interruptions either. Tested-by: Martin Willi <[email protected]> Reviewed-by: Linus Walleij <[email protected]> Signed-off-by: Ard Biesheuvel <[email protected]> Signed-off-by: Russell King (Oracle) <[email protected]>
1 parent 368ccec commit 62b95a7

File tree

5 files changed

+24
-12
lines changed

5 files changed

+24
-12
lines changed

arch/arm/include/asm/assembler.h

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -236,21 +236,26 @@ THUMB( fpreg .req r7 )
236236
sub \tmp, \tmp, #1 @ decrement it
237237
str \tmp, [\ti, #TI_PREEMPT]
238238
.endm
239-
240-
.macro dec_preempt_count_ti, ti, tmp
241-
get_thread_info \ti
242-
dec_preempt_count \ti, \tmp
243-
.endm
244239
#else
245240
.macro inc_preempt_count, ti, tmp
246241
.endm
247242

248243
.macro dec_preempt_count, ti, tmp
249244
.endm
245+
#endif
246+
247+
.macro local_bh_disable, ti, tmp
248+
ldr \tmp, [\ti, #TI_PREEMPT]
249+
add \tmp, \tmp, #SOFTIRQ_DISABLE_OFFSET
250+
str \tmp, [\ti, #TI_PREEMPT]
251+
.endm
250252

251-
.macro dec_preempt_count_ti, ti, tmp
253+
.macro local_bh_enable_ti, ti, tmp
254+
get_thread_info \ti
255+
ldr \tmp, [\ti, #TI_PREEMPT]
256+
sub \tmp, \tmp, #SOFTIRQ_DISABLE_OFFSET
257+
str \tmp, [\ti, #TI_PREEMPT]
252258
.endm
253-
#endif
254259

255260
#define USERL(l, x...) \
256261
9999: x; \

arch/arm/kernel/asm-offsets.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ int main(void)
5656
DEFINE(VFP_CPU, offsetof(union vfp_state, hard.cpu));
5757
#endif
5858
#endif
59+
DEFINE(SOFTIRQ_DISABLE_OFFSET,SOFTIRQ_DISABLE_OFFSET);
5960
#ifdef CONFIG_ARM_THUMBEE
6061
DEFINE(TI_THUMBEE_STATE, offsetof(struct thread_info, thumbee_state));
6162
#endif

arch/arm/vfp/entry.S

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@
2222
@ IRQs enabled.
2323
@
2424
ENTRY(do_vfp)
25-
inc_preempt_count r10, r4
25+
local_bh_disable r10, r4
2626
ldr r4, .LCvfp
2727
ldr r11, [r10, #TI_CPU] @ CPU number
2828
add r10, r10, #TI_VFPSTATE @ r10 = workspace
2929
ldr pc, [r4] @ call VFP entry point
3030
ENDPROC(do_vfp)
3131

3232
ENTRY(vfp_null_entry)
33-
dec_preempt_count_ti r10, r4
33+
local_bh_enable_ti r10, r4
3434
ret lr
3535
ENDPROC(vfp_null_entry)
3636

arch/arm/vfp/vfphw.S

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ vfp_hw_state_valid:
175175
@ else it's one 32-bit instruction, so
176176
@ always subtract 4 from the following
177177
@ instruction address.
178-
dec_preempt_count_ti r10, r4
178+
local_bh_enable_ti r10, r4
179179
ret r9 @ we think we have handled things
180180

181181

@@ -200,7 +200,7 @@ skip:
200200
@ not recognised by VFP
201201

202202
DBGSTR "not VFP"
203-
dec_preempt_count_ti r10, r4
203+
local_bh_enable_ti r10, r4
204204
ret lr
205205

206206
process_exception:

arch/arm/vfp/vfpmodule.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs)
416416
if (exceptions)
417417
vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs);
418418
exit:
419-
preempt_enable();
419+
local_bh_enable();
420420
}
421421

422422
static void vfp_enable(void *unused)
@@ -517,6 +517,8 @@ void vfp_sync_hwstate(struct thread_info *thread)
517517
{
518518
unsigned int cpu = get_cpu();
519519

520+
local_bh_disable();
521+
520522
if (vfp_state_in_hw(cpu, thread)) {
521523
u32 fpexc = fmrx(FPEXC);
522524

@@ -528,6 +530,7 @@ void vfp_sync_hwstate(struct thread_info *thread)
528530
fmxr(FPEXC, fpexc);
529531
}
530532

533+
local_bh_enable();
531534
put_cpu();
532535
}
533536

@@ -717,6 +720,8 @@ void kernel_neon_begin(void)
717720
unsigned int cpu;
718721
u32 fpexc;
719722

723+
local_bh_disable();
724+
720725
/*
721726
* Kernel mode NEON is only allowed outside of interrupt context
722727
* with preemption disabled. This will make sure that the kernel
@@ -739,6 +744,7 @@ void kernel_neon_begin(void)
739744
vfp_save_state(vfp_current_hw_state[cpu], fpexc);
740745
#endif
741746
vfp_current_hw_state[cpu] = NULL;
747+
local_bh_enable();
742748
}
743749
EXPORT_SYMBOL(kernel_neon_begin);
744750

0 commit comments

Comments
 (0)