CALLRCU_PERTHREAD,
};
+enum writer_state {
+ WRITER_STATE_SYNC_RCU,
+ WRITER_STATE_CALL_RCU,
+ WRITER_STATE_POLL_RCU,
+};
+
static enum callrcu_type callrcu_type = CALLRCU_GLOBAL;
long long n_reads = 0LL;
#endif
#ifndef mark_rcu_quiescent_state
-#define mark_rcu_quiescent_state() do ; while (0)
+#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)
+#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.
*/
+static
void *rcu_read_perf_test(void *arg)
{
int i;
return (NULL);
}
-void *rcu_update_perf_test(void *arg)
+static
+void *rcu_update_perf_test(void *arg __attribute__((unused)))
{
long long n_updates_local = 0;
return NULL;
}
+static
void perftestinit(void)
{
init_per_thread(n_reads_pt, 0LL);
uatomic_set(&nthreadsrunning, 0);
}
+static
int perftestrun(int nthreads, int nreaders, int nupdaters)
{
int t;
return 0;
}
+static
int perftest(int nreaders, int cpustride)
{
int i;
return perftestrun(i + 1, nreaders, 1);
}
+static
int rperftest(int nreaders, int cpustride)
{
int i;
return perftestrun(i, nreaders, 0);
}
+static
int uperftest(int nupdaters, int cpustride)
{
int i;
int mbtest;
};
-struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0 } };
+struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0, 0 } };
struct rcu_stress *rcu_stress_current;
int rcu_stress_idx = 0;
int garbage = 0;
-void *rcu_read_stress_test(void *arg)
+static
+void *rcu_read_stress_test(void *arg __attribute__((unused)))
{
int i;
int itercnt = 0;
static pthread_mutex_t call_rcu_test_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t call_rcu_test_cond = PTHREAD_COND_INITIALIZER;
+static bool call_rcu_wait;
-void rcu_update_stress_test_rcu(struct rcu_head *head)
+static
+void rcu_update_stress_test_rcu(struct rcu_head *head __attribute__((unused)))
{
int ret;
strerror(errno));
abort();
}
+ call_rcu_wait = false;
ret = pthread_mutex_unlock(&call_rcu_test_mutex);
if (ret) {
errno = ret;
}
}
-void *rcu_update_stress_test(void *arg)
+static
+void advance_writer_state(enum writer_state *state)
+{
+ switch (*state) {
+ case WRITER_STATE_SYNC_RCU:
+ *state = WRITER_STATE_CALL_RCU;
+ break;
+ case WRITER_STATE_CALL_RCU:
+ *state = WRITER_STATE_POLL_RCU;
+ break;
+ case WRITER_STATE_POLL_RCU:
+ *state = WRITER_STATE_SYNC_RCU;
+ break;
+ }
+}
+
+static
+void *rcu_update_stress_test(void *arg __attribute__((unused)))
{
int i;
struct rcu_stress *p;
struct rcu_head rh;
+ enum writer_state writer_state = WRITER_STATE_SYNC_RCU;
while (goflag == GOFLAG_INIT)
(void) poll(NULL, 0, 1);
for (i = 0; i < RCU_STRESS_PIPE_LEN; i++)
if (i != rcu_stress_idx)
rcu_stress_array[i].pipe_count++;
- if (n_updates & 0x1)
+ switch (writer_state) {
+ case WRITER_STATE_SYNC_RCU:
synchronize_rcu();
- else {
+ break;
+ case WRITER_STATE_CALL_RCU:
+ {
int ret;
ret = pthread_mutex_lock(&call_rcu_test_mutex);
strerror(errno));
abort();
}
+ rcu_register_thread();
call_rcu(&rh, rcu_update_stress_test_rcu);
- ret = pthread_cond_wait(&call_rcu_test_cond,
- &call_rcu_test_mutex);
+ 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).
+ */
+ call_rcu_wait = true;
+ do {
+ ret = pthread_cond_wait(&call_rcu_test_cond,
+ &call_rcu_test_mutex);
+ } while (call_rcu_wait);
if (ret) {
errno = ret;
diag("pthread_cond_signal: %s",
strerror(errno));
abort();
}
+ break;
+ }
+ case WRITER_STATE_POLL_RCU:
+ {
+ struct urcu_gp_poll_state poll_state;
+
+ rcu_register_thread();
+ poll_state = start_poll_synchronize_rcu();
+ rcu_unregister_thread();
+ while (!poll_state_synchronize_rcu(poll_state))
+ (void) poll(NULL, 0, 1); /* Wait for 1ms */
+ break;
+ }
}
n_updates++;
+ advance_writer_state(&writer_state);
}
+
return NULL;
}
-void *rcu_fake_update_stress_test(void *arg)
+static
+void *rcu_fake_update_stress_test(void *arg __attribute__((unused)))
{
if (callrcu_type == CALLRCU_PERTHREAD) {
struct call_rcu_data *crdp;
return NULL;
}
+static
int stresstest(int nreaders)
{
int i;
* Mainprogram.
*/
-void usage(int argc, char *argv[])
+static
+void usage(char *argv[]) __attribute__((__noreturn__));
+
+static
+void usage(char *argv[])
{
diag("Usage: %s nreaders [ perf | rperf | uperf | stress ] [ stride ] [ callrcu_global | callrcu_percpu | callrcu_perthread ]\n", argv[0]);
exit(-1);
} else if (strcmp(callrcu_str, "callrcu_perthread") == 0) {
callrcu_type = CALLRCU_PERTHREAD;
} else {
- usage(argc, argv);
+ usage(argv);
goto end;
}
}
-
+
switch (callrcu_type) {
case CALLRCU_GLOBAL:
diag("Using global per-process call_rcu thread.");
if (argc > 1) {
if (strcmp(argv[1], "-h") == 0
|| strcmp(argv[1], "--help") == 0) {
- usage(argc, argv);
+ usage(argv);
goto end;
}
nreaders = strtoul(argv[1], NULL, 0);
"stresstest readers: %d, stride: %d",
nreaders, cpustride);
else
- usage(argc, argv);
+ usage(argv);
} else {
- usage(argc, argv);
+ usage(argv);
}
end:
return exit_status();