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 <khand...@linux.vnet.ibm.com>
---
Changes in V2:
- Moved facility string detection earlier in the function
- Used pr_emerg() and die() instead of BUG_ON() if kernel context
- Removed redundant user_mode() check at the end

 arch/powerpc/kernel/traps.c | 32 +++++++++++++++++---------------
 1 file changed, 17 insertions(+), 15 deletions(-)

diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 1e48d15..317593d 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -1612,6 +1612,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
@@ -1678,25 +1694,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
 
-- 
1.8.3.1

Reply via email to