.. | .. |
---|
25 | 25 | */ |
---|
26 | 26 | union fpregs_state init_fpstate __read_mostly; |
---|
27 | 27 | |
---|
28 | | -/* |
---|
29 | | - * Track whether the kernel is using the FPU state |
---|
30 | | - * currently. |
---|
31 | | - * |
---|
32 | | - * This flag is used: |
---|
33 | | - * |
---|
34 | | - * - by IRQ context code to potentially use the FPU |
---|
35 | | - * if it's unused. |
---|
36 | | - * |
---|
37 | | - * - to debug kernel_fpu_begin()/end() correctness |
---|
38 | | - */ |
---|
| 28 | +/* Track in-kernel FPU usage */ |
---|
39 | 29 | static DEFINE_PER_CPU(bool, in_kernel_fpu); |
---|
40 | 30 | |
---|
41 | 31 | /* |
---|
.. | .. |
---|
43 | 33 | */ |
---|
44 | 34 | DEFINE_PER_CPU(struct fpu *, fpu_fpregs_owner_ctx); |
---|
45 | 35 | |
---|
46 | | -static bool kernel_fpu_disabled(void) |
---|
47 | | -{ |
---|
48 | | - return this_cpu_read(in_kernel_fpu); |
---|
49 | | -} |
---|
50 | | - |
---|
51 | | -static bool interrupted_kernel_fpu_idle(void) |
---|
52 | | -{ |
---|
53 | | - return !kernel_fpu_disabled(); |
---|
54 | | -} |
---|
55 | | - |
---|
56 | | -/* |
---|
57 | | - * Were we in user mode (or vm86 mode) when we were |
---|
58 | | - * interrupted? |
---|
59 | | - * |
---|
60 | | - * Doing kernel_fpu_begin/end() is ok if we are running |
---|
61 | | - * in an interrupt context from user mode - we'll just |
---|
62 | | - * save the FPU state as required. |
---|
63 | | - */ |
---|
64 | | -static bool interrupted_user_mode(void) |
---|
65 | | -{ |
---|
66 | | - struct pt_regs *regs = get_irq_regs(); |
---|
67 | | - return regs && user_mode(regs); |
---|
68 | | -} |
---|
69 | | - |
---|
70 | 36 | /* |
---|
71 | 37 | * Can we use the FPU in kernel mode with the |
---|
72 | 38 | * whole "kernel_fpu_begin/end()" sequence? |
---|
73 | | - * |
---|
74 | | - * It's always ok in process context (ie "not interrupt") |
---|
75 | | - * but it is sometimes ok even from an irq. |
---|
76 | 39 | */ |
---|
77 | 40 | bool irq_fpu_usable(void) |
---|
78 | 41 | { |
---|
79 | | - return !in_interrupt() || |
---|
80 | | - interrupted_user_mode() || |
---|
81 | | - interrupted_kernel_fpu_idle(); |
---|
| 42 | + if (WARN_ON_ONCE(in_nmi())) |
---|
| 43 | + return false; |
---|
| 44 | + |
---|
| 45 | + /* In kernel FPU usage already active? */ |
---|
| 46 | + if (this_cpu_read(in_kernel_fpu)) |
---|
| 47 | + return false; |
---|
| 48 | + |
---|
| 49 | + /* |
---|
| 50 | + * When not in NMI or hard interrupt context, FPU can be used in: |
---|
| 51 | + * |
---|
| 52 | + * - Task context except from within fpregs_lock()'ed critical |
---|
| 53 | + * regions. |
---|
| 54 | + * |
---|
| 55 | + * - Soft interrupt processing context which cannot happen |
---|
| 56 | + * while in a fpregs_lock()'ed critical region. |
---|
| 57 | + */ |
---|
| 58 | + if (!in_irq()) |
---|
| 59 | + return true; |
---|
| 60 | + |
---|
| 61 | + /* |
---|
| 62 | + * In hard interrupt context it's safe when soft interrupts |
---|
| 63 | + * are enabled, which means the interrupt did not hit in |
---|
| 64 | + * a fpregs_lock()'ed critical region. |
---|
| 65 | + */ |
---|
| 66 | + return !softirq_count(); |
---|
82 | 67 | } |
---|
83 | 68 | EXPORT_SYMBOL(irq_fpu_usable); |
---|
84 | 69 | |
---|