qemu/tests/rcutorture.c
<<
>>
Prefs
   1/*
   2 * rcutorture.c: simple user-level performance/stress test of RCU.
   3 *
   4 * Usage:
   5 *     ./rcu <nreaders> rperf [ <seconds> ]
   6 *         Run a read-side performance test with the specified
   7 *         number of readers for <seconds> seconds.
   8 *     ./rcu <nupdaters> uperf [ <seconds> ]
   9 *         Run an update-side performance test with the specified
  10 *         number of updaters and specified duration.
  11 *     ./rcu <nreaders> perf [ <seconds> ]
  12 *         Run a combined read/update performance test with the specified
  13 *         number of readers and one updater and specified duration.
  14 *
  15 * The above tests produce output as follows:
  16 *
  17 * n_reads: 46008000  n_updates: 146026  nreaders: 2  nupdaters: 1 duration: 1
  18 * ns/read: 43.4707  ns/update: 6848.1
  19 *
  20 * The first line lists the total number of RCU reads and updates executed
  21 * during the test, the number of reader threads, the number of updater
  22 * threads, and the duration of the test in seconds.  The second line
  23 * lists the average duration of each type of operation in nanoseconds,
  24 * or "nan" if the corresponding type of operation was not performed.
  25 *
  26 *     ./rcu <nreaders> stress [ <seconds> ]
  27 *         Run a stress test with the specified number of readers and
  28 *         one updater.
  29 *
  30 * This test produces output as follows:
  31 *
  32 * n_reads: 114633217  n_updates: 3903415  n_mberror: 0
  33 * rcu_stress_count: 114618391 14826 0 0 0 0 0 0 0 0 0
  34 *
  35 * The first line lists the number of RCU read and update operations
  36 * executed, followed by the number of memory-ordering violations
  37 * (which will be zero in a correct RCU implementation).  The second
  38 * line lists the number of readers observing progressively more stale
  39 * data.  A correct RCU implementation will have all but the first two
  40 * numbers non-zero.
  41 *
  42 * This program is free software; you can redistribute it and/or modify
  43 * it under the terms of the GNU General Public License as published by
  44 * the Free Software Foundation; either version 2 of the License, or
  45 * (at your option) any later version.
  46 *
  47 * This program is distributed in the hope that it will be useful,
  48 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  49 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  50 * GNU General Public License for more details.
  51 *
  52 * You should have received a copy of the GNU General Public License
  53 * along with this program; if not, write to the Free Software
  54 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  55 *
  56 * Copyright (c) 2008 Paul E. McKenney, IBM Corporation.
  57 */
  58
  59/*
  60 * Test variables.
  61 */
  62
  63#include "qemu/osdep.h"
  64#include <glib.h>
  65#include "qemu/atomic.h"
  66#include "qemu/rcu.h"
  67#include "qemu/thread.h"
  68
  69long long n_reads = 0LL;
  70long n_updates = 0L;
  71int nthreadsrunning;
  72
  73#define GOFLAG_INIT 0
  74#define GOFLAG_RUN  1
  75#define GOFLAG_STOP 2
  76
  77static volatile int goflag = GOFLAG_INIT;
  78
  79#define RCU_READ_RUN 1000
  80
  81#define NR_THREADS 100
  82static QemuMutex counts_mutex;
  83static QemuThread threads[NR_THREADS];
  84static struct rcu_reader_data *data[NR_THREADS];
  85static int n_threads;
  86
  87static void create_thread(void *(*func)(void *))
  88{
  89    if (n_threads >= NR_THREADS) {
  90        fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS);
  91        exit(-1);
  92    }
  93    qemu_thread_create(&threads[n_threads], "test", func, &data[n_threads],
  94                       QEMU_THREAD_JOINABLE);
  95    n_threads++;
  96}
  97
  98static void wait_all_threads(void)
  99{
 100    int i;
 101
 102    for (i = 0; i < n_threads; i++) {
 103        qemu_thread_join(&threads[i]);
 104    }
 105    n_threads = 0;
 106}
 107
 108/*
 109 * Performance test.
 110 */
 111
 112static void *rcu_read_perf_test(void *arg)
 113{
 114    int i;
 115    long long n_reads_local = 0;
 116
 117    rcu_register_thread();
 118
 119    *(struct rcu_reader_data **)arg = &rcu_reader;
 120    atomic_inc(&nthreadsrunning);
 121    while (goflag == GOFLAG_INIT) {
 122        g_usleep(1000);
 123    }
 124    while (goflag == GOFLAG_RUN) {
 125        for (i = 0; i < RCU_READ_RUN; i++) {
 126            rcu_read_lock();
 127            rcu_read_unlock();
 128        }
 129        n_reads_local += RCU_READ_RUN;
 130    }
 131    qemu_mutex_lock(&counts_mutex);
 132    n_reads += n_reads_local;
 133    qemu_mutex_unlock(&counts_mutex);
 134
 135    rcu_unregister_thread();
 136    return NULL;
 137}
 138
 139static void *rcu_update_perf_test(void *arg)
 140{
 141    long long n_updates_local = 0;
 142
 143    rcu_register_thread();
 144
 145    *(struct rcu_reader_data **)arg = &rcu_reader;
 146    atomic_inc(&nthreadsrunning);
 147    while (goflag == GOFLAG_INIT) {
 148        g_usleep(1000);
 149    }
 150    while (goflag == GOFLAG_RUN) {
 151        synchronize_rcu();
 152        n_updates_local++;
 153    }
 154    qemu_mutex_lock(&counts_mutex);
 155    n_updates += n_updates_local;
 156    qemu_mutex_unlock(&counts_mutex);
 157
 158    rcu_unregister_thread();
 159    return NULL;
 160}
 161
 162static void perftestinit(void)
 163{
 164    nthreadsrunning = 0;
 165}
 166
 167static void perftestrun(int nthreads, int duration, int nreaders, int nupdaters)
 168{
 169    while (atomic_read(&nthreadsrunning) < nthreads) {
 170        g_usleep(1000);
 171    }
 172    goflag = GOFLAG_RUN;
 173    g_usleep(duration * G_USEC_PER_SEC);
 174    goflag = GOFLAG_STOP;
 175    wait_all_threads();
 176    printf("n_reads: %lld  n_updates: %ld  nreaders: %d  nupdaters: %d duration: %d\n",
 177           n_reads, n_updates, nreaders, nupdaters, duration);
 178    printf("ns/read: %g  ns/update: %g\n",
 179           ((duration * 1000*1000*1000.*(double)nreaders) /
 180        (double)n_reads),
 181           ((duration * 1000*1000*1000.*(double)nupdaters) /
 182        (double)n_updates));
 183    exit(0);
 184}
 185
 186static void perftest(int nreaders, int duration)
 187{
 188    int i;
 189
 190    perftestinit();
 191    for (i = 0; i < nreaders; i++) {
 192        create_thread(rcu_read_perf_test);
 193    }
 194    create_thread(rcu_update_perf_test);
 195    perftestrun(i + 1, duration, nreaders, 1);
 196}
 197
 198static void rperftest(int nreaders, int duration)
 199{
 200    int i;
 201
 202    perftestinit();
 203    for (i = 0; i < nreaders; i++) {
 204        create_thread(rcu_read_perf_test);
 205    }
 206    perftestrun(i, duration, nreaders, 0);
 207}
 208
 209static void uperftest(int nupdaters, int duration)
 210{
 211    int i;
 212
 213    perftestinit();
 214    for (i = 0; i < nupdaters; i++) {
 215        create_thread(rcu_update_perf_test);
 216    }
 217    perftestrun(i, duration, 0, nupdaters);
 218}
 219
 220/*
 221 * Stress test.
 222 */
 223
 224#define RCU_STRESS_PIPE_LEN 10
 225
 226struct rcu_stress {
 227    int pipe_count;
 228    int mbtest;
 229};
 230
 231struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0 } };
 232struct rcu_stress *rcu_stress_current;
 233int rcu_stress_idx;
 234
 235int n_mberror;
 236long long rcu_stress_count[RCU_STRESS_PIPE_LEN + 1];
 237
 238
 239static void *rcu_read_stress_test(void *arg)
 240{
 241    int i;
 242    int itercnt = 0;
 243    struct rcu_stress *p;
 244    int pc;
 245    long long n_reads_local = 0;
 246    long long rcu_stress_local[RCU_STRESS_PIPE_LEN + 1] = { 0 };
 247    volatile int garbage = 0;
 248
 249    rcu_register_thread();
 250
 251    *(struct rcu_reader_data **)arg = &rcu_reader;
 252    while (goflag == GOFLAG_INIT) {
 253        g_usleep(1000);
 254    }
 255    while (goflag == GOFLAG_RUN) {
 256        rcu_read_lock();
 257        p = atomic_rcu_read(&rcu_stress_current);
 258        if (p->mbtest == 0) {
 259            n_mberror++;
 260        }
 261        rcu_read_lock();
 262        for (i = 0; i < 100; i++) {
 263            garbage++;
 264        }
 265        rcu_read_unlock();
 266        pc = p->pipe_count;
 267        rcu_read_unlock();
 268        if ((pc > RCU_STRESS_PIPE_LEN) || (pc < 0)) {
 269            pc = RCU_STRESS_PIPE_LEN;
 270        }
 271        rcu_stress_local[pc]++;
 272        n_reads_local++;
 273        if ((++itercnt % 0x1000) == 0) {
 274            synchronize_rcu();
 275        }
 276    }
 277    qemu_mutex_lock(&counts_mutex);
 278    n_reads += n_reads_local;
 279    for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) {
 280        rcu_stress_count[i] += rcu_stress_local[i];
 281    }
 282    qemu_mutex_unlock(&counts_mutex);
 283
 284    rcu_unregister_thread();
 285    return NULL;
 286}
 287
 288static void *rcu_update_stress_test(void *arg)
 289{
 290    int i;
 291    struct rcu_stress *p;
 292
 293    rcu_register_thread();
 294
 295    *(struct rcu_reader_data **)arg = &rcu_reader;
 296    while (goflag == GOFLAG_INIT) {
 297        g_usleep(1000);
 298    }
 299    while (goflag == GOFLAG_RUN) {
 300        i = rcu_stress_idx + 1;
 301        if (i >= RCU_STRESS_PIPE_LEN) {
 302            i = 0;
 303        }
 304        p = &rcu_stress_array[i];
 305        p->mbtest = 0;
 306        smp_mb();
 307        p->pipe_count = 0;
 308        p->mbtest = 1;
 309        atomic_rcu_set(&rcu_stress_current, p);
 310        rcu_stress_idx = i;
 311        for (i = 0; i < RCU_STRESS_PIPE_LEN; i++) {
 312            if (i != rcu_stress_idx) {
 313                rcu_stress_array[i].pipe_count++;
 314            }
 315        }
 316        synchronize_rcu();
 317        n_updates++;
 318    }
 319
 320    rcu_unregister_thread();
 321    return NULL;
 322}
 323
 324static void *rcu_fake_update_stress_test(void *arg)
 325{
 326    rcu_register_thread();
 327
 328    *(struct rcu_reader_data **)arg = &rcu_reader;
 329    while (goflag == GOFLAG_INIT) {
 330        g_usleep(1000);
 331    }
 332    while (goflag == GOFLAG_RUN) {
 333        synchronize_rcu();
 334        g_usleep(1000);
 335    }
 336
 337    rcu_unregister_thread();
 338    return NULL;
 339}
 340
 341static void stresstest(int nreaders, int duration)
 342{
 343    int i;
 344
 345    rcu_stress_current = &rcu_stress_array[0];
 346    rcu_stress_current->pipe_count = 0;
 347    rcu_stress_current->mbtest = 1;
 348    for (i = 0; i < nreaders; i++) {
 349        create_thread(rcu_read_stress_test);
 350    }
 351    create_thread(rcu_update_stress_test);
 352    for (i = 0; i < 5; i++) {
 353        create_thread(rcu_fake_update_stress_test);
 354    }
 355    goflag = GOFLAG_RUN;
 356    g_usleep(duration * G_USEC_PER_SEC);
 357    goflag = GOFLAG_STOP;
 358    wait_all_threads();
 359    printf("n_reads: %lld  n_updates: %ld  n_mberror: %d\n",
 360           n_reads, n_updates, n_mberror);
 361    printf("rcu_stress_count:");
 362    for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) {
 363        printf(" %lld", rcu_stress_count[i]);
 364    }
 365    printf("\n");
 366    exit(0);
 367}
 368
 369/* GTest interface */
 370
 371static void gtest_stress(int nreaders, int duration)
 372{
 373    int i;
 374
 375    rcu_stress_current = &rcu_stress_array[0];
 376    rcu_stress_current->pipe_count = 0;
 377    rcu_stress_current->mbtest = 1;
 378    for (i = 0; i < nreaders; i++) {
 379        create_thread(rcu_read_stress_test);
 380    }
 381    create_thread(rcu_update_stress_test);
 382    for (i = 0; i < 5; i++) {
 383        create_thread(rcu_fake_update_stress_test);
 384    }
 385    goflag = GOFLAG_RUN;
 386    g_usleep(duration * G_USEC_PER_SEC);
 387    goflag = GOFLAG_STOP;
 388    wait_all_threads();
 389    g_assert_cmpint(n_mberror, ==, 0);
 390    for (i = 2; i <= RCU_STRESS_PIPE_LEN; i++) {
 391        g_assert_cmpint(rcu_stress_count[i], ==, 0);
 392    }
 393}
 394
 395static void gtest_stress_1_1(void)
 396{
 397    gtest_stress(1, 1);
 398}
 399
 400static void gtest_stress_10_1(void)
 401{
 402    gtest_stress(10, 1);
 403}
 404
 405static void gtest_stress_1_5(void)
 406{
 407    gtest_stress(1, 5);
 408}
 409
 410static void gtest_stress_10_5(void)
 411{
 412    gtest_stress(10, 5);
 413}
 414
 415/*
 416 * Mainprogram.
 417 */
 418
 419static void usage(int argc, char *argv[])
 420{
 421    fprintf(stderr, "Usage: %s [nreaders [ perf | stress ] ]\n", argv[0]);
 422    exit(-1);
 423}
 424
 425int main(int argc, char *argv[])
 426{
 427    int nreaders = 1;
 428    int duration = 1;
 429
 430    qemu_mutex_init(&counts_mutex);
 431    if (argc >= 2 && argv[1][0] == '-') {
 432        g_test_init(&argc, &argv, NULL);
 433        if (g_test_quick()) {
 434            g_test_add_func("/rcu/torture/1reader", gtest_stress_1_1);
 435            g_test_add_func("/rcu/torture/10readers", gtest_stress_10_1);
 436        } else {
 437            g_test_add_func("/rcu/torture/1reader", gtest_stress_1_5);
 438            g_test_add_func("/rcu/torture/10readers", gtest_stress_10_5);
 439        }
 440        return g_test_run();
 441    }
 442
 443    if (argc >= 2) {
 444        nreaders = strtoul(argv[1], NULL, 0);
 445    }
 446    if (argc > 3) {
 447        duration = strtoul(argv[3], NULL, 0);
 448    }
 449    if (argc < 3 || strcmp(argv[2], "stress") == 0) {
 450        stresstest(nreaders, duration);
 451    } else if (strcmp(argv[2], "rperf") == 0) {
 452        rperftest(nreaders, duration);
 453    } else if (strcmp(argv[2], "uperf") == 0) {
 454        uperftest(nreaders, duration);
 455    } else if (strcmp(argv[2], "perf") == 0) {
 456        perftest(nreaders, duration);
 457    }
 458    usage(argc, argv);
 459    return 0;
 460}
 461