libqos/ahci: add ahci command size setters
[qemu.git] / tests / rcutorture.c
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 <glib.h>
64 #include <stdlib.h>
65 #include <stdio.h>
66 #include <string.h>
67 #include "qemu/atomic.h"
68 #include "qemu/rcu.h"
69 #include "qemu/compiler.h"
70 #include "qemu/thread.h"
71
72 long long n_reads = 0LL;
73 long n_updates = 0L;
74 int nthreadsrunning;
75
76 #define GOFLAG_INIT 0
77 #define GOFLAG_RUN  1
78 #define GOFLAG_STOP 2
79
80 static volatile int goflag = GOFLAG_INIT;
81
82 #define RCU_READ_RUN 1000
83
84 #define NR_THREADS 100
85 static QemuThread threads[NR_THREADS];
86 static struct rcu_reader_data *data[NR_THREADS];
87 static int n_threads;
88
89 static void create_thread(void *(*func)(void *))
90 {
91     if (n_threads >= NR_THREADS) {
92         fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS);
93         exit(-1);
94     }
95     qemu_thread_create(&threads[n_threads], "test", func, &data[n_threads],
96                        QEMU_THREAD_JOINABLE);
97     n_threads++;
98 }
99
100 static void wait_all_threads(void)
101 {
102     int i;
103
104     for (i = 0; i < n_threads; i++) {
105         qemu_thread_join(&threads[i]);
106     }
107     n_threads = 0;
108 }
109
110 /*
111  * Performance test.
112  */
113
114 static void *rcu_read_perf_test(void *arg)
115 {
116     int i;
117     long long n_reads_local = 0;
118
119     rcu_register_thread();
120
121     *(struct rcu_reader_data **)arg = &rcu_reader;
122     atomic_inc(&nthreadsrunning);
123     while (goflag == GOFLAG_INIT) {
124         g_usleep(1000);
125     }
126     while (goflag == GOFLAG_RUN) {
127         for (i = 0; i < RCU_READ_RUN; i++) {
128             rcu_read_lock();
129             rcu_read_unlock();
130         }
131         n_reads_local += RCU_READ_RUN;
132     }
133     atomic_add(&n_reads, n_reads_local);
134
135     rcu_unregister_thread();
136     return NULL;
137 }
138
139 static void *rcu_update_perf_test(void *arg)
140 {
141     long long n_updates_local = 0;
142
143     rcu_register_thread();
144
145     *(struct rcu_reader_data **)arg = &rcu_reader;
146     atomic_inc(&nthreadsrunning);
147     while (goflag == GOFLAG_INIT) {
148         g_usleep(1000);
149     }
150     while (goflag == GOFLAG_RUN) {
151         synchronize_rcu();
152         n_updates_local++;
153     }
154     atomic_add(&n_updates, n_updates_local);
155
156     rcu_unregister_thread();
157     return NULL;
158 }
159
160 static void perftestinit(void)
161 {
162     nthreadsrunning = 0;
163 }
164
165 static void perftestrun(int nthreads, int duration, int nreaders, int nupdaters)
166 {
167     while (atomic_read(&nthreadsrunning) < nthreads) {
168         g_usleep(1000);
169     }
170     goflag = GOFLAG_RUN;
171     g_usleep(duration * G_USEC_PER_SEC);
172     goflag = GOFLAG_STOP;
173     wait_all_threads();
174     printf("n_reads: %lld  n_updates: %ld  nreaders: %d  nupdaters: %d duration: %d\n",
175            n_reads, n_updates, nreaders, nupdaters, duration);
176     printf("ns/read: %g  ns/update: %g\n",
177            ((duration * 1000*1000*1000.*(double)nreaders) /
178         (double)n_reads),
179            ((duration * 1000*1000*1000.*(double)nupdaters) /
180         (double)n_updates));
181     exit(0);
182 }
183
184 static void perftest(int nreaders, int duration)
185 {
186     int i;
187
188     perftestinit();
189     for (i = 0; i < nreaders; i++) {
190         create_thread(rcu_read_perf_test);
191     }
192     create_thread(rcu_update_perf_test);
193     perftestrun(i + 1, duration, nreaders, 1);
194 }
195
196 static void rperftest(int nreaders, int duration)
197 {
198     int i;
199
200     perftestinit();
201     for (i = 0; i < nreaders; i++) {
202         create_thread(rcu_read_perf_test);
203     }
204     perftestrun(i, duration, nreaders, 0);
205 }
206
207 static void uperftest(int nupdaters, int duration)
208 {
209     int i;
210
211     perftestinit();
212     for (i = 0; i < nupdaters; i++) {
213         create_thread(rcu_update_perf_test);
214     }
215     perftestrun(i, duration, 0, nupdaters);
216 }
217
218 /*
219  * Stress test.
220  */
221
222 #define RCU_STRESS_PIPE_LEN 10
223
224 struct rcu_stress {
225     int pipe_count;
226     int mbtest;
227 };
228
229 struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0 } };
230 struct rcu_stress *rcu_stress_current;
231 int rcu_stress_idx;
232
233 int n_mberror;
234 long long rcu_stress_count[RCU_STRESS_PIPE_LEN + 1];
235
236
237 static void *rcu_read_stress_test(void *arg)
238 {
239     int i;
240     int itercnt = 0;
241     struct rcu_stress *p;
242     int pc;
243     long long n_reads_local = 0;
244     volatile int garbage = 0;
245
246     rcu_register_thread();
247
248     *(struct rcu_reader_data **)arg = &rcu_reader;
249     while (goflag == GOFLAG_INIT) {
250         g_usleep(1000);
251     }
252     while (goflag == GOFLAG_RUN) {
253         rcu_read_lock();
254         p = atomic_rcu_read(&rcu_stress_current);
255         if (p->mbtest == 0) {
256             n_mberror++;
257         }
258         rcu_read_lock();
259         for (i = 0; i < 100; i++) {
260             garbage++;
261         }
262         rcu_read_unlock();
263         pc = p->pipe_count;
264         rcu_read_unlock();
265         if ((pc > RCU_STRESS_PIPE_LEN) || (pc < 0)) {
266             pc = RCU_STRESS_PIPE_LEN;
267         }
268         atomic_inc(&rcu_stress_count[pc]);
269         n_reads_local++;
270         if ((++itercnt % 0x1000) == 0) {
271             synchronize_rcu();
272         }
273     }
274     atomic_add(&n_reads, n_reads_local);
275
276     rcu_unregister_thread();
277     return NULL;
278 }
279
280 static void *rcu_update_stress_test(void *arg)
281 {
282     int i;
283     struct rcu_stress *p;
284
285     rcu_register_thread();
286
287     *(struct rcu_reader_data **)arg = &rcu_reader;
288     while (goflag == GOFLAG_INIT) {
289         g_usleep(1000);
290     }
291     while (goflag == GOFLAG_RUN) {
292         i = rcu_stress_idx + 1;
293         if (i >= RCU_STRESS_PIPE_LEN) {
294             i = 0;
295         }
296         p = &rcu_stress_array[i];
297         p->mbtest = 0;
298         smp_mb();
299         p->pipe_count = 0;
300         p->mbtest = 1;
301         atomic_rcu_set(&rcu_stress_current, p);
302         rcu_stress_idx = i;
303         for (i = 0; i < RCU_STRESS_PIPE_LEN; i++) {
304             if (i != rcu_stress_idx) {
305                 rcu_stress_array[i].pipe_count++;
306             }
307         }
308         synchronize_rcu();
309         n_updates++;
310     }
311
312     rcu_unregister_thread();
313     return NULL;
314 }
315
316 static void *rcu_fake_update_stress_test(void *arg)
317 {
318     rcu_register_thread();
319
320     *(struct rcu_reader_data **)arg = &rcu_reader;
321     while (goflag == GOFLAG_INIT) {
322         g_usleep(1000);
323     }
324     while (goflag == GOFLAG_RUN) {
325         synchronize_rcu();
326         g_usleep(1000);
327     }
328
329     rcu_unregister_thread();
330     return NULL;
331 }
332
333 static void stresstest(int nreaders, int duration)
334 {
335     int i;
336
337     rcu_stress_current = &rcu_stress_array[0];
338     rcu_stress_current->pipe_count = 0;
339     rcu_stress_current->mbtest = 1;
340     for (i = 0; i < nreaders; i++) {
341         create_thread(rcu_read_stress_test);
342     }
343     create_thread(rcu_update_stress_test);
344     for (i = 0; i < 5; i++) {
345         create_thread(rcu_fake_update_stress_test);
346     }
347     goflag = GOFLAG_RUN;
348     g_usleep(duration * G_USEC_PER_SEC);
349     goflag = GOFLAG_STOP;
350     wait_all_threads();
351     printf("n_reads: %lld  n_updates: %ld  n_mberror: %d\n",
352            n_reads, n_updates, n_mberror);
353     printf("rcu_stress_count:");
354     for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) {
355         printf(" %lld", rcu_stress_count[i]);
356     }
357     printf("\n");
358     exit(0);
359 }
360
361 /* GTest interface */
362
363 static void gtest_stress(int nreaders, int duration)
364 {
365     int i;
366
367     rcu_stress_current = &rcu_stress_array[0];
368     rcu_stress_current->pipe_count = 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     g_assert_cmpint(n_mberror, ==, 0);
382     for (i = 2; i <= RCU_STRESS_PIPE_LEN; i++) {
383         g_assert_cmpint(rcu_stress_count[i], ==, 0);
384     }
385 }
386
387 static void gtest_stress_1_1(void)
388 {
389     gtest_stress(1, 1);
390 }
391
392 static void gtest_stress_10_1(void)
393 {
394     gtest_stress(10, 1);
395 }
396
397 static void gtest_stress_1_5(void)
398 {
399     gtest_stress(1, 5);
400 }
401
402 static void gtest_stress_10_5(void)
403 {
404     gtest_stress(10, 5);
405 }
406
407 /*
408  * Mainprogram.
409  */
410
411 static void usage(int argc, char *argv[])
412 {
413     fprintf(stderr, "Usage: %s [nreaders [ perf | stress ] ]\n", argv[0]);
414     exit(-1);
415 }
416
417 int main(int argc, char *argv[])
418 {
419     int nreaders = 1;
420     int duration = 1;
421
422     if (argc >= 2 && argv[1][0] == '-') {
423         g_test_init(&argc, &argv, NULL);
424         if (g_test_quick()) {
425             g_test_add_func("/rcu/torture/1reader", gtest_stress_1_1);
426             g_test_add_func("/rcu/torture/10readers", gtest_stress_10_1);
427         } else {
428             g_test_add_func("/rcu/torture/1reader", gtest_stress_1_5);
429             g_test_add_func("/rcu/torture/10readers", gtest_stress_10_5);
430         }
431         return g_test_run();
432     }
433
434     if (argc >= 2) {
435         nreaders = strtoul(argv[1], NULL, 0);
436     }
437     if (argc > 3) {
438         duration = strtoul(argv[3], NULL, 0);
439     }
440     if (argc < 3 || strcmp(argv[2], "stress") == 0) {
441         stresstest(nreaders, duration);
442     } else if (strcmp(argv[2], "rperf") == 0) {
443         rperftest(nreaders, duration);
444     } else if (strcmp(argv[2], "uperf") == 0) {
445         uperftest(nreaders, duration);
446     } else if (strcmp(argv[2], "perf") == 0) {
447         perftest(nreaders, duration);
448     }
449     usage(argc, argv);
450     return 0;
451 }
This page took 0.054243 seconds and 4 git commands to generate.