hjl / source-git / glibc

Forked from source-git/glibc 4 years ago
Clone

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

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