| .. | .. |
|---|
| 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 | |
|---|