powerpc/fscr: Enable interrupts earlier before calling get_user()
authorAnshuman Khandual <khandual@linux.vnet.ibm.com>
Thu, 29 Mar 2018 06:23:37 +0000 (11:53 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 10 Apr 2018 01:23:23 +0000 (11:23 +1000)
The function get_user() can sleep while trying to fetch instruction
from user address space and causes the following warning from the
scheduler.

BUG: sleeping function called from invalid context

Though interrupts get enabled back but it happens bit later after
get_user() is called. This change moves enabling these interrupts
earlier covering the function get_user(). While at this, lets check
for kernel mode and crash as this interrupt should not have been
triggered from the kernel context.

Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/kernel/traps.c

index a2ef0c0e6c315bf5b833d1d3c1d140d15a178b2e..0904492e7032eb4ba4409e54cadb1a74ef4df74f 100644 (file)
@@ -1613,6 +1613,22 @@ void facility_unavailable_exception(struct pt_regs *regs)
                value = mfspr(SPRN_FSCR);
 
        status = value >> 56;
+       if ((hv || status >= 2) &&
+           (status < ARRAY_SIZE(facility_strings)) &&
+           facility_strings[status])
+               facility = facility_strings[status];
+
+       /* We should not have taken this interrupt in kernel */
+       if (!user_mode(regs)) {
+               pr_emerg("Facility '%s' unavailable (%d) exception in kernel mode at %lx\n",
+                        facility, status, regs->nip);
+               die("Unexpected facility unavailable exception", regs, SIGABRT);
+       }
+
+       /* We restore the interrupt state now */
+       if (!arch_irq_disabled_regs(regs))
+               local_irq_enable();
+
        if (status == FSCR_DSCR_LG) {
                /*
                 * User is accessing the DSCR register using the problem
@@ -1679,25 +1695,11 @@ void facility_unavailable_exception(struct pt_regs *regs)
                return;
        }
 
-       if ((hv || status >= 2) &&
-           (status < ARRAY_SIZE(facility_strings)) &&
-           facility_strings[status])
-               facility = facility_strings[status];
-
-       /* We restore the interrupt state now */
-       if (!arch_irq_disabled_regs(regs))
-               local_irq_enable();
-
        pr_err_ratelimited("%sFacility '%s' unavailable (%d), exception at 0x%lx, MSR=%lx\n",
                hv ? "Hypervisor " : "", facility, status, regs->nip, regs->msr);
 
 out:
-       if (user_mode(regs)) {
-               _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
-               return;
-       }
-
-       die("Unexpected facility unavailable exception", regs, SIGABRT);
+       _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
 }
 #endif