diff options
author | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2016-10-10 15:08:39 -0300 |
---|---|---|
committer | Arjun Shankar <arjun.is@lostca.se> | 2017-06-07 12:42:53 +0200 |
commit | 206e03248c3f575e83c13f2aa709fa55c67e51c2 (patch) | |
tree | 6d88f5e28576d8a65ee156d48eb16c2b480ab7c8 /nptl | |
parent | 80718499347b1481713951528ced70693ad0e351 (diff) | |
download | glibc-206e03248c3f575e83c13f2aa709fa55c67e51c2.tar.gz glibc-206e03248c3f575e83c13f2aa709fa55c67e51c2.tar.xz glibc-206e03248c3f575e83c13f2aa709fa55c67e51c2.zip |
Remove cached PID/TID in clone
This patch remove the PID cache and usage in current GLIBC code. Current usage is mainly used a performance optimization to avoid the syscall, however it adds some issues: - The exposed clone syscall will try to set pid/tid to make the new thread somewhat compatible with current GLIBC assumptions. This cause a set of issue with new workloads and usecases (such as BZ#17214 and [1]) as well for new internal usage of clone to optimize other algorithms (such as clone plus CLONE_VM for posix_spawn, BZ#19957). - The caching complexity also added some bugs in the past [2] [3] and requires more effort of each port to handle such requirements (for both clone and vfork implementation). - Caching performance gain in mainly on getpid and some specific code paths. The getpid performance leverage is questionable [4], either by the idea of getpid being a hotspot as for the getpid implementation itself (if it is indeed a justifiable hotspot a vDSO symbol could let to a much more simpler solution). Other usage is mainly for non usual code paths, such as pthread cancellation signal and handling. For thread creation (on stack allocation) the code simplification in fact adds some performance gain due the no need of transverse the stack cache and invalidate each element pid. Other thread usages will require a direct getpid syscall, such as cancellation/setxid signal, thread cancellation, thread fail path (at create_thread), and thread signal (pthread_kill and pthread_sigqueue). However these are hardly usual hotspots and I think adding a syscall is justifiable. It also simplifies both the clone and vfork arch-specific implementation. And by review each fork implementation there are some discrepancies that this patch also solves: - microblaze clone/vfork does not set/reset the pid/tid field - hppa uses the default vfork implementation that fallback to fork. Since vfork is deprecated I do not think we should bother with it. The patch also removes the TID caching in clone. My understanding for such semantic is try provide some pthread usage after a user program issue clone directly (as done by thread creation with CLONE_PARENT_SETTID and pthread tid member). However, as stated before in multiple discussions threads, GLIBC provides clone syscalls without further supporting all this semantics. I ran a full make check on x86_64, x32, i686, armhf, aarch64, and powerpc64le. For sparc32, sparc64, and mips I ran the basic fork and vfork tests from posix/ folder (on a qemu system). So it would require further testing on alpha, hppa, ia64, m68k, nios2, s390, sh, and tile (I excluded microblaze because it is already implementing the patch semantic regarding clone/vfork). [1] https://codereview.chromium.org/800183004/ [2] https://sourceware.org/ml/libc-alpha/2006-07/msg00123.html [3] https://sourceware.org/bugzilla/show_bug.cgi?id=15368 [4] http://yarchive.net/comp/linux/getpid_caching.html * sysdeps/nptl/fork.c (__libc_fork): Remove pid cache setting. * nptl/allocatestack.c (allocate_stack): Likewise. (__reclaim_stacks): Likewise. (setxid_signal_thread): Obtain pid through syscall. * nptl/nptl-init.c (sigcancel_handler): Likewise. (sighandle_setxid): Likewise. * nptl/pthread_cancel.c (pthread_cancel): Likewise. * sysdeps/unix/sysv/linux/pthread_kill.c (__pthread_kill): Likewise. * sysdeps/unix/sysv/linux/pthread_sigqueue.c (pthread_sigqueue): Likewise. * sysdeps/unix/sysv/linux/createthread.c (create_thread): Likewise. * sysdeps/unix/sysv/linux/getpid.c: Remove file. * nptl/descr.h (struct pthread): Change comment about pid value. * nptl/pthread_getattr_np.c (pthread_getattr_np): Remove thread pid assert. * sysdeps/unix/sysv/linux/pthread-pids.h (__pthread_initialize_pids): Do not set pid value. * nptl_db/td_ta_thr_iter.c (iterate_thread_list): Remove thread pid cache check. * nptl_db/td_thr_validate.c (td_thr_validate): Likewise. * sysdeps/aarch64/nptl/tcb-offsets.sym: Remove pid offset. * sysdeps/alpha/nptl/tcb-offsets.sym: Likewise. * sysdeps/arm/nptl/tcb-offsets.sym: Likewise. * sysdeps/hppa/nptl/tcb-offsets.sym: Likewise. * sysdeps/i386/nptl/tcb-offsets.sym: Likewise. * sysdeps/ia64/nptl/tcb-offsets.sym: Likewise. * sysdeps/m68k/nptl/tcb-offsets.sym: Likewise. * sysdeps/microblaze/nptl/tcb-offsets.sym: Likewise. * sysdeps/mips/nptl/tcb-offsets.sym: Likewise. * sysdeps/nios2/nptl/tcb-offsets.sym: Likewise. * sysdeps/powerpc/nptl/tcb-offsets.sym: Likewise. * sysdeps/s390/nptl/tcb-offsets.sym: Likewise. * sysdeps/sh/nptl/tcb-offsets.sym: Likewise. * sysdeps/sparc/nptl/tcb-offsets.sym: Likewise. * sysdeps/tile/nptl/tcb-offsets.sym: Likewise. * sysdeps/x86_64/nptl/tcb-offsets.sym: Likewise. * sysdeps/unix/sysv/linux/aarch64/clone.S: Remove pid and tid caching. * sysdeps/unix/sysv/linux/alpha/clone.S: Likewise. * sysdeps/unix/sysv/linux/arm/clone.S: Likewise. * sysdeps/unix/sysv/linux/hppa/clone.S: Likewise. * sysdeps/unix/sysv/linux/i386/clone.S: Likewise. * sysdeps/unix/sysv/linux/ia64/clone2.S: Likewise. * sysdeps/unix/sysv/linux/mips/clone.S: Likewise. * sysdeps/unix/sysv/linux/nios2/clone.S: Likewise. * sysdeps/unix/sysv/linux/powerpc/powerpc32/clone.S: Likewise. * sysdeps/unix/sysv/linux/powerpc/powerpc64/clone.S: Likewise. * sysdeps/unix/sysv/linux/s390/s390-32/clone.S: Likewise. * sysdeps/unix/sysv/linux/s390/s390-64/clone.S: Likewise. * sysdeps/unix/sysv/linux/sh/clone.S: Likewise. * sysdeps/unix/sysv/linux/sparc/sparc32/clone.S: Likewise. * sysdeps/unix/sysv/linux/sparc/sparc64/clone.S: Likewise. * sysdeps/unix/sysv/linux/tile/clone.S: Likewise. * sysdeps/unix/sysv/linux/x86_64/clone.S: Likewise. * sysdeps/unix/sysv/linux/aarch64/vfork.S: Remove pid set and reset. * sysdeps/unix/sysv/linux/alpha/vfork.S: Likewise. * sysdeps/unix/sysv/linux/arm/vfork.S: Likewise. * sysdeps/unix/sysv/linux/i386/vfork.S: Likewise. * sysdeps/unix/sysv/linux/ia64/vfork.S: Likewise. * sysdeps/unix/sysv/linux/m68k/clone.S: Likewise. * sysdeps/unix/sysv/linux/m68k/vfork.S: Likewise. * sysdeps/unix/sysv/linux/mips/vfork.S: Likewise. * sysdeps/unix/sysv/linux/nios2/vfork.S: Likewise. * sysdeps/unix/sysv/linux/powerpc/powerpc32/vfork.S: Likewise. * sysdeps/unix/sysv/linux/powerpc/powerpc64/vfork.S: Likewise. * sysdeps/unix/sysv/linux/s390/s390-32/vfork.S: Likewise. * sysdeps/unix/sysv/linux/s390/s390-64/vfork.S: Likewise. * sysdeps/unix/sysv/linux/sh/vfork.S: Likewise. * sysdeps/unix/sysv/linux/sparc/sparc32/vfork.S: Likewise. * sysdeps/unix/sysv/linux/sparc/sparc64/vfork.S: Likewise. * sysdeps/unix/sysv/linux/tile/vfork.S: Likewise. * sysdeps/unix/sysv/linux/x86_64/vfork.S: Likewise. * sysdeps/unix/sysv/linux/tst-clone2.c (f): Remove direct pthread struct access. (clone_test): Remove function. (do_test): Rewrite to take in consideration pid is not cached anymore. (cherry picked from commit c579f48edba88380635ab98cb612030e3ed8691e)
Diffstat (limited to 'nptl')
-rw-r--r-- | nptl/allocatestack.c | 20 | ||||
-rw-r--r-- | nptl/descr.h | 4 | ||||
-rw-r--r-- | nptl/nptl-init.c | 15 | ||||
-rw-r--r-- | nptl/pthread_cancel.c | 18 | ||||
-rw-r--r-- | nptl/pthread_getattr_np.c | 1 |
5 files changed, 11 insertions, 47 deletions
diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c index 6b42b11d5a..7365ca606b 100644 --- a/nptl/allocatestack.c +++ b/nptl/allocatestack.c @@ -440,9 +440,6 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp, SETUP_THREAD_SYSINFO (pd); #endif - /* The process ID is also the same as that of the caller. */ - pd->pid = THREAD_GETMEM (THREAD_SELF, pid); - /* Don't allow setxid until cloned. */ pd->setxid_futex = -1; @@ -579,9 +576,6 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp, /* Don't allow setxid until cloned. */ pd->setxid_futex = -1; - /* The process ID is also the same as that of the caller. */ - pd->pid = THREAD_GETMEM (THREAD_SELF, pid); - /* Allocate the DTV for this thread. */ if (_dl_allocate_tls (TLS_TPADJ (pd)) == NULL) { @@ -875,9 +869,6 @@ __reclaim_stacks (void) /* This marks the stack as free. */ curp->tid = 0; - /* The PID field must be initialized for the new process. */ - curp->pid = self->pid; - /* Account for the size of the stack. */ stack_cache_actsize += curp->stackblock_size; @@ -903,13 +894,6 @@ __reclaim_stacks (void) } } - /* Reset the PIDs in any cached stacks. */ - list_for_each (runp, &stack_cache) - { - struct pthread *curp = list_entry (runp, struct pthread, list); - curp->pid = self->pid; - } - /* Add the stack of all running threads to the cache. */ list_splice (&stack_used, &stack_cache); @@ -1054,9 +1038,9 @@ setxid_signal_thread (struct xid_command *cmdp, struct pthread *t) return 0; int val; + pid_t pid = __getpid (); INTERNAL_SYSCALL_DECL (err); - val = INTERNAL_SYSCALL (tgkill, err, 3, THREAD_GETMEM (THREAD_SELF, pid), - t->tid, SIGSETXID); + val = INTERNAL_SYSCALL_CALL (tgkill, err, pid, t->tid, SIGSETXID); /* If this failed, it must have had not started yet or else exited. */ if (!INTERNAL_SYSCALL_ERROR_P (val, err)) diff --git a/nptl/descr.h b/nptl/descr.h index 8e4938deb5..bc92abf010 100644 --- a/nptl/descr.h +++ b/nptl/descr.h @@ -167,8 +167,8 @@ struct pthread therefore stack) used' flag. */ pid_t tid; - /* Process ID - thread group ID in kernel speak. */ - pid_t pid; + /* Ununsed. */ + pid_t pid_ununsed; /* List of robust mutexes the thread is holding. */ #ifdef __PTHREAD_MUTEX_HAVE_PREV diff --git a/nptl/nptl-init.c b/nptl/nptl-init.c index bdbdfedcef..48fab50c4e 100644 --- a/nptl/nptl-init.c +++ b/nptl/nptl-init.c @@ -184,18 +184,12 @@ __nptl_set_robust (struct pthread *self) static void sigcancel_handler (int sig, siginfo_t *si, void *ctx) { - /* Determine the process ID. It might be negative if the thread is - in the middle of a fork() call. */ - pid_t pid = THREAD_GETMEM (THREAD_SELF, pid); - if (__glibc_unlikely (pid < 0)) - pid = -pid; - /* Safety check. It would be possible to call this function for other signals and send a signal from another process. This is not correct and might even be a security problem. Try to catch as many incorrect invocations as possible. */ if (sig != SIGCANCEL - || si->si_pid != pid + || si->si_pid != __getpid() || si->si_code != SI_TKILL) return; @@ -243,19 +237,14 @@ struct xid_command *__xidcmd attribute_hidden; static void sighandler_setxid (int sig, siginfo_t *si, void *ctx) { - /* Determine the process ID. It might be negative if the thread is - in the middle of a fork() call. */ - pid_t pid = THREAD_GETMEM (THREAD_SELF, pid); int result; - if (__glibc_unlikely (pid < 0)) - pid = -pid; /* Safety check. It would be possible to call this function for other signals and send a signal from another process. This is not correct and might even be a security problem. Try to catch as many incorrect invocations as possible. */ if (sig != SIGSETXID - || si->si_pid != pid + || si->si_pid != __getpid () || si->si_code != SI_TKILL) return; diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c index 1419baf988..89d02e1741 100644 --- a/nptl/pthread_cancel.c +++ b/nptl/pthread_cancel.c @@ -22,7 +22,7 @@ #include "pthreadP.h" #include <atomic.h> #include <sysdep.h> - +#include <unistd.h> int pthread_cancel (pthread_t th) @@ -66,19 +66,11 @@ pthread_cancel (pthread_t th) #ifdef SIGCANCEL /* The cancellation handler will take care of marking the thread as canceled. */ - INTERNAL_SYSCALL_DECL (err); - - /* One comment: The PID field in the TCB can temporarily be - changed (in fork). But this must not affect this code - here. Since this function would have to be called while - the thread is executing fork, it would have to happen in - a signal handler. But this is no allowed, pthread_cancel - is not guaranteed to be async-safe. */ - int val; - val = INTERNAL_SYSCALL (tgkill, err, 3, - THREAD_GETMEM (THREAD_SELF, pid), pd->tid, - SIGCANCEL); + pid_t pid = getpid (); + INTERNAL_SYSCALL_DECL (err); + int val = INTERNAL_SYSCALL_CALL (tgkill, err, pid, pd->tid, + SIGCANCEL); if (INTERNAL_SYSCALL_ERROR_P (val, err)) result = INTERNAL_SYSCALL_ERRNO (val, err); #else diff --git a/nptl/pthread_getattr_np.c b/nptl/pthread_getattr_np.c index fb906f0484..32d7484bf8 100644 --- a/nptl/pthread_getattr_np.c +++ b/nptl/pthread_getattr_np.c @@ -68,7 +68,6 @@ pthread_getattr_np (pthread_t thread_id, pthread_attr_t *attr) { /* No stack information available. This must be for the initial thread. Get the info in some magical way. */ - assert (abs (thread->pid) == thread->tid); /* Stack size limit. */ struct rlimit rl; |