qemu

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

test-rcu-list.c (10654B)


      1 /*
      2  * rcuq_test.c
      3  *
      4  * usage: rcuq_test <readers> <duration>
      5  *
      6  * This program is free software; you can redistribute it and/or modify
      7  * it under the terms of the GNU General Public License as published by
      8  * the Free Software Foundation; either version 2 of the License, or
      9  * (at your option) any later version.
     10  *
     11  * This program is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  * GNU General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU General Public License
     17  * along with this program; if not, write to the Free Software
     18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     19  *
     20  * Copyright (c) 2013 Mike D. Day, IBM Corporation.
     21  */
     22 
     23 #include "qemu/osdep.h"
     24 #include "qemu/atomic.h"
     25 #include "qemu/rcu.h"
     26 #include "qemu/thread.h"
     27 #include "qemu/rcu_queue.h"
     28 
     29 /*
     30  * Test variables.
     31  */
     32 
     33 static QemuMutex counts_mutex;
     34 static long long n_reads = 0LL;
     35 static long long n_updates = 0LL;
     36 static int64_t n_reclaims;
     37 static int64_t n_nodes_removed;
     38 static long long n_nodes = 0LL;
     39 static int g_test_in_charge = 0;
     40 
     41 static int nthreadsrunning;
     42 
     43 #define GOFLAG_INIT 0
     44 #define GOFLAG_RUN  1
     45 #define GOFLAG_STOP 2
     46 
     47 static int goflag = GOFLAG_INIT;
     48 
     49 #define RCU_READ_RUN 1000
     50 #define RCU_UPDATE_RUN 10
     51 #define NR_THREADS 100
     52 #define RCU_Q_LEN 100
     53 
     54 static QemuThread threads[NR_THREADS];
     55 static struct rcu_reader_data *data[NR_THREADS];
     56 static int n_threads;
     57 
     58 static int select_random_el(int max)
     59 {
     60     return (rand() % max);
     61 }
     62 
     63 
     64 static void create_thread(void *(*func)(void *))
     65 {
     66     if (n_threads >= NR_THREADS) {
     67         fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS);
     68         exit(-1);
     69     }
     70     qemu_thread_create(&threads[n_threads], "test", func, &data[n_threads],
     71                        QEMU_THREAD_JOINABLE);
     72     n_threads++;
     73 }
     74 
     75 static void wait_all_threads(void)
     76 {
     77     int i;
     78 
     79     for (i = 0; i < n_threads; i++) {
     80         qemu_thread_join(&threads[i]);
     81     }
     82     n_threads = 0;
     83 }
     84 
     85 #ifndef TEST_LIST_TYPE
     86 #define TEST_LIST_TYPE 1
     87 #endif
     88 
     89 struct list_element {
     90 #if TEST_LIST_TYPE == 1
     91     QLIST_ENTRY(list_element) entry;
     92 #elif TEST_LIST_TYPE == 2
     93     QSIMPLEQ_ENTRY(list_element) entry;
     94 #elif TEST_LIST_TYPE == 3
     95     QTAILQ_ENTRY(list_element) entry;
     96 #elif TEST_LIST_TYPE == 4
     97     QSLIST_ENTRY(list_element) entry;
     98 #else
     99 #error Invalid TEST_LIST_TYPE
    100 #endif
    101     struct rcu_head rcu;
    102 };
    103 
    104 static void reclaim_list_el(struct rcu_head *prcu)
    105 {
    106     struct list_element *el = container_of(prcu, struct list_element, rcu);
    107     g_free(el);
    108     /* Accessed only from call_rcu thread.  */
    109     qatomic_set_i64(&n_reclaims, n_reclaims + 1);
    110 }
    111 
    112 #if TEST_LIST_TYPE == 1
    113 static QLIST_HEAD(, list_element) Q_list_head;
    114 
    115 #define TEST_NAME "qlist"
    116 #define TEST_LIST_REMOVE_RCU        QLIST_REMOVE_RCU
    117 #define TEST_LIST_INSERT_AFTER_RCU  QLIST_INSERT_AFTER_RCU
    118 #define TEST_LIST_INSERT_HEAD_RCU   QLIST_INSERT_HEAD_RCU
    119 #define TEST_LIST_FOREACH_RCU       QLIST_FOREACH_RCU
    120 #define TEST_LIST_FOREACH_SAFE_RCU  QLIST_FOREACH_SAFE_RCU
    121 
    122 #elif TEST_LIST_TYPE == 2
    123 static QSIMPLEQ_HEAD(, list_element) Q_list_head =
    124     QSIMPLEQ_HEAD_INITIALIZER(Q_list_head);
    125 
    126 #define TEST_NAME "qsimpleq"
    127 #define TEST_LIST_REMOVE_RCU(el, f)                             \
    128          QSIMPLEQ_REMOVE_RCU(&Q_list_head, el, list_element, f)
    129 
    130 #define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f)               \
    131          QSIMPLEQ_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f)
    132 
    133 #define TEST_LIST_INSERT_HEAD_RCU   QSIMPLEQ_INSERT_HEAD_RCU
    134 #define TEST_LIST_FOREACH_RCU       QSIMPLEQ_FOREACH_RCU
    135 #define TEST_LIST_FOREACH_SAFE_RCU  QSIMPLEQ_FOREACH_SAFE_RCU
    136 
    137 #elif TEST_LIST_TYPE == 3
    138 static QTAILQ_HEAD(, list_element) Q_list_head;
    139 
    140 #define TEST_NAME "qtailq"
    141 #define TEST_LIST_REMOVE_RCU(el, f) QTAILQ_REMOVE_RCU(&Q_list_head, el, f)
    142 
    143 #define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f)               \
    144            QTAILQ_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f)
    145 
    146 #define TEST_LIST_INSERT_HEAD_RCU   QTAILQ_INSERT_HEAD_RCU
    147 #define TEST_LIST_FOREACH_RCU       QTAILQ_FOREACH_RCU
    148 #define TEST_LIST_FOREACH_SAFE_RCU  QTAILQ_FOREACH_SAFE_RCU
    149 
    150 #elif TEST_LIST_TYPE == 4
    151 static QSLIST_HEAD(, list_element) Q_list_head;
    152 
    153 #define TEST_NAME "qslist"
    154 #define TEST_LIST_REMOVE_RCU(el, f)                              \
    155 	 QSLIST_REMOVE_RCU(&Q_list_head, el, list_element, f)
    156 
    157 #define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f)               \
    158          QSLIST_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f)
    159 
    160 #define TEST_LIST_INSERT_HEAD_RCU   QSLIST_INSERT_HEAD_RCU
    161 #define TEST_LIST_FOREACH_RCU       QSLIST_FOREACH_RCU
    162 #define TEST_LIST_FOREACH_SAFE_RCU  QSLIST_FOREACH_SAFE_RCU
    163 #else
    164 #error Invalid TEST_LIST_TYPE
    165 #endif
    166 
    167 static void *rcu_q_reader(void *arg)
    168 {
    169     long long n_reads_local = 0;
    170     struct list_element *el;
    171 
    172     rcu_register_thread();
    173 
    174     *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
    175     qatomic_inc(&nthreadsrunning);
    176     while (qatomic_read(&goflag) == GOFLAG_INIT) {
    177         g_usleep(1000);
    178     }
    179 
    180     while (qatomic_read(&goflag) == GOFLAG_RUN) {
    181         rcu_read_lock();
    182         TEST_LIST_FOREACH_RCU(el, &Q_list_head, entry) {
    183             n_reads_local++;
    184             if (qatomic_read(&goflag) == GOFLAG_STOP) {
    185                 break;
    186             }
    187         }
    188         rcu_read_unlock();
    189 
    190         g_usleep(100);
    191     }
    192     qemu_mutex_lock(&counts_mutex);
    193     n_reads += n_reads_local;
    194     qemu_mutex_unlock(&counts_mutex);
    195 
    196     rcu_unregister_thread();
    197     return NULL;
    198 }
    199 
    200 
    201 static void *rcu_q_updater(void *arg)
    202 {
    203     int j, target_el;
    204     long long n_nodes_local = 0;
    205     long long n_updates_local = 0;
    206     long long n_removed_local = 0;
    207     struct list_element *el, *prev_el;
    208 
    209     *(struct rcu_reader_data **)arg = get_ptr_rcu_reader();
    210     qatomic_inc(&nthreadsrunning);
    211     while (qatomic_read(&goflag) == GOFLAG_INIT) {
    212         g_usleep(1000);
    213     }
    214 
    215     while (qatomic_read(&goflag) == GOFLAG_RUN) {
    216         target_el = select_random_el(RCU_Q_LEN);
    217         j = 0;
    218         /* FOREACH_RCU could work here but let's use both macros */
    219         TEST_LIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) {
    220             j++;
    221             if (target_el == j) {
    222                 TEST_LIST_REMOVE_RCU(prev_el, entry);
    223                 /* may be more than one updater in the future */
    224                 call_rcu1(&prev_el->rcu, reclaim_list_el);
    225                 n_removed_local++;
    226                 break;
    227             }
    228         }
    229         if (qatomic_read(&goflag) == GOFLAG_STOP) {
    230             break;
    231         }
    232         target_el = select_random_el(RCU_Q_LEN);
    233         j = 0;
    234         TEST_LIST_FOREACH_RCU(el, &Q_list_head, entry) {
    235             j++;
    236             if (target_el == j) {
    237                 struct list_element *new_el = g_new(struct list_element, 1);
    238                 n_nodes_local++;
    239                 TEST_LIST_INSERT_AFTER_RCU(el, new_el, entry);
    240                 break;
    241             }
    242         }
    243 
    244         n_updates_local += 2;
    245         synchronize_rcu();
    246     }
    247     synchronize_rcu();
    248     qemu_mutex_lock(&counts_mutex);
    249     n_nodes += n_nodes_local;
    250     n_updates += n_updates_local;
    251     qatomic_set_i64(&n_nodes_removed, n_nodes_removed + n_removed_local);
    252     qemu_mutex_unlock(&counts_mutex);
    253     return NULL;
    254 }
    255 
    256 static void rcu_qtest_init(void)
    257 {
    258     struct list_element *new_el;
    259     int i;
    260     nthreadsrunning = 0;
    261     srand(time(0));
    262     for (i = 0; i < RCU_Q_LEN; i++) {
    263         new_el = g_new(struct list_element, 1);
    264         TEST_LIST_INSERT_HEAD_RCU(&Q_list_head, new_el, entry);
    265     }
    266     qemu_mutex_lock(&counts_mutex);
    267     n_nodes += RCU_Q_LEN;
    268     qemu_mutex_unlock(&counts_mutex);
    269 }
    270 
    271 static void rcu_qtest_run(int duration, int nreaders)
    272 {
    273     int nthreads = nreaders + 1;
    274     while (qatomic_read(&nthreadsrunning) < nthreads) {
    275         g_usleep(1000);
    276     }
    277 
    278     qatomic_set(&goflag, GOFLAG_RUN);
    279     sleep(duration);
    280     qatomic_set(&goflag, GOFLAG_STOP);
    281     wait_all_threads();
    282 }
    283 
    284 
    285 static void rcu_qtest(const char *test, int duration, int nreaders)
    286 {
    287     int i;
    288     long long n_removed_local = 0;
    289 
    290     struct list_element *el, *prev_el;
    291 
    292     rcu_qtest_init();
    293     for (i = 0; i < nreaders; i++) {
    294         create_thread(rcu_q_reader);
    295     }
    296     create_thread(rcu_q_updater);
    297     rcu_qtest_run(duration, nreaders);
    298 
    299     TEST_LIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) {
    300         TEST_LIST_REMOVE_RCU(prev_el, entry);
    301         call_rcu1(&prev_el->rcu, reclaim_list_el);
    302         n_removed_local++;
    303     }
    304     qemu_mutex_lock(&counts_mutex);
    305     qatomic_set_i64(&n_nodes_removed, n_nodes_removed + n_removed_local);
    306     qemu_mutex_unlock(&counts_mutex);
    307     synchronize_rcu();
    308     while (qatomic_read_i64(&n_nodes_removed) >
    309            qatomic_read_i64(&n_reclaims)) {
    310         g_usleep(100);
    311         synchronize_rcu();
    312     }
    313     if (g_test_in_charge) {
    314         g_assert_cmpint(qatomic_read_i64(&n_nodes_removed), ==,
    315                         qatomic_read_i64(&n_reclaims));
    316     } else {
    317         printf("%s: %d readers; 1 updater; nodes read: "  \
    318                "%lld, nodes removed: %"PRIi64"; nodes reclaimed: %"PRIi64"\n",
    319                test, nthreadsrunning - 1, n_reads,
    320                qatomic_read_i64(&n_nodes_removed),
    321                qatomic_read_i64(&n_reclaims));
    322         exit(0);
    323     }
    324 }
    325 
    326 static void usage(int argc, char *argv[])
    327 {
    328     fprintf(stderr, "Usage: %s duration nreaders\n", argv[0]);
    329     exit(-1);
    330 }
    331 
    332 static int gtest_seconds;
    333 
    334 static void gtest_rcuq_one(void)
    335 {
    336     rcu_qtest("rcuqtest", gtest_seconds / 4, 1);
    337 }
    338 
    339 static void gtest_rcuq_few(void)
    340 {
    341     rcu_qtest("rcuqtest", gtest_seconds / 4, 5);
    342 }
    343 
    344 static void gtest_rcuq_many(void)
    345 {
    346     rcu_qtest("rcuqtest", gtest_seconds / 2, 20);
    347 }
    348 
    349 
    350 int main(int argc, char *argv[])
    351 {
    352     int duration = 0, readers = 0;
    353 
    354     qemu_mutex_init(&counts_mutex);
    355     if (argc >= 2) {
    356         if (argv[1][0] == '-') {
    357             g_test_init(&argc, &argv, NULL);
    358             if (g_test_quick()) {
    359                 gtest_seconds = 4;
    360             } else {
    361                 gtest_seconds = 20;
    362             }
    363             g_test_add_func("/rcu/"TEST_NAME"/single-threaded", gtest_rcuq_one);
    364             g_test_add_func("/rcu/"TEST_NAME"/short-few", gtest_rcuq_few);
    365             g_test_add_func("/rcu/"TEST_NAME"/long-many", gtest_rcuq_many);
    366             g_test_in_charge = 1;
    367             return g_test_run();
    368         }
    369         duration = strtoul(argv[1], NULL, 0);
    370     }
    371     if (argc >= 3) {
    372         readers = strtoul(argv[2], NULL, 0);
    373     }
    374     if (duration && readers) {
    375         rcu_qtest(argv[0], duration, readers);
    376         return 0;
    377     }
    378 
    379     usage(argc, argv);
    380     return -1;
    381 }