[PATCH] Fixes TLS performance degradation after dlopen
Philipp Trommler
philipp.trommler@mailbox.tu-dresden.de
Tue Jun 7 14:28:00 GMT 2016
Fixes a performance degradation of TLS access in shared libraries which
can be observed after another shared library that uses TLS is loaded
via dl_open. In this case __tls_get_addr takes significant more time.
Once that shared library accesses it's TLS, the performance normalizes.
Attached is a minimal example which gives the following output:
time: 0.723744
after dlopen,
time: 1.789016
after tls access in loaded lib,
time: 0.672865
We do have a use-case where this is actually really significant. I
believe this happens for instance if libstdc++ is loaded implicitly,
but TLS features are not actively used.
I strongly suspect this is the same issue as discussed in this post on
the µClibc mailing list:
https://lists.osuosl.org/pipermail/uclibc/2009-December/043375.html
and therefore the patch provided mainly reuses the solution they've
found for µClibc.
Main development platform information (tested on multiple platforms):
* latest glibc git version (pulled at Tue Jun  7 15:45:13 CEST 2016)
* x86_64 (but should be independent)
*Â Linux TP_Trommler 4.5.4-1-ARCH #1 SMP PREEMPT Wed May 11 22:21:28
CEST 2016 x86_64 GNU/Linux
*Â gcc (GCC) 6.1.1 20160501
*Â GNU ld (GNU Binutils) 2.26.0.20160501
The patch provided touches code already altered (but not released) by
the work on bug 19329.
2016-06-07  Philipp Trommler  <philipp.trommler@tu-dresden.de>
[BZ #19924]
* elf/dl-close.c: Port changes from the µClibc to fix
performance degradation after dl_open
* elf/dl-open.c: Likewise
* elf/dl-tls.c: Likewise
---
 elf/dl-close.c |  2 ++
 elf/dl-open.c  | 10 ++++++++--
 elf/dl-tls.c   | 35 ++++++++++++++++++++++-------------
 3 files changed, 32 insertions(+), 15 deletions(-)
diff --git a/elf/dl-close.c b/elf/dl-close.c
index 687d7de..f1d008f 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -32,6 +32,7 @@
 #include <sysdep-cancel.h>
 #include <tls.h>
 #include <stap-probe.h>
+#include <atomic.h>
Â
 #include <dl-unmap-segments.h>
Â
@@ -753,6 +754,7 @@ _dl_close_worker (struct link_map *map, bool force)
   /* If we removed any object which uses TLS bump the generation
counter.  */
   if (any_tls)
     {
+Â Â Â Â Â Â atomic_write_barrier ();
       if (__glibc_unlikely (++GL(dl_tls_generation) == 0))
 _dl_fatal_printf ("TLS generation counter wrapped!  Please
report as described in "REPORT_BUGS_TO".\n");
Â
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 6f178b3..e696c4e 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -524,9 +524,15 @@ dl_open_worker (void *a)
     }
Â
   /* Bump the generation number if necessary.  */
-Â Â if (any_tls && __builtin_expect (++GL(dl_tls_generation) == 0, 0))
-Â Â Â Â _dl_fatal_printf (N_("\
+Â Â if (any_tls)
+Â Â Â Â {
+Â Â Â Â Â Â atomic_write_barrier ();
+Â Â Â Â Â Â if (__builtin_expect (++GL(dl_tls_generation) == 0, 0))
+Â Â Â Â Â Â Â Â {
+Â Â Â Â Â Â Â Â Â Â _dl_fatal_printf (N_("\
 TLS generation counter wrapped!  Please report this."));
+Â Â Â Â Â Â Â Â }
+Â Â Â Â }
Â
   /* We need a second pass for static tls data, because
_dl_update_slotinfo
      must not be run while calls to _dl_add_to_slotinfo are still
pending.  */
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
index ed13fd9..7c2c5c7 100644
--- a/elf/dl-tls.c
+++ b/elf/dl-tls.c
@@ -629,12 +629,16 @@ _dl_update_slotinfo (unsigned long int req_modid)
      possible that other threads at the same time dynamically load
      code and therefore add to the slotinfo list.  This is a problem
      since we must not pick up any information about incomplete work.
-Â Â Â Â Â The solution to this is to ignore all dtv slots which were
-     created after the one we are currently interested.  We know that
-Â Â Â Â Â dynamic loading for this module is completed and this is the last
-     load operation we know finished.  */
+Â Â Â Â Â The solution to this is to ensure that we capture the current
+Â Â Â Â Â generation count before walking the list, and ignore any slots
+Â Â Â Â Â which are marked with a higher generation count, since they may
+     still be in the process of being updated.  */
   unsigned long int idx = req_modid;
   struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list);
+Â Â size_t new_gen = GL(dl_tls_generation);
+
+Â Â /* Make sure we don't read _dl_tls_generation too late. */
+Â Â atomic_read_barrier();
Â
   while (idx >= listp->len)
     {
@@ -642,13 +646,8 @@ _dl_update_slotinfo (unsigned long int req_modid)
       listp = listp->next;
     }
Â
-Â Â if (dtv[0].counter < listp->slotinfo[idx].gen)
     {
-Â Â Â Â Â Â /* The generation counter for the slot is higher than what the
-  current dtv implements.  We have to update the whole dtv but
- Â only those entries with a generation counter <= the one for
-  the entry we need.  */
-Â Â Â Â Â Â size_t new_gen = listp->slotinfo[idx].gen;
+Â Â Â Â Â Â /* Tilera: leave scope to make diffs against our baseline
easier. */
       size_t total = 0;
Â
       /* We have to look through the entire dtv slotinfo list.  */
@@ -916,7 +915,7 @@ _dl_add_to_slotinfo (struct link_map *l)
  the first slot.  */
       assert (idx == 0);
Â
-Â Â Â Â Â Â listp = prevp->next = (struct dtv_slotinfo_list *)
+Â Â Â Â Â Â listp = (struct dtv_slotinfo_list *)
 malloc (sizeof (struct dtv_slotinfo_list)
 + TLS_SLOTINFO_SURPLUS * sizeof (struct
dtv_slotinfo));
       if (listp == NULL)
@@ -939,9 +938,19 @@ cannot create TLS data structures"));
       listp->next = NULL;
       memset (listp->slotinfo, '\0',
       TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo));
+
+Â Â Â Â Â Â /* Make sure the new slotinfo element is in memory before
exposing it. */
+Â Â Â Â Â Â atomic_write_barrier();
+Â Â Â Â Â Â prevp->next = listp;
     }
Â
-  /* Add the information into the slotinfo data structure.  */
-Â Â listp->slotinfo[idx].map = l;
+  /* Add the information into the slotinfo data structure.  Make sure
+Â Â Â Â Â to barrier as we do it, to present a consistent view to other
+Â Â Â Â Â threads that may be calling __tls_get_addr() as we do this.
+Â Â Â Â Â Barrier after the writes so we know everything is ready for an
+     increment of _dl_tls_generation.  */
   listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1;
+Â Â atomic_write_barrier();
+Â Â listp->slotinfo[idx].map = l;
+Â Â atomic_write_barrier();
 }
--Â
2.5.0
-------------- next part --------------
A non-text attachment was scrubbed...
Name: dummy.c
Type: text/x-csrc
Size: 88 bytes
Desc: not available
URL: <http://sourceware.org/pipermail/libc-alpha/attachments/20160607/61f5d7bd/attachment.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Makefile
Type: text/x-makefile
Size: 405 bytes
Desc: not available
URL: <http://sourceware.org/pipermail/libc-alpha/attachments/20160607/61f5d7bd/attachment-0001.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: tls_access.c
Type: text/x-csrc
Size: 86 bytes
Desc: not available
URL: <http://sourceware.org/pipermail/libc-alpha/attachments/20160607/61f5d7bd/attachment-0002.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: tls_mini.c
Type: text/x-csrc
Size: 938 bytes
Desc: not available
URL: <http://sourceware.org/pipermail/libc-alpha/attachments/20160607/61f5d7bd/attachment-0003.bin>
More information about the Libc-alpha
mailing list