| From 8ba0c6c8ae1e64600c8f15a42f55382b353cdea0 Mon Sep 17 00:00:00 2001 |
| From: David S. Miller <davem@davemloft.net> |
| Date: Wed, 7 May 2008 16:21:28 -0700 |
| Subject: sparc: Fix fork/clone/vfork system call restart. |
| |
| From: David S. Miller <davem@davemloft.net> |
| |
| [ Upstream commit: 1e38c126c9252b612697e34f43b1b3371c8ee31d ] |
| |
| We clobber %i1 as well as %i0 for these system calls, |
| because they give two return values. |
| |
| Therefore, on error, we have to restore %i1 properly |
| or else the restart explodes since it uses the wrong |
| arguments. |
| |
| This fixes glibc's nptl/tst-eintr1.c testcase. |
| |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| arch/sparc/kernel/process.c | 20 ++++++++++++++++---- |
| arch/sparc64/kernel/process.c | 18 +++++++++++++++--- |
| 2 files changed, 31 insertions(+), 7 deletions(-) |
| |
| --- a/arch/sparc64/kernel/process.c |
| +++ b/arch/sparc64/kernel/process.c |
| @@ -507,6 +507,8 @@ asmlinkage long sparc_do_fork(unsigned l |
| unsigned long stack_size) |
| { |
| int __user *parent_tid_ptr, *child_tid_ptr; |
| + unsigned long orig_i1 = regs->u_regs[UREG_I1]; |
| + long ret; |
| |
| #ifdef CONFIG_COMPAT |
| if (test_thread_flag(TIF_32BIT)) { |
| @@ -519,9 +521,19 @@ asmlinkage long sparc_do_fork(unsigned l |
| child_tid_ptr = (int __user *) regs->u_regs[UREG_I4]; |
| } |
| |
| - return do_fork(clone_flags, stack_start, |
| - regs, stack_size, |
| - parent_tid_ptr, child_tid_ptr); |
| + ret = do_fork(clone_flags, stack_start, |
| + regs, stack_size, |
| + parent_tid_ptr, child_tid_ptr); |
| + |
| + /* If we get an error and potentially restart the system |
| + * call, we're screwed because copy_thread() clobbered |
| + * the parent's %o1. So detect that case and restore it |
| + * here. |
| + */ |
| + if ((unsigned long)ret >= -ERESTART_RESTARTBLOCK) |
| + regs->u_regs[UREG_I1] = orig_i1; |
| + |
| + return ret; |
| } |
| |
| /* Copy a Sparc thread. The fork() return value conventions |
| --- a/arch/sparc/kernel/process.c |
| +++ b/arch/sparc/kernel/process.c |
| @@ -421,14 +421,26 @@ asmlinkage int sparc_do_fork(unsigned lo |
| unsigned long stack_size) |
| { |
| unsigned long parent_tid_ptr, child_tid_ptr; |
| + unsigned long orig_i1 = regs->u_regs[UREG_I1]; |
| + long ret; |
| |
| parent_tid_ptr = regs->u_regs[UREG_I2]; |
| child_tid_ptr = regs->u_regs[UREG_I4]; |
| |
| - return do_fork(clone_flags, stack_start, |
| - regs, stack_size, |
| - (int __user *) parent_tid_ptr, |
| - (int __user *) child_tid_ptr); |
| + ret = do_fork(clone_flags, stack_start, |
| + regs, stack_size, |
| + (int __user *) parent_tid_ptr, |
| + (int __user *) child_tid_ptr); |
| + |
| + /* If we get an error and potentially restart the system |
| + * call, we're screwed because copy_thread() clobbered |
| + * the parent's %o1. So detect that case and restore it |
| + * here. |
| + */ |
| + if ((unsigned long)ret >= -ERESTART_RESTARTBLOCK) |
| + regs->u_regs[UREG_I1] = orig_i1; |
| + |
| + return ret; |
| } |
| |
| /* Copy a Sparc thread. The fork() return value conventions |