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 <stuart.menefy@st.com> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
committed by
Paul Mundt
parent
068f59143d
commit
f2fb4e4f64
@@ -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(OR_R0_R0, &frame->retcode[6]);
|
||||||
err |= __put_user((__NR_sigreturn), &frame->retcode[7]);
|
err |= __put_user((__NR_sigreturn), &frame->retcode[7]);
|
||||||
regs->pr = (unsigned long) frame->retcode;
|
regs->pr = (unsigned long) frame->retcode;
|
||||||
|
flush_icache_range(regs->pr, regs->pr + sizeof(frame->retcode));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err)
|
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",
|
pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n",
|
||||||
current->comm, task_pid_nr(current), frame, regs->pc, regs->pr);
|
current->comm, task_pid_nr(current), frame, regs->pc, regs->pr);
|
||||||
|
|
||||||
flush_icache_range(regs->pr, regs->pr + sizeof(frame->retcode));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
give_sigsegv:
|
give_sigsegv:
|
||||||
|
@@ -37,16 +37,12 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
|
|||||||
int fault;
|
int fault;
|
||||||
siginfo_t info;
|
siginfo_t info;
|
||||||
|
|
||||||
trace_hardirqs_on();
|
|
||||||
local_irq_enable();
|
|
||||||
|
|
||||||
#ifdef CONFIG_SH_KGDB
|
#ifdef CONFIG_SH_KGDB
|
||||||
if (kgdb_nofault && kgdb_bus_err_hook)
|
if (kgdb_nofault && kgdb_bus_err_hook)
|
||||||
kgdb_bus_err_hook();
|
kgdb_bus_err_hook();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
tsk = current;
|
tsk = current;
|
||||||
mm = tsk->mm;
|
|
||||||
si_code = SEGV_MAPERR;
|
si_code = SEGV_MAPERR;
|
||||||
|
|
||||||
if (unlikely(address >= TASK_SIZE)) {
|
if (unlikely(address >= TASK_SIZE)) {
|
||||||
@@ -88,6 +84,14 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
|
|||||||
return;
|
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
|
* If we're in an interrupt or have no user
|
||||||
* context, we must not take the fault..
|
* context, we must not take the fault..
|
||||||
|
Reference in New Issue
Block a user