From 80f536dbf20510cbabdb042c30baa453d0b069c1 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sun, 20 Jul 2003 08:56:05 +0000 Subject: [PATCH] Update. 2003-07-20 Ulrich Drepper * 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. --- nptl/ChangeLog | 18 ++ nptl/Makefile | 1 + nptl/Versions | 1 + nptl/pthread_attr_destroy.c | 14 +- nptl/pthread_create.c | 2 +- nptl/sysdeps/pthread/createthread.c | 196 ++++++++++++++------- nptl/sysdeps/pthread/pthread.h | 13 +- nptl/sysdeps/unix/sysv/linux/internaltypes.h | 2 + .../unix/sysv/linux/pthread_attr_getaffinity.c | 41 +++++ .../unix/sysv/linux/pthread_attr_setaffinity.c | 47 +++++ nptl/tst-cancel-wrappers.sh | 2 - sysdeps/unix/sysv/linux/bits/sched.h | 4 +- 12 files changed, 265 insertions(+), 76 deletions(-) create mode 100644 nptl/sysdeps/unix/sysv/linux/pthread_attr_getaffinity.c create mode 100644 nptl/sysdeps/unix/sysv/linux/pthread_attr_setaffinity.c diff --git a/nptl/ChangeLog b/nptl/ChangeLog index 804968a..c2e5055 100644 --- a/nptl/ChangeLog +++ b/nptl/ChangeLog @@ -1,3 +1,21 @@ +2003-07-20 Ulrich Drepper + + * 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 * tst-tcancel-wrappers.sh: lseek and llseek are not cancelation points. diff --git a/nptl/Makefile b/nptl/Makefile index b562e66..4c7749e 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 0f98663..8eb863d 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 fec0416..a04f5fe 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 , 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 22024d5..7565826 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 7563a2b..4a02d1c 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 56d40e7..80409bc 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 17d78e4..e2f7b04 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 0000000..e6c795b --- /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 , 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 +#include +#include +#include +#include +#include + + +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 0000000..f25ccb2 --- /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 , 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 +#include +#include +#include +#include + + +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 e2035c7..d6f16d1 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 diff --git a/sysdeps/unix/sysv/linux/bits/sched.h b/sysdeps/unix/sysv/linux/bits/sched.h index b250752..4e963d1 100644 --- a/sysdeps/unix/sysv/linux/bits/sched.h +++ b/sysdeps/unix/sysv/linux/bits/sched.h @@ -110,8 +110,8 @@ typedef struct # define __CPU_ZERO(cpusetp) \ do { \ unsigned int __i; \ - cpu_set *__arr = (cpusetp); \ - for (__i = 0; __i < sizeof (cpu_set) / sizeof (__cpu_mask); ++__i) \ + cpu_set_t *__arr = (cpusetp); \ + for (__i = 0; __i < sizeof (cpu_set_t) / sizeof (__cpu_mask); ++__i) \ __arr->__bits[__i] = 0; \ } while (0) # define __CPU_SET(cpu, cpusetp) \ -- 2.7.4