xref: /dpdk/app/test/test_pflock.c (revision b6a7e6852e9ab82ae0e05e2d2a0b83abca17de3b)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2021 Microsoft 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_pflock.h>
17 #include <rte_eal.h>
18 #include <rte_lcore.h>
19 #include <rte_cycles.h>
20 
21 #include "test.h"
22 
23 /*
24  * phase-fair lock test
25  * ====================
26  * Provides UT for phase-fair lock API.
27  * Main concern is on functional testing, but also provides some
28  * performance measurements.
29  * Obviously for proper testing need to be executed with more than one lcore.
30  */
31 
32 static rte_pflock_t sl;
33 static rte_pflock_t sl_tab[RTE_MAX_LCORE];
34 static RTE_ATOMIC(uint32_t) synchro;
35 
36 static int
test_pflock_per_core(__rte_unused void * arg)37 test_pflock_per_core(__rte_unused void *arg)
38 {
39 	rte_pflock_write_lock(&sl);
40 	printf("Global write lock taken on core %u\n", rte_lcore_id());
41 	rte_pflock_write_unlock(&sl);
42 
43 	rte_pflock_write_lock(&sl_tab[rte_lcore_id()]);
44 	printf("Hello from core %u !\n", rte_lcore_id());
45 	rte_pflock_write_unlock(&sl_tab[rte_lcore_id()]);
46 
47 	rte_pflock_read_lock(&sl);
48 	printf("Global read lock taken on core %u\n", rte_lcore_id());
49 	rte_delay_ms(100);
50 	printf("Release global read lock on core %u\n", rte_lcore_id());
51 	rte_pflock_read_unlock(&sl);
52 
53 	return 0;
54 }
55 
56 static rte_pflock_t lk = RTE_PFLOCK_INITIALIZER;
57 static uint64_t time_count[RTE_MAX_LCORE] = {0};
58 
59 #define MAX_LOOP 10000
60 
61 static int
load_loop_fn(void * arg)62 load_loop_fn(void *arg)
63 {
64 	uint64_t time_diff = 0, begin;
65 	uint64_t hz = rte_get_timer_hz();
66 	uint64_t lcount = 0;
67 	const int use_lock = *(int *)arg;
68 	const unsigned int lcore = rte_lcore_id();
69 
70 	/* wait synchro for workers */
71 	if (lcore != rte_get_main_lcore())
72 		rte_wait_until_equal_32((uint32_t *)(uintptr_t)&synchro, 1,
73 				rte_memory_order_relaxed);
74 
75 	begin = rte_rdtsc_precise();
76 	while (lcount < MAX_LOOP) {
77 		if (use_lock)
78 			rte_pflock_write_lock(&lk);
79 		lcount++;
80 		if (use_lock)
81 			rte_pflock_write_unlock(&lk);
82 
83 		if (use_lock) {
84 			rte_pflock_read_lock(&lk);
85 			rte_pflock_read_unlock(&lk);
86 		}
87 	}
88 
89 	time_diff = rte_rdtsc_precise() - begin;
90 	time_count[lcore] = time_diff * 1000000 / hz;
91 	return 0;
92 }
93 
94 static int
test_pflock_perf(void)95 test_pflock_perf(void)
96 {
97 	unsigned int i;
98 	int lock = 0;
99 	uint64_t total = 0;
100 	const unsigned int lcore = rte_lcore_id();
101 
102 	printf("\nTest with no lock on single core...\n");
103 	rte_atomic_store_explicit(&synchro, 1, rte_memory_order_relaxed);
104 	load_loop_fn(&lock);
105 	printf("Core [%u] Cost Time = %"PRIu64" us\n",
106 			lcore, time_count[lcore]);
107 	memset(time_count, 0, sizeof(time_count));
108 
109 	printf("\nTest with phase-fair lock on single core...\n");
110 	lock = 1;
111 	rte_atomic_store_explicit(&synchro, 1, rte_memory_order_relaxed);
112 	load_loop_fn(&lock);
113 	printf("Core [%u] Cost Time = %"PRIu64" us\n",
114 			lcore, time_count[lcore]);
115 	memset(time_count, 0, sizeof(time_count));
116 
117 	printf("\nPhase-fair test on %u cores...\n", rte_lcore_count());
118 
119 	/* clear synchro and start workers */
120 	rte_atomic_store_explicit(&synchro, 0, rte_memory_order_relaxed);
121 	if (rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MAIN) < 0)
122 		return -1;
123 
124 	/* start synchro and launch test on main */
125 	rte_atomic_store_explicit(&synchro, 1, rte_memory_order_relaxed);
126 	load_loop_fn(&lock);
127 
128 	rte_eal_mp_wait_lcore();
129 
130 	RTE_LCORE_FOREACH(i) {
131 		printf("Core [%u] cost time = %"PRIu64" us\n",
132 			i, time_count[i]);
133 		total += time_count[i];
134 	}
135 
136 	printf("Total cost time = %"PRIu64" us\n", total);
137 	memset(time_count, 0, sizeof(time_count));
138 
139 	return 0;
140 }
141 
142 /*
143  * - There is a global pflock and a table of pflocks (one per lcore).
144  *
145  * - The test function takes all of these locks and launches the
146  *   ``test_pflock_per_core()`` function on each core (except the main).
147  *
148  *   - The function takes the global write lock, display something,
149  *     then releases the global lock.
150  *   - Then, it takes the per-lcore write lock, display something, and
151  *     releases the per-core lock.
152  *   - Finally, a read lock is taken during 100 ms, then released.
153  *
154  * - The main function unlocks the per-lcore locks sequentially and
155  *   waits between each lock. This triggers the display of a message
156  *   for each core, in the correct order.
157  *
158  *   Then, it tries to take the global write lock and display the last
159  *   message. The autotest script checks that the message order is correct.
160  */
161 static int
test_pflock(void)162 test_pflock(void)
163 {
164 	int i;
165 
166 	rte_pflock_init(&sl);
167 	for (i = 0; i < RTE_MAX_LCORE; i++)
168 		rte_pflock_init(&sl_tab[i]);
169 
170 	rte_pflock_write_lock(&sl);
171 
172 	RTE_LCORE_FOREACH_WORKER(i) {
173 		rte_pflock_write_lock(&sl_tab[i]);
174 		rte_eal_remote_launch(test_pflock_per_core, NULL, i);
175 	}
176 
177 	rte_pflock_write_unlock(&sl);
178 
179 	RTE_LCORE_FOREACH_WORKER(i) {
180 		rte_pflock_write_unlock(&sl_tab[i]);
181 		rte_delay_ms(100);
182 	}
183 
184 	rte_pflock_write_lock(&sl);
185 	/* this message should be the last message of test */
186 	printf("Global write lock taken on main core %u\n", rte_lcore_id());
187 	rte_pflock_write_unlock(&sl);
188 
189 	rte_eal_mp_wait_lcore();
190 
191 	if (test_pflock_perf() < 0)
192 		return -1;
193 
194 	return 0;
195 }
196 
197 REGISTER_FAST_TEST(pflock_autotest, true, true, test_pflock);
198