cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

rcutorture.c (12895B)


      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 = &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 = &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 = &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 = &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 = &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}