summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--linuxthreads/ChangeLog33
-rw-r--r--linuxthreads/Makefile2
-rw-r--r--linuxthreads/cancel.c27
-rw-r--r--linuxthreads/condvar.c8
-rw-r--r--linuxthreads/internals.h4
-rw-r--r--linuxthreads/join.c11
-rw-r--r--linuxthreads/manager.c2
-rw-r--r--linuxthreads/oldsemaphore.c2
-rw-r--r--linuxthreads/pthread.c2
-rw-r--r--linuxthreads/ptlongjmp.c21
-rw-r--r--linuxthreads/semaphore.c8
-rw-r--r--linuxthreads/tst-cancel.c153
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S10
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-32/setcontext.S3
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-64/getcontext.S10
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-64/setcontext.S3
-rw-r--r--sysdeps/unix/sysv/linux/s390/swapcontext.c10
18 files changed, 281 insertions, 39 deletions
diff --git a/ChangeLog b/ChangeLog
index 3c3d8a9a54..2522b1095b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2001-04-10  Martin Schwidefsky  <schwidefsky@de.ibm.com>
+
+	* sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S: Fix return
+	value of getcontext.
+	* sysdeps/unix/sysv/linux/s390/s390-64/getcontext.S: Likewise.
+	* sysdeps/unix/sysv/linux/s390/s390-32/setcontext.S: Fix return
+	value of setcontext.
+	* sysdeps/unix/sysv/linux/s390/s390-64/setcontext.S: Likewise.
+	* sysdeps/unix/sysv/linux/s390/swapcontext.c: Skip setcontext
+	call by changing the saved context.
+
 2001-04-10  Ulrich Drepper  <drepper@redhat.com>
 
 	* sysdeps/alpha/stackinfo.h: New file.
diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog
index de4047c5ac..917ab715f1 100644
--- a/linuxthreads/ChangeLog
+++ b/linuxthreads/ChangeLog
@@ -1,3 +1,36 @@
+2001-04-10  Ulrich Drepper  <drepper@redhat.com>
+
+	* join.c (pthread_exit): Move code to new function __pthread_do_exit
+	which takes an extra parameter with the current frame pointer.
+	Call new function with CURRENT_STACK_FRAME.
+	(__pthread_do_exit): New function.  Call __pthread_perform_cleanup
+	with the new parameter.
+	(pthread_join): Call __pthread_do_exit instead of pthread_exit.
+	* cancel.c (__pthread_perform_cleanup): Takes extra parameter.  Use
+	this parameter as the initial value the cleanup handler records are
+	compared against.  No active cleanup handler record must have an
+	address lower than the previous one and the initial record must be
+	above (below on PA) the frame address passed in.
+	(pthread_setcancelstate): Call __pthread_do_exit instead of
+	pthread_exit.
+	(pthread_setcanceltype): Likewise.
+	(pthread_testcancel): Likewise.
+	(_pthread_cleanup_pop_restore): Likewise.
+	* condvar.c (pthread_cond_wait): Likewise.
+	(pthread_cond_timedwait_relative): Likewise.
+	* manager.c (pthread_start_thread): Likewise.
+	* oldsemaphore.c (__old_sem_wait): Likewise.
+	* pthread.c (pthread_handle_sigcancel): Likewise.
+	* semaphore.c (__new_sem_wait): Likewise.
+	(sem_timedwait): Likewise.
+	* ptlongjmp.c (pthread_cleanup_upto): Also use current stack frame
+	to limit the cleanup handlers which get run.
+	* internals.h: Add prototype for __pthread_do_exit.  Adjust prototype
+	for __pthread_perform_cleanup.
+
+	* Makefile (tests): Add tst-cancel.
+	* tst-cancel.c: New file.
+
 2001-04-08  Hans-Peter Nilsson  <hp@axis.com>
 
 	* sysdeps/cris/pt-machine.h: New file.
diff --git a/linuxthreads/Makefile b/linuxthreads/Makefile
index 8ecbd22d56..b719ff8f21 100644
--- a/linuxthreads/Makefile
+++ b/linuxthreads/Makefile
@@ -56,7 +56,7 @@ endif
 librt-tests = ex10 ex11
 tests = ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 $(librt-tests) ex12 ex13 joinrace \
 	tststack $(tests-nodelete-$(have-z-nodelete)) ecmutex ex14 ex15 ex16 \
-	ex17
+	ex17 tst-cancel
 
 ifeq (yes,$(build-shared))
 tests-nodelete-yes = unload
diff --git a/linuxthreads/cancel.c b/linuxthreads/cancel.c
index 0ae0d12887..ed67a6845f 100644
--- a/linuxthreads/cancel.c
+++ b/linuxthreads/cancel.c
@@ -20,6 +20,9 @@
 #include "internals.h"
 #include "spinlock.h"
 #include "restart.h"
+#include <stackinfo.h>
+
+#include <stdio.h>
 
 int pthread_setcancelstate(int state, int * oldstate)
 {
@@ -31,7 +34,7 @@ int pthread_setcancelstate(int state, int * oldstate)
   if (THREAD_GETMEM(self, p_canceled) &&
       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
       THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
-    pthread_exit(PTHREAD_CANCELED);
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
   return 0;
 }
 
@@ -45,7 +48,7 @@ int pthread_setcanceltype(int type, int * oldtype)
   if (THREAD_GETMEM(self, p_canceled) &&
       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
       THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
-    pthread_exit(PTHREAD_CANCELED);
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
   return 0;
 }
 
@@ -112,7 +115,7 @@ void pthread_testcancel(void)
   pthread_descr self = thread_self();
   if (THREAD_GETMEM(self, p_canceled)
       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)
-    pthread_exit(PTHREAD_CANCELED);
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
 }
 
 void _pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer,
@@ -155,15 +158,27 @@ void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer,
   if (THREAD_GETMEM(self, p_canceled) &&
       THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
       THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
-    pthread_exit(PTHREAD_CANCELED);
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
 }
 
-void __pthread_perform_cleanup(void)
+void __pthread_perform_cleanup(char *currentframe)
 {
   pthread_descr self = thread_self();
   struct _pthread_cleanup_buffer * c;
+
   for (c = THREAD_GETMEM(self, p_cleanup); c != NULL; c = c->__prev)
-    c->__routine(c->__arg);
+    {
+#if _STACK_GROWS_DOWN
+      if ((char *) c <= currentframe)
+	break;
+#elif _STACK_GROWS_UP
+      if ((char *) c >= currentframe)
+	break;
+#else
+# error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
+#endif
+      c->__routine(c->__arg);
+    }
 
   /* And the TSD which needs special help.  */
   if (THREAD_GETMEM(self, p_libc_specific[_LIBC_TSD_KEY_RPC_VARS]) != NULL)
diff --git a/linuxthreads/condvar.c b/linuxthreads/condvar.c
index f9c46a3316..fd0db50fa2 100644
--- a/linuxthreads/condvar.c
+++ b/linuxthreads/condvar.c
@@ -93,7 +93,7 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
 
   if (already_canceled) {
     __pthread_set_own_extricate_if(self, 0);
-    pthread_exit(PTHREAD_CANCELED);
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
   }
 
   pthread_mutex_unlock(mutex);
@@ -122,7 +122,7 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
     THREAD_SETMEM(self, p_woken_by_cancel, 0);
     pthread_mutex_lock(mutex);
-    pthread_exit(PTHREAD_CANCELED);
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
   }
 
   /* Put back any resumes we caught that don't belong to us. */
@@ -168,7 +168,7 @@ pthread_cond_timedwait_relative(pthread_cond_t *cond,
 
   if (already_canceled) {
     __pthread_set_own_extricate_if(self, 0);
-    pthread_exit(PTHREAD_CANCELED);
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
   }
 
   pthread_mutex_unlock(mutex);
@@ -216,7 +216,7 @@ pthread_cond_timedwait_relative(pthread_cond_t *cond,
       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
     THREAD_SETMEM(self, p_woken_by_cancel, 0);
     pthread_mutex_lock(mutex);
-    pthread_exit(PTHREAD_CANCELED);
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
   }
 
   /* Put back any resumes we caught that don't belong to us. */
diff --git a/linuxthreads/internals.h b/linuxthreads/internals.h
index ba0c14d29d..998f7788e8 100644
--- a/linuxthreads/internals.h
+++ b/linuxthreads/internals.h
@@ -428,8 +428,10 @@ static inline pthread_descr thread_self (void)
 
 /* Internal global functions */
 
+extern void __pthread_do_exit (void *retval, char *currentframe)
+     __attribute__ ((__noreturn__));
 extern void __pthread_destroy_specifics (void);
-extern void __pthread_perform_cleanup (void);
+extern void __pthread_perform_cleanup (char *currentframe);
 extern void __pthread_init_max_stacksize (void);
 extern int __pthread_initialize_manager (void);
 extern void __pthread_message (char * fmt, ...);
diff --git a/linuxthreads/join.c b/linuxthreads/join.c
index bbcebe12ae..a0cdb41cd3 100644
--- a/linuxthreads/join.c
+++ b/linuxthreads/join.c
@@ -25,6 +25,11 @@
 
 void pthread_exit(void * retval)
 {
+  __pthread_do_exit (retval, CURRENT_STACK_FRAME);
+}
+
+void __pthread_do_exit(void *retval, char *currentframe)
+{
   pthread_descr self = thread_self();
   pthread_descr joining;
   struct pthread_request request;
@@ -33,7 +38,7 @@ void pthread_exit(void * retval)
      contain cancellation points */
   THREAD_SETMEM(self, p_canceled, 0);
   /* Call cleanup functions and destroy the thread-specific data */
-  __pthread_perform_cleanup();
+  __pthread_perform_cleanup(currentframe);
   __pthread_destroy_specifics();
   /* Store return value */
   __pthread_lock(THREAD_GETMEM(self, p_lock), self);
@@ -144,7 +149,7 @@ int pthread_join(pthread_t thread_id, void ** thread_return)
 
     if (already_canceled) {
       __pthread_set_own_extricate_if(self, 0);
-      pthread_exit(PTHREAD_CANCELED);
+      __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
     }
 
     suspend(self);
@@ -155,7 +160,7 @@ int pthread_join(pthread_t thread_id, void ** thread_return)
     if (THREAD_GETMEM(self, p_woken_by_cancel)
 	&& THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
       THREAD_SETMEM(self, p_woken_by_cancel, 0);
-      pthread_exit(PTHREAD_CANCELED);
+      __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
     }
     __pthread_lock(&handle->h_lock, self);
   }
diff --git a/linuxthreads/manager.c b/linuxthreads/manager.c
index 567ba658d1..431e149212 100644
--- a/linuxthreads/manager.c
+++ b/linuxthreads/manager.c
@@ -262,7 +262,7 @@ static int pthread_start_thread(void *arg)
   outcome = self->p_start_args.start_routine(THREAD_GETMEM(self,
 							   p_start_args.arg));
   /* Exit with the given return value */
-  pthread_exit(outcome);
+  __pthread_do_exit(outcome, CURRENT_STACK_FRAME);
   return 0;
 }
 
diff --git a/linuxthreads/oldsemaphore.c b/linuxthreads/oldsemaphore.c
index da5272cd24..2099b8bbda 100644
--- a/linuxthreads/oldsemaphore.c
+++ b/linuxthreads/oldsemaphore.c
@@ -144,7 +144,7 @@ int __old_sem_wait(old_sem_t * sem)
 		    }
 		}
 	    }
-            pthread_exit(PTHREAD_CANCELED);
+            __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
 	}
     }
 }
diff --git a/linuxthreads/pthread.c b/linuxthreads/pthread.c
index 479531b5a4..ff9c083010 100644
--- a/linuxthreads/pthread.c
+++ b/linuxthreads/pthread.c
@@ -817,7 +817,7 @@ static void pthread_handle_sigcancel(int sig)
   if (__builtin_expect (THREAD_GETMEM(self, p_canceled), 0)
       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
     if (THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
-      pthread_exit(PTHREAD_CANCELED);
+      __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
     jmpbuf = THREAD_GETMEM(self, p_cancel_jmp);
     if (jmpbuf != NULL) {
       THREAD_SETMEM(self, p_cancel_jmp, NULL);
diff --git a/linuxthreads/ptlongjmp.c b/linuxthreads/ptlongjmp.c
index 1c12508101..68b9235cb8 100644
--- a/linuxthreads/ptlongjmp.c
+++ b/linuxthreads/ptlongjmp.c
@@ -18,6 +18,7 @@
 #include <setjmp.h>
 #include "pthread.h"
 #include "internals.h"
+#include <stackinfo.h>
 
 /* These functions are not declared anywhere since they shouldn't be
    used at another place but here.  */
@@ -31,11 +32,29 @@ static void pthread_cleanup_upto(__jmp_buf target)
 {
   pthread_descr self = thread_self();
   struct _pthread_cleanup_buffer * c;
+  char *currentframe = CURRENT_STACK_FRAME;
 
   for (c = THREAD_GETMEM(self, p_cleanup);
        c != NULL && _JMPBUF_UNWINDS(target, c);
        c = c->__prev)
-    c->__routine(c->__arg);
+    {
+#if _STACK_GROWS_DOWN
+      if ((char *) c <= currentframe)
+	{
+	  c = NULL;
+	  break;
+	}
+#elif _STACK_GROWS_UP
+      if ((char *) c >= currentframe)
+	{
+	  c = NULL;
+	  break;
+	}
+#else
+# error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
+#endif
+      c->__routine(c->__arg);
+    }
   THREAD_SETMEM(self, p_cleanup, c);
   if (THREAD_GETMEM(self, p_in_sighandler)
       && _JMPBUF_UNWINDS(target, THREAD_GETMEM(self, p_in_sighandler)))
diff --git a/linuxthreads/semaphore.c b/linuxthreads/semaphore.c
index e5afce43eb..bb681b3621 100644
--- a/linuxthreads/semaphore.c
+++ b/linuxthreads/semaphore.c
@@ -85,7 +85,7 @@ int __new_sem_wait(sem_t * sem)
 
   if (already_canceled) {
     __pthread_set_own_extricate_if(self, 0);
-    pthread_exit(PTHREAD_CANCELED);
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
   }
 
   /* Wait for sem_post or cancellation, or fall through if already canceled */
@@ -111,7 +111,7 @@ int __new_sem_wait(sem_t * sem)
   if (THREAD_GETMEM(self, p_woken_by_cancel)
       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
     THREAD_SETMEM(self, p_woken_by_cancel, 0);
-    pthread_exit(PTHREAD_CANCELED);
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
   }
   /* We got the semaphore */
   return 0;
@@ -245,7 +245,7 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime)
 
   if (already_canceled) {
     __pthread_set_own_extricate_if(self, 0);
-    pthread_exit(PTHREAD_CANCELED);
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
   }
 
   spurious_wakeup_count = 0;
@@ -289,7 +289,7 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime)
   if (THREAD_GETMEM(self, p_woken_by_cancel)
       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
     THREAD_SETMEM(self, p_woken_by_cancel, 0);
-    pthread_exit(PTHREAD_CANCELED);
+    __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
   }
   /* We got the semaphore */
   return 0;
diff --git a/linuxthreads/tst-cancel.c b/linuxthreads/tst-cancel.c
new file mode 100644
index 0000000000..da70d1278f
--- /dev/null
+++ b/linuxthreads/tst-cancel.c
@@ -0,0 +1,153 @@
+/* Tests for cancelation handling.  */
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+int fd;
+
+pthread_barrier_t bar;
+
+
+static void
+cleanup (void *arg)
+{
+  int nr = (int) (long int) arg;
+  char s[30];
+  char *cp = stpcpy (s, "cleanup ");
+  *cp++ = '0' + nr;
+  *cp++ = '\n';
+  __libc_write (fd, s, cp - s);
+}
+
+
+static void *
+t1 (void *arg)
+{
+  pthread_cleanup_push (cleanup, (void *) (long int) 1);
+  return NULL;
+  pthread_cleanup_pop (0);
+}
+
+
+static void
+inner (int a)
+{
+  pthread_cleanup_push (cleanup, (void *) (long int) a);
+  if (a)
+    return;
+  pthread_cleanup_pop (0);
+}
+
+
+static void *
+t2 (void *arg)
+{
+  pthread_cleanup_push (cleanup, (void *) (long int) 2);
+  inner ((int) (long int) arg);
+  return NULL;
+  pthread_cleanup_pop (0);
+}
+
+
+static void *
+t3 (void *arg)
+{
+  pthread_cleanup_push (cleanup, (void *) (long int) 4);
+  inner ((int) (long int) arg);
+  pthread_exit (NULL);
+  pthread_cleanup_pop (0);
+}
+
+
+int
+main (void)
+{
+  pthread_t td;
+  int err;
+  char tmp[] = "thtstXXXXXX";
+  struct stat64 st;
+  int result = 0;
+
+  fd = mkstemp (tmp);
+  if (fd == -1)
+    {
+      printf ("cannot create temporary file: %m");
+      exit (1);
+    }
+  unlink (tmp);
+
+  err = pthread_barrier_init (&bar, NULL, 2);
+  if (err != 0 )
+    {
+      printf ("cannot create barrier: %s\n", strerror (err));
+      exit (1);
+    }
+
+  err = pthread_create (&td, NULL, t1, NULL);
+  if (err != 0)
+    {
+      printf ("cannot create thread t1: %s\n", strerror (err));
+      exit (1);
+    }
+
+  err = pthread_join (td, NULL);
+  if (err != 0)
+    {
+      printf ("cannot join thread: %s\n", strerror (err));
+      exit (1);
+    }
+
+  err = pthread_create (&td, NULL, t2, (void *) 3);
+  if (err != 0)
+    {
+      printf ("cannot create thread t2: %s\n", strerror (err));
+      exit (1);
+    }
+
+  err = pthread_join (td, NULL);
+  if (err != 0)
+    {
+      printf ("cannot join thread: %s\n", strerror (err));
+      exit (1);
+    }
+
+  err = pthread_create (&td, NULL, t3, (void *) 5);
+  if (err != 0)
+    {
+      printf ("cannot create thread t3: %s\n", strerror (err));
+      exit (1);
+    }
+
+  err = pthread_join (td, NULL);
+  if (err != 0)
+    {
+      printf ("cannot join thread: %s\n", strerror (err));
+      exit (1);
+    }
+
+  if (fstat64 (fd, &st) < 0)
+    {
+      printf ("cannot stat temporary file: %m\n");
+      result = 1;
+    }
+  else if (st.st_size != 0)
+    {
+      char buf[512];
+      puts ("some cleanup handlers ran:");
+      fflush (stdout);
+      while (1)
+	{
+	  ssize_t n = read (fd, buf, sizeof buf);
+	  if (n < 0)
+	    break;
+	  write (STDOUT_FILENO, buf, n);
+	}
+      result = 1;
+    }
+
+  return result;
+}
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S b/sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S
index e64cc8588c..b3cae1c7fe 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S
@@ -25,9 +25,7 @@
 /*  __getcontext (const ucontext_t *ucp)
 
   Saves the machine context in UCP such that when it is activated,
-  it appears as if __getcontext() returned again.  The only difference
-  is that on a first return, %r2 contains 1 and on a subsequent
-  return, it contains 0.
+  it appears as if __getcontext() returned again. 
 
   This implementation is intended to be used for *synchronous* context
   switches only.  Therefore, it does not have to save anything
@@ -61,11 +59,13 @@ ENTRY(__getcontext)
 	std     %f14,SC_FPRS+112(%r5)
 	std     %f15,SC_FPRS+120(%r5)
 
+	/* Set __getcontext return value to 0.  */
+	slr     %r2,%r2
+
 	/* Store general purpose registers.  */
 	stm     %r0,%r15,SC_GPRS(%r5)
 
-	/* Return 0.  */
-	slr	%r2,%r2
+	/* Return.  */
 	br	%r14
 END(__getcontext)
 
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/setcontext.S b/sysdeps/unix/sysv/linux/s390/s390-32/setcontext.S
index 40adc8574a..2bed31847a 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/setcontext.S
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/setcontext.S
@@ -62,8 +62,7 @@ ENTRY(__setcontext)
 	/* Load general purpose registers.  */
 	lm	%r0,%r15,SC_GPRS(%r5)
 
-        /* Return 1.  */
-	la      %r2,1
+	/* Return.  */
 	br	%r14
 END(__setcontext)
 
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/getcontext.S b/sysdeps/unix/sysv/linux/s390/s390-64/getcontext.S
index 2fc21e2b60..9eba838edf 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/getcontext.S
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/getcontext.S
@@ -25,9 +25,7 @@
 /*  __getcontext (const ucontext_t *ucp)
 
   Saves the machine context in UCP such that when it is activated,
-  it appears as if __getcontext() returned again.  The only difference
-  is that on a first return, %r2 contains 1 and on a subsequent
-  return, it contains 0.
+  it appears as if __getcontext() returned again.
 
   This implementation is intended to be used for *synchronous* context
   switches only.  Therefore, it does not have to save anything
@@ -61,11 +59,13 @@ ENTRY(__getcontext)
 	std     %f14,SC_FPRS+112(%r5)
 	std     %f15,SC_FPRS+120(%r5)
 
+	/* Set __getcontext return value to 0.  */
+	slr     %r2,%r2
+
 	/* Store general purpose registers.  */
 	stmg    %r0,%r15,SC_GPRS(%r5)
 
-	/* Return 0.  */
-	slgr	%r2,%r2
+	/* Return.  */
 	br	%r14
 END(__getcontext)
 
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/setcontext.S b/sysdeps/unix/sysv/linux/s390/s390-64/setcontext.S
index b3a9fea405..a652ad3173 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/setcontext.S
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/setcontext.S
@@ -62,8 +62,7 @@ ENTRY(__setcontext)
 	/* Load general purpose registers.  */
 	lmg	%r0,%r15,SC_GPRS(%r5)
 
-        /* Return 1.  */
-	la      %r2,1
+	/* Return.  */
 	br	%r14
 END(__setcontext)
 
diff --git a/sysdeps/unix/sysv/linux/s390/swapcontext.c b/sysdeps/unix/sysv/linux/s390/swapcontext.c
index 9f1be058db..2b34b63ce4 100644
--- a/sysdeps/unix/sysv/linux/s390/swapcontext.c
+++ b/sysdeps/unix/sysv/linux/s390/swapcontext.c
@@ -25,8 +25,14 @@ extern int __setcontext (__const ucontext_t *__ucp) __THROW;
 int
 __swapcontext (ucontext_t *oucp, const ucontext_t *ucp)
 {
-  if (__getcontext (oucp) == 0)
-    __setcontext (ucp);
+  /* Save the current machine context to oucp.  */
+  __getcontext (oucp);
+  /* Modify oucp to skip the __setcontext call on reactivation.  */
+  oucp->uc_mcontext.gregs[14] = &&fake_return;
+  /* Restore the machine context in ucp.  */
+  __setcontext (ucp);
+
+fake_return:
   return 0;
 }