From f2fb4e4f647dabf1177d3ce164988e73482d76b1 Mon Sep 17 00:00:00 2001 From: Stuart Menefy Date: Wed, 2 Jul 2008 17:51:23 +0900 Subject: [PATCH] sh: Conditionally re-enable IRQs in fault path. 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 Signed-off-by: Paul Mundt --- arch/sh/kernel/signal_32.c | 3 +-- arch/sh/mm/fault_32.c | 12 ++++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/arch/sh/kernel/signal_32.c b/arch/sh/kernel/signal_32.c index eee29257a8..4bbbde895a 100644 --- a/arch/sh/kernel/signal_32.c +++ b/arch/sh/kernel/signal_32.c @@ -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: diff --git a/arch/sh/mm/fault_32.c b/arch/sh/mm/fault_32.c index d1fa27594c..0c776fdfbd 100644 --- a/arch/sh/mm/fault_32.c +++ b/arch/sh/mm/fault_32.c @@ -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.. -- 2.39.5