1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2010-2014 Intel Corporation
12 #include <rte_common.h>
13 #include <rte_memory.h>
14 #include <rte_per_lcore.h>
15 #include <rte_launch.h>
16 #include <rte_atomic.h>
17 #include <rte_rwlock.h>
19 #include <rte_lcore.h>
20 #include <rte_cycles.h>
27 * Provides UT for rte_rwlock API.
28 * Main concern is on functional testing, but also provides some
29 * performance measurements.
30 * Obviously for proper testing need to be executed with more than one lcore.
37 static rte_rwlock_t sl;
38 static rte_rwlock_t sl_tab[RTE_MAX_LCORE];
39 static rte_atomic32_t synchro;
50 uint8_t u8[RTE_CACHE_LINE_SIZE];
51 uint64_t u64[RTE_CACHE_LINE_SIZE / sizeof(uint64_t)];
53 } __rte_cache_aligned try_rwlock_data;
55 struct try_rwlock_lcore {
63 } __rte_cache_aligned;
65 static struct try_rwlock_lcore try_lcore_data[RTE_MAX_LCORE];
68 test_rwlock_per_core(__attribute__((unused)) void *arg)
70 rte_rwlock_write_lock(&sl);
71 printf("Global write lock taken on core %u\n", rte_lcore_id());
72 rte_rwlock_write_unlock(&sl);
74 rte_rwlock_write_lock(&sl_tab[rte_lcore_id()]);
75 printf("Hello from core %u !\n", rte_lcore_id());
76 rte_rwlock_write_unlock(&sl_tab[rte_lcore_id()]);
78 rte_rwlock_read_lock(&sl);
79 printf("Global read lock taken on core %u\n", rte_lcore_id());
81 printf("Release global read lock on core %u\n", rte_lcore_id());
82 rte_rwlock_read_unlock(&sl);
87 static rte_rwlock_t lk = RTE_RWLOCK_INITIALIZER;
88 static volatile uint64_t rwlock_data;
89 static uint64_t lock_count[RTE_MAX_LCORE] = {0};
92 #define TEST_RWLOCK_DEBUG 0
95 load_loop_fn(__attribute__((unused)) void *arg)
97 uint64_t time_diff = 0, begin;
98 uint64_t hz = rte_get_timer_hz();
100 const unsigned int lcore = rte_lcore_id();
102 /* wait synchro for slaves */
103 if (lcore != rte_get_master_lcore())
104 while (rte_atomic32_read(&synchro) == 0)
107 begin = rte_rdtsc_precise();
108 while (time_diff < hz * TIME_MS / 1000) {
109 rte_rwlock_write_lock(&lk);
111 rte_rwlock_write_unlock(&lk);
113 rte_rwlock_read_lock(&lk);
114 if (TEST_RWLOCK_DEBUG && !(lcount % 100))
115 printf("Core [%u] rwlock_data = %"PRIu64"\n",
117 rte_rwlock_read_unlock(&lk);
120 /* delay to make lock duty cycle slightly realistic */
122 time_diff = rte_rdtsc_precise() - begin;
125 lock_count[lcore] = lcount;
130 test_rwlock_perf(void)
135 printf("\nRwlock Perf Test on %u cores...\n", rte_lcore_count());
137 /* clear synchro and start slaves */
138 rte_atomic32_set(&synchro, 0);
139 if (rte_eal_mp_remote_launch(load_loop_fn, NULL, SKIP_MASTER) < 0)
142 /* start synchro and launch test on master */
143 rte_atomic32_set(&synchro, 1);
146 rte_eal_mp_wait_lcore();
148 RTE_LCORE_FOREACH(i) {
149 printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
150 total += lock_count[i];
153 printf("Total count = %"PRIu64"\n", total);
159 * - There is a global rwlock and a table of rwlocks (one per lcore).
161 * - The test function takes all of these locks and launches the
162 * ``test_rwlock_per_core()`` function on each core (except the master).
164 * - The function takes the global write lock, display something,
165 * then releases the global lock.
166 * - Then, it takes the per-lcore write lock, display something, and
167 * releases the per-core lock.
168 * - Finally, a read lock is taken during 100 ms, then released.
170 * - The main function unlocks the per-lcore locks sequentially and
171 * waits between each lock. This triggers the display of a message
172 * for each core, in the correct order.
174 * Then, it tries to take the global write lock and display the last
175 * message. The autotest script checks that the message order is correct.
182 rte_rwlock_init(&sl);
183 for (i=0; i<RTE_MAX_LCORE; i++)
184 rte_rwlock_init(&sl_tab[i]);
186 rte_rwlock_write_lock(&sl);
188 RTE_LCORE_FOREACH_SLAVE(i) {
189 rte_rwlock_write_lock(&sl_tab[i]);
190 rte_eal_remote_launch(test_rwlock_per_core, NULL, i);
193 rte_rwlock_write_unlock(&sl);
195 RTE_LCORE_FOREACH_SLAVE(i) {
196 rte_rwlock_write_unlock(&sl_tab[i]);
200 rte_rwlock_write_lock(&sl);
201 /* this message should be the last message of test */
202 printf("Global write lock taken on master core %u\n", rte_lcore_id());
203 rte_rwlock_write_unlock(&sl);
205 rte_eal_mp_wait_lcore();
207 if (test_rwlock_perf() < 0)
214 try_read(uint32_t lc)
219 rc = rte_rwlock_read_trylock(&try_rwlock_data.lock);
223 for (i = 0; i != RTE_DIM(try_rwlock_data.data.u64); i++) {
225 /* race condition occurred, lock doesn't work properly */
226 if (try_rwlock_data.data.u64[i] != 0) {
227 printf("%s(%u) error: unexpected data pattern\n",
229 rte_memdump(stdout, NULL,
230 (void *)(uintptr_t)&try_rwlock_data.data,
231 sizeof(try_rwlock_data.data));
237 rte_rwlock_read_unlock(&try_rwlock_data.lock);
242 try_write(uint32_t lc)
247 v = RTE_MAX(lc % UINT8_MAX, 1U);
249 rc = rte_rwlock_write_trylock(&try_rwlock_data.lock);
253 /* update by bytes in reverese order */
254 for (i = RTE_DIM(try_rwlock_data.data.u8); i-- != 0; ) {
256 /* race condition occurred, lock doesn't work properly */
257 if (try_rwlock_data.data.u8[i] != 0) {
258 printf("%s:%d(%u) error: unexpected data pattern\n",
259 __func__, __LINE__, lc);
260 rte_memdump(stdout, NULL,
261 (void *)(uintptr_t)&try_rwlock_data.data,
262 sizeof(try_rwlock_data.data));
267 try_rwlock_data.data.u8[i] = v;
270 /* restore by bytes in reverese order */
271 for (i = RTE_DIM(try_rwlock_data.data.u8); i-- != 0; ) {
273 /* race condition occurred, lock doesn't work properly */
274 if (try_rwlock_data.data.u8[i] != v) {
275 printf("%s:%d(%u) error: unexpected data pattern\n",
276 __func__, __LINE__, lc);
277 rte_memdump(stdout, NULL,
278 (void *)(uintptr_t)&try_rwlock_data.data,
279 sizeof(try_rwlock_data.data));
284 try_rwlock_data.data.u8[i] = 0;
287 rte_rwlock_write_unlock(&try_rwlock_data.lock);
292 try_read_lcore(__rte_unused void *data)
296 uint64_t ftm, stm, tm;
297 struct try_rwlock_lcore *lcd;
300 lcd = try_lcore_data + lc;
301 lcd->type = LC_TYPE_RDLOCK;
303 ftm = try_rwlock_data.tick;
304 stm = rte_get_timer_cycles();
307 for (i = 0; i != ITER_NUM; i++) {
311 else if (rc == -EBUSY)
317 tm = rte_get_timer_cycles() - stm;
318 } while (tm < ftm && rc == 0);
326 try_write_lcore(__rte_unused void *data)
330 uint64_t ftm, stm, tm;
331 struct try_rwlock_lcore *lcd;
334 lcd = try_lcore_data + lc;
335 lcd->type = LC_TYPE_WRLOCK;
337 ftm = try_rwlock_data.tick;
338 stm = rte_get_timer_cycles();
341 for (i = 0; i != ITER_NUM; i++) {
345 else if (rc == -EBUSY)
351 tm = rte_get_timer_cycles() - stm;
352 } while (tm < ftm && rc == 0);
360 print_try_lcore_stats(const struct try_rwlock_lcore *tlc, uint32_t lc)
364 f = RTE_MAX(tlc->stat.fail, 1ULL);
365 s = RTE_MAX(tlc->stat.success, 1ULL);
367 printf("try_lcore_data[%u]={\n"
370 "\tfail=%" PRIu64 ",\n"
371 "\tsuccess=%" PRIu64 ",\n"
372 "\tcycles=%" PRIu64 ",\n"
373 "\tcycles/op=%#Lf,\n"
374 "\tcycles/success=%#Lf,\n"
375 "\tsuccess/fail=%#Lf,\n"
379 tlc->type == LC_TYPE_RDLOCK ? "RDLOCK" : "WRLOCK",
383 (long double)tlc->stat.tick /
384 (tlc->stat.fail + tlc->stat.success),
385 (long double)tlc->stat.tick / s,
386 (long double)tlc->stat.success / f);
390 collect_try_lcore_stats(struct try_rwlock_lcore *tlc,
391 const struct try_rwlock_lcore *lc)
393 tlc->stat.tick += lc->stat.tick;
394 tlc->stat.fail += lc->stat.fail;
395 tlc->stat.success += lc->stat.success;
399 * Process collected results:
401 * - collect and print statistics
404 process_try_lcore_stats(void)
408 struct try_rwlock_lcore rlc, wlc;
410 memset(&rlc, 0, sizeof(rlc));
411 memset(&wlc, 0, sizeof(wlc));
413 rlc.type = LC_TYPE_RDLOCK;
414 wlc.type = LC_TYPE_WRLOCK;
419 RTE_LCORE_FOREACH(lc) {
420 rc |= try_lcore_data[lc].rc;
421 if (try_lcore_data[lc].type == LC_TYPE_RDLOCK) {
422 collect_try_lcore_stats(&rlc, try_lcore_data + lc);
425 collect_try_lcore_stats(&wlc, try_lcore_data + lc);
431 RTE_LCORE_FOREACH(lc)
432 print_try_lcore_stats(try_lcore_data + lc, lc);
435 printf("aggregated stats for %u RDLOCK cores:\n", rd);
436 print_try_lcore_stats(&rlc, rd);
440 printf("aggregated stats for %u WRLOCK cores:\n", wr);
441 print_try_lcore_stats(&wlc, wr);
451 memset(&try_lcore_data, 0, sizeof(try_lcore_data));
452 memset(&try_rwlock_data, 0, sizeof(try_rwlock_data));
453 try_rwlock_data.tick = TEST_SEC * rte_get_tsc_hz();
456 /* all lcores grab RDLOCK */
458 try_rwlock_test_rda(void)
462 /* start read test on all avaialble lcores */
463 rte_eal_mp_remote_launch(try_read_lcore, NULL, CALL_MASTER);
464 rte_eal_mp_wait_lcore();
466 return process_try_lcore_stats();
469 /* all slave lcores grab RDLOCK, master one grabs WRLOCK */
471 try_rwlock_test_rds_wrm(void)
475 rte_eal_mp_remote_launch(try_read_lcore, NULL, SKIP_MASTER);
476 try_write_lcore(NULL);
477 rte_eal_mp_wait_lcore();
479 return process_try_lcore_stats();
482 /* master and even slave lcores grab RDLOCK, odd lcores grab WRLOCK */
484 try_rwlock_test_rde_wro(void)
490 mlc = rte_get_master_lcore();
492 RTE_LCORE_FOREACH(lc) {
495 rte_eal_remote_launch(try_read_lcore,
498 rte_eal_remote_launch(try_write_lcore,
502 try_read_lcore(NULL);
503 rte_eal_mp_wait_lcore();
505 return process_try_lcore_stats();
514 static const struct {
519 .name = "rwlock_test1",
520 .ftst = rwlock_test1,
523 .name = "try_rwlock_test_rda",
524 .ftst = try_rwlock_test_rda,
527 .name = "try_rwlock_test_rds_wrm",
528 .ftst = try_rwlock_test_rds_wrm,
531 .name = "try_rwlock_test_rde_wro",
532 .ftst = try_rwlock_test_rde_wro,
537 for (i = 0; i != RTE_DIM(test); i++) {
538 printf("starting test %s;\n", test[i].name);
540 printf("test %s completed with status %d\n", test[i].name, rc);
547 REGISTER_TEST_COMMAND(rwlock_autotest, test_rwlock);