xref: /openbmc/qemu/tests/unit/rcutorture.c (revision c36dd41b)
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 
67 int nthreadsrunning;
68 
69 #define GOFLAG_INIT 0
70 #define GOFLAG_RUN  1
71 #define GOFLAG_STOP 2
72 
73 static volatile int goflag = GOFLAG_INIT;
74 
75 #define RCU_READ_RUN 1000
76 
77 #define NR_THREADS 100
78 static QemuThread threads[NR_THREADS];
79 static struct rcu_reader_data *data[NR_THREADS];
80 static 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  */
88 static QemuMutex counts_mutex;
89 long long n_reads = 0LL;
90 long n_updates = 0L;
91 
92 static 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 
103 static 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 
117 static 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 
144 static 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 
167 static void perftestinit(void)
168 {
169     nthreadsrunning = 0;
170 }
171 
172 static 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 
191 static 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 
203 static 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 
214 static 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 
231 struct rcu_stress {
232     int age;  /* how many update cycles while not rcu_stress_current */
233     int mbtest;
234 };
235 
236 struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0 } };
237 struct rcu_stress *rcu_stress_current;
238 int n_mberror;
239 
240 /* Updates protected by counts_mutex */
241 long long rcu_stress_count[RCU_STRESS_PIPE_LEN + 1];
242 
243 
244 static 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  */
300 static 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 
345 static 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 
362 static 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 
392 static 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 
416 static void gtest_stress_1_1(void)
417 {
418     gtest_stress(1, 1);
419 }
420 
421 static void gtest_stress_10_1(void)
422 {
423     gtest_stress(10, 1);
424 }
425 
426 static void gtest_stress_1_5(void)
427 {
428     gtest_stress(1, 5);
429 }
430 
431 static void gtest_stress_10_5(void)
432 {
433     gtest_stress(10, 5);
434 }
435 
436 /*
437  * Mainprogram.
438  */
439 
440 static 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 
447 int 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