1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-08-01 10:06:57 +03:00
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.
This commit is contained in:
Ulrich Drepper
2003-07-20 08:56:05 +00:00
parent 4e0dc4af0b
commit 80f536dbf2
12 changed files with 259 additions and 70 deletions

View File

@ -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> 2003-07-15 Ulrich Drepper <drepper@redhat.com>
* tst-tcancel-wrappers.sh: lseek and llseek are not cancelation points. * tst-tcancel-wrappers.sh: lseek and llseek are not cancelation points.

View File

@ -117,6 +117,7 @@ libpthread-routines = init events version \
herrno res pt-allocrtsig \ herrno res pt-allocrtsig \
pthread_kill_other_threads \ pthread_kill_other_threads \
pthread_getaffinity pthread_setaffinity \ pthread_getaffinity pthread_setaffinity \
pthread_attr_getaffinity pthread_attr_setaffinity \
cleanup_routine cleanup_routine
libpthread-shared-only-routines = version pt-allocrtsig libpthread-shared-only-routines = version pt-allocrtsig

View File

@ -220,6 +220,7 @@ libpthread {
# New affinity interfaces. # New affinity interfaces.
pthread_getaffinity_np; pthread_setaffinity_np; pthread_getaffinity_np; pthread_setaffinity_np;
pthread_attr_getaffinity_np; pthread_attr_setaffinity_np;
} }
GLIBC_PRIVATE { GLIBC_PRIVATE {

View File

@ -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. This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
@ -28,16 +28,17 @@ int
__pthread_attr_destroy (attr) __pthread_attr_destroy (attr)
pthread_attr_t *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. */ /* Enqueue the attributes to the list of all known variables. */
if (DEBUGGING_P) if (DEBUGGING_P)
{ {
struct pthread_attr *iattr;
struct pthread_attr *prevp = NULL; struct pthread_attr *prevp = NULL;
struct pthread_attr *runp; struct pthread_attr *runp;
assert (sizeof (*attr) >= sizeof (struct pthread_attr));
iattr = (struct pthread_attr *) attr;
lll_lock (__attr_list_lock); lll_lock (__attr_list_lock);
runp = __attr_list; runp = __attr_list;
@ -62,6 +63,9 @@ __pthread_attr_destroy (attr)
return EINVAL; return EINVAL;
} }
/* The affinity CPU set might be allocated dynamically. */
free (iattr->cpuset);
return 0; return 0;
} }
strong_alias (__pthread_attr_destroy, pthread_attr_destroy) strong_alias (__pthread_attr_destroy, pthread_attr_destroy)

View File

@ -431,7 +431,7 @@ __pthread_create_2_1 (newthread, attr, start_routine, arg)
*newthread = (pthread_t) pd; *newthread = (pthread_t) pd;
/* Start the thread. */ /* Start the thread. */
err = create_thread (pd, STACK_VARIABLES_ARGS); err = create_thread (pd, iattr, STACK_VARIABLES_ARGS);
if (err != 0) if (err != 0)
{ {
/* Something went wrong. Free the resources. */ /* Something went wrong. Free the resources. */

View File

@ -27,6 +27,10 @@
#define CLONE_SIGNAL (CLONE_SIGHAND | CLONE_THREAD) #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 /* Unless otherwise specified, the thread "register" is going to be
initialized with a pointer to the TCB. */ initialized with a pointer to the TCB. */
@ -48,72 +52,84 @@ int *__libc_multiple_threads_ptr attribute_hidden;
static int 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 #ifdef PREPARE_CREATE
PREPARE_CREATE; PREPARE_CREATE;
#endif #endif
#ifdef TLS_TCB_AT_TP if (ARCH_CLONE (fct, STACK_VARIABLES_ARGS, clone_flags,
assert (pd->header.tcb != NULL); pd, &pd->tid, TLS_VALUE, &pd->tid) == -1)
#endif /* 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 INTERNAL_SYSCALL_DECL (err);
the TD_CREATE event is needed, too. */ int res = 0;
const int _idx = __td_eventword (TD_CREATE);
const uint32_t _mask = __td_eventmask (TD_CREATE);
if ((_mask & (__nptl_threads_events.event_bits[_idx] /* Set the affinity mask if necessary. */
| pd->eventbuf.eventmask.event_bits[_idx])) != 0) if (attr->cpuset != NULL)
{ {
/* We have to report the new thread. Make sure the thread res = INTERNAL_SYSCALL (sched_setaffinity, err, 3, pd->tid,
does not run far by forcing it to get a lock. We lock it sizeof (cpu_set_t), attr->cpuset);
here too so that the new thread cannot continue until we
tell it to. */
lll_lock (pd->lock);
/* Create the thread. */ if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
if (ARCH_CLONE (start_thread_debug, STACK_VARIABLES_ARGS, goto err_out;
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 /* Set the scheduling parameters. */
thread might not yet have the flag set. No need to set if ((attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0)
the global variable again if this is what we use. */ {
THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1); res = INTERNAL_SYSCALL (sched_setparam, err, 2, pd->tid,
&pd->schedparam);
/* Now fill in the information about the new thread in if (__builtin_expect (! INTERNAL_SYSCALL_ERROR_P (res, err), 1))
the newly created thread's data structure. We cannot let {
the new thread do this since we don't know whether it was res = INTERNAL_SYSCALL (sched_setscheduler, err, 2, pd->tid,
already scheduled when we send the event. */ &pd->schedpolicy);
pd->eventbuf.eventnum = TD_CREATE;
pd->eventbuf.eventdata = pd;
/* Enqueue the descriptor. */ if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
do goto err_out;
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. */ /* Now start the thread for real. */
__nptl_create_event (); res = INTERNAL_SYSCALL (tkill, err, 2, pd->tid, SIGCONT);
/* And finally restart the new thread. */ /* If something went wrong, kill the thread. */
lll_unlock (pd->lock); 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);
return 0; /* 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 /* We now have for sure more than one thread. The main thread might
assert (THREAD_GETMEM (THREAD_SELF, header.sysinfo) == pd->header.sysinfo); 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 #endif
/* We rely heavily on various flags the CLONE function understands: /* 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 The termination signal is chosen to be zero which means no signal
is sent. */ is sent. */
if (ARCH_CLONE (start_thread, STACK_VARIABLES_ARGS, int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL | | CLONE_SETTLS | CLONE_PARENT_SETTID
CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | | CLONE_CHILD_CLEARTID | CLONE_DETACHED | CLONE_SYSVSEM
CLONE_DETACHED | CLONE_SYSVSEM | 0, pd, &pd->tid, TLS_VALUE, | 0);
&pd->tid) == -1)
/* Failed. */
return errno;
/* We now have for sure more than one thread. The main thread might /* If the newly created threads has to be started stopped since we
not yet have the flag set. No need to set the global variable have to set the scheduling parameters or set the affinity we set
again if this is what we use. */ the CLONE_STOPPED flag. */
THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1); if (attr != NULL && (attr->cpuset != NULL
|| (attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0))
clone_flags |= CLONE_STOPPED;
return 0; 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);
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);
} }

View File

@ -321,6 +321,17 @@ extern int pthread_attr_setstack (pthread_attr_t *__attr, void *__stackaddr,
#endif #endif
#ifdef __USE_GNU #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. */ /* Get thread attributes corresponding to the already running thread TH. */
extern int pthread_getattr_np (pthread_t __th, pthread_attr_t *__attr) __THROW; extern int pthread_getattr_np (pthread_t __th, pthread_attr_t *__attr) __THROW;
#endif #endif
@ -359,7 +370,7 @@ extern int pthread_yield (void) __THROW;
/* Limit specified thread TH to run only on the processors represented /* Limit specified thread TH to run only on the processors represented
in CPUSET. */ 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; __THROW;
/* Get bit set in CPUSET representing the processors TH can run on. */ /* Get bit set in CPUSET representing the processors TH can run on. */

View File

@ -35,6 +35,8 @@ struct pthread_attr
/* Stack handling. */ /* Stack handling. */
void *stackaddr; void *stackaddr;
size_t stacksize; size_t stacksize;
/* Affinity map. */
cpu_set_t *cpuset;
/* Chain of all initialized attributes. Keep this last since it is /* Chain of all initialized attributes. Keep this last since it is
not always used. */ not always used. */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -27,8 +27,6 @@ C["connect"]=1
C["creat"]=1 C["creat"]=1
C["fcntl"]=1 C["fcntl"]=1
C["fsync"]=1 C["fsync"]=1
C["llseek"]=1
C["lseek"]=1
C["msgrcv"]=1 C["msgrcv"]=1
C["msgsnd"]=1 C["msgsnd"]=1
C["msync"]=1 C["msync"]=1

View File

@ -110,8 +110,8 @@ typedef struct
# define __CPU_ZERO(cpusetp) \ # define __CPU_ZERO(cpusetp) \
do { \ do { \
unsigned int __i; \ unsigned int __i; \
cpu_set *__arr = (cpusetp); \ cpu_set_t *__arr = (cpusetp); \
for (__i = 0; __i < sizeof (cpu_set) / sizeof (__cpu_mask); ++__i) \ for (__i = 0; __i < sizeof (cpu_set_t) / sizeof (__cpu_mask); ++__i) \
__arr->__bits[__i] = 0; \ __arr->__bits[__i] = 0; \
} while (0) } while (0)
# define __CPU_SET(cpu, cpusetp) \ # define __CPU_SET(cpu, cpusetp) \