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];
49 uint8_t u8[RTE_CACHE_LINE_SIZE];
50 uint64_t u64[RTE_CACHE_LINE_SIZE / sizeof(uint64_t)];
52 } __rte_cache_aligned try_rwlock_data;
54 struct try_rwlock_lcore {
62 } __rte_cache_aligned;
64 static struct try_rwlock_lcore try_lcore_data[RTE_MAX_LCORE];
67 test_rwlock_per_core(__attribute__((unused)) void *arg)
69 rte_rwlock_write_lock(&sl);
70 printf("Global write lock taken on core %u\n", rte_lcore_id());
71 rte_rwlock_write_unlock(&sl);
73 rte_rwlock_write_lock(&sl_tab[rte_lcore_id()]);
74 printf("Hello from core %u !\n", rte_lcore_id());
75 rte_rwlock_write_unlock(&sl_tab[rte_lcore_id()]);
77 rte_rwlock_read_lock(&sl);
78 printf("Global read lock taken on core %u\n", rte_lcore_id());
80 printf("Release global read lock on core %u\n", rte_lcore_id());
81 rte_rwlock_read_unlock(&sl);
87 * - There is a global rwlock and a table of rwlocks (one per lcore).
89 * - The test function takes all of these locks and launches the
90 * ``test_rwlock_per_core()`` function on each core (except the master).
92 * - The function takes the global write lock, display something,
93 * then releases the global lock.
94 * - Then, it takes the per-lcore write lock, display something, and
95 * releases the per-core lock.
96 * - Finally, a read lock is taken during 100 ms, then released.
98 * - The main function unlocks the per-lcore locks sequentially and
99 * waits between each lock. This triggers the display of a message
100 * for each core, in the correct order.
102 * Then, it tries to take the global write lock and display the last
103 * message. The autotest script checks that the message order is correct.
110 rte_rwlock_init(&sl);
111 for (i=0; i<RTE_MAX_LCORE; i++)
112 rte_rwlock_init(&sl_tab[i]);
114 rte_rwlock_write_lock(&sl);
116 RTE_LCORE_FOREACH_SLAVE(i) {
117 rte_rwlock_write_lock(&sl_tab[i]);
118 rte_eal_remote_launch(test_rwlock_per_core, NULL, i);
121 rte_rwlock_write_unlock(&sl);
123 RTE_LCORE_FOREACH_SLAVE(i) {
124 rte_rwlock_write_unlock(&sl_tab[i]);
128 rte_rwlock_write_lock(&sl);
129 /* this message should be the last message of test */
130 printf("Global write lock taken on master core %u\n", rte_lcore_id());
131 rte_rwlock_write_unlock(&sl);
133 rte_eal_mp_wait_lcore();
139 try_read(uint32_t lc)
144 rc = rte_rwlock_read_trylock(&try_rwlock_data.lock);
148 for (i = 0; i != RTE_DIM(try_rwlock_data.data.u64); i++) {
150 /* race condition occurred, lock doesn't work properly */
151 if (try_rwlock_data.data.u64[i] != 0) {
152 printf("%s(%u) error: unexpected data pattern\n",
154 rte_memdump(stdout, NULL,
155 (void *)(uintptr_t)&try_rwlock_data.data,
156 sizeof(try_rwlock_data.data));
162 rte_rwlock_read_unlock(&try_rwlock_data.lock);
167 try_write(uint32_t lc)
172 v = RTE_MAX(lc % UINT8_MAX, 1U);
174 rc = rte_rwlock_write_trylock(&try_rwlock_data.lock);
178 /* update by bytes in reverese order */
179 for (i = RTE_DIM(try_rwlock_data.data.u8); i-- != 0; ) {
181 /* race condition occurred, lock doesn't work properly */
182 if (try_rwlock_data.data.u8[i] != 0) {
183 printf("%s:%d(%u) error: unexpected data pattern\n",
184 __func__, __LINE__, lc);
185 rte_memdump(stdout, NULL,
186 (void *)(uintptr_t)&try_rwlock_data.data,
187 sizeof(try_rwlock_data.data));
192 try_rwlock_data.data.u8[i] = v;
195 /* restore by bytes in reverese order */
196 for (i = RTE_DIM(try_rwlock_data.data.u8); i-- != 0; ) {
198 /* race condition occurred, lock doesn't work properly */
199 if (try_rwlock_data.data.u8[i] != v) {
200 printf("%s:%d(%u) error: unexpected data pattern\n",
201 __func__, __LINE__, lc);
202 rte_memdump(stdout, NULL,
203 (void *)(uintptr_t)&try_rwlock_data.data,
204 sizeof(try_rwlock_data.data));
209 try_rwlock_data.data.u8[i] = 0;
212 rte_rwlock_write_unlock(&try_rwlock_data.lock);
217 try_read_lcore(__rte_unused void *data)
221 uint64_t ftm, stm, tm;
222 struct try_rwlock_lcore *lcd;
225 lcd = try_lcore_data + lc;
226 lcd->type = LC_TYPE_RDLOCK;
228 ftm = try_rwlock_data.tick;
229 stm = rte_get_timer_cycles();
232 for (i = 0; i != ITER_NUM; i++) {
236 else if (rc == -EBUSY)
242 tm = rte_get_timer_cycles() - stm;
243 } while (tm < ftm && rc == 0);
251 try_write_lcore(__rte_unused void *data)
255 uint64_t ftm, stm, tm;
256 struct try_rwlock_lcore *lcd;
259 lcd = try_lcore_data + lc;
260 lcd->type = LC_TYPE_WRLOCK;
262 ftm = try_rwlock_data.tick;
263 stm = rte_get_timer_cycles();
266 for (i = 0; i != ITER_NUM; i++) {
270 else if (rc == -EBUSY)
276 tm = rte_get_timer_cycles() - stm;
277 } while (tm < ftm && rc == 0);
285 print_try_lcore_stats(const struct try_rwlock_lcore *tlc, uint32_t lc)
289 f = RTE_MAX(tlc->stat.fail, 1ULL);
290 s = RTE_MAX(tlc->stat.success, 1ULL);
292 printf("try_lcore_data[%u]={\n"
295 "\tfail=%" PRIu64 ",\n"
296 "\tsuccess=%" PRIu64 ",\n"
297 "\tcycles=%" PRIu64 ",\n"
298 "\tcycles/op=%#Lf,\n"
299 "\tcycles/success=%#Lf,\n"
300 "\tsuccess/fail=%#Lf,\n"
304 tlc->type == LC_TYPE_RDLOCK ? "RDLOCK" : "WRLOCK",
308 (long double)tlc->stat.tick /
309 (tlc->stat.fail + tlc->stat.success),
310 (long double)tlc->stat.tick / s,
311 (long double)tlc->stat.success / f);
315 collect_try_lcore_stats(struct try_rwlock_lcore *tlc,
316 const struct try_rwlock_lcore *lc)
318 tlc->stat.tick += lc->stat.tick;
319 tlc->stat.fail += lc->stat.fail;
320 tlc->stat.success += lc->stat.success;
324 * Process collected results:
326 * - collect and print statistics
329 process_try_lcore_stats(void)
333 struct try_rwlock_lcore rlc, wlc;
335 memset(&rlc, 0, sizeof(rlc));
336 memset(&wlc, 0, sizeof(wlc));
338 rlc.type = LC_TYPE_RDLOCK;
339 wlc.type = LC_TYPE_WRLOCK;
344 RTE_LCORE_FOREACH(lc) {
345 rc |= try_lcore_data[lc].rc;
346 if (try_lcore_data[lc].type == LC_TYPE_RDLOCK) {
347 collect_try_lcore_stats(&rlc, try_lcore_data + lc);
350 collect_try_lcore_stats(&wlc, try_lcore_data + lc);
356 RTE_LCORE_FOREACH(lc)
357 print_try_lcore_stats(try_lcore_data + lc, lc);
360 printf("aggregated stats for %u RDLOCK cores:\n", rd);
361 print_try_lcore_stats(&rlc, rd);
365 printf("aggregated stats for %u WRLOCK cores:\n", wr);
366 print_try_lcore_stats(&wlc, wr);
376 memset(&try_lcore_data, 0, sizeof(try_lcore_data));
377 memset(&try_rwlock_data, 0, sizeof(try_rwlock_data));
378 try_rwlock_data.tick = TEST_SEC * rte_get_tsc_hz();
381 /* all lcores grab RDLOCK */
383 try_rwlock_test_rda(void)
387 /* start read test on all avaialble lcores */
388 rte_eal_mp_remote_launch(try_read_lcore, NULL, CALL_MASTER);
389 rte_eal_mp_wait_lcore();
391 return process_try_lcore_stats();
394 /* all slave lcores grab RDLOCK, master one grabs WRLOCK */
396 try_rwlock_test_rds_wrm(void)
400 rte_eal_mp_remote_launch(try_read_lcore, NULL, SKIP_MASTER);
401 try_write_lcore(NULL);
402 rte_eal_mp_wait_lcore();
404 return process_try_lcore_stats();
407 /* master and even slave lcores grab RDLOCK, odd lcores grab WRLOCK */
409 try_rwlock_test_rde_wro(void)
415 mlc = rte_get_master_lcore();
417 RTE_LCORE_FOREACH(lc) {
420 rte_eal_remote_launch(try_read_lcore,
423 rte_eal_remote_launch(try_write_lcore,
427 try_read_lcore(NULL);
428 rte_eal_mp_wait_lcore();
430 return process_try_lcore_stats();
439 static const struct {
444 .name = "rwlock_test1",
445 .ftst = rwlock_test1,
448 .name = "try_rwlock_test_rda",
449 .ftst = try_rwlock_test_rda,
452 .name = "try_rwlock_test_rds_wrm",
453 .ftst = try_rwlock_test_rds_wrm,
456 .name = "try_rwlock_test_rde_wro",
457 .ftst = try_rwlock_test_rde_wro,
462 for (i = 0; i != RTE_DIM(test); i++) {
463 printf("starting test %s;\n", test[i].name);
465 printf("test %s completed with status %d\n", test[i].name, rc);
472 REGISTER_TEST_COMMAND(rwlock_autotest, test_rwlock);