]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
sparc: Fix fork/clone/vfork system call restart.
authorDavid S. Miller <davem@davemloft.net>
Wed, 7 May 2008 23:21:28 +0000 (16:21 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 15 May 2008 14:50:01 +0000 (07:50 -0700)
[ 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
arch/sparc64/kernel/process.c

index 70c0dd22491d2a5e4d3e4aaf1ebdc72ec7ca855f..709de5ec373b09470df8f31f7a7b59ab1cfb0907 100644 (file)
@@ -421,14 +421,26 @@ asmlinkage int sparc_do_fork(unsigned long clone_flags,
                              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
index acf8c5250aa9822f633e7367b662ea6f883a4059..334e64cc6c2baed3fbe6d753cbc6f8e89214d343 100644 (file)
@@ -507,6 +507,8 @@ asmlinkage long sparc_do_fork(unsigned long clone_flags,
                              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 long clone_flags,
                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