qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

rcutorture.c (12940B)


      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 
     68 int nthreadsrunning;
     69 
     70 #define GOFLAG_INIT 0
     71 #define GOFLAG_RUN  1
     72 #define GOFLAG_STOP 2
     73 
     74 static volatile int goflag = GOFLAG_INIT;
     75 
     76 #define RCU_READ_RUN 1000
     77 
     78 #define NR_THREADS 100
     79 static QemuThread threads[NR_THREADS];
     80 static struct rcu_reader_data *data[NR_THREADS];
     81 static 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  */
     89 static QemuMutex counts_mutex;
     90 long long n_reads = 0LL;
     91 long n_updates = 0L;
     92 
     93 static 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 
    104 static 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 
    118 static 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 
    145 static 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 
    168 static void perftestinit(void)
    169 {
    170     nthreadsrunning = 0;
    171 }
    172 
    173 static 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 
    192 static 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 
    204 static 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 
    215 static 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 
    232 struct rcu_stress {
    233     int age;  /* how many update cycles while not rcu_stress_current */
    234     int mbtest;
    235 };
    236 
    237 struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0 } };
    238 struct rcu_stress *rcu_stress_current;
    239 int n_mberror;
    240 
    241 /* Updates protected by counts_mutex */
    242 long long rcu_stress_count[RCU_STRESS_PIPE_LEN + 1];
    243 
    244 
    245 static 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  */
    301 static 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 
    346 static 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 
    363 static 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 
    393 static 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 
    417 static void gtest_stress_1_1(void)
    418 {
    419     gtest_stress(1, 1);
    420 }
    421 
    422 static void gtest_stress_10_1(void)
    423 {
    424     gtest_stress(10, 1);
    425 }
    426 
    427 static void gtest_stress_1_5(void)
    428 {
    429     gtest_stress(1, 5);
    430 }
    431 
    432 static void gtest_stress_10_5(void)
    433 {
    434     gtest_stress(10, 5);
    435 }
    436 
    437 /*
    438  * Mainprogram.
    439  */
    440 
    441 static 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 
    448 int 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 }