]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Add adaptive elision to rwlocks
authorAndi Kleen <ak@linux.intel.com>
Mon, 31 Mar 2014 15:07:46 +0000 (08:07 -0700)
committerAndi Kleen <ak@linux.intel.com>
Fri, 13 Jun 2014 20:15:28 +0000 (13:15 -0700)
This patch relies on the C version of the rwlocks posted earlier.
With C rwlocks it is very straight forward to do adaptive elision
using TSX. It is based on the infrastructure added earlier
for mutexes, but uses its own elision macros. The macros
are fairly general purpose and could be used for other
elision purposes too.

This version is much cleaner than the earlier assembler based
version, and in particular implements adaptation which makes
it safer.

I changed the behavior slightly to not require any changes
in the test suite and fully conform to all expected
behaviors (generally at the cost of not eliding in
various situations). In particular this means the timedlock
variants are not elided.  Nested trylock aborts.

23 files changed:
ChangeLog
nptl/pthread_rwlock_rdlock.c
nptl/pthread_rwlock_tryrdlock.c
nptl/pthread_rwlock_trywrlock.c
nptl/pthread_rwlock_unlock.c
nptl/pthread_rwlock_wrlock.c
nptl/sysdeps/unix/sysv/linux/s390/bits/pthreadtypes.h
sysdeps/arm/nptl/bits/pthreadtypes.h
sysdeps/nptl/pthread.h
sysdeps/sh/nptl/bits/pthreadtypes.h
sysdeps/sparc/nptl/bits/pthreadtypes.h
sysdeps/tile/nptl/bits/pthreadtypes.h
sysdeps/unix/sysv/linux/aarch64/nptl/bits/pthreadtypes.h
sysdeps/unix/sysv/linux/alpha/bits/pthreadtypes.h
sysdeps/unix/sysv/linux/hppa/nptl/bits/pthreadtypes.h
sysdeps/unix/sysv/linux/ia64/nptl/bits/pthreadtypes.h
sysdeps/unix/sysv/linux/m68k/nptl/bits/pthreadtypes.h
sysdeps/unix/sysv/linux/microblaze/nptl/bits/pthreadtypes.h
sysdeps/unix/sysv/linux/mips/nptl/bits/pthreadtypes.h
sysdeps/unix/sysv/linux/powerpc/bits/pthreadtypes.h
sysdeps/unix/sysv/linux/x86/elision-conf.c
sysdeps/x86/nptl/bits/pthreadtypes.h
sysdeps/x86/nptl/elide.h [new file with mode: 0644]

index c2de6f8a05dd8609b49f5cf3c8ec67db20a17b2b..6e3d2a9a72d606307f9cf26014031cfbda47da91 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,51 @@
+2014-06-13  Andi Kleen  <ak@linux.intel.com>
+
+       * nptl/pthread_rwlock_rdlock.c: Include elide.h.
+        (pthread_rwlock_rdlock): Add elision.
+        * nptl/pthread_rwlock_wrlock.c: Include elide.h.
+        (pthread_rwlock_wrlock): Add elision.
+        * nptl/pthread_rwlock_trywrlock.c: Include elide.h.
+        (pthread_rwlock_trywrlock): Add elision.
+        * nptl/pthread_rwlock_tryrdlock.c: Include elide.h.
+        (pthread_rwlock_tryrdlock): Add elision.
+        * nptl/pthread_rwlock_unlock.c: Include elide.h.
+        (pthread_rwlock_tryrdlock): Add elision unlock.
+        * nptl/sysdeps/pthread/pthread.h:
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Handle new define
+        (PTHREAD_RWLOCK_INITIALIZER,
+        PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP):
+        Handle new elision field.
+        * sysdeps/x86/nptl/elide.h: New file. Add generic elision macros.
+        * sysdeps/arm/nptl/bits/pthreadtypes.h
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Add.
+        * sysdeps/sh/nptl/bits/pthreadtypes.h
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Add.
+        * sysdeps/tile/nptl/bits/pthreadtypes.h
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Add.
+        * sysdeps/a/nptl/bits/pthreadtypes.h
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Add.
+        * sysdeps/unix/sysv/linux/aarch64/nptl/bits/pthreadtypes.h
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Add.
+        * sysdeps/unix/sysv/linux/alpha/nptl/bits/pthreadtypes.h
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Add.
+        * sysdeps/unix/sysv/linux/hppa/nptl/bits/pthreadtypes.h
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Add.
+        * sysdeps/unix/sysv/linux/ia64/nptl/bits/pthreadtypes.h
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Add.
+        * sysdeps/unix/sysv/linux/m68k/nptl/bits/pthreadtypes.h
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Add.
+        * sysdeps/unix/sysv/linux/microblaze/nptl/bits/pthreadtypes.h
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Add.
+        * sysdeps/unix/sysv/linux/mips/nptl/bits/pthreadtypes.h
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Add.
+        * sysdeps/unix/sysv/linux/powerpc/nptl/bits/pthreadtypes.h
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Add.
+        * sysdeps/unix/sysv/linux/x86/elision-conf.c:
+        (elision_init): Set try_xbegin to zero when no RTM.
+        * sysdeps/x86/nptl/bits/pthreadtypes.h
+        (pthread_rwlock_t): Change __pad1 to __rwelision.
+        (__PTHREAD_RWLOCK_ELISION_EXTRA): Add.
+
 2014-06-13  Andi Kleen  <ak@linux.intel.com>
 
        * nptl/pthread_rwlock_rdlock (__pthread_rwlock_rdlock):
index 1df0327a22da6ab949d2e1d70030db209dc20b2c..6eb9e091c49bddfb11a0710f25088edee75b9ab2 100644 (file)
@@ -22,6 +22,7 @@
 #include <pthread.h>
 #include <pthreadP.h>
 #include <stap-probe.h>
+#include <elide.h>
 
 
 /* Acquire read lock for RWLOCK.  Slow path.  */
@@ -102,6 +103,12 @@ __pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
 
   LIBC_PROBE (rdlock_entry, 1, rwlock);
 
+  if (ELIDE_LOCK (rwlock->__data.__rwelision,
+                 rwlock->__data.__lock == 0
+                 && rwlock->__data.__writer == 0
+                 && rwlock->__data.__nr_readers == 0))
+    return 0;
+
   /* Make sure we are alone.  */
   lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);
 
index f7b1e6b8acba758314667ea3ef09ff2497b784dc..cf8d68eb3f65a288653bb23741cb07ea89d78126 100644 (file)
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include "pthreadP.h"
 #include <lowlevellock.h>
+#include <elide.h>
 
 
 int
@@ -26,6 +27,12 @@ __pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
 {
   int result = EBUSY;
 
+  if (ELIDE_TRYLOCK (rwlock->__data.__rwelision,
+                    rwlock->__data.__lock == 0
+                    && rwlock->__data.__nr_readers == 0
+                    && rwlock->__data.__writer, 0))
+    return 0;
+
   lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);
 
   if (rwlock->__data.__writer == 0
index 106f157c1dd861e1ba03e6ccc1e4471c13a835d3..0291fc9e3c4c8d50c554f136bc402ed6c742a3a0 100644 (file)
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include "pthreadP.h"
 #include <lowlevellock.h>
+#include <elide.h>
 
 
 int
@@ -26,6 +27,12 @@ __pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
 {
   int result = EBUSY;
 
+  if (ELIDE_TRYLOCK (rwlock->__data.__rwelision,
+                    rwlock->__data.__lock == 0
+                    && rwlock->__data.__nr_readers == 0
+                    && rwlock->__data.__writer, 1))
+    return 0;
+
   lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);
 
   if (rwlock->__data.__writer == 0 && rwlock->__data.__nr_readers == 0)
index d4923838e718b03b5739a0ebf0490d55caf36776..3ebddeb246e1e910ea00a1634930fb66ebbdaa56 100644 (file)
@@ -22,6 +22,8 @@
 #include <pthread.h>
 #include <pthreadP.h>
 #include <stap-probe.h>
+#include <elide.h>
+
 
 /* Unlock RWLOCK.  */
 int
@@ -29,6 +31,10 @@ __pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
 {
   LIBC_PROBE (rwlock_unlock, 1, rwlock);
 
+  if (ELIDE_UNLOCK (rwlock->__data.__writer == 0
+                   && rwlock->__data.__nr_readers == 0))
+    return 0;
+
   lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);
   if (rwlock->__data.__writer)
     rwlock->__data.__writer = 0;
index de54e51432bd4db9ebd4354d9b533dcd7f8f29ee..91ad82a1bed96b60bad4759487ef479c98990e4f 100644 (file)
@@ -22,6 +22,7 @@
 #include <pthread.h>
 #include <pthreadP.h>
 #include <stap-probe.h>
+#include <elide.h>
 
 
 /* Acquire write lock for RWLOCK.  */
@@ -91,6 +92,12 @@ __pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
 {
   LIBC_PROBE (wrlock_entry, 1, rwlock);
 
+  if (ELIDE_LOCK (rwlock->__data.__rwelision,
+                 rwlock->__data.__lock == 0
+                 && rwlock->__data.__writer == 0
+                 && rwlock->__data.__nr_readers == 0))
+    return 0;
+
   /* Make sure we are alone.  */
   lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);
 
index d70f8b35b158835322091558adfcad164625facc..c9f1c833a8f007b5546222a68e7407f5951760af 100644 (file)
@@ -214,6 +214,8 @@ typedef union
   long int __align;
 } pthread_rwlock_t;
 
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 0
+
 typedef union
 {
   char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
index 64b9e0963e682d109816a6f3d9ad0525503ec645..3ece5b9a1a15a13f6bc70cde0404d189d9110f63 100644 (file)
@@ -155,6 +155,8 @@ typedef union
   long int __align;
 } pthread_rwlock_t;
 
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 0
+
 typedef union
 {
   char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
index 40a3e215cc7fbee089ef089f1d8abbb123ddf506..682a1ae5973009ff8fbe7e10bfa5ac15e181067a 100644 (file)
@@ -132,17 +132,17 @@ enum
 
 /* Read-write lock initializers.  */
 # define PTHREAD_RWLOCK_INITIALIZER \
-  { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }
+  { { 0, 0, 0, 0, 0, 0, 0, 0, __PTHREAD_RWLOCK_ELISION_EXTRA, 0, 0 } }
 # ifdef __USE_GNU
 #  ifdef __PTHREAD_RWLOCK_INT_FLAGS_SHARED
 #   define PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP \
-  { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                                          \
+  { { 0, 0, 0, 0, 0, 0, 0, 0, __PTHREAD_RWLOCK_ELISION_EXTRA, 0,                                             \
        PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP } }
 #  else
 #   if __BYTE_ORDER == __LITTLE_ENDIAN
 #    define PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP \
   { { 0, 0, 0, 0, 0, 0, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP, \
-      0, 0, 0, 0 } }
+      0, __PTHREAD_RWLOCK_ELISION_EXTRA, 0, 0 } }
 #   else
 #    define PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP \
   { { 0, 0, 0, 0, 0, 0, 0, 0, 0, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP,\
index a177f2834700d9608156c11c0fc666cf4c03ea68..363a94848cf2f050de3dce1bf4c7772d7c2c77b3 100644 (file)
@@ -155,6 +155,8 @@ typedef union
   long int __align;
 } pthread_rwlock_t;
 
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 0
+
 typedef union
 {
   char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
index 8c0340a963dd40c5449c28be0768584c213798de..0d225a3c65583741fc3aded8b48d2235807c381b 100644 (file)
@@ -194,6 +194,8 @@ typedef union
   long int __align;
 } pthread_rwlock_t;
 
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 0
+
 typedef union
 {
   char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
index 5ca3391ce54c5e96d64cd74beba118a44b06409b..f808bb253345f2b4dba1c6e380dc9a8f1a403f4f 100644 (file)
@@ -194,6 +194,8 @@ typedef union
   long int __align;
 } pthread_rwlock_t;
 
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 0
+
 typedef union
 {
   char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
index 1a44bb6101dd9d9caad73ae450c541090e8eff4b..5ad0e700beb228dd7494c8f36753e468fb7145f3 100644 (file)
@@ -141,6 +141,8 @@ typedef union
   long int __align;
 } pthread_rwlock_t;
 
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 0
+
 typedef union
 {
   char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
index 3dbe6127ddeb9c707f4615e0786e14a3a1390f98..55f1067c701989bf8a9018d52e82fb4d28b5c4a9 100644 (file)
@@ -142,6 +142,8 @@ typedef union
   long int __align;
 } pthread_rwlock_t;
 
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 0
+
 typedef union
 {
   char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
index a79c195d9a8288752997aae84770845e9370d645..d6fdc2c44d71d984d707c43e4c45dab486fea2a8 100644 (file)
@@ -196,6 +196,8 @@ typedef union
   long int __align;
 } pthread_rwlock_t;
 
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 0
+
 typedef union
 {
   char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
index 9468329000a9896efa7dfabc9072a990ce2d5286..71e4785e642b068ac17b922147cf86d39dcf7213 100644 (file)
@@ -143,6 +143,8 @@ typedef union
   long int __align;
 } pthread_rwlock_t;
 
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 0
+
 typedef union
 {
   char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
index 1e1fed82dcae852b4f87bbedd189f51690b9c4c4..fd46c781b3ace6227d122768571042e792de727d 100644 (file)
@@ -146,6 +146,8 @@ typedef union
   long int __align;
 } pthread_rwlock_t;
 
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 0
+
 typedef union
 {
   char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
index 9c7e620e2e49839e1370e33ca88d9ecae759c70f..943445384b51c9cac93bb1396492f3287f80756b 100644 (file)
@@ -150,6 +150,8 @@ typedef union
   long int __align;
 } pthread_rwlock_t;
 
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 0
+
 typedef union
 {
   char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
index cefd2b6316bd4c6b89b272c9eeb06f5c8d6b09de..843031e854115e185a7d327b445a2062b88296fd 100644 (file)
@@ -203,6 +203,8 @@ typedef union
   long int __align;
 } pthread_rwlock_t;
 
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 0
+
 typedef union
 {
   char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
index eda3d1a3e4dc108683987f9ee8abe90b4045eceb..4e9c5184aa6b56f84ede207cdd91269078f66027 100644 (file)
@@ -194,6 +194,8 @@ typedef union
   long int __align;
 } pthread_rwlock_t;
 
+#define __PTHREAD_RWLOCK_ELISION_EXTRA 0
+
 typedef union
 {
   char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
index e6f5d6d1aba95a8090ddd7990d1d43e2511493b0..28e48d9e922417a3ceb3fc3c8b5d22e459485d4d 100644 (file)
@@ -66,6 +66,8 @@ elision_init (int argc __attribute__ ((unused)),
 #ifdef ENABLE_LOCK_ELISION
   __pthread_force_elision = __libc_enable_secure ? 0 : __elision_available;
 #endif
+  if (!HAS_RTM)
+    __elision_aconf.retry_try_xbegin = 0; /* Disable elision on rwlocks */
 }
 
 #ifdef SHARED
index b4329f652b42c5ed692ef01184f2e053ac5a2c85..b04c32b11f9cad6b0bd44b3c8517ccc2be580359 100644 (file)
@@ -184,11 +184,13 @@ typedef union
     unsigned int __nr_writers_queued;
     int __writer;
     int __shared;
-    unsigned long int __pad1;
+    signed char __rwelision;
+    unsigned char __pad1[7];
     unsigned long int __pad2;
     /* FLAGS must stay at this position in the structure to maintain
        binary compatibility.  */
     unsigned int __flags;
+# define __PTHREAD_RWLOCK_ELISION_EXTRA 0, {0, 0, 0, 0, 0, 0, 0 }
 # define __PTHREAD_RWLOCK_INT_FLAGS_SHARED     1
   } __data;
 # else
@@ -204,7 +206,8 @@ typedef union
        binary compatibility.  */
     unsigned char __flags;
     unsigned char __shared;
-    unsigned char __pad1;
+    signed char __rwelision;
+# define __PTHREAD_RWLOCK_ELISION_EXTRA 0
     unsigned char __pad2;
     int __writer;
   } __data;
diff --git a/sysdeps/x86/nptl/elide.h b/sysdeps/x86/nptl/elide.h
new file mode 100644 (file)
index 0000000..19f27e5
--- /dev/null
@@ -0,0 +1,109 @@
+/* elide.h: Generic lock elision support.
+   Copyright (C) 2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   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, see
+   <http://www.gnu.org/licenses/>.  */
+#ifndef ELIDE_H
+#define ELIDE_H 1
+
+#include <hle.h>
+#include <elision-conf.h>
+
+#define ACCESS_ONCE(x) (* (volatile typeof(x) *) &(x))
+
+/* Adapt elision with ADAPT_COUNT and STATUS and decide retries.  */
+
+static inline bool
+elision_adapt(uint8_t *adapt_count, unsigned int status)
+{
+  if (status & _XABORT_RETRY)
+    return false;
+  if ((status & _XABORT_EXPLICIT)
+      && _XABORT_CODE (status) == _ABORT_LOCK_BUSY)
+    {
+      /* Right now we skip here.  Better would be to wait a bit
+        and retry.  This likely needs some spinning. Be careful
+        to avoid writing the lock.  */
+      if (*adapt_count != __elision_aconf.skip_lock_busy)
+       ACCESS_ONCE (*adapt_count) = __elision_aconf.skip_lock_busy;
+    }
+  /* Internal abort.  There is no chance for retry.
+     Use the normal locking and next time use lock.
+     Be careful to avoid writing to the lock.  */
+  else if (*adapt_count != __elision_aconf.skip_lock_internal_abort)
+    ACCESS_ONCE (*adapt_count) = __elision_aconf.skip_lock_internal_abort;
+  return true;
+}
+
+/* is_lock_free must be executed inside the transaction */
+
+/* Returns true if lock defined by IS_LOCK_FREE was elided.
+   ADAPT_COUNT is a pointer to per-lock state variable. */
+
+#define ELIDE_LOCK(adapt_count, is_lock_free)                  \
+  ({                                                           \
+    int ret = 0;                                               \
+                                                               \
+    if ((adapt_count) <= 0)                                    \
+      {                                                                \
+        for (int i = __elision_aconf.retry_try_xbegin; i > 0; i--) \
+          {                                                    \
+            unsigned int status;                               \
+           if ((status = _xbegin ()) == _XBEGIN_STARTED)       \
+             {                                                 \
+               if (is_lock_free)                               \
+                 {                                             \
+                   ret = 1;                                    \
+                   break;                                      \
+                 }                                             \
+               _xabort (_ABORT_LOCK_BUSY);                     \
+             }                                                 \
+           if (!elision_adapt (&(adapt_count), status))        \
+             break;                                            \
+          }                                                    \
+      }                                                                \
+    else                                                       \
+      (adapt_count)--; /* missing updates ok */                        \
+    ret;                                                       \
+  })
+
+/* Returns true if lock defined by IS_LOCK_FREE was try-elided.
+   ADAPT_COUNT is a pointer to per-lock state variable.  */
+
+#define ELIDE_TRYLOCK(adapt_count, is_lock_free, write) ({     \
+  int ret = 0;                                         \
+  if (__elision_aconf.retry_try_xbegin > 0)            \
+    {                                                          \
+      if (write)                                       \
+        _xabort (_ABORT_NESTED_TRYLOCK);               \
+      ret = ELIDE_LOCK (adapt_count, is_lock_free);     \
+    }                                                  \
+    ret;                                               \
+    })
+
+/* Returns true if lock defined by IS_LOCK_FREE was elided.  */
+
+#define ELIDE_UNLOCK(is_lock_free)             \
+  ({                                           \
+  int ret = 0;                                 \
+  if (is_lock_free)                            \
+    {                                          \
+      _xend ();                                        \
+      ret = 1;                                 \
+    }                                          \
+  ret;                                         \
+  })
+
+#endif