about summary refs log tree commit diff
path: root/nptl
diff options
context:
space:
mode:
Diffstat (limited to 'nptl')
-rw-r--r--nptl/ChangeLog18
-rw-r--r--nptl/Makefile1
-rw-r--r--nptl/Versions1
-rw-r--r--nptl/pthread_attr_destroy.c14
-rw-r--r--nptl/pthread_create.c2
-rw-r--r--nptl/sysdeps/pthread/createthread.c196
-rw-r--r--nptl/sysdeps/pthread/pthread.h13
-rw-r--r--nptl/sysdeps/unix/sysv/linux/internaltypes.h2
-rw-r--r--nptl/sysdeps/unix/sysv/linux/pthread_attr_getaffinity.c41
-rw-r--r--nptl/sysdeps/unix/sysv/linux/pthread_attr_setaffinity.c47
-rw-r--r--nptl/tst-cancel-wrappers.sh2
11 files changed, 263 insertions, 74 deletions
diff --git a/nptl/ChangeLog b/nptl/ChangeLog
index 804968a8c5..c2e50552d8 100644
--- a/nptl/ChangeLog
+++ b/nptl/ChangeLog
@@ -1,3 +1,21 @@
+2003-07-20  Ulrich Drepper  <drepper@redhat.com>
+
+	* Makefile (libpthread-routines): Add pthread_attr_getaffinity and
+	pthread_attr_setaffinity.
+	* Versions [libpthread] (GLIBC_2.3.3): Likewise.
+	* sysdeps/unix/sysv/linux/pthread_attr_getaffinity.c: New file.
+	* sysdeps/unix/sysv/linux/pthread_attr_setaffinity.c: New file.
+	* pthread_attr_destroy.c: Free cpuset element if allocated.
+	* pthread_create.c: Pass iattr as additional parameter to
+	create_thread.
+	* sysdeps/pthread/createthread.c: If attribute is provided and
+	a new thread is created with affinity set or scheduling parameters,
+	start thread with CLONE_STOPPED.
+	* sysdeps/pthread/pthread.h: Declare pthread_attr_getaffinity and
+	pthread_attr_setaffinity.
+	* sysdeps/unix/sysv/linux/internaltypes.h (struct pthread_attr): Add
+	cpuset element.
+
 2003-07-15  Ulrich Drepper  <drepper@redhat.com>
 
 	* tst-tcancel-wrappers.sh: lseek and llseek are not cancelation points.
diff --git a/nptl/Makefile b/nptl/Makefile
index b562e6618c..4c7749eb48 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -117,6 +117,7 @@ libpthread-routines = init events version \
 		      herrno res pt-allocrtsig \
 		      pthread_kill_other_threads \
 		      pthread_getaffinity pthread_setaffinity \
+		      pthread_attr_getaffinity pthread_attr_setaffinity \
 		      cleanup_routine
 
 libpthread-shared-only-routines = version pt-allocrtsig
diff --git a/nptl/Versions b/nptl/Versions
index 0f98663d72..8eb863d0f4 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -220,6 +220,7 @@ libpthread {
 
     # New affinity interfaces.
     pthread_getaffinity_np; pthread_setaffinity_np;
+    pthread_attr_getaffinity_np; pthread_attr_setaffinity_np;
   }
 
   GLIBC_PRIVATE {
diff --git a/nptl/pthread_attr_destroy.c b/nptl/pthread_attr_destroy.c
index fec04163b1..a04f5fe441 100644
--- a/nptl/pthread_attr_destroy.c
+++ b/nptl/pthread_attr_destroy.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -28,16 +28,17 @@ int
 __pthread_attr_destroy (attr)
      pthread_attr_t *attr;
 {
+  struct pthread_attr *iattr;
+
+  assert (sizeof (*attr) >= sizeof (struct pthread_attr));
+  iattr = (struct pthread_attr *) attr;
+
   /* Enqueue the attributes to the list of all known variables.  */
   if (DEBUGGING_P)
     {
-      struct pthread_attr *iattr;
       struct pthread_attr *prevp = NULL;
       struct pthread_attr *runp;
 
-      assert (sizeof (*attr) >= sizeof (struct pthread_attr));
-      iattr = (struct pthread_attr *) attr;
-
       lll_lock (__attr_list_lock);
 
       runp = __attr_list;
@@ -62,6 +63,9 @@ __pthread_attr_destroy (attr)
 	return EINVAL;
     }
 
+  /* The affinity CPU set might be allocated dynamically.  */
+  free (iattr->cpuset);
+
   return 0;
 }
 strong_alias (__pthread_attr_destroy, pthread_attr_destroy)
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
index 22024d5c8f..7565826997 100644
--- a/nptl/pthread_create.c
+++ b/nptl/pthread_create.c
@@ -431,7 +431,7 @@ __pthread_create_2_1 (newthread, attr, start_routine, arg)
   *newthread = (pthread_t) pd;
 
   /* Start the thread.  */
-  err = create_thread (pd, STACK_VARIABLES_ARGS);
+  err = create_thread (pd, iattr, STACK_VARIABLES_ARGS);
   if (err != 0)
     {
       /* Something went wrong.  Free the resources.  */
diff --git a/nptl/sysdeps/pthread/createthread.c b/nptl/sysdeps/pthread/createthread.c
index 7563a2b71c..4a02d1c14b 100644
--- a/nptl/sysdeps/pthread/createthread.c
+++ b/nptl/sysdeps/pthread/createthread.c
@@ -27,6 +27,10 @@
 
 
 #define CLONE_SIGNAL    	(CLONE_SIGHAND | CLONE_THREAD)
+/* XXX Remove when definition is common place.  */
+#ifndef CLONE_STOPPED
+# define CLONE_STOPPED 0x02000000
+#endif
 
 /* Unless otherwise specified, the thread "register" is going to be
    initialized with a pointer to the TCB.  */
@@ -48,72 +52,84 @@ int *__libc_multiple_threads_ptr attribute_hidden;
 
 
 static int
-create_thread (struct pthread *pd, STACK_VARIABLES_PARMS)
+do_clone (struct pthread *pd, struct pthread_attr *attr, int clone_flags,
+	  int (*fct) (void *), STACK_VARIABLES_PARMS)
 {
 #ifdef PREPARE_CREATE
   PREPARE_CREATE;
 #endif
 
-#ifdef TLS_TCB_AT_TP
-  assert (pd->header.tcb != NULL);
-#endif
+  if (ARCH_CLONE (fct, STACK_VARIABLES_ARGS, clone_flags,
+		  pd, &pd->tid, TLS_VALUE, &pd->tid) == -1)
+    /* Failed.  */
+    return errno;
 
-  if (__builtin_expect (THREAD_GETMEM (THREAD_SELF, report_events), 0))
+  /* Now we have the possibility to set scheduling parameters etc.  */
+  if (__builtin_expect ((clone_flags & CLONE_STOPPED) != 0, 0))
     {
-      /* The parent thread is supposed to report events.  Check whether
-	 the TD_CREATE event is needed, too.  */
-      const int _idx = __td_eventword (TD_CREATE);
-      const uint32_t _mask = __td_eventmask (TD_CREATE);
+      INTERNAL_SYSCALL_DECL (err);
+      int res = 0;
 
-      if ((_mask & (__nptl_threads_events.event_bits[_idx]
-		    | pd->eventbuf.eventmask.event_bits[_idx])) != 0)
+      /* Set the affinity mask if necessary.  */
+      if (attr->cpuset != NULL)
 	{
-	  /* We have to report the new thread.  Make sure the thread
-	     does not run far by forcing it to get a lock.  We lock it
-	     here too so that the new thread cannot continue until we
-	     tell it to.  */
-	  lll_lock (pd->lock);
+	  res = INTERNAL_SYSCALL (sched_setaffinity, err, 3, pd->tid,
+				  sizeof (cpu_set_t), attr->cpuset);
 
-	  /* Create the thread.  */
-	  if (ARCH_CLONE (start_thread_debug, STACK_VARIABLES_ARGS,
-			  CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL |
-			  CLONE_SETTLS | CLONE_PARENT_SETTID |
-			  CLONE_CHILD_CLEARTID | CLONE_DETACHED |
-			  CLONE_SYSVSEM | 0, pd, &pd->tid, TLS_VALUE,
-			  &pd->tid) == -1)
-	    /* Failed.  */
-	    return errno;
-
-	  /* We now have for sure more than one thread.  The main
-	     thread might not yet have the flag set.  No need to set
-	     the global variable again if this is what we use.  */
-	  THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1);
-
-	  /* Now fill in the information about the new thread in
-	     the newly created thread's data structure.  We cannot let
-	     the new thread do this since we don't know whether it was
-	     already scheduled when we send the event.  */
-	  pd->eventbuf.eventnum = TD_CREATE;
-	  pd->eventbuf.eventdata = pd;
-
-	  /* Enqueue the descriptor.  */
-	  do
-	    pd->nextevent = __nptl_last_event;
-	  while (atomic_compare_and_exchange_bool_acq (&__nptl_last_event, pd,
-						       pd->nextevent) != 0);
-
-	  /* Now call the function which signals the event.  */
-	  __nptl_create_event ();
-
-	  /* And finally restart the new thread.  */
-	  lll_unlock (pd->lock);
-
-	  return 0;
+	  if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
+	    goto err_out;
+	}
+
+      /* Set the scheduling parameters.  */
+      if ((attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0)
+	{
+	  res = INTERNAL_SYSCALL (sched_setparam, err, 2, pd->tid,
+				  &pd->schedparam);
+
+	  if (__builtin_expect (! INTERNAL_SYSCALL_ERROR_P (res, err), 1))
+	    {
+	      res = INTERNAL_SYSCALL (sched_setscheduler, err, 2, pd->tid,
+				      &pd->schedpolicy);
+
+	      if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
+		goto err_out;
+	    }
+	}
+
+      /* Now start the thread for real.  */
+      res = INTERNAL_SYSCALL (tkill, err, 2, pd->tid, SIGCONT);
+
+      /* If something went wrong, kill the thread.  */
+      if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
+	{
+	  /* The operation failed.  We have to kill the thread.  First
+             send it the cancellation signal.  */
+	  INTERNAL_SYSCALL_DECL (err2);
+	err_out:
+	  (void) INTERNAL_SYSCALL (tkill, err2, 2, pd->tid, SIGCANCEL);
+
+	  /* Then wake it up so that the signal can be processed.  */
+	  (void) INTERNAL_SYSCALL (tkill, err, 2, pd->tid, SIGCONT);
+
+	  return INTERNAL_SYSCALL_ERRNO (res, err);
 	}
     }
 
-#ifdef NEED_DL_SYSINFO
-  assert (THREAD_GETMEM (THREAD_SELF, header.sysinfo) == pd->header.sysinfo);
+  /* We now have for sure more than one thread.  The main thread might
+     not yet have the flag set.  No need to set the global variable
+     again if this is what we use.  */
+  THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1);
+
+  return 0;
+}
+
+
+static int
+create_thread (struct pthread *pd, struct pthread_attr *attr,
+	       STACK_VARIABLES_PARMS)
+{
+#ifdef TLS_TCB_AT_TP
+  assert (pd->header.tcb != NULL);
 #endif
 
   /* We rely heavily on various flags the CLONE function understands:
@@ -147,18 +163,68 @@ create_thread (struct pthread *pd, STACK_VARIABLES_PARMS)
 
      The termination signal is chosen to be zero which means no signal
      is sent.  */
-  if (ARCH_CLONE (start_thread, STACK_VARIABLES_ARGS,
-		  CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL |
-		  CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID |
-		  CLONE_DETACHED | CLONE_SYSVSEM | 0, pd, &pd->tid, TLS_VALUE,
-		  &pd->tid) == -1)
-    /* Failed.  */
-    return errno;
+  int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL
+		     | CLONE_SETTLS | CLONE_PARENT_SETTID
+		     | CLONE_CHILD_CLEARTID | CLONE_DETACHED | CLONE_SYSVSEM
+		     | 0);
+
+  /* If the newly created threads has to be started stopped since we
+     have to set the scheduling parameters or set the affinity we set
+     the CLONE_STOPPED flag.  */
+  if (attr != NULL && (attr->cpuset != NULL
+		       || (attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0))
+    clone_flags |= CLONE_STOPPED;
 
-  /* We now have for sure more than one thread.  The main thread might
-     not yet have the flag set.  No need to set the global variable
-     again if this is what we use.  */
-  THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1);
+  if (__builtin_expect (THREAD_GETMEM (THREAD_SELF, report_events), 0))
+    {
+      /* The parent thread is supposed to report events.  Check whether
+	 the TD_CREATE event is needed, too.  */
+      const int _idx = __td_eventword (TD_CREATE);
+      const uint32_t _mask = __td_eventmask (TD_CREATE);
 
-  return 0;
+      if ((_mask & (__nptl_threads_events.event_bits[_idx]
+		    | pd->eventbuf.eventmask.event_bits[_idx])) != 0)
+	{
+	  /* We have to report the new thread.  Make sure the thread
+	     does not run far by forcing it to get a lock.  We lock it
+	     here too so that the new thread cannot continue until we
+	     tell it to.  */
+	  lll_lock (pd->lock);
+
+	  /* Create the thread.  */
+	  int res = do_clone (pd, attr, clone_flags, start_thread_debug,
+			      STACK_VARIABLES_ARGS);
+	  if (res == 0)
+	    {
+	      /* Now fill in the information about the new thread in
+		 the newly created thread's data structure.  We cannot let
+		 the new thread do this since we don't know whether it was
+		 already scheduled when we send the event.  */
+	      pd->eventbuf.eventnum = TD_CREATE;
+	      pd->eventbuf.eventdata = pd;
+
+	      /* Enqueue the descriptor.  */
+	      do
+		pd->nextevent = __nptl_last_event;
+	      while (atomic_compare_and_exchange_bool_acq (&__nptl_last_event,
+							   pd, pd->nextevent)
+		     != 0);
+
+	      /* Now call the function which signals the event.  */
+	      __nptl_create_event ();
+
+	      /* And finally restart the new thread.  */
+	      lll_unlock (pd->lock);
+	    }
+
+	  return res;
+	}
+    }
+
+#ifdef NEED_DL_SYSINFO
+  assert (THREAD_GETMEM (THREAD_SELF, header.sysinfo) == pd->header.sysinfo);
+#endif
+
+  /* Actually create the thread.  */
+  return do_clone (pd, attr, clone_flags, start_thread, STACK_VARIABLES_ARGS);
 }
diff --git a/nptl/sysdeps/pthread/pthread.h b/nptl/sysdeps/pthread/pthread.h
index 56d40e716e..80409bcac3 100644
--- a/nptl/sysdeps/pthread/pthread.h
+++ b/nptl/sysdeps/pthread/pthread.h
@@ -321,6 +321,17 @@ extern int pthread_attr_setstack (pthread_attr_t *__attr, void *__stackaddr,
 #endif
 
 #ifdef __USE_GNU
+/* Thread created with attribute ATTR will be limited to run only on
+   the processors represented in CPUSET.  */
+extern int pthread_attr_setaffinity_np (pthread_attr_t *__attr,
+					__const cpu_set_t *__cpuset) __THROW;
+
+/* Get bit set in CPUSET representing the processors threads created with
+   ATTR can run on.  */
+extern int pthread_attr_getaffinity_np (__const pthread_attr_t *__attr,
+					cpu_set_t *__cpuset) __THROW;
+
+
 /* Get thread attributes corresponding to the already running thread TH.  */
 extern int pthread_getattr_np (pthread_t __th, pthread_attr_t *__attr) __THROW;
 #endif
@@ -359,7 +370,7 @@ extern int pthread_yield (void) __THROW;
 
 /* Limit specified thread TH to run only on the processors represented
    in CPUSET.  */
-extern int pthread_setaffinity_np (pthread_t __th, const cpu_set_t *__cpuset)
+extern int pthread_setaffinity_np (pthread_t __th, __const cpu_set_t *__cpuset)
      __THROW;
 
 /* Get bit set in CPUSET representing the processors TH can run on.  */
diff --git a/nptl/sysdeps/unix/sysv/linux/internaltypes.h b/nptl/sysdeps/unix/sysv/linux/internaltypes.h
index 17d78e4b9f..e2f7b047f1 100644
--- a/nptl/sysdeps/unix/sysv/linux/internaltypes.h
+++ b/nptl/sysdeps/unix/sysv/linux/internaltypes.h
@@ -35,6 +35,8 @@ struct pthread_attr
   /* Stack handling.  */
   void *stackaddr;
   size_t stacksize;
+  /* Affinity map.  */
+  cpu_set_t *cpuset;
 
   /* Chain of all initialized attributes.  Keep this last since it is
      not always used.  */
diff --git a/nptl/sysdeps/unix/sysv/linux/pthread_attr_getaffinity.c b/nptl/sysdeps/unix/sysv/linux/pthread_attr_getaffinity.c
new file mode 100644
index 0000000000..e6c795b8b4
--- /dev/null
+++ b/nptl/sysdeps/unix/sysv/linux/pthread_attr_getaffinity.c
@@ -0,0 +1,41 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <pthreadP.h>
+#include <string.h>
+#include <sysdep.h>
+#include <sys/types.h>
+
+
+int
+pthread_attr_getaffinity_np (attr, cpuset)
+     const pthread_attr_t *attr;
+     cpu_set_t *cpuset;
+{
+  struct pthread_attr *iattr;
+
+  assert (sizeof (*attr) >= sizeof (struct pthread_attr));
+  iattr = (struct pthread_attr *) attr;
+
+  memcpy (cpuset, iattr->cpuset, sizeof (cpu_set_t));
+
+  return 0;
+}
diff --git a/nptl/sysdeps/unix/sysv/linux/pthread_attr_setaffinity.c b/nptl/sysdeps/unix/sysv/linux/pthread_attr_setaffinity.c
new file mode 100644
index 0000000000..f25ccb213e
--- /dev/null
+++ b/nptl/sysdeps/unix/sysv/linux/pthread_attr_setaffinity.c
@@ -0,0 +1,47 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthreadP.h>
+
+
+int
+pthread_attr_setaffinity_np (attr, cpuset)
+     pthread_attr_t *attr;
+     const cpu_set_t *cpuset;
+{
+  struct pthread_attr *iattr;
+
+  assert (sizeof (*attr) >= sizeof (struct pthread_attr));
+  iattr = (struct pthread_attr *) attr;
+
+  if (iattr->cpuset == NULL)
+    {
+      iattr->cpuset = (cpu_set_t *) malloc (sizeof (cpu_set_t));
+      if (iattr->cpuset == NULL)
+	return ENOMEM;
+    }
+
+  memcpy (iattr->cpuset, cpuset, sizeof (cpu_set_t));
+
+  return 0;
+}
diff --git a/nptl/tst-cancel-wrappers.sh b/nptl/tst-cancel-wrappers.sh
index e2035c7ed5..d6f16d1ed2 100644
--- a/nptl/tst-cancel-wrappers.sh
+++ b/nptl/tst-cancel-wrappers.sh
@@ -27,8 +27,6 @@ C["connect"]=1
 C["creat"]=1
 C["fcntl"]=1
 C["fsync"]=1
-C["llseek"]=1
-C["lseek"]=1
 C["msgrcv"]=1
 C["msgsnd"]=1
 C["msync"]=1