test: move to app directory
[dpdk.git] / app / test / test_rwlock.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <inttypes.h>
8 #include <unistd.h>
9 #include <sys/queue.h>
10 #include <string.h>
11
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>
18 #include <rte_eal.h>
19 #include <rte_lcore.h>
20 #include <rte_cycles.h>
21
22 #include "test.h"
23
24 /*
25  * rwlock test
26  * ===========
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.
31  */
32
33 #define ITER_NUM        0x80
34
35 #define TEST_SEC        5
36
37 static rte_rwlock_t sl;
38 static rte_rwlock_t sl_tab[RTE_MAX_LCORE];
39
40 enum {
41         LC_TYPE_RDLOCK,
42         LC_TYPE_WRLOCK,
43 };
44
45 static struct {
46         rte_rwlock_t lock;
47         uint64_t tick;
48         volatile union {
49                 uint8_t u8[RTE_CACHE_LINE_SIZE];
50                 uint64_t u64[RTE_CACHE_LINE_SIZE / sizeof(uint64_t)];
51         } data;
52 } __rte_cache_aligned try_rwlock_data;
53
54 struct try_rwlock_lcore {
55         int32_t rc;
56         int32_t type;
57         struct {
58                 uint64_t tick;
59                 uint64_t fail;
60                 uint64_t success;
61         } stat;
62 } __rte_cache_aligned;
63
64 static struct try_rwlock_lcore try_lcore_data[RTE_MAX_LCORE];
65
66 static int
67 test_rwlock_per_core(__attribute__((unused)) void *arg)
68 {
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);
72
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()]);
76
77         rte_rwlock_read_lock(&sl);
78         printf("Global read lock taken on core %u\n", rte_lcore_id());
79         rte_delay_ms(100);
80         printf("Release global read lock on core %u\n", rte_lcore_id());
81         rte_rwlock_read_unlock(&sl);
82
83         return 0;
84 }
85
86 /*
87  * - There is a global rwlock and a table of rwlocks (one per lcore).
88  *
89  * - The test function takes all of these locks and launches the
90  *   ``test_rwlock_per_core()`` function on each core (except the master).
91  *
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.
97  *
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.
101  *
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.
104  */
105 static int
106 rwlock_test1(void)
107 {
108         int i;
109
110         rte_rwlock_init(&sl);
111         for (i=0; i<RTE_MAX_LCORE; i++)
112                 rte_rwlock_init(&sl_tab[i]);
113
114         rte_rwlock_write_lock(&sl);
115
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);
119         }
120
121         rte_rwlock_write_unlock(&sl);
122
123         RTE_LCORE_FOREACH_SLAVE(i) {
124                 rte_rwlock_write_unlock(&sl_tab[i]);
125                 rte_delay_ms(100);
126         }
127
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);
132
133         rte_eal_mp_wait_lcore();
134
135         return 0;
136 }
137
138 static int
139 try_read(uint32_t lc)
140 {
141         int32_t rc;
142         uint32_t i;
143
144         rc = rte_rwlock_read_trylock(&try_rwlock_data.lock);
145         if (rc != 0)
146                 return rc;
147
148         for (i = 0; i != RTE_DIM(try_rwlock_data.data.u64); i++) {
149
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",
153                                 __func__, lc);
154                         rte_memdump(stdout, NULL,
155                                 (void *)(uintptr_t)&try_rwlock_data.data,
156                                 sizeof(try_rwlock_data.data));
157                         rc = -EFAULT;
158                         break;
159                 }
160         }
161
162         rte_rwlock_read_unlock(&try_rwlock_data.lock);
163         return rc;
164 }
165
166 static int
167 try_write(uint32_t lc)
168 {
169         int32_t rc;
170         uint32_t i, v;
171
172         v = RTE_MAX(lc % UINT8_MAX, 1U);
173
174         rc = rte_rwlock_write_trylock(&try_rwlock_data.lock);
175         if (rc != 0)
176                 return rc;
177
178         /* update by bytes in reverese order */
179         for (i = RTE_DIM(try_rwlock_data.data.u8); i-- != 0; ) {
180
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));
188                         rc = -EFAULT;
189                         break;
190                 }
191
192                 try_rwlock_data.data.u8[i] = v;
193         }
194
195         /* restore by bytes in reverese order */
196         for (i = RTE_DIM(try_rwlock_data.data.u8); i-- != 0; ) {
197
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));
205                         rc = -EFAULT;
206                         break;
207                 }
208
209                 try_rwlock_data.data.u8[i] = 0;
210         }
211
212         rte_rwlock_write_unlock(&try_rwlock_data.lock);
213         return rc;
214 }
215
216 static int
217 try_read_lcore(__rte_unused void *data)
218 {
219         int32_t rc;
220         uint32_t i, lc;
221         uint64_t ftm, stm, tm;
222         struct try_rwlock_lcore *lcd;
223
224         lc = rte_lcore_id();
225         lcd = try_lcore_data + lc;
226         lcd->type = LC_TYPE_RDLOCK;
227
228         ftm = try_rwlock_data.tick;
229         stm = rte_get_timer_cycles();
230
231         do {
232                 for (i = 0; i != ITER_NUM; i++) {
233                         rc = try_read(lc);
234                         if (rc == 0)
235                                 lcd->stat.success++;
236                         else if (rc == -EBUSY)
237                                 lcd->stat.fail++;
238                         else
239                                 break;
240                         rc = 0;
241                 }
242                 tm = rte_get_timer_cycles() - stm;
243         } while (tm < ftm && rc == 0);
244
245         lcd->rc = rc;
246         lcd->stat.tick = tm;
247         return rc;
248 }
249
250 static int
251 try_write_lcore(__rte_unused void *data)
252 {
253         int32_t rc;
254         uint32_t i, lc;
255         uint64_t ftm, stm, tm;
256         struct try_rwlock_lcore *lcd;
257
258         lc = rte_lcore_id();
259         lcd = try_lcore_data + lc;
260         lcd->type = LC_TYPE_WRLOCK;
261
262         ftm = try_rwlock_data.tick;
263         stm = rte_get_timer_cycles();
264
265         do {
266                 for (i = 0; i != ITER_NUM; i++) {
267                         rc = try_write(lc);
268                         if (rc == 0)
269                                 lcd->stat.success++;
270                         else if (rc == -EBUSY)
271                                 lcd->stat.fail++;
272                         else
273                                 break;
274                         rc = 0;
275                 }
276                 tm = rte_get_timer_cycles() - stm;
277         } while (tm < ftm && rc == 0);
278
279         lcd->rc = rc;
280         lcd->stat.tick = tm;
281         return rc;
282 }
283
284 static void
285 print_try_lcore_stats(const struct try_rwlock_lcore *tlc, uint32_t lc)
286 {
287         uint64_t f, s;
288
289         f = RTE_MAX(tlc->stat.fail, 1ULL);
290         s = RTE_MAX(tlc->stat.success, 1ULL);
291
292         printf("try_lcore_data[%u]={\n"
293                 "\trc=%d,\n"
294                 "\ttype=%s,\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"
301                 "};\n",
302                 lc,
303                 tlc->rc,
304                 tlc->type == LC_TYPE_RDLOCK ? "RDLOCK" : "WRLOCK",
305                 tlc->stat.fail,
306                 tlc->stat.success,
307                 tlc->stat.tick,
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);
312 }
313
314 static void
315 collect_try_lcore_stats(struct try_rwlock_lcore *tlc,
316         const struct try_rwlock_lcore *lc)
317 {
318         tlc->stat.tick += lc->stat.tick;
319         tlc->stat.fail += lc->stat.fail;
320         tlc->stat.success += lc->stat.success;
321 }
322
323 /*
324  * Process collected results:
325  *  - check status
326  *  - collect and print statistics
327  */
328 static int
329 process_try_lcore_stats(void)
330 {
331         int32_t rc;
332         uint32_t lc, rd, wr;
333         struct try_rwlock_lcore rlc, wlc;
334
335         memset(&rlc, 0, sizeof(rlc));
336         memset(&wlc, 0, sizeof(wlc));
337
338         rlc.type = LC_TYPE_RDLOCK;
339         wlc.type = LC_TYPE_WRLOCK;
340         rd = 0;
341         wr = 0;
342
343         rc = 0;
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);
348                         rd++;
349                 } else {
350                         collect_try_lcore_stats(&wlc, try_lcore_data + lc);
351                         wr++;
352                 }
353         }
354
355         if (rc == 0) {
356                 RTE_LCORE_FOREACH(lc)
357                         print_try_lcore_stats(try_lcore_data + lc, lc);
358
359                 if (rd != 0) {
360                         printf("aggregated stats for %u RDLOCK cores:\n", rd);
361                         print_try_lcore_stats(&rlc, rd);
362                 }
363
364                 if (wr != 0) {
365                         printf("aggregated stats for %u WRLOCK cores:\n", wr);
366                         print_try_lcore_stats(&wlc, wr);
367                 }
368         }
369
370         return rc;
371 }
372
373 static void
374 try_test_reset(void)
375 {
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();
379 }
380
381 /* all lcores grab RDLOCK */
382 static int
383 try_rwlock_test_rda(void)
384 {
385         try_test_reset();
386
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();
390
391         return process_try_lcore_stats();
392 }
393
394 /* all slave lcores grab RDLOCK, master one grabs WRLOCK */
395 static int
396 try_rwlock_test_rds_wrm(void)
397 {
398         try_test_reset();
399
400         rte_eal_mp_remote_launch(try_read_lcore, NULL, SKIP_MASTER);
401         try_write_lcore(NULL);
402         rte_eal_mp_wait_lcore();
403
404         return process_try_lcore_stats();
405 }
406
407 /* master and even slave lcores grab RDLOCK, odd lcores grab WRLOCK */
408 static int
409 try_rwlock_test_rde_wro(void)
410 {
411         uint32_t lc, mlc;
412
413         try_test_reset();
414
415         mlc = rte_get_master_lcore();
416
417         RTE_LCORE_FOREACH(lc) {
418                 if (lc != mlc) {
419                         if ((lc & 1) == 0)
420                                 rte_eal_remote_launch(try_read_lcore,
421                                                 NULL, lc);
422                         else
423                                 rte_eal_remote_launch(try_write_lcore,
424                                                 NULL, lc);
425                 }
426         }
427         try_read_lcore(NULL);
428         rte_eal_mp_wait_lcore();
429
430         return process_try_lcore_stats();
431 }
432
433 static int
434 test_rwlock(void)
435 {
436         uint32_t i;
437         int32_t rc, ret;
438
439         static const struct {
440                 const char *name;
441                 int (*ftst)(void);
442         } test[] = {
443                 {
444                         .name = "rwlock_test1",
445                         .ftst = rwlock_test1,
446                 },
447                 {
448                         .name = "try_rwlock_test_rda",
449                         .ftst = try_rwlock_test_rda,
450                 },
451                 {
452                         .name = "try_rwlock_test_rds_wrm",
453                         .ftst = try_rwlock_test_rds_wrm,
454                 },
455                 {
456                         .name = "try_rwlock_test_rde_wro",
457                         .ftst = try_rwlock_test_rde_wro,
458                 },
459         };
460
461         ret = 0;
462         for (i = 0; i != RTE_DIM(test); i++) {
463                 printf("starting test %s;\n", test[i].name);
464                 rc = test[i].ftst();
465                 printf("test %s completed with status %d\n", test[i].name, rc);
466                 ret |= rc;
467         }
468
469         return ret;
470 }
471
472 REGISTER_TEST_COMMAND(rwlock_autotest, test_rwlock);