From patchwork Fri Dec 19 07:37:46 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andi Kleen X-Patchwork-Id: 4361 Received: (qmail 15836 invoked by alias); 19 Dec 2014 07:38:13 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 15774 invoked by uid 89); 19 Dec 2014 07:38:12 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=AWL, BAYES_00, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mga09.intel.com X-ExtLoop1: 1 From: Andi Kleen To: libc-alpha@sourceware.org Cc: Andi Kleen Subject: [PATCH 6/7] Add elision test cases Date: Thu, 18 Dec 2014 23:37:46 -0800 Message-Id: <1418974667-32587-7-git-send-email-andi@firstfloor.org> In-Reply-To: <1418974667-32587-1-git-send-email-andi@firstfloor.org> References: <1418974667-32587-1-git-send-email-andi@firstfloor.org> From: Andi Kleen Add the elision test cases that didn't make it last time. I use this to the test the environment variables. 2014-12-17 Andi Kleen * Makefile (tst-elision1, tst-elision2, tst-elision1b, tst-elision1c): Add. * tst-elision1.c: New file to test mutex and rwlock elision. * tst-elision2.c: New file to test trylock elision * tst-elision-common.c: New file with common test code. * tst-elision1b.c: Stub to allow env var enable testing. * tst-elision1c.c: Stub to allow env var disable testing. --- nptl/Makefile | 11 +- nptl/tst-elision-common.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++ nptl/tst-elision1.c | 133 +++++++++++++++++++++++ nptl/tst-elision1b.c | 1 + nptl/tst-elision1c.c | 1 + nptl/tst-elision2.c | 84 +++++++++++++++ nptl/tst-elision2b.c | 1 + nptl/tst-elision2c.c | 1 + 8 files changed, 495 insertions(+), 1 deletion(-) create mode 100644 nptl/tst-elision-common.c create mode 100644 nptl/tst-elision1.c create mode 100644 nptl/tst-elision1b.c create mode 100644 nptl/tst-elision1c.c create mode 100644 nptl/tst-elision2.c create mode 100644 nptl/tst-elision2b.c create mode 100644 nptl/tst-elision2c.c diff --git a/nptl/Makefile b/nptl/Makefile index 3d61ec1..78a3f90 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -272,7 +272,8 @@ tests = tst-typesizes \ tst-getpid1 tst-getpid2 tst-getpid3 \ tst-setuid3 \ tst-initializers1 $(addprefix tst-initializers1-,c89 gnu89 c99 gnu99) \ - tst-bad-schedattr + tst-bad-schedattr \ + tst-elision1 tst-elision2 tst-elision1b tst-elision2b tst-elision1c tst-elision2c xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \ tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 test-srcs = tst-oddstacklimit @@ -469,6 +470,14 @@ tst-umask1-ARGS = $(objpfx)tst-umask1.temp # resolution. Avoid this race by disabling lazy binding. BZ #11214. tst-getpid2-ENV = LD_BIND_NOW=1 +# test if disabling elision works through environment arguments +tst-elision1b-ENV = GLIBC_PTHREAD_MUTEX=none GLIBC_PTHREAD_RWLOCK=none +tst-elision2b-ENV = GLIBC_PTHREAD_MUTEX=none GLIBC_PTHREAD_RWLOCK=none +# test if enabling elision works +# XXX requires env file +tst-elision1c-ENV = GLIBC_PTHREAD_MUTEX=elision GLIBC_PTHREAD_RWLOCK=elision +tst-elision2c-ENV = GLIBC_PTHREAD_MUTEX=elision GLIBC_PTHREAD_RWLOCK=elision + $(objpfx)tst-atfork2: $(libdl) $(shared-thread-library) LDFLAGS-tst-atfork2 = -rdynamic tst-atfork2-ENV = MALLOC_TRACE=$(objpfx)tst-atfork2.mtrace diff --git a/nptl/tst-elision-common.c b/nptl/tst-elision-common.c new file mode 100644 index 0000000..5aaf0f6 --- /dev/null +++ b/nptl/tst-elision-common.c @@ -0,0 +1,264 @@ +/* tst-elision-common: Elision test harness. + 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 + . */ + +#include +#include +#include +#include "config.h" + +#define CPUID_FEATURE_RTM (1U << 11) + +static int +cpu_has_rtm (void) +{ + if (__get_cpuid_max (0, NULL) >= 7) + { + unsigned a, b, c, d; + + __cpuid_count (7, 0, a, b, c, d); + if (b & CPUID_FEATURE_RTM) + return 1; + } + return 0; +} + +#define ITER 10 +#define MAXTRY 100 + +pthread_mutex_t lock; + +#ifndef USE_TRYLOCK_ONLY +static int +pthread_mutex_timedlock_wrapper(pthread_mutex_t *l) +{ + struct timespec wait = { 0, 0 }; + return pthread_mutex_timedlock (l, &wait); +} +#endif + +/* Note this test program will fail when single stepped. + It also assumes that simple transactions always work. There is no + guarantee in the architecture that this is the case. We do some + retries to handle random abort cases like interrupts. But it's + not fully guaranteed. However when this fails it is somewhat worrying. */ + +int +run_mutex (int expected, const char *name, int force) +{ + int i; + int try = 0; + int txn __attribute__((unused)); + int err; + +#ifndef USE_TRYLOCK_ONLY + TESTLOCK(lock, pthread_mutex_lock, pthread_mutex_unlock, force); + TESTLOCK(lock, pthread_mutex_timedlock_wrapper, pthread_mutex_unlock, force); + TESTLOCK(lock, pthread_mutex_trylock, pthread_mutex_unlock, force); +#else + TESTTRYLOCK(lock, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, force); +#endif + + err = pthread_mutex_destroy (&lock); + if (err != 0) + { + printf ("destroy for %s failed: %d\n", name, err); + return 1; + } + return 0; +} + +static int +run_mutex_init (int iter, const char *name, int type, int has_type, int force, + int elision) +{ + pthread_mutexattr_t attr; + + pthread_mutexattr_init (&attr); + if (type != -1) + pthread_mutexattr_settype (&attr, type); + + pthread_mutex_init (&lock, has_type ? &attr : NULL); + return run_mutex (iter, name, force); +} + +static int +default_elision_enabled (void) +{ +#ifdef ENABLE_LOCK_ELISION + return 1; +#else + return 0; +#endif +} + +static int +elision_override (const char *var, int iter) +{ + char *s = getenv(var); + + s = getenv(var); + if (s && strstr (s, "none")) + return 0; + if (s && strstr (s, "elision")) + return ITER; + return iter; +} + +int +mutex_test (void) +{ + int ret = 0; + int default_iter = default_elision_enabled () ? ITER : 0; + + default_iter = elision_override ("GLIBC_PTHREAD_MUTEX", default_iter); + printf("default_iter %d\n", default_iter); + + lock = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER; + ret += run_mutex (default_iter, "default initializer timed", 0); + +#ifdef ADAPTIVE_SUPPORTS_ELISION + lock = (pthread_mutex_t) PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; + run_mutex (default_iter, "adaptive initializer default", 0); +#endif + + ret += run_mutex_init (default_iter, "timed init default", -1, 0, 0, -1); + ret += run_mutex_init (0, "normal no elision", + PTHREAD_MUTEX_NORMAL, 1, 2, -1); + +#ifdef ADAPTIVE_SUPPORTS_ELISION + ret += run_mutex_init (default_iter, "adaptive init default", + PTHREAD_MUTEX_ADAPTIVE_NP, 1, 0, -1); + ret += run_mutex_init (ITER, "adaptive init elision", + PTHREAD_MUTEX_ADAPTIVE_NP, 1, 1, 1); + ret += run_mutex_init (0, "adaptive init no elision", + PTHREAD_MUTEX_ADAPTIVE_NP, + 1, 2, 0); +#endif + + return ret; +} + +pthread_rwlock_t rwlock; + +#ifndef USE_TRYLOCK_ONLY +static int +pthread_rwlock_timedwrlock_wrapper(pthread_rwlock_t *l) +{ + struct timespec wait = { 0, 0 }; + return pthread_rwlock_timedwrlock (l, &wait); +} + +static int +pthread_rwlock_timedrdlock_wrapper(pthread_rwlock_t *l) +{ + struct timespec wait = { 0, 0 }; + return pthread_rwlock_timedrdlock (l, &wait); +} +#endif + +int +run_rwlock (int expected, const char *name, int force) +{ + int i; + int try = 0; + int txn __attribute__((unused)); + int err; + +#ifndef USE_TRYLOCK_ONLY + TESTLOCK(rwlock, pthread_rwlock_rdlock, pthread_rwlock_unlock, force); + TESTLOCK(rwlock, pthread_rwlock_wrlock, pthread_rwlock_unlock, force); + TESTLOCK(rwlock, pthread_rwlock_rdlock, pthread_rwlock_unlock, force); + TESTLOCK(rwlock, pthread_rwlock_tryrdlock, pthread_rwlock_unlock, force); + TESTLOCK(rwlock, pthread_rwlock_trywrlock, pthread_rwlock_unlock, force); + TESTLOCK(rwlock, pthread_rwlock_timedrdlock_wrapper, + pthread_rwlock_unlock, force); + TESTLOCK(rwlock, pthread_rwlock_timedwrlock_wrapper, + pthread_rwlock_unlock, force); +#else + TESTTRYLOCK(rwlock, pthread_rwlock_wrlock, pthread_rwlock_trywrlock, + pthread_rwlock_unlock, force); +#endif + + err = pthread_rwlock_destroy (&rwlock); + if (err != 0) + { + printf ("pthread_rwlock_destroy for %s failed: %d\n", name, err); + return 1; + } + return 0; +} + +int +run_rwlock_attr (int iter, const char *name, int type, int el, int force) +{ + pthread_rwlockattr_t attr; + pthread_rwlockattr_init (&attr); + pthread_rwlockattr_setkind_np (&attr, type); + pthread_rwlock_init (&rwlock, &attr); + return run_rwlock (iter, name, force); +} + +int +run_rwlock_attr_set (int iter, const char *extra, int flag, int force) +{ + char str[100]; + int ret = 0; + + snprintf(str, sizeof str, "rwlock attr prefer reader %s", extra); + ret += run_rwlock_attr (iter, str, + PTHREAD_RWLOCK_PREFER_READER_NP, flag, force); + snprintf(str, sizeof str, "rwlock attr prefer writer %s", extra); + ret += run_rwlock_attr (iter, str, + PTHREAD_RWLOCK_PREFER_WRITER_NP, flag, force); + snprintf(str, sizeof str, "rwlock attr prefer writer non recursive %s", extra); + ret += run_rwlock_attr (iter, str, + PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP, flag, force); + return ret; +} + + +int +rwlock_test (void) +{ + int ret = 0; + int default_iter = default_elision_enabled () ? ITER : 0; + + default_iter = elision_override ("GLIBC_PTHREAD_RWLOCK", default_iter); + + pthread_rwlock_init (&rwlock, NULL); + ret += run_rwlock (default_iter, "rwlock created", 0); + + rwlock = (pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER; + ret += run_rwlock (default_iter, "rwlock initialized", 0); + + rwlock = (pthread_rwlock_t)PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP; + ret += run_rwlock (default_iter, "rwlock initialized writer non recursive", 0); + + rwlock = (pthread_rwlock_t)PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP; + ret += run_rwlock (default_iter, "rwlock initialized writer non recursive", 0); + +#ifdef PTHREAD_RWLOCK_WRITER_INITIALIZER_NP + // XXX includes are missing PTHREAD_RWLOCK_WRITER_INITIALIZER_NP + rwlock = (pthread_rwlock_t)PTHREAD_RWLOCK_WRITER_INITIALIZER_NP; + ret += run_rwlock (default_iter, "rwlock initialized writer", 0); +#endif + + ret += run_rwlock_attr_set (default_iter, "", 0, 0); + + return ret; +} diff --git a/nptl/tst-elision1.c b/nptl/tst-elision1.c new file mode 100644 index 0000000..5460863 --- /dev/null +++ b/nptl/tst-elision1.c @@ -0,0 +1,133 @@ +/* tst-elision1: Test basic elision success. + 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 + . */ + +/* To use on other architectures you would need an own version + of cpu_has_rtm and _xtest. */ +#if defined(__i386__) || defined(__x86_64__) +# include +# include +# include +# include +# include +# include +# include + +int disabled; +int forced; + +int +check (const char *name, const char *lock, int try, int txn, int max, + int override) +{ + int should_run = 1; + + if (override == 0) + should_run = disabled == 0; + else if (override == 1) + should_run = 1; + else if (override == 2) + should_run = 0; + + /* forced noop right now, so not tested. But test if the defaults change. */ + if (!should_run) + { + if (txn != 0) + { + printf ("%s %s transaction run unexpected txn:%d\n", name, lock, txn); + return 1; + } + } + else + { + if (try == max) + { + printf ("%s %s no transactions when expected\n", name, lock); + return 1; + } + } + return 0; +} + +# define TESTLOCK(l, lock, unlock, force)\ + do \ + { \ + txn = 0; \ + for (i = 0; i < ITER; i++) \ + { \ + lock (&l); \ + if (_xtest ()) \ + txn++; \ + unlock (&l); \ + } \ + } \ + while (try++ < MAXTRY && txn != expected); \ + if (check (name, #lock, try, txn, MAXTRY, force)) \ + return 1; + +# include "tst-elision-common.c" + +static void +set_default (const char *var) +{ + char *s = getenv (var); + + if (s != NULL) + { + if (strstr (s, "elision")) + forced = 1; + else if (strstr (s, "none")) + disabled = 1; + } + else + { +#ifdef ENABLE_LOCK_ELISION + forced = 1; +#else + disabled = 1; +#endif + } +} + +int +do_test (void) +{ + if (cpu_has_rtm () == 0) + { + printf ("elision test requires RTM capable CPU. not tested\n"); + return 0; + } + + set_default ("GLIBC_PTHREAD_MUTEX"); + if (mutex_test ()) + return 1; + + set_default ("GLIBC_PTHREAD_RWLOCK"); + if (rwlock_test ()) + return 1; + + return 0; +} +#else +int do_test (void) +{ + return 0; +} +#endif + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/nptl/tst-elision1b.c b/nptl/tst-elision1b.c new file mode 100644 index 0000000..9f5ec3d --- /dev/null +++ b/nptl/tst-elision1b.c @@ -0,0 +1 @@ +#include "tst-elision1.c" diff --git a/nptl/tst-elision1c.c b/nptl/tst-elision1c.c new file mode 100644 index 0000000..9f5ec3d --- /dev/null +++ b/nptl/tst-elision1c.c @@ -0,0 +1 @@ +#include "tst-elision1.c" diff --git a/nptl/tst-elision2.c b/nptl/tst-elision2.c new file mode 100644 index 0000000..4ca0bed --- /dev/null +++ b/nptl/tst-elision2.c @@ -0,0 +1,84 @@ +/* tst-elision2: Test elided trylock semantics + 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 + . */ + +/* To use on other architectures you would need an own version of + cpu_has_rtm. */ +#if defined(__i386__) || defined(__x86_64__) +# include +# include +# include +# include + +int check (int success, int force) +{ + /* Any nested trylock disabled right now. */ + return success == 0; +} + +# define TESTTRYLOCK(l, lock, trylock, unlock, force) \ + do \ + { \ + txn = 0; \ + for (i = 0; i < ITER; i++) \ + { \ + lock (&l); \ + if (trylock (&l) == 0) \ + { \ + txn++; \ + unlock (&l); \ + } \ + unlock (&l); \ + } \ + } \ + while (try++ < MAXTRY && txn != ITER); \ + if (!check (txn == ITER, force)) \ + { \ + printf ("%s %s nested trylock check failed txn:%d iter:%d force:%d\n", \ + name, #lock, txn, ITER, force); \ + return 1; \ + } + +# define USE_TRYLOCK_ONLY 1 +# include "tst-elision-common.c" + +int +do_test (void) +{ + if (cpu_has_rtm () == 0) + { + printf ("elision test requires RTM capable CPU. not tested\n"); + return 0; + } + + if (mutex_test ()) + return 1; + + if (rwlock_test ()) + return 1; + + return 0; +} +#else +int do_test (void) +{ + return 0; +} +#endif + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/nptl/tst-elision2b.c b/nptl/tst-elision2b.c new file mode 100644 index 0000000..499c98f --- /dev/null +++ b/nptl/tst-elision2b.c @@ -0,0 +1 @@ +#include "tst-elision2.c" diff --git a/nptl/tst-elision2c.c b/nptl/tst-elision2c.c new file mode 100644 index 0000000..499c98f --- /dev/null +++ b/nptl/tst-elision2c.c @@ -0,0 +1 @@ +#include "tst-elision2.c"