[RFC] tunables: Add elision tunable

Message ID 83765281-100b-a041-405e-d36b97581095@linux.vnet.ibm.com
State New
Headers show
Series
  • [RFC] tunables: Add elision tunable
Related show

Commit Message

rcardoso Sept. 11, 2017, 1:25 p.m.
Hello,

Attached to this mail I have a patch which add elision to tunables 
framework. This is a rebase of Paul Murphy's patch [1]. Also there's a 
RFC related to this [2].

[1] https://patchwork.sourceware.org/patch/10342/
[2] https://patchwork.sourceware.org/patch/20407/

Comments

Stefan Liebler Sept. 12, 2017, 9:35 a.m. | #1
On 09/11/2017 03:25 PM, rcardoso wrote:
> Hello,
> 
> Attached to this mail I have a patch which add elision to tunables 
> framework. This is a rebase of Paul Murphy's patch [1]. Also there's a 
> RFC related to this [2].
> 
> [1] https://patchwork.sourceware.org/patch/10342/
> [2] https://patchwork.sourceware.org/patch/20407/


Hi Rogerio,

Thanks for working on this topic.
In addition to Paul's patches, here is a link to a newer patch from Carlos:
"RFC: Always-on elision with per-process opt-in using tunables."
https://www.sourceware.org/ml/libc-alpha/2017-05/msg00335.html

He also removes the --enable-lock-elision configure flag.
Is this also required for your patch?

I've applied your patch to test it on s390x.
Some changes are needed in order to get it work:


diff --git a/manual/tunables.texi b/manual/tunables.texi
index 166624c..fb845b1 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -211,6 +211,7 @@ bounded, the user may set this tunable to limit the 
number of chunks
  that are scanned from the unsorted list while searching for chunks to
  pre-fill the per-thread cache with.  The default, or when set to zero,
  is no limit.
+@end deftp

  @node Elision Tunables
  @section Elision Tunables
diff --git a/sysdeps/unix/sysv/linux/s390/elision-conf.c 
b/sysdeps/unix/sysv/linux/s390/elision-conf.c
index 5f9c6d1..3dcf580 100644
--- a/sysdeps/unix/sysv/linux/s390/elision-conf.c
+++ b/sysdeps/unix/sysv/linux/s390/elision-conf.c
@@ -21,9 +21,9 @@
  #include <elision-conf.h>
  #include <unistd.h>
  #include <dl-procinfo.h>
-#ifdef HAVE_TUNABLES
-# include <elf/dl-tunables.h>
+#if HAVE_TUNABLES
  # define TUNABLE_NAMESPACE elision
+# include <elf/dl-tunables.h>
  #endif

  /* Reasonable initial tuning values, may be revised in the future.
@@ -57,7 +57,7 @@ struct elision_config __elision_aconf =

  int __pthread_force_elision attribute_hidden = 0;

-#ifdef HAVE_TUNABLES
+#if HAVE_TUNABLES
  static inline void
  __always_inline
  do_set_elision_enable (int32_t elision_enable)
@@ -106,7 +106,7 @@ elision_init (int argc __attribute__ ((unused)),
               char **argv  __attribute__ ((unused)),
               char **environ)
  {
-#ifdef HAVE_TUNABLES
+#if HAVE_TUNABLES
    /* Elision depends on tunables and must be explicitly turned on by 
setting
       the appropriate tunable on a supported platform.  */

Note: I haven't tested it on power or x86.


With these mentioned changes and the comments below,
I could build glibc and the testsuite runs without regressions.

I've also used a small s390 specific test program to check wether a 
mutex was elided or not:
./prog
Lock was a normal lock!

GLIBC_TUNABLES=glibc.elision.enable=0 ./prog
Lock was a normal lock!

GLIBC_TUNABLES=glibc.elision.enable=1 ./prog
Lock was elided via a transaction! (nesting-depth=1)

GLIBC_TUNABLES=glibc.elision.enable=1 ./prog_secure
Lock was a normal lock!

GLIBC_TUNABLES=glibc.elision.enable=2 ./prog
Lock was a normal lock!

GLIBC_TUNABLES=glibc.elision.enable=yes ./prog
Lock was a normal lock!

Note: I haven't tested setting the other variables.

Thanks,
Stefan
Gabriel F. T. Gomes Sept. 13, 2017, 1:42 p.m. | #2
On 12 Sep 2017, Stefan Liebler wrote:

>I've also used a small s390 specific test program to check wether a 
>mutex was elided or not:
>./prog
>Lock was a normal lock!
>
>GLIBC_TUNABLES=glibc.elision.enable=0 ./prog
>Lock was a normal lock!
>
>GLIBC_TUNABLES=glibc.elision.enable=1 ./prog
>Lock was elided via a transaction! (nesting-depth=1)
>
>GLIBC_TUNABLES=glibc.elision.enable=1 ./prog_secure
>Lock was a normal lock!

Would you be willing to share this test program (and your build
configurations)?  I understand that it is s390-specific, but it could help
anyway (at least for my education, it will).
Stefan Liebler Sept. 13, 2017, 2:13 p.m. | #3
On 09/13/2017 03:42 PM, Gabriel F. T. Gomes wrote:
> On 12 Sep 2017, Stefan Liebler wrote:
> 
>> I've also used a small s390 specific test program to check wether a
>> mutex was elided or not:
>> ./prog
>> Lock was a normal lock!
>>
>> GLIBC_TUNABLES=glibc.elision.enable=0 ./prog
>> Lock was a normal lock!
>>
>> GLIBC_TUNABLES=glibc.elision.enable=1 ./prog
>> Lock was elided via a transaction! (nesting-depth=1)
>>
>> GLIBC_TUNABLES=glibc.elision.enable=1 ./prog_secure
>> Lock was a normal lock!
> 
> Would you be willing to share this test program (and your build
> configurations)?  I understand that it is s390-specific, but it could help
> anyway (at least for my education, it will).
> 

Yes sure, please see the notes in the attached file.

Bye
Stefan
//CFLAGS=-march=zEC12 -mzarch
/* We need at least zEC12 and zarch (on 31bit) for transaction instructions!  */
//LDFLAGS=-lpthread
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <htmintrin.h> //for builtin_tx_nesting_depth
//https://gcc.gnu.org/onlinedocs/gcc/S_002f390-System-z-Built-in-Functions.html#S_002f390-System-z-Built-in-Functions
#include <sys/auxv.h>
#include <unistd.h>
#include <sys/types.h>

/* Note:
   You have to use configure flag --enable-lock-elision.
   Then transactions are always used (before the tunables patch).

   stli@xyz:# GLIBC_TUNABLES=glibc.elision.enable=1 ./isintransaction-s390x.new
   uid=12345; euid=12345
   Lock was elided via a transaction!

   Testing setuid:
   root@xyz:# cp isintransaction-s390x isintransaction-s390x_suid
   root@xyz:# chown root:root isintransaction-s390x_suid
   root@xyz:# chmod +s isintransaction-s390x_suid
   root@:# ls -lah
   -rwxrwxr-x  1 stli stli 142K Mar 31 09:33 isintransaction-s390x
   -rwsrwsr-x  1 root root 142K Mar 31 09:34 isintransaction-s390x_suid

   => Run isintransaction-s390x_suid as e.g. user stli and no transactions
   should be used even if you enabled it!
   stli@xyz:# GLIBC_TUNABLES=glibc.elision.enable=1 ./isintransaction-s390x_suid
   uid=12345; euid=0
   Lock was a normal lock!
*/

#define GETNESTINGDEPTH(var)					\
  ({								\
  __asm__ (".machine push \n\t"					\
	   ".machine \"all\"");					\
  var = __builtin_tx_nesting_depth ();				\
  __asm__ (".machine pop");					\
  })

int
main (void)
{
  uid_t uid = getuid ();
  uid_t euid = geteuid ();
  printf ("uid=%d; euid=%d\n", (int) uid, (int) euid);

  int have_te = (getauxval (AT_HWCAP) & HWCAP_S390_TE) ? 1 : 0;
  if (have_te == 0)
    puts ("This machine do not support transactional execution!\n"
	  "You need a >= zEC12 lpar or z/VM >= 6.4!\n");

  pthread_mutex_t testlock;
  if (pthread_mutex_init (&testlock, NULL) != 0)
    {
      printf("mutex init failed: %m \n");
      return EXIT_FAILURE;
    }

  int nestdepthInLock = -1;

  if (pthread_mutex_lock (&testlock))
    {
      printf ("LOCK failed: %m\n");
      return EXIT_FAILURE;
    }

  if (have_te)
    GETNESTINGDEPTH (nestdepthInLock);

  if (pthread_mutex_unlock (&testlock))
    {
      printf ("UNLOCK failed %m\n");
      return EXIT_FAILURE;
    }

  /* Note: a transaction can also be aborted even if lock-elision is available.
     If the lock should be elided but was not, then try again and set a debug
     the transaction fallback path.  */
  if (nestdepthInLock > 0)
    printf ("Lock was elided via a transaction! (nesting-depth=%d)\n",
	    nestdepthInLock);
  else if (nestdepthInLock < 0)
    puts ("Nesting-depth could not be determined!");
  else
    puts ("Lock was a normal lock!");

  if (pthread_mutex_destroy (&testlock))
    {
      printf("mutex destroy failed: %m \n");
      return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;
}

Patch

From eb98c17fb08e7e5810af2108e820924cebf062d0 Mon Sep 17 00:00:00 2001
From: Rogerio Alves <rcardoso@linux.vnet.ibm.com>
Date: Thu, 6 Jul 2017 13:21:08 -0500
Subject: [PATCH v6] Add elision tunables.

This patch adds several new tunables to control the behavior of
elision on supported platforms.  This also disables elision
by default on powerpc.

This patch was initially proposed by Paul Murphy[1] but was
"staled" because the framework have changed since the patch was
originally proposed.

[1] https://patchwork.sourceware.org/patch/10342/

2017-06-06  Rogerio A. Cardoso  <rcardoso@linux.vnet.ibm.com>,
	    Paul E. Murphy  <murphyp@linux.vnet.ibm.com>

	* elf/dl-tunables.list: Add elision parameters.
	* manual/tunables.texi: Add entries about elision tunable.
	* sysdeps/unix/sysv/linux/powerpc/elision-conf.c:
	Add callback functions to dynamically enable/disable elision.
	Add multiple callbacks functions to set elision parameters.
	* sysdeps/unix/sysv/linux/s390/elision-conf.c: Likewise.
	* sysdeps/unix/sysv/linux/x86/elision-conf.c: Likewise.
---
 Changes in v6: Based on Gabriel review. Fix small issues. Add
set_elision on CALLBACK macro for x86 s390.

 Changes in v5: Based on Gabriel review. Fix things that was uncompliant with 
 code standards. Fix others small things on manual that I was forgot on the 
 last review. 

 Changes in v4: Based on Gabriel review. Fix some small things on manual.

 Changes in v3: Based on Tulio review. Changed namespace from pthread to elision.
 Fix lots of extra spaces and typos. Fix some space/tab issues. Running 
 checkpath script to catch some code format issues. Reworked on commit message. 

 Changes in v2: Based on Raji review. Created manual entry for elision tunables. 
 Fix extra spaces on tunables list. Fix incorrect macro parameter on elision 
 enable for s390. Fix identation on defines. Include missing tunable namespace 
 on x86/elision-conf.c. Using #ifdef instead #if

 elf/dl-tunables.list                           | 34 ++++++++++++
 manual/tunables.texi                           | 64 +++++++++++++++++++++++
 sysdeps/unix/sysv/linux/powerpc/elision-conf.c | 72 +++++++++++++++++++++++---
 sysdeps/unix/sysv/linux/s390/elision-conf.c    | 69 ++++++++++++++++++++++--
 sysdeps/unix/sysv/linux/x86/elision-conf.c     | 71 ++++++++++++++++++++++---
 5 files changed, 293 insertions(+), 17 deletions(-)

diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
index c188c6a..77dfcb4 100644
--- a/elf/dl-tunables.list
+++ b/elf/dl-tunables.list
@@ -96,4 +96,38 @@  glibc {
       default: HWCAP_IMPORTANT
       }
   }
+
+  elision {
+    enable {
+      type: INT_32
+      minval: 0
+      maxval: 1
+      security_level: SXID_IGNORE
+    }
+    skip_lock_busy {
+      type: INT_32
+      default: 3
+      security_level: SXID_IGNORE
+    }
+    skip_lock_internal_abort {
+      type: INT_32
+      default: 3
+      security_level: SXID_IGNORE
+    }
+    skip_lock_after_retries {
+      type: INT_32
+      default: 3
+      security_level: SXID_IGNORE
+    }
+    tries {
+      type: INT_32
+      default: 3
+      security_level: SXID_IGNORE
+    }
+    skip_trylock_internal_abort {
+      type: INT_32
+      default: 3
+      security_level: SXID_IGNORE
+    }
+  }
 }
diff --git a/manual/tunables.texi b/manual/tunables.texi
index 3c19567..5b73f73 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -31,6 +31,7 @@  their own namespace.
 @menu
 * Tunable names::  The structure of a tunable name
 * Memory Allocation Tunables::  Tunables in the memory allocation subsystem
+* Elision Tunables::  Tunables in elision subsystem
 * Hardware Capability Tunables::  Tunables that modify the hardware
 				  capabilities seen by @theglibc{}
 @end menu
@@ -224,6 +225,69 @@  bounded, the user may set this tunable to limit the number of chunks
 that are scanned from the unsorted list while searching for chunks to
 pre-fill the per-thread cache with.  The default, or when set to zero,
 is no limit.
+
+@node Elision Tunables
+@section Elision Tunables
+@cindex elision tunables
+@cindex tunables, elision
+
+@deftp {Tunable namespace} glibc.elision
+Elision behavior can be modified by setting any of the following tunables in
+the @code{elision} namespace:
+@end deftp
+
+@deftp Tunable glibc.elision.enable
+The @code{glibc.elision.enable} tunable enables lock elision if the feature is
+supported by the hardware.  If elision is not supported by the hardware this
+tunable has no effect.
+
+Elision tunables are supported for x86-64, PowerPC, and S390 architectures.
+@end deftp
+
+@deftp Tunable glibc.elision.skip_lock_busy
+The @code{glibc.elision.skip_lock_busy} tunable sets how many times to use a
+non-transactional lock after a transactional failure has occurred because the
+lock is already acquired.  Expressed in number of lock acquisition attempts.
+
+The default value of this tunable is @samp{3}.
+@end deftp
+
+@deftp Tunable glibc.elision.skip_lock_internal_abort
+The @code{glibc.elision.skip_lock_internal_abort} tunable sets how many times
+to not attempt to use elision if a transaction aborted due to reasons other
+than other threads' memory accesses.  Expressed in number of lock acquisition
+attempts.
+
+The default value of this tunable is @samp{3}.
+@end deftp
+
+@deftp Tunable glibc.elision.skip_lock_after_retries
+The @code{glibc.elision.skip_lock_after_retries} tunable sets how my times to
+try to elide a lock with transactions that only fail due other threads' memory
+accesses, before falling back to regular lock.
+Expressed in number of lock elision attempts.
+
+This tunable is not supported by x86.
+
+The default value of this tunable is @samp{3}.
+@end deftp
+
+@deftp Tunable glibc.elision.tries
+The @code{glibc.elision.tries} sets how many times we retry using elision if
+there is chance for the transaction to finish execution (e.g., it wasn't aborted
+due to the lock being already acquired).  This tunable is set to @samp{0} if
+elision is not supported by the hardware to avoid retries.
+
+The default value of this tunable is @samp{3}.
+@end deftp
+
+@deftp Tunable glibc.elision.skip_trylock_internal_abort
+The @code{glibc.elision.skip_trylock_internal_abort} tunable sets how many times
+to not attempt to use elision for trylocks if a transaction aborted due to
+reasons other than other threads' memory accesses.  Expressed in number of try
+lock attempts.
+
+The default value of this tunable is @samp{3}.
 @end deftp
 
 @node Hardware Capability Tunables
diff --git a/sysdeps/unix/sysv/linux/powerpc/elision-conf.c b/sysdeps/unix/sysv/linux/powerpc/elision-conf.c
index f631f0a..07ebe37 100644
--- a/sysdeps/unix/sysv/linux/powerpc/elision-conf.c
+++ b/sysdeps/unix/sysv/linux/powerpc/elision-conf.c
@@ -21,6 +21,10 @@ 
 #include <elision-conf.h>
 #include <unistd.h>
 #include <dl-procinfo.h>
+#ifdef HAVE_TUNABLES
+# include <elf/dl-tunables.h>
+# define TUNABLE_NAMESPACE elision
+#endif
 
 /* Reasonable initial tuning values, may be revised in the future.
    This is a conservative initial value.  */
@@ -50,7 +54,50 @@  struct elision_config __elision_aconf =
    DEFAULT locks should be automatically use elision in pthread_mutex_lock().
    Disabled for suid programs.  Only used when elision is available.  */
 
-int __pthread_force_elision attribute_hidden;
+int __pthread_force_elision attribute_hidden = 0;
+
+#ifdef HAVE_TUNABLES
+static inline void
+__always_inline
+do_set_elision_enable (int32_t elision_enable)
+{
+  /* Enable elision if it's avaliable in hardware.  */
+  if (elision_enable && !__libc_enable_secure)
+    __pthread_force_elision = (GLRO (dl_hwcap2)
+			       & PPC_FEATURE2_HAS_HTM) ? 1 : 0;
+}
+
+/* The pthread->elision_enable tunable is 0 or 1 indicating that elision
+   should be disabled or enabled respectively.  The feature will only be used
+   if it's supported by the hardware.  */
+
+void
+TUNABLE_CALLBACK (set_elision_enable) (tunable_val_t *valp)
+{
+  int32_t elision_enable = (int32_t) valp->numval;
+  do_set_elision_enable (elision_enable);
+}
+
+#define TUNABLE_CALLBACK_FNDECL(__name, __type)			\
+static inline void						\
+__always_inline							\
+do_set_elision_ ## __name (__type value)			\
+{								\
+  __elision_aconf.__name = value;				\
+}								\
+void								\
+TUNABLE_CALLBACK (set_elision_ ## __name) (tunable_val_t *valp) \
+{								\
+  __type value = (__type) (valp)->numval;			\
+  do_set_elision_ ## __name (value);				\
+}
+
+TUNABLE_CALLBACK_FNDECL (skip_lock_busy, int32_t);
+TUNABLE_CALLBACK_FNDECL (skip_lock_internal_abort, int32_t);
+TUNABLE_CALLBACK_FNDECL (skip_lock_out_of_tbegin_retries, int32_t);
+TUNABLE_CALLBACK_FNDECL (try_tbegin, int32_t);
+TUNABLE_CALLBACK_FNDECL (skip_trylock_internal_abort, int32_t);
+#endif
 
 /* Initialize elision.  */
 
@@ -59,13 +106,26 @@  elision_init (int argc __attribute__ ((unused)),
 	      char **argv  __attribute__ ((unused)),
 	      char **environ)
 {
-#ifdef ENABLE_LOCK_ELISION
-  int elision_available = (GLRO (dl_hwcap2) & PPC_FEATURE2_HAS_HTM) ? 1 : 0;
-  __pthread_force_elision = __libc_enable_secure ? 0 : elision_available;
+#ifdef HAVE_TUNABLES
+  /* Elision depends on tunables and must be explicitly turned on by setting
+     the appropriate tunable on a supported platform.  */
+
+  TUNABLE_GET (enable, int32_t,
+	       TUNABLE_CALLBACK (set_elision_enable));
+  TUNABLE_GET (skip_lock_busy, int32_t,
+	       TUNABLE_CALLBACK (set_elision_skip_lock_busy));
+  TUNABLE_GET (skip_lock_internal_abort, int32_t,
+	       TUNABLE_CALLBACK (set_elision_skip_lock_internal_abort));
+  TUNABLE_GET (skip_lock_after_retries, int32_t,
+	       TUNABLE_CALLBACK (set_elision_skip_lock_out_of_tbegin_retries));
+  TUNABLE_GET (tries, int32_t,
+	       TUNABLE_CALLBACK (set_elision_try_tbegin));
+  TUNABLE_GET (skip_trylock_internal_abort, int32_t,
+	       TUNABLE_CALLBACK (set_elision_skip_trylock_internal_abort));
 #endif
+
   if (!__pthread_force_elision)
-    /* Disable elision on rwlocks.  */
-    __elision_aconf.try_tbegin = 0;
+    __elision_aconf.try_tbegin = 0; /* Disable elision on rwlocks.  */
 }
 
 #ifdef SHARED
diff --git a/sysdeps/unix/sysv/linux/s390/elision-conf.c b/sysdeps/unix/sysv/linux/s390/elision-conf.c
index cc0fdef..5f9c6d1 100644
--- a/sysdeps/unix/sysv/linux/s390/elision-conf.c
+++ b/sysdeps/unix/sysv/linux/s390/elision-conf.c
@@ -21,6 +21,10 @@ 
 #include <elision-conf.h>
 #include <unistd.h>
 #include <dl-procinfo.h>
+#ifdef HAVE_TUNABLES
+# include <elf/dl-tunables.h>
+# define TUNABLE_NAMESPACE elision
+#endif
 
 /* Reasonable initial tuning values, may be revised in the future.
    This is a conservative initial value.  */
@@ -53,6 +57,48 @@  struct elision_config __elision_aconf =
 
 int __pthread_force_elision attribute_hidden = 0;
 
+#ifdef HAVE_TUNABLES
+static inline void
+__always_inline
+do_set_elision_enable (int32_t elision_enable)
+{
+  /* Enable elision if it's avaliable in hardware.  */
+  if (elision_enable && !__libc_enable_secure)
+    __pthread_force_elision = (GLRO (dl_hwcap) & HWCAP_S390_TE) ? 1 : 0;
+}
+
+/* The pthread->elision_enable tunable is 0 or 1 indicating that elision
+   should be disabled or enabled respectively.  The feature will only be used
+   if it's supported by the hardware.  */
+
+void
+TUNABLE_CALLBACK (set_elision_enable) (tunable_val_t *valp)
+{
+  int32_t elision_enable = (int32_t) valp->numval;
+  do_set_elision_enable (elision_enable);
+}
+
+#define TUNABLE_CALLBACK_FNDECL(__name, __type)			\
+static inline void						\
+__always_inline							\
+do_set_elision_ ## __name (__type value)			\
+{								\
+  __elision_aconf.__name = value;				\
+}								\
+void								\
+TUNABLE_CALLBACK (set_elision_ ## __name) (tunable_val_t *valp) \
+{								\
+  __type value = (__type) (valp)->numval;			\
+  do_set_elision_ ## __name (value);				\
+}
+
+TUNABLE_CALLBACK_FNDECL (skip_lock_busy, int32_t);
+TUNABLE_CALLBACK_FNDECL (skip_lock_internal_abort, int32_t);
+TUNABLE_CALLBACK_FNDECL (skip_lock_out_of_tbegin_retries, int32_t);
+TUNABLE_CALLBACK_FNDECL (try_tbegin, int32_t);
+TUNABLE_CALLBACK_FNDECL (skip_trylock_internal_abort, int32_t);
+#endif
+
 /* Initialize elison.  */
 
 static void
@@ -60,11 +106,26 @@  elision_init (int argc __attribute__ ((unused)),
 	      char **argv  __attribute__ ((unused)),
 	      char **environ)
 {
-  /* Set when the CPU and the kernel supports transactional execution.
-     When false elision is never attempted.  */
-  int elision_available = (GLRO (dl_hwcap) & HWCAP_S390_TE) ? 1 : 0;
+#ifdef HAVE_TUNABLES
+  /* Elision depends on tunables and must be explicitly turned on by setting
+     the appropriate tunable on a supported platform.  */
+
+  TUNABLE_GET (enable, int32_t,
+	       TUNABLE_CALLBACK (set_elision_enable));
+  TUNABLE_GET (skip_lock_busy, int32_t,
+	       TUNABLE_CALLBACK (set_elision_skip_lock_busy));
+  TUNABLE_GET (skip_lock_internal_abort, int32_t,
+	       TUNABLE_CALLBACK (set_elision_skip_lock_internal_abort));
+  TUNABLE_GET (skip_lock_after_retries, int32_t,
+	       TUNABLE_CALLBACK (set_elision_skip_lock_out_of_tbegin_retries));
+  TUNABLE_GET (tries, int32_t,
+	       TUNABLE_CALLBACK (set_elision_try_tbegin));
+  TUNABLE_GET (skip_trylock_internal_abort, int32_t,
+	       TUNABLE_CALLBACK (set_elision_skip_trylock_internal_abort));
+#endif
 
-  __pthread_force_elision = __libc_enable_secure ? 0 : elision_available;
+  if (!__pthread_force_elision)
+    __elision_aconf.try_tbegin = 0; /* Disable elision on rwlocks.  */
 }
 
 #ifdef SHARED
diff --git a/sysdeps/unix/sysv/linux/x86/elision-conf.c b/sysdeps/unix/sysv/linux/x86/elision-conf.c
index 673b000..02e87dc 100644
--- a/sysdeps/unix/sysv/linux/x86/elision-conf.c
+++ b/sysdeps/unix/sysv/linux/x86/elision-conf.c
@@ -21,6 +21,10 @@ 
 #include <init-arch.h>
 #include <elision-conf.h>
 #include <unistd.h>
+#ifdef HAVE_TUNABLES
+# include <elf/dl-tunables.h>
+# define TUNABLE_NAMESPACE elision
+#endif
 
 /* Reasonable initial tuning values, may be revised in the future.
    This is a conservative initial value.  */
@@ -48,21 +52,74 @@  struct elision_config __elision_aconf =
    pthread_mutex_lock().  Disabled for suid programs.  Only used when elision
    is available.  */
 
-int __pthread_force_elision attribute_hidden;
+int __pthread_force_elision attribute_hidden = 0;
+
+#ifdef HAVE_TUNABLES
+static inline void
+__always_inline
+do_set_elision_enable (int32_t elision_enable)
+{
+  /* Enable elision if it's avaliable in hardware.  */
+  if (elision_enable && !__libc_enable_secure)
+    __pthread_force_elision = HAS_CPU_FEATURE (RTM) ? 1 : 0;
+}
+
+/* The pthread->elision_enable tunable is 0 or 1 indicating that elision
+   should be disabled or enabled respectively.  The feature will only be used
+   if it's supported by the hardware.  */
+
+void
+TUNABLE_CALLBACK (set_elision_enable) (tunable_val_t *valp)
+{
+  int32_t elision_enable = (int32_t) valp->numval;
+  do_set_elision_enable (elision_enable);
+}
+
+#define TUNABLE_CALLBACK_FNDECL(__name, __type)			\
+static inline void						\
+__always_inline							\
+do_set_elision_ ## __name (__type value)			\
+{								\
+  __elision_aconf.__name = value;				\
+}								\
+void								\
+TUNABLE_CALLBACK (set_elision_ ## __name) (tunable_val_t *valp) \
+{								\
+  __type value = (__type) (valp)->numval;			\
+  do_set_elision_ ## __name (value);				\
+}
 
-/* Initialize elison.  */
+TUNABLE_CALLBACK_FNDECL (skip_lock_busy, int32_t);
+TUNABLE_CALLBACK_FNDECL (skip_lock_internal_abort, int32_t);
+TUNABLE_CALLBACK_FNDECL (retry_try_xbegin, int32_t);
+TUNABLE_CALLBACK_FNDECL (skip_trylock_internal_abort, int32_t);
+#endif
+
+/* Initialize elision.  */
 
 static void
 elision_init (int argc __attribute__ ((unused)),
 	      char **argv  __attribute__ ((unused)),
 	      char **environ)
 {
-  int elision_available = HAS_CPU_FEATURE (RTM);
-#ifdef ENABLE_LOCK_ELISION
-  __pthread_force_elision = __libc_enable_secure ? 0 : elision_available;
+#ifdef HAVE_TUNABLES
+  /* Elision depends on tunables and must be explicitly turned on by setting
+     the appropriate tunable on a supported platform.  */
+
+  TUNABLE_GET (enable, int32_t,
+	       TUNABLE_CALLBACK (set_elision_enable));
+  TUNABLE_GET (skip_lock_busy, int32_t,
+	       TUNABLE_CALLBACK (set_elision_skip_lock_busy));
+  TUNABLE_GET (skip_lock_internal_abort, int32_t,
+	       TUNABLE_CALLBACK (set_elision_skip_lock_internal_abort));
+  TUNABLE_GET (tries, int32_t,
+	       TUNABLE_CALLBACK (set_elision_retry_try_xbegin));
+  TUNABLE_GET (skip_trylock_internal_abort, int32_t,
+	       TUNABLE_CALLBACK (set_elision_skip_trylock_internal_abort));
 #endif
-  if (!elision_available)
-    __elision_aconf.retry_try_xbegin = 0; /* Disable elision on rwlocks */
+
+  if (!__pthread_force_elision)
+    __elision_aconf.retry_try_xbegin = 0; /* Disable elision on rwlocks.  */
 }
 
 #ifdef SHARED
-- 
2.7.4