1  /* elision-conf.c: Lock elision tunable parameters.
       2     Copyright (C) 2015-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include "config.h"
      20  #include <pthreadP.h>
      21  #include <elision-conf.h>
      22  #include <unistd.h>
      23  #include <dl-procinfo.h>
      24  
      25  #define TUNABLE_NAMESPACE elision
      26  #include <elf/dl-tunables.h>
      27  
      28  /* Reasonable initial tuning values, may be revised in the future.
      29     This is a conservative initial value.  */
      30  
      31  struct elision_config __elision_aconf =
      32    {
      33      /* How many times to use a non-transactional lock after a transactional
      34         failure has occurred because the lock is already acquired.  Expressed
      35         in number of lock acquisition attempts.  */
      36      .skip_lock_busy = 3,
      37      /* How often to not attempt to use elision if a transaction aborted due
      38         to reasons other than other threads' memory accesses.  Expressed in
      39         number of lock acquisition attempts.  */
      40      .skip_lock_internal_abort = 3,
      41      /* How often to not attempt to use elision if a lock used up all retries
      42         without success.  Expressed in number of lock acquisition attempts.  */
      43      .skip_lock_out_of_tbegin_retries = 3,
      44      /* How often we retry using elision if there is chance for the transaction
      45         to finish execution (e.g., it wasn't aborted due to the lock being
      46         already acquired.  */
      47      .try_tbegin = 3,
      48      /* Same as SKIP_LOCK_INTERNAL_ABORT but for trylock.  */
      49      .skip_trylock_internal_abort = 3,
      50    };
      51  
      52  static inline void
      53  __always_inline
      54  do_set_elision_enable (int32_t elision_enable)
      55  {
      56    /* Enable elision if it's available in hardware. It's not necessary to check
      57       if __libc_enable_secure isn't enabled since elision_enable will be set
      58       according to the default, which is disabled.  */
      59    if (elision_enable == 1)
      60      __pthread_force_elision = (GLRO (dl_hwcap2)
      61  			       & PPC_FEATURE2_HAS_HTM) ? 1 : 0;
      62  }
      63  
      64  /* The pthread->elision_enable tunable is 0 or 1 indicating that elision
      65     should be disabled or enabled respectively.  The feature will only be used
      66     if it's supported by the hardware.  */
      67  
      68  void
      69  TUNABLE_CALLBACK (set_elision_enable) (tunable_val_t *valp)
      70  {
      71    int32_t elision_enable = (int32_t) valp->numval;
      72    do_set_elision_enable (elision_enable);
      73  }
      74  
      75  #define TUNABLE_CALLBACK_FNDECL(__name, __type)			\
      76  static inline void						\
      77  __always_inline							\
      78  do_set_elision_ ## __name (__type value)			\
      79  {								\
      80    __elision_aconf.__name = value;				\
      81  }								\
      82  void								\
      83  TUNABLE_CALLBACK (set_elision_ ## __name) (tunable_val_t *valp) \
      84  {								\
      85    __type value = (__type) (valp)->numval;			\
      86    do_set_elision_ ## __name (value);				\
      87  }
      88  
      89  TUNABLE_CALLBACK_FNDECL (skip_lock_busy, int32_t);
      90  TUNABLE_CALLBACK_FNDECL (skip_lock_internal_abort, int32_t);
      91  TUNABLE_CALLBACK_FNDECL (skip_lock_out_of_tbegin_retries, int32_t);
      92  TUNABLE_CALLBACK_FNDECL (try_tbegin, int32_t);
      93  TUNABLE_CALLBACK_FNDECL (skip_trylock_internal_abort, int32_t);
      94  
      95  /* Initialize elision.  */
      96  
      97  void
      98  __lll_elision_init (void)
      99  {
     100    /* Elision depends on tunables and must be explicitly turned on by setting
     101       the appropriate tunable on a supported platform.  */
     102  
     103    TUNABLE_GET (enable, int32_t,
     104  	       TUNABLE_CALLBACK (set_elision_enable));
     105    TUNABLE_GET (skip_lock_busy, int32_t,
     106  	       TUNABLE_CALLBACK (set_elision_skip_lock_busy));
     107    TUNABLE_GET (skip_lock_internal_abort, int32_t,
     108  	       TUNABLE_CALLBACK (set_elision_skip_lock_internal_abort));
     109    TUNABLE_GET (skip_lock_after_retries, int32_t,
     110  	       TUNABLE_CALLBACK (set_elision_skip_lock_out_of_tbegin_retries));
     111    TUNABLE_GET (tries, int32_t,
     112  	       TUNABLE_CALLBACK (set_elision_try_tbegin));
     113    TUNABLE_GET (skip_trylock_internal_abort, int32_t,
     114  	       TUNABLE_CALLBACK (set_elision_skip_trylock_internal_abort));
     115  
     116    /* Linux from 3.9 through 4.2 do not abort HTM transaction on syscalls,
     117       instead it suspends the transaction and resumes it when returning to
     118       usercode.  The side-effects of the syscall will always remain visible,
     119       even if the transaction is aborted.  This is an issue when a transaction
     120       is used along with futex syscall, on pthread_cond_wait for instance,
     121       where futex might succeed but the transaction is rolled back leading
     122       the condition variable object in an inconsistent state.
     123  
     124       Glibc used to prevent it by always aborting a transaction before issuing
     125       a syscall.  Linux 4.2 also decided to abort active transaction in
     126       syscalls which makes the glibc workaround superflours.  Worse, glibc
     127       transaction abortions leads to a performance issues on recent kernels.
     128  
     129       So Lock Elision is just enabled when it has been explicitly set (either
     130       by tunables of by a configure switch) and if kernel aborts HTM
     131       transactions on syscalls (PPC_FEATURE2_HTM_NOSC)  */
     132  
     133    __pthread_force_elision = (__pthread_force_elision
     134  			     && GLRO (dl_hwcap2) & PPC_FEATURE2_HTM_NOSC);
     135  
     136    if (!__pthread_force_elision)
     137      __elision_aconf.try_tbegin = 0; /* Disable elision on rwlocks.  */
     138  }