qemu/tests/unit/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 "qemu/atomic.h"
  65#include "qemu/rcu.h"
  66#include "qemu/thread.h"
  67
  68int nthreadsrunning;
  69
  70#define GOFLAG_INIT 0
  71#define GOFLAG_RUN  1
  72#define GOFLAG_STOP 2
  73
  74static volatile int goflag = GOFLAG_INIT;
  75
  76#define RCU_READ_RUN 1000
  77
  78#define NR_THREADS 100
  79static QemuThread threads[NR_THREADS];
  80static struct rcu_reader_data *data[NR_THREADS];
  81static int n_threads;
  82
  83/*
  84 * Statistical counts
  85 *
  86 * These are the sum of local counters at the end of a run.
  87 * Updates are protected by a mutex.
  88 */
  89static QemuMutex counts_mutex;
  90long long n_reads = 0LL;
  91long n_updates = 0L;
  92
  93static void create_thread(void *(*func)(void *))
  94{
  95    if (n_threads >= NR_THREADS) {
  96        fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS);
  97        exit(-1);
  98    }
  99    qemu_thread_create(&threads[n_threads], "test", func, &data[n_threads],
 100                       QEMU_THREAD_JOINABLE);
 101    n_threads++;
 102}
 103
 104static void wait_all_threads(void)
 105{
 106    int i;
 107
 108    for (i = 0; i < n_threads; i++) {
 109        qemu_thread_join(&threads[i]);
 110    }
 111    n_threads = 0;
 112}
 113
 114/*
 115 * Performance test.
 116 */
 117
 118static void *rcu_read_perf_test(void *arg)
 119{
 120    int i;
 121    long long n_reads_local = 0;
 122
 123    rcu_register_thread();
 124
 125    *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
 126    qatomic_inc(&nthreadsrunning);
 127    while (goflag == GOFLAG_INIT) {
 128        g_usleep(1000);
 129    }
 130    while (goflag == GOFLAG_RUN) {
 131        for (i = 0; i < RCU_READ_RUN; i++) {
 132            rcu_read_lock();
 133            rcu_read_unlock();
 134        }
 135        n_reads_local += RCU_READ_RUN;
 136    }
 137    qemu_mutex_lock(&counts_mutex);
 138    n_reads += n_reads_local;
 139    qemu_mutex_unlock(&counts_mutex);
 140
 141    rcu_unregister_thread();
 142    return NULL;
 143}
 144
 145static void *rcu_update_perf_test(void *arg)
 146{
 147    long long n_updates_local = 0;
 148
 149    rcu_register_thread();
 150
 151    *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
 152    qatomic_inc(&nthreadsrunning);
 153    while (goflag == GOFLAG_INIT) {
 154        g_usleep(1000);
 155    }
 156    while (goflag == GOFLAG_RUN) {
 157        synchronize_rcu();
 158        n_updates_local++;
 159    }
 160    qemu_mutex_lock(&counts_mutex);
 161    n_updates += n_updates_local;
 162    qemu_mutex_unlock(&counts_mutex);
 163
 164    rcu_unregister_thread();
 165    return NULL;
 166}
 167
 168static void perftestinit(void)
 169{
 170    nthreadsrunning = 0;
 171}
 172
 173static void perftestrun(int nthreads, int duration, int nreaders, int nupdaters)
 174{
 175    while (qatomic_read(&nthreadsrunning) < nthreads) {
 176        g_usleep(1000);
 177    }
 178    goflag = GOFLAG_RUN;
 179    g_usleep(duration * G_USEC_PER_SEC);
 180    goflag = GOFLAG_STOP;
 181    wait_all_threads();
 182    printf("n_reads: %lld  n_updates: %ld  nreaders: %d  nupdaters: %d duration: %d\n",
 183           n_reads, n_updates, nreaders, nupdaters, duration);
 184    printf("ns/read: %g  ns/update: %g\n",
 185           ((duration * 1000*1000*1000.*(double)nreaders) /
 186        (double)n_reads),
 187           ((duration * 1000*1000*1000.*(double)nupdaters) /
 188        (double)n_updates));
 189    exit(0);
 190}
 191
 192static void perftest(int nreaders, int duration)
 193{
 194    int i;
 195
 196    perftestinit();
 197    for (i = 0; i < nreaders; i++) {
 198        create_thread(rcu_read_perf_test);
 199    }
 200    create_thread(rcu_update_perf_test);
 201    perftestrun(i + 1, duration, nreaders, 1);
 202}
 203
 204static void rperftest(int nreaders, int duration)
 205{
 206    int i;
 207
 208    perftestinit();
 209    for (i = 0; i < nreaders; i++) {
 210        create_thread(rcu_read_perf_test);
 211    }
 212    perftestrun(i, duration, nreaders, 0);
 213}
 214
 215static void uperftest(int nupdaters, int duration)
 216{
 217    int i;
 218
 219    perftestinit();
 220    for (i = 0; i < nupdaters; i++) {
 221        create_thread(rcu_update_perf_test);
 222    }
 223    perftestrun(i, duration, 0, nupdaters);
 224}
 225
 226/*
 227 * Stress test.
 228 */
 229
 230#define RCU_STRESS_PIPE_LEN 10
 231
 232struct rcu_stress {
 233    int age;  /* how many update cycles while not rcu_stress_current */
 234    int mbtest;
 235};
 236
 237struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0 } };
 238struct rcu_stress *rcu_stress_current;
 239int n_mberror;
 240
 241/* Updates protected by counts_mutex */
 242long long rcu_stress_count[RCU_STRESS_PIPE_LEN + 1];
 243
 244
 245static void *rcu_read_stress_test(void *arg)
 246{
 247    int i;
 248    struct rcu_stress *p;
 249    int pc;
 250    long long n_reads_local = 0;
 251    long long rcu_stress_local[RCU_STRESS_PIPE_LEN + 1] = { 0 };
 252    volatile int garbage = 0;
 253
 254    rcu_register_thread();
 255
 256    *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
 257    while (goflag == GOFLAG_INIT) {
 258        g_usleep(1000);
 259    }
 260    while (goflag == GOFLAG_RUN) {
 261        rcu_read_lock();
 262        p = qatomic_rcu_read(&rcu_stress_current);
 263        if (qatomic_read(&p->mbtest) == 0) {
 264            n_mberror++;
 265        }
 266        rcu_read_lock();
 267        for (i = 0; i < 100; i++) {
 268            garbage++;
 269        }
 270        rcu_read_unlock();
 271        pc = qatomic_read(&p->age);
 272        rcu_read_unlock();
 273        if ((pc > RCU_STRESS_PIPE_LEN) || (pc < 0)) {
 274            pc = RCU_STRESS_PIPE_LEN;
 275        }
 276        rcu_stress_local[pc]++;
 277        n_reads_local++;
 278    }
 279    qemu_mutex_lock(&counts_mutex);
 280    n_reads += n_reads_local;
 281    for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) {
 282        rcu_stress_count[i] += rcu_stress_local[i];
 283    }
 284    qemu_mutex_unlock(&counts_mutex);
 285
 286    rcu_unregister_thread();
 287    return NULL;
 288}
 289
 290/*
 291 * Stress Test Updater
 292 *
 293 * The updater cycles around updating rcu_stress_current to point at
 294 * one of the rcu_stress_array_entries and resets it's age. It
 295 * then increments the age of all the other entries. The age
 296 * will be read under an rcu_read_lock() and distribution of values
 297 * calculated. The final result gives an indication of how many
 298 * previously current rcu_stress entries are in flight until the RCU
 299 * cycle complete.
 300 */
 301static void *rcu_update_stress_test(void *arg)
 302{
 303    int i, rcu_stress_idx = 0;
 304    struct rcu_stress *cp = qatomic_read(&rcu_stress_current);
 305
 306    rcu_register_thread();
 307    *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
 308
 309    while (goflag == GOFLAG_INIT) {
 310        g_usleep(1000);
 311    }
 312
 313    while (goflag == GOFLAG_RUN) {
 314        struct rcu_stress *p;
 315        rcu_stress_idx++;
 316        if (rcu_stress_idx >= RCU_STRESS_PIPE_LEN) {
 317            rcu_stress_idx = 0;
 318        }
 319        p = &rcu_stress_array[rcu_stress_idx];
 320        /* catching up with ourselves would be a bug */
 321        assert(p != cp);
 322        qatomic_set(&p->mbtest, 0);
 323        smp_mb();
 324        qatomic_set(&p->age, 0);
 325        qatomic_set(&p->mbtest, 1);
 326        qatomic_rcu_set(&rcu_stress_current, p);
 327        cp = p;
 328        /*
 329         * New RCU structure is now live, update pipe counts on old
 330         * ones.
 331         */
 332        for (i = 0; i < RCU_STRESS_PIPE_LEN; i++) {
 333            if (i != rcu_stress_idx) {
 334                qatomic_set(&rcu_stress_array[i].age,
 335                           rcu_stress_array[i].age + 1);
 336            }
 337        }
 338        synchronize_rcu();
 339        n_updates++;
 340    }
 341
 342    rcu_unregister_thread();
 343    return NULL;
 344}
 345
 346static void *rcu_fake_update_stress_test(void *arg)
 347{
 348    rcu_register_thread();
 349
 350    *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
 351    while (goflag == GOFLAG_INIT) {
 352        g_usleep(1000);
 353    }
 354    while (goflag == GOFLAG_RUN) {
 355        synchronize_rcu();
 356        g_usleep(1000);
 357    }
 358
 359    rcu_unregister_thread();
 360    return NULL;
 361}
 362
 363static void stresstest(int nreaders, int duration)
 364{
 365    int i;
 366
 367    rcu_stress_current = &rcu_stress_array[0];
 368    rcu_stress_current->age = 0;
 369    rcu_stress_current->mbtest = 1;
 370    for (i = 0; i < nreaders; i++) {
 371        create_thread(rcu_read_stress_test);
 372    }
 373    create_thread(rcu_update_stress_test);
 374    for (i = 0; i < 5; i++) {
 375        create_thread(rcu_fake_update_stress_test);
 376    }
 377    goflag = GOFLAG_RUN;
 378    g_usleep(duration * G_USEC_PER_SEC);
 379    goflag = GOFLAG_STOP;
 380    wait_all_threads();
 381    printf("n_reads: %lld  n_updates: %ld  n_mberror: %d\n",
 382           n_reads, n_updates, n_mberror);
 383    printf("rcu_stress_count:");
 384    for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) {
 385        printf(" %lld", rcu_stress_count[i]);
 386    }
 387    printf("\n");
 388    exit(0);
 389}
 390
 391/* GTest interface */
 392
 393static void gtest_stress(int nreaders, int duration)
 394{
 395    int i;
 396
 397    rcu_stress_current = &rcu_stress_array[0];
 398    rcu_stress_current->age = 0;
 399    rcu_stress_current->mbtest = 1;
 400    for (i = 0; i < nreaders; i++) {
 401        create_thread(rcu_read_stress_test);
 402    }
 403    create_thread(rcu_update_stress_test);
 404    for (i = 0; i < 5; i++) {
 405        create_thread(rcu_fake_update_stress_test);
 406    }
 407    goflag = GOFLAG_RUN;
 408    g_usleep(duration * G_USEC_PER_SEC);
 409    goflag = GOFLAG_STOP;
 410    wait_all_threads();
 411    g_assert_cmpint(n_mberror, ==, 0);
 412    for (i = 2; i <= RCU_STRESS_PIPE_LEN; i++) {
 413        g_assert_cmpint(rcu_stress_count[i], ==, 0);
 414    }
 415}
 416
 417static void gtest_stress_1_1(void)
 418{
 419    gtest_stress(1, 1);
 420}
 421
 422static void gtest_stress_10_1(void)
 423{
 424    gtest_stress(10, 1);
 425}
 426
 427static void gtest_stress_1_5(void)
 428{
 429    gtest_stress(1, 5);
 430}
 431
 432static void gtest_stress_10_5(void)
 433{
 434    gtest_stress(10, 5);
 435}
 436
 437/*
 438 * Mainprogram.
 439 */
 440
 441static void usage(int argc, char *argv[])
 442{
 443    fprintf(stderr, "Usage: %s [nreaders [ [r|u]perf | stress [duration]]\n",
 444            argv[0]);
 445    exit(-1);
 446}
 447
 448int main(int argc, char *argv[])
 449{
 450    int nreaders = 1;
 451    int duration = 1;
 452
 453    qemu_mutex_init(&counts_mutex);
 454    if (argc >= 2 && argv[1][0] == '-') {
 455        g_test_init(&argc, &argv, NULL);
 456        if (g_test_quick()) {
 457            g_test_add_func("/rcu/torture/1reader", gtest_stress_1_1);
 458            g_test_add_func("/rcu/torture/10readers", gtest_stress_10_1);
 459        } else {
 460            g_test_add_func("/rcu/torture/1reader", gtest_stress_1_5);
 461            g_test_add_func("/rcu/torture/10readers", gtest_stress_10_5);
 462        }
 463        return g_test_run();
 464    }
 465
 466    if (argc >= 2) {
 467        nreaders = strtoul(argv[1], NULL, 0);
 468    }
 469    if (argc > 3) {
 470        duration = strtoul(argv[3], NULL, 0);
 471    }
 472    if (argc < 3 || strcmp(argv[2], "stress") == 0) {
 473        stresstest(nreaders, duration);
 474    } else if (strcmp(argv[2], "rperf") == 0) {
 475        rperftest(nreaders, duration);
 476    } else if (strcmp(argv[2], "uperf") == 0) {
 477        uperftest(nreaders, duration);
 478    } else if (strcmp(argv[2], "perf") == 0) {
 479        perftest(nreaders, duration);
 480    }
 481    usage(argc, argv);
 482    return 0;
 483}
 484