sh: Conditionally re-enable IRQs in fault path.
authorStuart Menefy <stuart.menefy@st.com>
Wed, 2 Jul 2008 08:51:23 +0000 (17:51 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Mon, 28 Jul 2008 09:10:33 +0000 (18:10 +0900)
The current kernel behaviour is to reenable interrupts unconditionally
when taking a page fault. This patch changes this to only enable them
if interrupts were previously enabled.

It also fixes a problem seen with this fix in place: the kernel previously
flushed the vsyscall page when handling a signal, which is not only
unncessary, but caused a possible sleep with interrupts disabled.

Signed-off-by: Stuart Menefy <stuart.menefy@st.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
arch/sh/kernel/signal_32.c
arch/sh/mm/fault_32.c

index eee29257a8ae0bdbc4cb26a3a9a1470c88d09876..4bbbde895a5353c41e7ecacd6cf019545c8e8202 100644 (file)
@@ -373,6 +373,7 @@ static int setup_frame(int sig, struct k_sigaction *ka,
                err |= __put_user(OR_R0_R0, &frame->retcode[6]);
                err |= __put_user((__NR_sigreturn), &frame->retcode[7]);
                regs->pr = (unsigned long) frame->retcode;
+               flush_icache_range(regs->pr, regs->pr + sizeof(frame->retcode));
        }
 
        if (err)
@@ -398,8 +399,6 @@ static int setup_frame(int sig, struct k_sigaction *ka,
        pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n",
                 current->comm, task_pid_nr(current), frame, regs->pc, regs->pr);
 
-       flush_icache_range(regs->pr, regs->pr + sizeof(frame->retcode));
-
        return 0;
 
 give_sigsegv:
index d1fa27594c6e4f0d7340670597e0c5ee4517b9c5..0c776fdfbddae6800480dc0a3dc7be54103509b3 100644 (file)
@@ -37,16 +37,12 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
        int fault;
        siginfo_t info;
 
-       trace_hardirqs_on();
-       local_irq_enable();
-
 #ifdef CONFIG_SH_KGDB
        if (kgdb_nofault && kgdb_bus_err_hook)
                kgdb_bus_err_hook();
 #endif
 
        tsk = current;
-       mm = tsk->mm;
        si_code = SEGV_MAPERR;
 
        if (unlikely(address >= TASK_SIZE)) {
@@ -88,6 +84,14 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
                return;
        }
 
+       /* Only enable interrupts if they were on before the fault */
+       if ((regs->sr & SR_IMASK) != SR_IMASK) {
+               trace_hardirqs_on();
+               local_irq_enable();
+       }
+
+       mm = tsk->mm;
+
        /*
         * If we're in an interrupt or have no user
         * context, we must not take the fault..