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}