Blame sysdeps/unix/sysv/linux/powerpc/elision-conf.c

Packit Service 82fcde
/* elision-conf.c: Lock elision tunable parameters.
Packit Service 82fcde
   Copyright (C) 2015-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include "config.h"
Packit Service 82fcde
#include <pthreadP.h>
Packit Service 82fcde
#include <elision-conf.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
#include <dl-procinfo.h>
Packit Service 82fcde
Packit Service 82fcde
#if HAVE_TUNABLES
Packit Service 82fcde
# define TUNABLE_NAMESPACE elision
Packit Service 82fcde
#endif
Packit Service 82fcde
#include <elf/dl-tunables.h>
Packit Service 82fcde
Packit Service 82fcde
/* Reasonable initial tuning values, may be revised in the future.
Packit Service 82fcde
   This is a conservative initial value.  */
Packit Service 82fcde
Packit Service 82fcde
struct elision_config __elision_aconf =
Packit Service 82fcde
  {
Packit Service 82fcde
    /* How many times to use a non-transactional lock after a transactional
Packit Service 82fcde
       failure has occurred because the lock is already acquired.  Expressed
Packit Service 82fcde
       in number of lock acquisition attempts.  */
Packit Service 82fcde
    .skip_lock_busy = 3,
Packit Service 82fcde
    /* How often to not attempt to use elision if a transaction aborted due
Packit Service 82fcde
       to reasons other than other threads' memory accesses.  Expressed in
Packit Service 82fcde
       number of lock acquisition attempts.  */
Packit Service 82fcde
    .skip_lock_internal_abort = 3,
Packit Service 82fcde
    /* How often to not attempt to use elision if a lock used up all retries
Packit Service 82fcde
       without success.  Expressed in number of lock acquisition attempts.  */
Packit Service 82fcde
    .skip_lock_out_of_tbegin_retries = 3,
Packit Service 82fcde
    /* How often we retry using elision if there is chance for the transaction
Packit Service 82fcde
       to finish execution (e.g., it wasn't aborted due to the lock being
Packit Service 82fcde
       already acquired.  */
Packit Service 82fcde
    .try_tbegin = 3,
Packit Service 82fcde
    /* Same as SKIP_LOCK_INTERNAL_ABORT but for trylock.  */
Packit Service 82fcde
    .skip_trylock_internal_abort = 3,
Packit Service 82fcde
  };
Packit Service 82fcde
Packit Service 82fcde
/* Force elision for all new locks.  This is used to decide whether existing
Packit Service 82fcde
   DEFAULT locks should be automatically use elision in pthread_mutex_lock().
Packit Service 82fcde
   Disabled for suid programs.  Only used when elision is available.  */
Packit Service 82fcde
Packit Service 82fcde
int __pthread_force_elision attribute_hidden = 0;
Packit Service 82fcde
Packit Service 82fcde
#if HAVE_TUNABLES
Packit Service 82fcde
static inline void
Packit Service 82fcde
__always_inline
Packit Service 82fcde
do_set_elision_enable (int32_t elision_enable)
Packit Service 82fcde
{
Packit Service 82fcde
  /* Enable elision if it's avaliable in hardware. It's not necessary to check
Packit Service 82fcde
     if __libc_enable_secure isn't enabled since elision_enable will be set
Packit Service 82fcde
     according to the default, which is disabled.  */
Packit Service 82fcde
  if (elision_enable == 1)
Packit Service 82fcde
    __pthread_force_elision = (GLRO (dl_hwcap2)
Packit Service 82fcde
			       & PPC_FEATURE2_HAS_HTM) ? 1 : 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* The pthread->elision_enable tunable is 0 or 1 indicating that elision
Packit Service 82fcde
   should be disabled or enabled respectively.  The feature will only be used
Packit Service 82fcde
   if it's supported by the hardware.  */
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
TUNABLE_CALLBACK (set_elision_enable) (tunable_val_t *valp)
Packit Service 82fcde
{
Packit Service 82fcde
  int32_t elision_enable = (int32_t) valp->numval;
Packit Service 82fcde
  do_set_elision_enable (elision_enable);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
#define TUNABLE_CALLBACK_FNDECL(__name, __type)			\
Packit Service 82fcde
static inline void						\
Packit Service 82fcde
__always_inline							\
Packit Service 82fcde
do_set_elision_ ## __name (__type value)			\
Packit Service 82fcde
{								\
Packit Service 82fcde
  __elision_aconf.__name = value;				\
Packit Service 82fcde
}								\
Packit Service 82fcde
void								\
Packit Service 82fcde
TUNABLE_CALLBACK (set_elision_ ## __name) (tunable_val_t *valp) \
Packit Service 82fcde
{								\
Packit Service 82fcde
  __type value = (__type) (valp)->numval;			\
Packit Service 82fcde
  do_set_elision_ ## __name (value);				\
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
TUNABLE_CALLBACK_FNDECL (skip_lock_busy, int32_t);
Packit Service 82fcde
TUNABLE_CALLBACK_FNDECL (skip_lock_internal_abort, int32_t);
Packit Service 82fcde
TUNABLE_CALLBACK_FNDECL (skip_lock_out_of_tbegin_retries, int32_t);
Packit Service 82fcde
TUNABLE_CALLBACK_FNDECL (try_tbegin, int32_t);
Packit Service 82fcde
TUNABLE_CALLBACK_FNDECL (skip_trylock_internal_abort, int32_t);
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
/* Initialize elision.  */
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
elision_init (int argc __attribute__ ((unused)),
Packit Service 82fcde
	      char **argv  __attribute__ ((unused)),
Packit Service 82fcde
	      char **environ)
Packit Service 82fcde
{
Packit Service 82fcde
#if HAVE_TUNABLES
Packit Service 82fcde
  /* Elision depends on tunables and must be explicitly turned on by setting
Packit Service 82fcde
     the appropriate tunable on a supported platform.  */
Packit Service 82fcde
Packit Service 82fcde
  TUNABLE_GET (enable, int32_t,
Packit Service 82fcde
	       TUNABLE_CALLBACK (set_elision_enable));
Packit Service 82fcde
  TUNABLE_GET (skip_lock_busy, int32_t,
Packit Service 82fcde
	       TUNABLE_CALLBACK (set_elision_skip_lock_busy));
Packit Service 82fcde
  TUNABLE_GET (skip_lock_internal_abort, int32_t,
Packit Service 82fcde
	       TUNABLE_CALLBACK (set_elision_skip_lock_internal_abort));
Packit Service 82fcde
  TUNABLE_GET (skip_lock_after_retries, int32_t,
Packit Service 82fcde
	       TUNABLE_CALLBACK (set_elision_skip_lock_out_of_tbegin_retries));
Packit Service 82fcde
  TUNABLE_GET (tries, int32_t,
Packit Service 82fcde
	       TUNABLE_CALLBACK (set_elision_try_tbegin));
Packit Service 82fcde
  TUNABLE_GET (skip_trylock_internal_abort, int32_t,
Packit Service 82fcde
	       TUNABLE_CALLBACK (set_elision_skip_trylock_internal_abort));
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service e5510d
  /* Linux from 3.9 through 4.2 do not abort HTM transaction on syscalls,
Packit Service e5510d
     instead it suspends the transaction and resumes it when returning to
Packit Service e5510d
     usercode.  The side-effects of the syscall will always remain visible,
Packit Service e5510d
     even if the transaction is aborted.  This is an issue when a transaction
Packit Service e5510d
     is used along with futex syscall, on pthread_cond_wait for instance,
Packit Service e5510d
     where futex might succeed but the transaction is rolled back leading
Packit Service e5510d
     the condition variable object in an inconsistent state.
Packit Service e5510d
Packit Service e5510d
     Glibc used to prevent it by always aborting a transaction before issuing
Packit Service e5510d
     a syscall.  Linux 4.2 also decided to abort active transaction in
Packit Service e5510d
     syscalls which makes the glibc workaround superflours.  Worse, glibc
Packit Service e5510d
     transaction abortions leads to a performance issues on recent kernels.
Packit Service e5510d
Packit Service e5510d
     So Lock Elision is just enabled when it has been explict set (either
Packit Service e5510d
     by tunables of by a configure switch) and if kernel aborts HTM
Packit Service e5510d
     transactions on syscalls (PPC_FEATURE2_HTM_NOSC)  */
Packit Service e5510d
Packit Service e5510d
  __pthread_force_elision = (__pthread_force_elision
Packit Service e5510d
			     && GLRO (dl_hwcap2) & PPC_FEATURE2_HTM_NOSC);
Packit Service e5510d
Packit Service 82fcde
  if (!__pthread_force_elision)
Packit Service 82fcde
    __elision_aconf.try_tbegin = 0; /* Disable elision on rwlocks.  */
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
#ifdef SHARED
Packit Service 82fcde
# define INIT_SECTION ".init_array"
Packit Service 82fcde
# define MAYBE_CONST
Packit Service 82fcde
#else
Packit Service 82fcde
# define INIT_SECTION ".preinit_array"
Packit Service 82fcde
# define MAYBE_CONST const
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
void (*MAYBE_CONST __pthread_init_array []) (int, char **, char **)
Packit Service 82fcde
  __attribute__ ((section (INIT_SECTION), aligned (sizeof (void *)))) =
Packit Service 82fcde
{
Packit Service 82fcde
  &elision_init
Packit Service 82fcde
};