Commit a19b3e7a authored by Russell King's avatar Russell King

[ARM] Add restart block infrastructure

Add the necessary infrastructure to handle restartable syscalls via
the restart_block method.  This isn't perfect, since we are unable
to modify the syscall number with ARM executables (it's typically
encoded into the instruction in the text segment) so instead we
build some code on the user space stack.

There is a potential problem - what happens if we are sleeping,
someone suspends us, resume (we continue to sleep via the restart
block method) suspend and then resume again?  We will build two
sets of restart code on the user stack.  This is probably a little
inefficient, but it should be harmless.
parent 8284034a
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
#else #else
__syscall_start: __syscall_start:
/* 0 */ .long sys_ni_syscall /* 0 */ .long sys_restart_syscall
.long sys_exit .long sys_exit
.long sys_fork_wrapper .long sys_fork_wrapper
.long sys_read .long sys_read
......
...@@ -603,7 +603,7 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) ...@@ -603,7 +603,7 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
case SIGQUIT: case SIGILL: case SIGTRAP: case SIGQUIT: case SIGILL: case SIGTRAP:
case SIGABRT: case SIGFPE: case SIGSEGV: case SIGABRT: case SIGFPE: case SIGSEGV:
case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ:
if (do_coredump(signr, regs)) if (do_coredump(signr, exit_code, regs))
exit_code |= 0x80; exit_code |= 0x80;
/* FALLTHRU */ /* FALLTHRU */
...@@ -615,7 +615,11 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) ...@@ -615,7 +615,11 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
/* Are we from a system call? */ /* Are we from a system call? */
if (syscall) { if (syscall) {
/* If so, check system call restarting.. */
switch (regs->ARM_r0) { switch (regs->ARM_r0) {
case -ERESTART_RESTARTBLOCK:
current_thread_info()->restart_block.fn =
do_no_restart_syscall;
case -ERESTARTNOHAND: case -ERESTARTNOHAND:
regs->ARM_r0 = -EINTR; regs->ARM_r0 = -EINTR;
break; break;
...@@ -638,13 +642,36 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) ...@@ -638,13 +642,36 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
return 1; return 1;
} }
if (syscall && /*
(regs->ARM_r0 == -ERESTARTNOHAND || * No signal to deliver to the process - restart the syscall.
*/
if (syscall) {
if (regs->ARM_r0 == -ERESTART_RESTARTBLOCK) {
if (thumb_mode(regs)) {
regs->ARM_r7 = __NR_restart_syscall;
regs->ARM_pc -= 2;
} else {
u32 *usp;
regs->ARM_sp -= 12;
usp = (u32 *)regs->ARM_sp;
put_user(regs->ARM_pc, &usp[0]);
/* swi __NR_restart_syscall */
put_user(0xef000000 | __NR_restart_syscall, &usp[1]);
/* ldr pc, [sp], #12 */
put_user(0xe49df00c, &usp[2]);
regs->ARM_pc = regs->ARM_sp + 4;
}
}
if (regs->ARM_r0 == -ERESTARTNOHAND ||
regs->ARM_r0 == -ERESTARTSYS || regs->ARM_r0 == -ERESTARTSYS ||
regs->ARM_r0 == -ERESTARTNOINTR)) { regs->ARM_r0 == -ERESTARTNOINTR) {
regs->ARM_r0 = regs->ARM_ORIG_r0; regs->ARM_r0 = regs->ARM_ORIG_r0;
regs->ARM_pc -= 4; regs->ARM_pc -= 4;
} }
}
if (single_stepping) if (single_stepping)
ptrace_set_bpt(current); ptrace_set_bpt(current);
return 0; return 0;
......
#ifndef _ASMARM_CURRENT_H #ifndef _ASMARM_CURRENT_H
#define _ASMARM_CURRENT_H #define _ASMARM_CURRENT_H
#include <asm/thread_info.h> #include <linux/thread_info.h>
static inline struct task_struct *get_current(void) __attribute__ (( __const__ )); static inline struct task_struct *get_current(void) __attribute__ (( __const__ ));
......
...@@ -51,16 +51,20 @@ struct thread_info { ...@@ -51,16 +51,20 @@ struct thread_info {
__u32 cpu; /* cpu */ __u32 cpu; /* cpu */
__u32 cpu_domain; /* cpu domain */ __u32 cpu_domain; /* cpu domain */
struct cpu_context_save cpu_context; /* cpu context */ struct cpu_context_save cpu_context; /* cpu context */
struct restart_block restart_block;
union fp_state fpstate; union fp_state fpstate;
}; };
#define INIT_THREAD_INFO(tsk) \ #define INIT_THREAD_INFO(tsk) \
{ \ { \
task: &tsk, \ .task = &tsk, \
exec_domain: &default_exec_domain, \ .exec_domain = &default_exec_domain, \
flags: 0, \ .flags = 0, \
preempt_count: 0, \ .preempt_count = 1, \
addr_limit: KERNEL_DS, \ .addr_limit = KERNEL_DS, \
.restart_block = { \
.fn = do_no_restart_syscall, \
}, \
INIT_EXTRA_THREAD_INFO, \ INIT_EXTRA_THREAD_INFO, \
} }
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
* This file contains the system call numbers. * This file contains the system call numbers.
*/ */
#define __NR_restart_syscall (__NR_SYSCALL_BASE+ 0)
#define __NR_exit (__NR_SYSCALL_BASE+ 1) #define __NR_exit (__NR_SYSCALL_BASE+ 1)
#define __NR_fork (__NR_SYSCALL_BASE+ 2) #define __NR_fork (__NR_SYSCALL_BASE+ 2)
#define __NR_read (__NR_SYSCALL_BASE+ 3) #define __NR_read (__NR_SYSCALL_BASE+ 3)
......
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