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