mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-20 12:54:36 +08:00
ARM: 7473/1: deal with handlerless restarts without leaving the kernel
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
0a267fa6a1
commit
81783786d5
@ -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)
|
|||||||
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,
|
|||||||
* 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)
|
|||||||
* 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)
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user