Commit 81783786 authored by Al Viro's avatar Al Viro Committed by Russell King

ARM: 7473/1: deal with handlerless restarts without leaving the kernel

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 0a267fa6
...@@ -54,7 +54,11 @@ work_pending: ...@@ -54,7 +54,11 @@ work_pending:
mov r0, sp @ 'regs' mov r0, sp @ 'regs'
mov r2, why @ 'syscall' mov r2, why @ 'syscall'
bl do_work_pending bl do_work_pending
b no_work_pending tst r0, #1
beq no_work_pending
ldmia sp, {r0 - r6} @ have to reload r0 - r6
b local_restart @ ... and off we go
/* /*
* "slow" syscall return path. "why" tells us if this was a real syscall. * "slow" syscall return path. "why" tells us if this was a real syscall.
*/ */
...@@ -396,6 +400,7 @@ ENTRY(vector_swi) ...@@ -396,6 +400,7 @@ ENTRY(vector_swi)
eor scno, scno, #__NR_SYSCALL_BASE @ check OS number eor scno, scno, #__NR_SYSCALL_BASE @ check OS number
#endif #endif
local_restart:
ldr r10, [tsk, #TI_FLAGS] @ check for syscall tracing ldr r10, [tsk, #TI_FLAGS] @ check for syscall tracing
stmdb sp!, {r4, r5} @ push fifth and sixth args stmdb sp!, {r4, r5} @ push fifth and sixth args
......
...@@ -569,12 +569,13 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, ...@@ -569,12 +569,13 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
* the kernel can handle, and then we build all the user-level signal handling * the kernel can handle, and then we build all the user-level signal handling
* stack-frames in one go after that. * stack-frames in one go after that.
*/ */
static void do_signal(struct pt_regs *regs, int syscall) static int do_signal(struct pt_regs *regs, int syscall)
{ {
unsigned int retval = 0, continue_addr = 0, restart_addr = 0; unsigned int retval = 0, continue_addr = 0, restart_addr = 0;
struct k_sigaction ka; struct k_sigaction ka;
siginfo_t info; siginfo_t info;
int signr; int signr;
int restart = 0;
/* /*
* If we were from a system call, check for system call restarting... * If we were from a system call, check for system call restarting...
...@@ -589,10 +590,12 @@ static void do_signal(struct pt_regs *regs, int syscall) ...@@ -589,10 +590,12 @@ static void do_signal(struct pt_regs *regs, int syscall)
* debugger will see the already changed PSW. * debugger will see the already changed PSW.
*/ */
switch (retval) { switch (retval) {
case -ERESTART_RESTARTBLOCK:
restart++;
case -ERESTARTNOHAND: case -ERESTARTNOHAND:
case -ERESTARTSYS: case -ERESTARTSYS:
case -ERESTARTNOINTR: case -ERESTARTNOINTR:
case -ERESTART_RESTARTBLOCK: restart++;
regs->ARM_r0 = regs->ARM_ORIG_r0; regs->ARM_r0 = regs->ARM_ORIG_r0;
regs->ARM_pc = restart_addr; regs->ARM_pc = restart_addr;
break; break;
...@@ -604,13 +607,15 @@ static void do_signal(struct pt_regs *regs, int syscall) ...@@ -604,13 +607,15 @@ static void do_signal(struct pt_regs *regs, int syscall)
* point the debugger may change all our registers ... * point the debugger may change all our registers ...
*/ */
signr = get_signal_to_deliver(&info, &ka, regs, NULL); signr = get_signal_to_deliver(&info, &ka, regs, NULL);
/*
* Depending on the signal settings we may need to revert the
* decision to restart the system call. But skip this if a
* debugger has chosen to restart at a different PC.
*/
if (regs->ARM_pc != restart_addr)
restart = 0;
if (signr > 0) { if (signr > 0) {
/* if (unlikely(restart)) {
* Depending on the signal settings we may need to revert the
* decision to restart the system call. But skip this if a
* debugger has chosen to restart at a different PC.
*/
if (regs->ARM_pc == restart_addr) {
if (retval == -ERESTARTNOHAND || if (retval == -ERESTARTNOHAND ||
retval == -ERESTART_RESTARTBLOCK retval == -ERESTART_RESTARTBLOCK
|| (retval == -ERESTARTSYS || (retval == -ERESTARTSYS
...@@ -618,28 +623,23 @@ static void do_signal(struct pt_regs *regs, int syscall) ...@@ -618,28 +623,23 @@ static void do_signal(struct pt_regs *regs, int syscall)
regs->ARM_r0 = -EINTR; regs->ARM_r0 = -EINTR;
regs->ARM_pc = continue_addr; regs->ARM_pc = continue_addr;
} }
clear_thread_flag(TIF_SYSCALL_RESTARTSYS);
} }
handle_signal(signr, &ka, &info, regs); handle_signal(signr, &ka, &info, regs);
return; return 0;
} }
if (syscall) { if (unlikely(restart)) {
/* if (restart > 1)
* Handle restarting a different system call. As above,
* if a debugger has chosen to restart at a different PC,
* ignore the restart.
*/
if (retval == -ERESTART_RESTARTBLOCK
&& regs->ARM_pc == restart_addr)
set_thread_flag(TIF_SYSCALL_RESTARTSYS); set_thread_flag(TIF_SYSCALL_RESTARTSYS);
regs->ARM_pc = continue_addr;
} }
restore_saved_sigmask(); restore_saved_sigmask();
return restart;
} }
asmlinkage void asmlinkage int
do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
{ {
do { do {
...@@ -647,10 +647,17 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) ...@@ -647,10 +647,17 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
schedule(); schedule();
} else { } else {
if (unlikely(!user_mode(regs))) if (unlikely(!user_mode(regs)))
return; return 0;
local_irq_enable(); local_irq_enable();
if (thread_flags & _TIF_SIGPENDING) { if (thread_flags & _TIF_SIGPENDING) {
do_signal(regs, syscall); if (unlikely(do_signal(regs, syscall))) {
/*
* Restart without handlers.
* Deal with it without leaving
* the kernel space.
*/
return 1;
}
syscall = 0; syscall = 0;
} else { } else {
clear_thread_flag(TIF_NOTIFY_RESUME); clear_thread_flag(TIF_NOTIFY_RESUME);
...@@ -660,4 +667,5 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) ...@@ -660,4 +667,5 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
local_irq_disable(); local_irq_disable();
thread_flags = current_thread_info()->flags; thread_flags = current_thread_info()->flags;
} while (thread_flags & _TIF_WORK_MASK); } while (thread_flags & _TIF_WORK_MASK);
return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment