Compare commits

...

No commits in common. "c8" and "c9s" have entirely different histories.
c8 ... c9s

24 changed files with 2246 additions and 51 deletions

19
.gitignore vendored
View File

@ -1 +1,18 @@
SOURCES/userspace-rcu-0.10.1.tar.bz2
userspace-rcu-0.4.1.tar.bz2
/userspace-rcu-0.7.3.tar.bz2
/userspace-rcu-0.7.5.tar.bz2
/userspace-rcu-0.7.6.tar.bz2
/userspace-rcu-0.7.7.tar.bz2
/userspace-rcu-0.7.9.tar.bz2
/userspace-rcu-0.8.1.tar.bz2
/userspace-rcu-0.8.5.tar.bz2
/userspace-rcu-0.8.6.tar.bz2
/userspace-rcu-0.9.2.tar.bz2
/userspace-rcu-0.9.3.tar.bz2
/userspace-rcu-0.10.0.tar.bz2
/userspace-rcu-0.10.1.tar.bz2
/userspace-rcu-0.10.2.tar.bz2
/userspace-rcu-0.11.0.tar.bz2
/userspace-rcu-0.11.1.tar.bz2
/userspace-rcu-0.12.0.tar.bz2
/userspace-rcu-0.12.1.tar.bz2

View File

@ -1 +1 @@
32755b4e99306fca50ededf2e4b8ef987813696c SOURCES/userspace-rcu-0.10.1.tar.bz2
93ad363e3889e55fac7d35d406927e5925e40dd9 userspace-rcu-0.12.1.tar.bz2

View File

@ -1,26 +0,0 @@
Remove the benchmarks from the regtest target, they timeout on the buildds.
--- a/tests/benchmark/Makefile.am
+++ b/tests/benchmark/Makefile.am
@@ -229,13 +229,10 @@
done; \
fi
-.PHONY: short_bench long_bench regtest
+.PHONY: short_bench long_bench
short_bench:
./run.sh short_bench_tests
long_bench:
./run.sh long_bench_tests
-
-regtest:
- ./run.sh regression_tests
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -8,4 +8,3 @@
cd benchmark && $(MAKE) $(AM_MAKEFLAGS) long_bench
regtest:
cd regression && $(MAKE) $(AM_MAKEFLAGS) regtest
- cd benchmark && $(MAKE) $(AM_MAKEFLAGS) regtest

Binary file not shown.

View File

@ -0,0 +1,12 @@
Remove the benchmarks from the regtest target, they timeout on the buildds.
--- a/tests/Makefile.am.orig 2019-06-03 15:46:16.380163423 -0400
+++ b/tests/Makefile.am 2019-06-03 15:46:26.099004417 -0400
@@ -8,7 +8,6 @@
cd benchmark && $(MAKE) $(AM_MAKEFLAGS) long_bench
regtest:
cd regression && $(MAKE) $(AM_MAKEFLAGS) regtest
- cd benchmark && $(MAKE) $(AM_MAKEFLAGS) regtest
check-loop:
while [ 0 ]; do \

1
sources Normal file
View File

@ -0,0 +1 @@
SHA512 (userspace-rcu-0.12.1.tar.bz2) = c0c14eede77358904dfb10774390fd86b4fa9e0ab25d278c869728d40eb263bfa7e489b20ce7198169e71d74fe41bb5018fc1553a5e0654840c9765e088c83db

View File

@ -0,0 +1,12 @@
BUILDDIRS = utils regression
all: recurse
$(MAKE) -C regression regtest
clean: recurse_clean
recurse:
@for dir in $(BUILDDIRS); do $(MAKE) -C $$dir || exit $?; done
recurse_clean:
@for dir in $(BUILDDIRS); do $(MAKE) -C $$dir clean || exit $?; done

View File

@ -0,0 +1,308 @@
#ifndef _INCLUDE_API_H
#define _INCLUDE_API_H
/*
* common.h: Common Linux kernel-isms.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; but version 2 of the License only due
* to code included from the Linux kernel.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (c) 2006 Paul E. McKenney, IBM.
*
* Much code taken from the Linux kernel. For such code, the option
* to redistribute under later versions of GPL might not be available.
*/
#include <urcu/compiler.h>
#include <urcu/arch.h>
#include "cpuset.h"
/*
* Machine parameters.
*/
#define ____cacheline_internodealigned_in_smp \
__attribute__((__aligned__(CAA_CACHE_LINE_SIZE)))
/*
* api_pthreads.h: API mapping to pthreads environment.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. However, please note that much
* of the code in this file derives from the Linux kernel, and that such
* code may not be available except under GPLv2.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (c) 2006 Paul E. McKenney, IBM.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <pthread.h>
#include <sys/param.h>
/* #include "atomic.h" */
/*
* Exclusive locking primitives.
*/
typedef pthread_mutex_t spinlock_t;
#define DEFINE_SPINLOCK(lock) spinlock_t lock = PTHREAD_MUTEX_INITIALIZER;
#define __SPIN_LOCK_UNLOCKED(lockp) PTHREAD_MUTEX_INITIALIZER
static void spin_lock_init(spinlock_t *sp)
{
if (pthread_mutex_init(sp, NULL) != 0) {
perror("spin_lock_init:pthread_mutex_init");
exit(-1);
}
}
static void spin_lock(spinlock_t *sp)
{
if (pthread_mutex_lock(sp) != 0) {
perror("spin_lock:pthread_mutex_lock");
exit(-1);
}
}
static void spin_unlock(spinlock_t *sp)
{
if (pthread_mutex_unlock(sp) != 0) {
perror("spin_unlock:pthread_mutex_unlock");
exit(-1);
}
}
#define spin_lock_irqsave(l, f) do { f = 1; spin_lock(l); } while (0)
#define spin_unlock_irqrestore(l, f) do { f = 0; spin_unlock(l); } while (0)
/*
* Thread creation/destruction primitives.
*/
typedef pthread_t thread_id_t;
#define NR_THREADS 128
#define __THREAD_ID_MAP_EMPTY ((thread_id_t) 0)
#define __THREAD_ID_MAP_WAITING ((thread_id_t) 1)
thread_id_t __thread_id_map[NR_THREADS];
spinlock_t __thread_id_map_mutex;
#define for_each_thread(t) \
for (t = 0; t < NR_THREADS; t++)
#define for_each_running_thread(t) \
for (t = 0; t < NR_THREADS; t++) \
if ((__thread_id_map[t] != __THREAD_ID_MAP_EMPTY) && \
(__thread_id_map[t] != __THREAD_ID_MAP_WAITING))
#define for_each_tid(t, tid) \
for (t = 0; t < NR_THREADS; t++) \
if ((((tid) = __thread_id_map[t]) != __THREAD_ID_MAP_EMPTY) && \
((tid) != __THREAD_ID_MAP_WAITING))
pthread_key_t thread_id_key;
static int __smp_thread_id(void)
{
int i;
thread_id_t tid = pthread_self();
for (i = 0; i < NR_THREADS; i++) {
if (__thread_id_map[i] == tid) {
long v = i + 1; /* must be non-NULL. */
if (pthread_setspecific(thread_id_key, (void *)v) != 0) {
perror("pthread_setspecific");
exit(-1);
}
return i;
}
}
spin_lock(&__thread_id_map_mutex);
for (i = 0; i < NR_THREADS; i++) {
if (__thread_id_map[i] == tid) {
spin_unlock(&__thread_id_map_mutex);
return i;
}
}
spin_unlock(&__thread_id_map_mutex);
fprintf(stderr, "smp_thread_id: Rogue thread, id: %lu(%#lx)\n",
(unsigned long) tid, (unsigned long) tid);
exit(-1);
}
static int smp_thread_id(void)
{
void *id;
id = pthread_getspecific(thread_id_key);
if (id == NULL)
return __smp_thread_id();
return (long)(id - 1);
}
static thread_id_t create_thread(void *(*func)(void *), void *arg)
{
thread_id_t tid;
int i;
spin_lock(&__thread_id_map_mutex);
for (i = 0; i < NR_THREADS; i++) {
if (__thread_id_map[i] == __THREAD_ID_MAP_EMPTY)
break;
}
if (i >= NR_THREADS) {
spin_unlock(&__thread_id_map_mutex);
fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS);
exit(-1);
}
__thread_id_map[i] = __THREAD_ID_MAP_WAITING;
spin_unlock(&__thread_id_map_mutex);
if (pthread_create(&tid, NULL, func, arg) != 0) {
perror("create_thread:pthread_create");
exit(-1);
}
__thread_id_map[i] = tid;
return tid;
}
static void *wait_thread(thread_id_t tid)
{
int i;
void *vp;
for (i = 0; i < NR_THREADS; i++) {
if (__thread_id_map[i] == tid)
break;
}
if (i >= NR_THREADS){
fprintf(stderr, "wait_thread: bad tid = %lu(%#lx)\n",
(unsigned long)tid, (unsigned long)tid);
exit(-1);
}
if (pthread_join(tid, &vp) != 0) {
perror("wait_thread:pthread_join");
exit(-1);
}
__thread_id_map[i] = __THREAD_ID_MAP_EMPTY;
return vp;
}
static void wait_all_threads(void)
{
int i;
thread_id_t tid;
for (i = 1; i < NR_THREADS; i++) {
tid = __thread_id_map[i];
if (tid != __THREAD_ID_MAP_EMPTY &&
tid != __THREAD_ID_MAP_WAITING)
(void)wait_thread(tid);
}
}
static void run_on(int cpu)
{
#if HAVE_SCHED_SETAFFINITY
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(cpu, &mask);
#if SCHED_SETAFFINITY_ARGS == 2
sched_setaffinity(0, &mask);
#else
sched_setaffinity(0, sizeof(mask), &mask);
#endif
#endif /* HAVE_SCHED_SETAFFINITY */
}
/*
* timekeeping -- very crude -- should use MONOTONIC...
*/
long long get_microseconds(void)
{
struct timeval tv;
if (gettimeofday(&tv, NULL) != 0)
abort();
return ((long long)tv.tv_sec) * 1000000LL + (long long)tv.tv_usec;
}
/*
* Per-thread variables.
*/
#define DEFINE_PER_THREAD(type, name) \
struct { \
__typeof__(type) v \
__attribute__((__aligned__(CAA_CACHE_LINE_SIZE))); \
} __per_thread_##name[NR_THREADS]
#define DECLARE_PER_THREAD(type, name) extern DEFINE_PER_THREAD(type, name)
#define per_thread(name, thread) __per_thread_##name[thread].v
#define __get_thread_var(name) per_thread(name, smp_thread_id())
#define init_per_thread(name, v) \
do { \
int __i_p_t_i; \
for (__i_p_t_i = 0; __i_p_t_i < NR_THREADS; __i_p_t_i++) \
per_thread(name, __i_p_t_i) = v; \
} while (0)
DEFINE_PER_THREAD(int, smp_processor_id);
/*
* Bug checks.
*/
#define BUG_ON(c) do { if (!(c)) abort(); } while (0)
/*
* Initialization -- Must be called before calling any primitives.
*/
static void smp_init(void)
{
int i;
spin_lock_init(&__thread_id_map_mutex);
__thread_id_map[0] = pthread_self();
for (i = 1; i < NR_THREADS; i++)
__thread_id_map[i] = __THREAD_ID_MAP_EMPTY;
init_per_thread(smp_processor_id, 0);
if (pthread_key_create(&thread_id_key, NULL) != 0) {
perror("pthread_key_create");
exit(-1);
}
}
#endif

View File

@ -0,0 +1,3 @@
#ifndef _COMPAT_RAND_H
#define _COMPAT_RAND_H
#endif /* _COMPAT_RAND_H */

View File

@ -0,0 +1,43 @@
#ifndef _URCU_TESTS_CPUSET_H
#define _URCU_TESTS_CPUSET_H
/*
* cpuset.h
*
* Userspace RCU library - test cpuset header
*
* Copyright 2009-2013 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#if defined(HAVE_SCHED_SETAFFINITY) || defined(HAVE_CPU_SET_T) \
|| defined(HAVE_CPU_ZERO) || defined(HAVE_CPU_SET)
# include <sched.h>
#endif
#ifndef HAVE_CPU_SET_T
typedef unsigned long cpu_set_t;
#endif
#ifndef HAVE_CPU_ZERO
# define CPU_ZERO(cpuset) do { *(cpuset) = 0; } while(0)
#endif
#ifndef HAVE_CPU_SET
# define CPU_SET(cpu, cpuset) do { *(cpuset) |= (1UL << (cpu)); } while(0)
#endif
#endif /* _URCU_TESTS_CPUSET_H */

View File

@ -0,0 +1,103 @@
#ifndef URCU_TESTS_DEBUG_YIELD_H
#define URCU_TESTS_DEBUG_YIELD_H
/*
* debug-yield.h
*
* Userspace RCU library tests - Debugging header
*
* Copyright (c) 2009 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
* Copyright (c) 2009 Paul E. McKenney, IBM Corporation.
*
* This 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.
*
* This 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 this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* IBM's contributions to this file may be relicensed under LGPLv2 or later.
*/
#include <sched.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <compat-rand.h>
#define RCU_YIELD_READ (1 << 0)
#define RCU_YIELD_WRITE (1 << 1)
/*
* Updates with RCU_SIGNAL are much slower. Account this in the delay.
*/
#ifdef RCU_SIGNAL
/* maximum sleep delay, in us */
#define MAX_SLEEP 30000
#else
#define MAX_SLEEP 50
#endif
extern unsigned int rcu_yield_active;
extern DECLARE_URCU_TLS(unsigned int, rcu_rand_yield);
#ifdef DEBUG_YIELD
static inline void rcu_debug_yield_read(void)
{
if (rcu_yield_active & RCU_YIELD_READ)
if (rand_r(&URCU_TLS(rcu_rand_yield)) & 0x1)
usleep(rand_r(&URCU_TLS(rcu_rand_yield)) % MAX_SLEEP);
}
static inline void rcu_debug_yield_write(void)
{
if (rcu_yield_active & RCU_YIELD_WRITE)
if (rand_r(&URCU_TLS(rcu_rand_yield)) & 0x1)
usleep(rand_r(&URCU_TLS(rcu_rand_yield)) % MAX_SLEEP);
}
static inline void rcu_debug_yield_enable(unsigned int flags)
{
rcu_yield_active |= flags;
}
static inline void rcu_debug_yield_disable(unsigned int flags)
{
rcu_yield_active &= ~flags;
}
static inline void rcu_debug_yield_init(void)
{
URCU_TLS(rcu_rand_yield) = time(NULL) ^ (unsigned long) pthread_self();
}
#else /* DEBUG_YIELD */
static inline void rcu_debug_yield_read(void)
{
}
static inline void rcu_debug_yield_write(void)
{
}
static inline void rcu_debug_yield_enable(unsigned int flags)
{
}
static inline void rcu_debug_yield_disable(unsigned int flags)
{
}
static inline void rcu_debug_yield_init(void)
{
}
#endif
#endif /* URCU_TESTS_DEBUG_YIELD_H */

View File

@ -0,0 +1,81 @@
#ifndef _TEST_THREAD_ID_H
#define _TEST_THREAD_ID_H
/*
* thread-id.h
*
* Userspace RCU library - thread ID
*
* Copyright 2013 - Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
*
* Permission is hereby granted to use or copy this program
* for any purpose, provided the above notices are retained on all copies.
* Permission to modify the code and to distribute modified code is granted,
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
#ifdef __linux__
# include <urcu/syscall-compat.h>
# if defined(HAVE_GETTID)
/*
* Do not redefine gettid() as it is already included
* in bionic through <unistd.h>. Some other libc
* may also already contain an implementation of gettid.
*/
# elif defined(_syscall0)
_syscall0(pid_t, gettid)
# elif defined(__NR_gettid)
static inline pid_t gettid(void)
{
return syscall(__NR_gettid);
}
# endif
static inline
unsigned long urcu_get_thread_id(void)
{
return (unsigned long) gettid();
}
#elif defined(__FreeBSD__)
# include <pthread_np.h>
static inline
unsigned long urcu_get_thread_id(void)
{
return (unsigned long) pthread_getthreadid_np();
}
#elif defined(__sun__) || defined(__APPLE__)
#include <pthread.h>
static inline
unsigned long urcu_get_thread_id(void)
{
return (unsigned long) pthread_self();
}
#elif defined(__CYGWIN__)
#include <pthread.h>
extern unsigned long pthread_getsequence_np(pthread_t *);
static inline
unsigned long urcu_get_thread_id(void)
{
pthread_t thr = pthread_self();
return pthread_getsequence_np(&thr);
}
#else
# warning "use pid as thread ID"
static inline
unsigned long urcu_get_thread_id(void)
{
return (unsigned long) getpid();
}
#endif
#endif /* _TEST_THREAD_ID_H */

View File

@ -0,0 +1,31 @@
CFLAGS = -O2 -g -Wall -I../utils -I../common -D_GNU_SOURCE -DHAVE_SCHED_SETAFFINITY -DHAVE_CPU_SET_T -DHAVE_CPU_ZERO -DHAVE_CPU_SET -pthread
EXECS = test_urcu_fork rcutorture_urcu rcutorture_urcu_signal rcutorture_urcu_mb rcutorture_urcu_bp rcutorture_urcu_qsbr
SRCS = urcutorture.c ../utils/libtap.a
all: $(EXECS)
test_urcu_fork: test_urcu_fork.c ../utils/libtap.a
$(CC) $(CFLAGS) $? -o $@ -lurcu
rcutorture_urcu: $(SRCS)
$(CC) $(CFLAGS) -DRCU_MEMBARRIER $? -o $@ -lurcu
rcutorture_urcu_signal: $(SRCS)
$(CC) $(CFLAGS) -DRCU_SIGNAL $? -o $@ -lurcu-signal
rcutorture_urcu_mb: $(SRCS)
$(CC) $(CFLAGS) -DRCU_MB $? -o $@ -lurcu-mb
rcutorture_urcu_bp: $(SRCS)
$(CC) $(CFLAGS) -DRCU_BP $? -o $@ -lurcu-bp
rcutorture_urcu_qsbr: $(SRCS)
$(CC) $(CFLAGS) -DTORTURE_QSBR -DRCU_QSBR $? -o $@ -lurcu-qsbr
regtest:
./run.sh regression_tests
clean:
rm -f $(EXECS)

View File

@ -0,0 +1,612 @@
/*
* rcutorture.h: simple user-level performance/stress test of RCU.
*
* Usage:
* ./rcu <nreaders> rperf [ <cpustride> ]
* Run a read-side performance test with the specified
* number of readers spaced by <cpustride>.
* Thus "./rcu 16 rperf 2" would run 16 readers on even-numbered
* CPUs from 0 to 30.
* ./rcu <nupdaters> uperf [ <cpustride> ]
* Run an update-side performance test with the specified
* number of updaters and specified CPU spacing.
* ./rcu <nreaders> perf [ <cpustride> ]
* Run a combined read/update performance test with the specified
* number of readers and one updater and specified CPU spacing.
* The readers run on the low-numbered CPUs and the updater
* of the highest-numbered CPU.
*
* The above tests produce output as follows:
*
* n_reads: 46008000 n_updates: 146026 nreaders: 2 nupdaters: 1 duration: 1
* ns/read: 43.4707 ns/update: 6848.1
*
* The first line lists the total number of RCU reads and updates executed
* during the test, the number of reader threads, the number of updater
* threads, and the duration of the test in seconds. The second line
* lists the average duration of each type of operation in nanoseconds,
* or "nan" if the corresponding type of operation was not performed.
*
* ./rcu <nreaders> stress
* Run a stress test with the specified number of readers and
* one updater. None of the threads are affinitied to any
* particular CPU.
*
* This test produces output as follows:
*
* n_reads: 114633217 n_updates: 3903415 n_mberror: 0
* rcu_stress_count: 114618391 14826 0 0 0 0 0 0 0 0 0
*
* The first line lists the number of RCU read and update operations
* executed, followed by the number of memory-ordering violations
* (which will be zero in a correct RCU implementation). The second
* line lists the number of readers observing progressively more stale
* data. A correct RCU implementation will have all but the first two
* numbers non-zero.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (c) 2008 Paul E. McKenney, IBM Corporation.
*/
/*
* Test variables.
*/
#include <stdlib.h>
#include "tap.h"
#define NR_TESTS 1
DEFINE_PER_THREAD(long long, n_reads_pt);
DEFINE_PER_THREAD(long long, n_updates_pt);
enum callrcu_type {
CALLRCU_GLOBAL,
CALLRCU_PERCPU,
CALLRCU_PERTHREAD,
};
static enum callrcu_type callrcu_type = CALLRCU_GLOBAL;
long long n_reads = 0LL;
long n_updates = 0L;
int nthreadsrunning;
char argsbuf[64];
#define GOFLAG_INIT 0
#define GOFLAG_RUN 1
#define GOFLAG_STOP 2
volatile int goflag __attribute__((__aligned__(CAA_CACHE_LINE_SIZE)))
= GOFLAG_INIT;
#define RCU_READ_RUN 1000
//MD
#define RCU_READ_NESTABLE
#ifdef RCU_READ_NESTABLE
#define rcu_read_lock_nest() rcu_read_lock()
#define rcu_read_unlock_nest() rcu_read_unlock()
#else /* #ifdef RCU_READ_NESTABLE */
#define rcu_read_lock_nest()
#define rcu_read_unlock_nest()
#endif /* #else #ifdef RCU_READ_NESTABLE */
#ifdef TORTURE_QSBR
#define mark_rcu_quiescent_state rcu_quiescent_state
#define put_thread_offline rcu_thread_offline
#define put_thread_online rcu_thread_online
#endif
#ifndef mark_rcu_quiescent_state
#define mark_rcu_quiescent_state() do ; while (0)
#endif /* #ifdef mark_rcu_quiescent_state */
#ifndef put_thread_offline
#define put_thread_offline() do ; while (0)
#define put_thread_online() do ; while (0)
#define put_thread_online_delay() do ; while (0)
#else /* #ifndef put_thread_offline */
#define put_thread_online_delay() synchronize_rcu()
#endif /* #else #ifndef put_thread_offline */
/*
* Performance test.
*/
void *rcu_read_perf_test(void *arg)
{
int i;
int me = (long)arg;
long long n_reads_local = 0;
rcu_register_thread();
run_on(me);
uatomic_inc(&nthreadsrunning);
put_thread_offline();
while (goflag == GOFLAG_INIT)
(void) poll(NULL, 0, 1);
put_thread_online();
while (goflag == GOFLAG_RUN) {
for (i = 0; i < RCU_READ_RUN; i++) {
rcu_read_lock();
/* rcu_read_lock_nest(); */
/* rcu_read_unlock_nest(); */
rcu_read_unlock();
}
n_reads_local += RCU_READ_RUN;
mark_rcu_quiescent_state();
}
__get_thread_var(n_reads_pt) += n_reads_local;
put_thread_offline();
rcu_unregister_thread();
return (NULL);
}
void *rcu_update_perf_test(void *arg)
{
long long n_updates_local = 0;
if (callrcu_type == CALLRCU_PERTHREAD) {
struct call_rcu_data *crdp;
crdp = create_call_rcu_data(0, -1);
if (crdp != NULL) {
diag("Successfully using per-thread call_rcu() worker.");
set_thread_call_rcu_data(crdp);
}
}
uatomic_inc(&nthreadsrunning);
while (goflag == GOFLAG_INIT)
(void) poll(NULL, 0, 1);
while (goflag == GOFLAG_RUN) {
synchronize_rcu();
n_updates_local++;
}
__get_thread_var(n_updates_pt) += n_updates_local;
if (callrcu_type == CALLRCU_PERTHREAD) {
struct call_rcu_data *crdp;
crdp = get_thread_call_rcu_data();
set_thread_call_rcu_data(NULL);
call_rcu_data_free(crdp);
}
return NULL;
}
void perftestinit(void)
{
init_per_thread(n_reads_pt, 0LL);
init_per_thread(n_updates_pt, 0LL);
uatomic_set(&nthreadsrunning, 0);
}
int perftestrun(int nthreads, int nreaders, int nupdaters)
{
int t;
int duration = 1;
cmm_smp_mb();
while (uatomic_read(&nthreadsrunning) < nthreads)
(void) poll(NULL, 0, 1);
goflag = GOFLAG_RUN;
cmm_smp_mb();
sleep(duration);
cmm_smp_mb();
goflag = GOFLAG_STOP;
cmm_smp_mb();
wait_all_threads();
for_each_thread(t) {
n_reads += per_thread(n_reads_pt, t);
n_updates += per_thread(n_updates_pt, t);
}
diag("n_reads: %lld n_updates: %ld nreaders: %d nupdaters: %d duration: %d",
n_reads, n_updates, nreaders, nupdaters, duration);
diag("ns/read: %g ns/update: %g",
((duration * 1000*1000*1000.*(double)nreaders) /
(double)n_reads),
((duration * 1000*1000*1000.*(double)nupdaters) /
(double)n_updates));
if (get_cpu_call_rcu_data(0)) {
diag("Deallocating per-CPU call_rcu threads.\n");
free_all_cpu_call_rcu_data();
}
return 0;
}
int perftest(int nreaders, int cpustride)
{
int i;
long arg;
perftestinit();
for (i = 0; i < nreaders; i++) {
arg = (long)(i * cpustride);
create_thread(rcu_read_perf_test, (void *)arg);
}
arg = (long)(i * cpustride);
create_thread(rcu_update_perf_test, (void *)arg);
return perftestrun(i + 1, nreaders, 1);
}
int rperftest(int nreaders, int cpustride)
{
int i;
long arg;
perftestinit();
init_per_thread(n_reads_pt, 0LL);
for (i = 0; i < nreaders; i++) {
arg = (long)(i * cpustride);
create_thread(rcu_read_perf_test, (void *)arg);
}
return perftestrun(i, nreaders, 0);
}
int uperftest(int nupdaters, int cpustride)
{
int i;
long arg;
perftestinit();
init_per_thread(n_reads_pt, 0LL);
for (i = 0; i < nupdaters; i++) {
arg = (long)(i * cpustride);
create_thread(rcu_update_perf_test, (void *)arg);
}
return perftestrun(i, 0, nupdaters);
}
/*
* Stress test.
*/
#define RCU_STRESS_PIPE_LEN 10
struct rcu_stress {
int pipe_count;
int mbtest;
};
struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0 } };
struct rcu_stress *rcu_stress_current;
int rcu_stress_idx = 0;
int n_mberror = 0;
DEFINE_PER_THREAD(long long [RCU_STRESS_PIPE_LEN + 1], rcu_stress_count);
int garbage = 0;
void *rcu_read_stress_test(void *arg)
{
int i;
int itercnt = 0;
struct rcu_stress *p;
int pc;
rcu_register_thread();
put_thread_offline();
while (goflag == GOFLAG_INIT)
(void) poll(NULL, 0, 1);
put_thread_online();
while (goflag == GOFLAG_RUN) {
rcu_read_lock();
p = rcu_dereference(rcu_stress_current);
if (p->mbtest == 0)
n_mberror++;
rcu_read_lock_nest();
for (i = 0; i < 100; i++)
garbage++;
rcu_read_unlock_nest();
pc = p->pipe_count;
rcu_read_unlock();
if ((pc > RCU_STRESS_PIPE_LEN) || (pc < 0))
pc = RCU_STRESS_PIPE_LEN;
__get_thread_var(rcu_stress_count)[pc]++;
__get_thread_var(n_reads_pt)++;
mark_rcu_quiescent_state();
if ((++itercnt % 0x1000) == 0) {
put_thread_offline();
put_thread_online_delay();
put_thread_online();
}
}
put_thread_offline();
rcu_unregister_thread();
return (NULL);
}
static pthread_mutex_t call_rcu_test_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t call_rcu_test_cond = PTHREAD_COND_INITIALIZER;
void rcu_update_stress_test_rcu(struct rcu_head *head)
{
int ret;
ret = pthread_mutex_lock(&call_rcu_test_mutex);
if (ret) {
errno = ret;
diag("pthread_mutex_lock: %s",
strerror(errno));
abort();
}
ret = pthread_cond_signal(&call_rcu_test_cond);
if (ret) {
errno = ret;
diag("pthread_cond_signal: %s",
strerror(errno));
abort();
}
ret = pthread_mutex_unlock(&call_rcu_test_mutex);
if (ret) {
errno = ret;
diag("pthread_mutex_unlock: %s",
strerror(errno));
abort();
}
}
void *rcu_update_stress_test(void *arg)
{
int i;
struct rcu_stress *p;
struct rcu_head rh;
while (goflag == GOFLAG_INIT)
(void) poll(NULL, 0, 1);
while (goflag == GOFLAG_RUN) {
i = rcu_stress_idx + 1;
if (i >= RCU_STRESS_PIPE_LEN)
i = 0;
p = &rcu_stress_array[i];
p->mbtest = 0;
cmm_smp_mb();
p->pipe_count = 0;
p->mbtest = 1;
rcu_assign_pointer(rcu_stress_current, p);
rcu_stress_idx = i;
for (i = 0; i < RCU_STRESS_PIPE_LEN; i++)
if (i != rcu_stress_idx)
rcu_stress_array[i].pipe_count++;
if (n_updates & 0x1)
synchronize_rcu();
else {
int ret;
ret = pthread_mutex_lock(&call_rcu_test_mutex);
if (ret) {
errno = ret;
diag("pthread_mutex_lock: %s",
strerror(errno));
abort();
}
rcu_register_thread();
call_rcu(&rh, rcu_update_stress_test_rcu);
rcu_unregister_thread();
/*
* Our MacOS X test machine with the following
* config:
* 15.6.0 Darwin Kernel Version 15.6.0
* root:xnu-3248.60.10~1/RELEASE_X86_64
* appears to have issues with liburcu-signal
* signal being delivered on top of
* pthread_cond_wait. It seems to make the
* thread continue, and therefore corrupt the
* rcu_head. Work around this issue by
* unregistering the RCU read-side thread
* immediately after call_rcu (call_rcu needs
* us to be registered RCU readers).
*/
ret = pthread_cond_wait(&call_rcu_test_cond,
&call_rcu_test_mutex);
if (ret) {
errno = ret;
diag("pthread_cond_signal: %s",
strerror(errno));
abort();
}
ret = pthread_mutex_unlock(&call_rcu_test_mutex);
if (ret) {
errno = ret;
diag("pthread_mutex_unlock: %s",
strerror(errno));
abort();
}
}
n_updates++;
}
return NULL;
}
void *rcu_fake_update_stress_test(void *arg)
{
if (callrcu_type == CALLRCU_PERTHREAD) {
struct call_rcu_data *crdp;
crdp = create_call_rcu_data(0, -1);
if (crdp != NULL) {
diag("Successfully using per-thread call_rcu() worker.");
set_thread_call_rcu_data(crdp);
}
}
while (goflag == GOFLAG_INIT)
(void) poll(NULL, 0, 1);
while (goflag == GOFLAG_RUN) {
synchronize_rcu();
(void) poll(NULL, 0, 1);
}
if (callrcu_type == CALLRCU_PERTHREAD) {
struct call_rcu_data *crdp;
crdp = get_thread_call_rcu_data();
set_thread_call_rcu_data(NULL);
call_rcu_data_free(crdp);
}
return NULL;
}
int stresstest(int nreaders)
{
int i;
int t;
long long *p;
long long sum;
init_per_thread(n_reads_pt, 0LL);
for_each_thread(t) {
p = &per_thread(rcu_stress_count,t)[0];
for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++)
p[i] = 0LL;
}
rcu_stress_current = &rcu_stress_array[0];
rcu_stress_current->pipe_count = 0;
rcu_stress_current->mbtest = 1;
for (i = 0; i < nreaders; i++)
create_thread(rcu_read_stress_test, NULL);
create_thread(rcu_update_stress_test, NULL);
for (i = 0; i < 5; i++)
create_thread(rcu_fake_update_stress_test, NULL);
cmm_smp_mb();
goflag = GOFLAG_RUN;
cmm_smp_mb();
sleep(10);
cmm_smp_mb();
goflag = GOFLAG_STOP;
cmm_smp_mb();
wait_all_threads();
for_each_thread(t)
n_reads += per_thread(n_reads_pt, t);
diag("n_reads: %lld n_updates: %ld n_mberror: %d",
n_reads, n_updates, n_mberror);
rdiag_start();
rdiag("rcu_stress_count:");
for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) {
sum = 0LL;
for_each_thread(t) {
sum += per_thread(rcu_stress_count, t)[i];
}
rdiag(" %lld", sum);
}
rdiag_end();
if (get_cpu_call_rcu_data(0)) {
diag("Deallocating per-CPU call_rcu threads.");
free_all_cpu_call_rcu_data();
}
if (!n_mberror)
return 0;
else
return -1;
}
/*
* Mainprogram.
*/
void usage(int argc, char *argv[])
{
diag("Usage: %s nreaders [ perf | rperf | uperf | stress ] [ stride ] [ callrcu_global | callrcu_percpu | callrcu_perthread ]\n", argv[0]);
exit(-1);
}
int main(int argc, char *argv[])
{
int nreaders = 1;
int cpustride = 1;
plan_tests(NR_TESTS);
smp_init();
//rcu_init();
if (argc > 4) {
const char *callrcu_str = argv[4];;
if (strcmp(callrcu_str, "callrcu_global") == 0) {
callrcu_type = CALLRCU_GLOBAL;
} else if (strcmp(callrcu_str, "callrcu_percpu") == 0) {
callrcu_type = CALLRCU_PERCPU;
} else if (strcmp(callrcu_str, "callrcu_perthread") == 0) {
callrcu_type = CALLRCU_PERTHREAD;
} else {
usage(argc, argv);
goto end;
}
}
switch (callrcu_type) {
case CALLRCU_GLOBAL:
diag("Using global per-process call_rcu thread.");
break;
case CALLRCU_PERCPU:
diag("Using per-CPU call_rcu threads.");
if (create_all_cpu_call_rcu_data(0))
diag("create_all_cpu_call_rcu_data: %s",
strerror(errno));
break;
case CALLRCU_PERTHREAD:
diag("Using per-thread call_rcu() worker.");
break;
default:
abort();
}
#ifdef DEBUG_YIELD
yield_active |= YIELD_READ;
yield_active |= YIELD_WRITE;
#endif
if (argc > 1) {
if (strcmp(argv[1], "-h") == 0
|| strcmp(argv[1], "--help") == 0) {
usage(argc, argv);
goto end;
}
nreaders = strtoul(argv[1], NULL, 0);
if (argc == 2) {
ok(!perftest(nreaders, cpustride),
"perftest readers: %d, stride: %d",
nreaders, cpustride);
goto end;
}
if (argc > 3)
cpustride = strtoul(argv[3], NULL, 0);
if (strcmp(argv[2], "perf") == 0)
ok(!perftest(nreaders, cpustride),
"perftest readers: %d, stride: %d",
nreaders, cpustride);
else if (strcmp(argv[2], "rperf") == 0)
ok(!rperftest(nreaders, cpustride),
"rperftest readers: %d, stride: %d",
nreaders, cpustride);
else if (strcmp(argv[2], "uperf") == 0)
ok(!uperftest(nreaders, cpustride),
"uperftest readers: %d, stride: %d",
nreaders, cpustride);
else if (strcmp(argv[2], "stress") == 0)
ok(!stresstest(nreaders),
"stresstest readers: %d, stride: %d",
nreaders, cpustride);
else
usage(argc, argv);
} else {
usage(argc, argv);
}
end:
return exit_status();
}

View File

@ -0,0 +1,61 @@
./test_urcu_fork
./rcutorture_urcu `nproc` perf 1 callrcu_global
./rcutorture_urcu_signal `nproc` perf 1 callrcu_global
./rcutorture_urcu_mb `nproc` perf 1 callrcu_global
./rcutorture_urcu_bp `nproc` perf 1 callrcu_global
./rcutorture_urcu_qsbr `nproc` perf 1 callrcu_global
./rcutorture_urcu `nproc` rperf 1 callrcu_global
./rcutorture_urcu_signal `nproc` rperf 1 callrcu_global
./rcutorture_urcu_mb `nproc` rperf 1 callrcu_global
./rcutorture_urcu_bp `nproc` rperf 1 callrcu_global
./rcutorture_urcu_qsbr `nproc` rperf 1 callrcu_global
./rcutorture_urcu `nproc` uperf 1 callrcu_global
./rcutorture_urcu_signal `nproc` uperf 1 callrcu_global
./rcutorture_urcu_mb `nproc` uperf 1 callrcu_global
./rcutorture_urcu_bp `nproc` uperf 1 callrcu_global
./rcutorture_urcu_qsbr `nproc` uperf 1 callrcu_global
./rcutorture_urcu `nproc` stress 1 callrcu_global
./rcutorture_urcu_signal `nproc` stress 1 callrcu_global
./rcutorture_urcu_mb `nproc` stress 1 callrcu_global
./rcutorture_urcu_bp `nproc` stress 1 callrcu_global
./rcutorture_urcu_qsbr `nproc` stress 1 callrcu_global
./rcutorture_urcu `nproc` perf 1 callrcu_percpu
./rcutorture_urcu_signal `nproc` perf 1 callrcu_percpu
./rcutorture_urcu_mb `nproc` perf 1 callrcu_percpu
./rcutorture_urcu_bp `nproc` perf 1 callrcu_percpu
./rcutorture_urcu_qsbr `nproc` perf 1 callrcu_percpu
./rcutorture_urcu `nproc` rperf 1 callrcu_percpu
./rcutorture_urcu_signal `nproc` rperf 1 callrcu_percpu
./rcutorture_urcu_mb `nproc` rperf 1 callrcu_percpu
./rcutorture_urcu_bp `nproc` rperf 1 callrcu_percpu
./rcutorture_urcu_qsbr `nproc` rperf 1 callrcu_percpu
./rcutorture_urcu `nproc` uperf 1 callrcu_percpu
./rcutorture_urcu_signal `nproc` uperf 1 callrcu_percpu
./rcutorture_urcu_mb `nproc` uperf 1 callrcu_percpu
./rcutorture_urcu_bp `nproc` uperf 1 callrcu_percpu
./rcutorture_urcu_qsbr `nproc` uperf 1 callrcu_percpu
./rcutorture_urcu `nproc` stress 1 callrcu_percpu
./rcutorture_urcu_signal `nproc` stress 1 callrcu_percpu
./rcutorture_urcu_mb `nproc` stress 1 callrcu_percpu
./rcutorture_urcu_bp `nproc` stress 1 callrcu_percpu
./rcutorture_urcu_qsbr `nproc` stress 1 callrcu_percpu
./rcutorture_urcu `nproc` perf 1 callrcu_perthread
./rcutorture_urcu_signal `nproc` perf 1 callrcu_perthread
./rcutorture_urcu_mb `nproc` perf 1 callrcu_perthread
./rcutorture_urcu_bp `nproc` perf 1 callrcu_perthread
./rcutorture_urcu_qsbr `nproc` perf 1 callrcu_perthread
./rcutorture_urcu `nproc` rperf 1 callrcu_perthread
./rcutorture_urcu_signal `nproc` rperf 1 callrcu_perthread
./rcutorture_urcu_mb `nproc` rperf 1 callrcu_perthread
./rcutorture_urcu_bp `nproc` rperf 1 callrcu_perthread
./rcutorture_urcu_qsbr `nproc` rperf 1 callrcu_perthread
./rcutorture_urcu `nproc` uperf 1 callrcu_perthread
./rcutorture_urcu_signal `nproc` uperf 1 callrcu_perthread
./rcutorture_urcu_mb `nproc` uperf 1 callrcu_perthread
./rcutorture_urcu_bp `nproc` uperf 1 callrcu_perthread
./rcutorture_urcu_qsbr `nproc` uperf 1 callrcu_perthread
./rcutorture_urcu `nproc` stress 1 callrcu_perthread
./rcutorture_urcu_signal `nproc` stress 1 callrcu_perthread
./rcutorture_urcu_mb `nproc` stress 1 callrcu_perthread
./rcutorture_urcu_bp `nproc` stress 1 callrcu_perthread
./rcutorture_urcu_qsbr `nproc` stress 1 callrcu_perthread

View File

@ -0,0 +1,34 @@
#!/bin/bash
#
# Copyright (C) 2013 - Christian Babeux <christian.babeux@efficios.com>
# 2016 - Michael Jeanson <mjeanson@efficios.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; only version 2
# of the License.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
INPUT=$1
ARGS=()
shift 1
if [ -z "${INPUT}" ]; then
echo "Error: No testlist. Please specify a testlist to run."
exit 1
fi
if [ "x$V" == "x1" ]; then
ARGS+=('-v')
fi
prove "${@}" "${ARGS[@]}" --merge --exec '' - < "${INPUT}"

View File

@ -0,0 +1,214 @@
/*
* test_urcu_fork.c
*
* Userspace RCU library - test program (fork)
*
* Copyright February 2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
#include <sched.h>
#include <errno.h>
#include <urcu/arch.h>
#include <urcu/tls-compat.h>
#ifndef DYNAMIC_LINK_TEST
#define _LGPL_SOURCE
#else
#define rcu_debug_yield_read()
#endif
#include <urcu.h>
#include "tap.h"
/* We generate children 3 levels deep */
#define FORK_DEPTH 3
/* Each generation spawns 10 children */
#define NR_FORK 10
#define NR_TESTS NR_FORK
static int fork_generation;
/*
* Only print diagnostic for top level parent process, else the console
* has trouble formatting the tap output.
*/
#define diag_gen0(...) \
do { \
if (!fork_generation) \
diag(__VA_ARGS__); \
} while (0)
struct test_node {
int somedata;
struct rcu_head head;
};
static void cb(struct rcu_head *head)
{
struct test_node *node;
diag_gen0("rcu callback invoked in pid: %d", (int) getpid());
node = caa_container_of(head, struct test_node, head);
free(node);
}
static void test_rcu(void)
{
struct test_node *node;
rcu_register_thread();
synchronize_rcu();
rcu_read_lock();
rcu_read_unlock();
node = malloc(sizeof(*node));
assert(node);
call_rcu(&node->head, cb);
synchronize_rcu();
rcu_unregister_thread();
}
/*
* Return 0 if child, > 0 if parent, < 0 on error.
*/
static int do_fork(const char *execname)
{
pid_t pid;
diag_gen0("%s parent pid: %d, before fork",
execname, (int) getpid());
call_rcu_before_fork();
pid = fork();
if (pid == 0) {
/* child */
fork_generation++;
tap_disable();
call_rcu_after_fork_child();
diag_gen0("%s child pid: %d, after fork",
execname, (int) getpid());
test_rcu();
diag_gen0("%s child pid: %d, after rcu test",
execname, (int) getpid());
if (fork_generation >= FORK_DEPTH)
exit(EXIT_SUCCESS);
return 0;
} else if (pid > 0) {
int status;
/* parent */
call_rcu_after_fork_parent();
diag_gen0("%s parent pid: %d, after fork",
execname, (int) getpid());
test_rcu();
diag_gen0("%s parent pid: %d, after rcu test",
execname, (int) getpid());
for (;;) {
pid = wait(&status);
if (pid < 0) {
if (!fork_generation)
perror("wait");
return -1;
}
if (WIFEXITED(status)) {
diag_gen0("child %u exited normally with status %u",
pid, WEXITSTATUS(status));
if (WEXITSTATUS(status))
return -1;
break;
} else if (WIFSIGNALED(status)) {
diag_gen0("child %u was terminated by signal %u",
pid, WTERMSIG(status));
return -1;
} else {
continue;
}
}
return 1;
} else {
if (!fork_generation)
perror("fork");
return -1;
}
}
int main(int argc, char **argv)
{
unsigned int i;
plan_tests(NR_TESTS);
#if 0
/* pthread_atfork does not work with malloc/free in callbacks */
ret = pthread_atfork(call_rcu_before_fork,
call_rcu_after_fork_parent,
call_rcu_after_fork_child);
if (ret) {
errno = ret;
perror("pthread_atfork");
exit(EXIT_FAILURE);
}
#endif
restart:
for (i = 0; i < NR_FORK; i++) {
int ret;
test_rcu();
synchronize_rcu();
ret = do_fork(argv[0]);
if (!fork_generation) {
ok(ret >= 0, "child status %d", ret);
}
if (ret == 0) { /* child */
goto restart;
} else if (ret < 0) {
goto error;
} else {
/* else parent, continue. */
}
}
if (!fork_generation) {
return exit_status();
} else {
exit(EXIT_SUCCESS);
}
error:
if (!fork_generation) {
return exit_status();
} else {
exit(EXIT_FAILURE);
}
}

View File

@ -0,0 +1,29 @@
#include <string.h>
#include <sys/time.h>
#include <poll.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <urcu/arch.h>
#include "api.h"
#define _LGPL_SOURCE
#ifdef RCU_MEMBARRIER
#include <urcu.h>
#endif
#ifdef RCU_SIGNAL
#include <urcu.h>
#endif
#ifdef RCU_MB
#include <urcu.h>
#endif
#ifdef RCU_QSBR
#include <urcu-qsbr.h>
#endif
#ifdef RCU_BP
#include <urcu-bp.h>
#endif
#include <urcu/uatomic.h>
#include <urcu/rculist.h>
#include "rcutorture.h"

View File

@ -0,0 +1,10 @@
CFLAGS = -O2 -g -Wall -D_GNU_SOURCE -DHAVE_LIBPTHREAD -pthread
LIB = libtap.a
OBJS = tap.o
$(LIB): $(OBJS)
ar rcs $@ $?
clean:
rm -f $(LIB) $(OBJS)

View File

@ -0,0 +1,473 @@
/*-
* Copyright (c) 2004 Nik Clayton
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "tap.h"
static int no_plan = 0;
static int skip_all = 0;
static int have_plan = 0;
static unsigned int test_count = 0; /* Number of tests that have been run */
static unsigned int e_tests = 0; /* Expected number of tests to run */
static unsigned int failures = 0; /* Number of tests that failed */
static char *todo_msg = NULL;
static char *todo_msg_fixed = "libtap malloc issue";
static int todo = 0;
static int test_died = 0;
static int tap_is_disabled = 0;
/* Encapsulate the pthread code in a conditional. In the absence of
libpthread the code does nothing */
#ifdef HAVE_LIBPTHREAD
#include <pthread.h>
static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
# define LOCK pthread_mutex_lock(&M);
# define UNLOCK pthread_mutex_unlock(&M);
#else
# define LOCK
# define UNLOCK
#endif
static void _expected_tests(unsigned int);
static void _tap_init(void);
static void _cleanup(void);
/*
* Generate a test result.
*
* ok -- boolean, indicates whether or not the test passed.
* test_name -- the name of the test, may be NULL
* test_comment -- a comment to print afterwards, may be NULL
*/
unsigned int
_gen_result(int ok, const char *func, char *file, unsigned int line,
char *test_name, ...)
{
va_list ap;
char *local_test_name = NULL;
char *c;
int name_is_digits;
LOCK;
test_count++;
/* Start by taking the test name and performing any printf()
expansions on it */
if(test_name != NULL) {
va_start(ap, test_name);
if (vasprintf(&local_test_name, test_name, ap) == -1) {
local_test_name = NULL;
}
va_end(ap);
/* Make sure the test name contains more than digits
and spaces. Emit an error message and exit if it
does */
if(local_test_name) {
name_is_digits = 1;
for(c = local_test_name; *c != '\0'; c++) {
if(!isdigit(*c) && !isspace(*c)) {
name_is_digits = 0;
break;
}
}
if(name_is_digits) {
diag(" You named your test '%s'. You shouldn't use numbers for your test names.", local_test_name);
diag(" Very confusing.");
}
}
}
if(!ok) {
printf("not ");
failures++;
}
printf("ok %d", test_count);
if(test_name != NULL) {
printf(" - ");
/* Print the test name, escaping any '#' characters it
might contain */
if(local_test_name != NULL) {
flockfile(stdout);
for(c = local_test_name; *c != '\0'; c++) {
if(*c == '#')
fputc('\\', stdout);
fputc((int)*c, stdout);
}
funlockfile(stdout);
} else { /* vasprintf() failed, use a fixed message */
printf("%s", todo_msg_fixed);
}
}
/* If we're in a todo_start() block then flag the test as being
TODO. todo_msg should contain the message to print at this
point. If it's NULL then asprintf() failed, and we should
use the fixed message.
This is not counted as a failure, so decrement the counter if
the test failed. */
if(todo) {
printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
if(!ok)
failures--;
}
printf("\n");
if(!ok) {
if(getenv("HARNESS_ACTIVE") != NULL)
fputs("\n", stderr);
diag(" Failed %stest (%s:%s() at line %d)",
todo ? "(TODO) " : "", file, func, line);
}
free(local_test_name);
UNLOCK;
/* We only care (when testing) that ok is positive, but here we
specifically only want to return 1 or 0 */
return ok ? 1 : 0;
}
/*
* Initialise the TAP library. Will only do so once, however many times it's
* called.
*/
void
_tap_init(void)
{
static int run_once = 0;
if(!run_once) {
atexit(_cleanup);
/* stdout needs to be unbuffered so that the output appears
in the same place relative to stderr output as it does
with Test::Harness */
setbuf(stdout, 0);
run_once = 1;
}
}
/*
* Note that there's no plan.
*/
int
plan_no_plan(void)
{
LOCK;
_tap_init();
if(have_plan != 0) {
fprintf(stderr, "You tried to plan twice!\n");
test_died = 1;
UNLOCK;
exit(255);
}
have_plan = 1;
no_plan = 1;
UNLOCK;
return 1;
}
/*
* Note that the plan is to skip all tests
*/
int
plan_skip_all(char *reason)
{
LOCK;
_tap_init();
skip_all = 1;
printf("1..0");
if(reason != NULL)
printf(" # Skip %s", reason);
printf("\n");
UNLOCK;
exit(0);
}
/*
* Note the number of tests that will be run.
*/
int
plan_tests(unsigned int tests)
{
LOCK;
_tap_init();
if(have_plan != 0) {
fprintf(stderr, "You tried to plan twice!\n");
test_died = 1;
UNLOCK;
exit(255);
}
if(tests == 0) {
fprintf(stderr, "You said to run 0 tests! You've got to run something.\n");
test_died = 1;
UNLOCK;
exit(255);
}
have_plan = 1;
_expected_tests(tests);
UNLOCK;
return e_tests;
}
unsigned int
diag(char *fmt, ...)
{
va_list ap;
fputs("# ", stderr);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputs("\n", stderr);
return 0;
}
unsigned int
rdiag_start(void)
{
fputs("# ", stderr);
return 0;
}
unsigned int
rdiag(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
return 0;
}
unsigned int
rdiag_end(void)
{
fputs("\n", stderr);
return 0;
}
void
_expected_tests(unsigned int tests)
{
printf("1..%d\n", tests);
e_tests = tests;
}
int
skip(unsigned int n, char *fmt, ...)
{
va_list ap;
char *skip_msg = NULL;
LOCK;
va_start(ap, fmt);
if (asprintf(&skip_msg, fmt, ap) == -1) {
skip_msg = NULL;
}
va_end(ap);
while(n-- > 0) {
test_count++;
printf("ok %d # skip %s\n", test_count,
skip_msg != NULL ?
skip_msg : "libtap():malloc() failed");
}
free(skip_msg);
UNLOCK;
return 1;
}
void
todo_start(char *fmt, ...)
{
va_list ap;
LOCK;
va_start(ap, fmt);
if (vasprintf(&todo_msg, fmt, ap) == -1) {
todo_msg = NULL;
}
va_end(ap);
todo = 1;
UNLOCK;
}
void
todo_end(void)
{
LOCK;
todo = 0;
free(todo_msg);
UNLOCK;
}
int
exit_status(void)
{
int r;
LOCK;
/* If there's no plan, just return the number of failures */
if(no_plan || !have_plan) {
UNLOCK;
return failures;
}
/* Ran too many tests? Return the number of tests that were run
that shouldn't have been */
if(e_tests < test_count) {
r = test_count - e_tests;
UNLOCK;
return r;
}
/* Return the number of tests that failed + the number of tests
that weren't run */
r = failures + e_tests - test_count;
UNLOCK;
return r;
}
/*
* Cleanup at the end of the run, produce any final output that might be
* required.
*/
void
_cleanup(void)
{
LOCK;
if (tap_is_disabled) {
UNLOCK;
return;
}
/* If plan_no_plan() wasn't called, and we don't have a plan,
and we're not skipping everything, then something happened
before we could produce any output */
if(!no_plan && !have_plan && !skip_all) {
diag("Looks like your test died before it could output anything.");
UNLOCK;
return;
}
if(test_died) {
diag("Looks like your test died just after %d.", test_count);
UNLOCK;
return;
}
/* No plan provided, but now we know how many tests were run, and can
print the header at the end */
if(!skip_all && (no_plan || !have_plan)) {
printf("1..%d\n", test_count);
}
if((have_plan && !no_plan) && e_tests < test_count) {
diag("Looks like you planned %d %s but ran %d extra.",
e_tests, e_tests == 1 ? "test" : "tests", test_count - e_tests);
UNLOCK;
return;
}
if((have_plan || !no_plan) && e_tests > test_count) {
diag("Looks like you planned %d %s but only ran %d.",
e_tests, e_tests == 1 ? "test" : "tests", test_count);
UNLOCK;
return;
}
if(failures)
diag("Looks like you failed %d %s of %d.",
failures, failures == 1 ? "test" : "tests", test_count);
UNLOCK;
}
/* Disable tap for this process. */
void
tap_disable(void)
{
LOCK;
tap_is_disabled = 1;
UNLOCK;
}

View File

@ -0,0 +1,95 @@
/*-
* Copyright (c) 2004 Nik Clayton
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* '## __VA_ARGS__' is a gcc'ism. C99 doesn't allow the token pasting
and requires the caller to add the final comma if they've ommitted
the optional arguments */
#ifdef __GNUC__
# define ok(e, test, ...) ((e) ? \
_gen_result(1, __func__, __FILE__, __LINE__, \
test, ## __VA_ARGS__) : \
_gen_result(0, __func__, __FILE__, __LINE__, \
test, ## __VA_ARGS__))
# define ok1(e) ((e) ? \
_gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
_gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
# define pass(test, ...) ok(1, test, ## __VA_ARGS__);
# define fail(test, ...) ok(0, test, ## __VA_ARGS__);
# define skip_start(test, n, fmt, ...) \
do { \
if((test)) { \
skip(n, fmt, ## __VA_ARGS__); \
continue; \
}
#elif __STDC_VERSION__ >= 199901L /* __GNUC__ */
# define ok(e, ...) ((e) ? \
_gen_result(1, __func__, __FILE__, __LINE__, \
__VA_ARGS__) : \
_gen_result(0, __func__, __FILE__, __LINE__, \
__VA_ARGS__))
# define ok1(e) ((e) ? \
_gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \
_gen_result(0, __func__, __FILE__, __LINE__, "%s", #e))
# define pass(...) ok(1, __VA_ARGS__);
# define fail(...) ok(0, __VA_ARGS__);
# define skip_start(test, n, ...) \
do { \
if((test)) { \
skip(n, __VA_ARGS__); \
continue; \
}
#else /* __STDC_VERSION__ */
# error "Needs gcc or C99 compiler for variadic macros."
#endif /* __STDC_VERSION__ */
#define skip_end() } while(0);
unsigned int _gen_result(int, const char *, char *, unsigned int, char *, ...);
int plan_no_plan(void);
int plan_skip_all(char *);
int plan_tests(unsigned int);
unsigned int diag(char *, ...);
int skip(unsigned int, char *, ...);
void todo_start(char *, ...);
void todo_end(void);
int exit_status(void);
void tap_disable(void);
unsigned int rdiag_start(void);
unsigned int rdiag(char *fmt, ...);
unsigned int rdiag_end(void);

20
tests/tests.yml Normal file
View File

@ -0,0 +1,20 @@
---
# No tests suitable for atomic environment
# No tests suitable for container environment
# Tests suitable for classic environment
- hosts: localhost
roles:
- role: standard-test-basic
tags:
- classic
tests:
- regression_tests:
dir: regression_tests
run: make
required_packages:
- userspace-rcu
- userspace-rcu-devel
- perl-Test-Harness
- make
- gcc

View File

@ -0,0 +1,11 @@
-----BEGIN PGP SIGNATURE-----
iQEzBAABCAAdFiEEKgtO2RXy0/pF9bFiFygKl4EYas8FAl6gQH4ACgkQFygKl4EY
as8h/AgAkN5SYABEXrJlwr/KVhGMRYdz6aTJXhnD+WUeLEEf7/2C2kumO5xCkiU5
hM/UVpD1I/tDDHBopz8C/sU3pvnyY/OLDkWq9KlxcHWyQvCr9rrlYNhpndL495vS
OwiY5GXrMnsthTNGuxsg+ViZ3o8gu4buWBeji7yO6OBsMI6YJp4xQNfIZl6lGMcY
k4LB9rP2OnK39+1JGuXkRouFLKIIY8LxJzF1SAeG281xYkgEr8ffrAIfiebZ1Wra
H3C4Fn5LolD9gE2UqVyEFDMaunNgJrFQvWOLmyFg/S31WWE93mKQBXEBMagNf1ZR
QlBu66HFSswrHYnJEdCevrAAP3fs3Q==
=yzRV
-----END PGP SIGNATURE-----

View File

@ -1,13 +1,16 @@
Name: userspace-rcu
Version: 0.10.1
Release: 4%{?dist}
Version: 0.12.1
Release: 6%{?dist}
Summary: RCU (read-copy-update) implementation in user-space
Group: System Environment/Libraries
License: LGPLv2+
URL: http://liburcu.org
Source0: http://lttng.org/files/urcu/%{name}-%{version}.tar.bz2
Source1: http://lttng.org/files/urcu/%{name}-%{version}.tar.bz2.asc
# gpg2 --export --export-options export-minimal 2A0B4ED915F2D3FA45F5B16217280A9781186ACF > gpgkey-2A0B4ED915F2D3FA45F5B16217280A9781186ACF.gpg
Source2: gpgkey-2A0B4ED915F2D3FA45F5B16217280A9781186ACF.gpg
Patch0: regtest-without-bench.patch
BuildRequires: make
BuildRequires: pkgconfig
BuildRequires: perl-Test-Harness
BuildRequires: autoconf automake libtool
@ -22,17 +25,15 @@ reclamation is possible.
%package devel
Summary: Development files for %{name}
Group: Development/Libraries
Requires: %{name}%{?_isa} = %{version}-%{release}
%description devel
The %{name}-devel package contains libraries and header files for
developing applications that use %{name}.
This package contains libraries and header files for developing applications
that use %{name}
%prep
%setup -q
%patch0 -p1
%autosetup -p1
%build
# Reinitialize libtool with the fedora version to remove Rpath
@ -44,27 +45,41 @@ V=1 make %{?_smp_mflags}
%install
make install DESTDIR=$RPM_BUILD_ROOT
rm -vf $RPM_BUILD_ROOT%{_libdir}/*.la
find %{buildroot} -type f -name "*.la" -delete
rm %{buildroot}/%{_docdir}/%{name}/LICENSE
# Replace arch-dependent header file with arch-independent stub (when needed).
%multilib_fix_c_header --file %{_includedir}/urcu/config.h
%check
make check
make regtest
%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig
%ldconfig_scriptlets
%files
%doc ChangeLog LICENSE README.md gpl-2.0.txt lgpl-relicensing.txt lgpl-2.1.txt
%{_libdir}/*.so.*
%license LICENSE gpl-2.0.txt lgpl-relicensing.txt lgpl-2.1.txt
%doc ChangeLog README.md
%{_libdir}/liburcu-bp.so.6*
%{_libdir}/liburcu-cds.so.6*
%{_libdir}/liburcu-common.so.6*
%{_libdir}/liburcu-mb.so.6*
%{_libdir}/liburcu-memb.so.6*
%{_libdir}/liburcu-qsbr.so.6*
%{_libdir}/liburcu-signal.so.6*
%{_libdir}/liburcu.so.6*
%files devel
%doc %{_pkgdocdir}/examples
%{_includedir}/*
%{_libdir}/*.so
%{_libdir}/liburcu-bp.so
%{_libdir}/liburcu-cds.so
%{_libdir}/liburcu-common.so
%{_libdir}/liburcu-mb.so
%{_libdir}/liburcu-memb.so
%{_libdir}/liburcu-qsbr.so
%{_libdir}/liburcu-signal.so
%{_libdir}/liburcu.so
%{_libdir}/pkgconfig/liburcu*.pc
%{_docdir}/%{name}/cds-api.md
%{_docdir}/%{name}/rcu-api.md
@ -73,15 +88,51 @@ make regtest
%changelog
* Wed Feb 10 2021 Benjamin Marzinski <bmarzins@redhat.com> - 0.10.1-4
- Fix CI test package requirements
- Resolves: bz #1682555
* Tue Aug 10 2021 Mohan Boddu <mboddu@redhat.com> - 0.12.1-6
- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags
Related: rhbz#1991688
* Fri Oct 04 2019 Benjamin Marzinski <bmarzins@redhat.com> - 0.10.1-3
* Mon Apr 19 2021 Benjamin Marzinski <bmarzins@redhat.com> - 0.12.1-5
- Replace arch-dependent /usr/include/urcu/config.h with arch-independent
stub when needed. (bz #1853168)
- Added CI gating tests (bz #1682555)
- Resolves: bz #1682555, #1853168
stub when needed. (bz# 1951223)
- Added CI gating tests
- Resolves: bz #1951223
* Fri Apr 16 2021 Mohan Boddu <mboddu@redhat.com> - 0.12.1-4
- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
* Wed Jan 27 2021 Fedora Release Engineering <releng@fedoraproject.org> - 0.12.1-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
* Wed Jul 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 0.12.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
* Tue Apr 22 2020 Michael Jeanson <mjeanson@efficios.com> - 0.12.1-1
- New upstream release
* Tue Apr 14 2020 Michael Jeanson <mjeanson@efficios.com> - 0.12.0-1
- New upstream release
* Fri Jan 31 2020 Fedora Release Engineering <releng@fedoraproject.org> - 0.11.1-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
* Sat Jul 27 2019 Fedora Release Engineering <releng@fedoraproject.org> - 0.11.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
* Mon Jun 03 2019 Michael Jeanson <mjeanson@efficios.com> - 0.11.1-1
- New upstream release
* Fri May 03 2019 Michael Jeanson <mjeanson@efficios.com> - 0.10.2-1
- New upstream release
* Sun Feb 03 2019 Fedora Release Engineering <releng@fedoraproject.org> - 0.10.1-5
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
* Sat Jul 14 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.10.1-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Thu May 10 2018 Peter Robinson <pbrobinson@fedoraproject.org> 0.10.1-3
- Use %%license, spec cleanups
* Fri Feb 09 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.10.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild