New upstream version 17.11-rc3
[deb_dpdk.git] / test / test / test_spinlock.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <stdio.h>
35 #include <stdint.h>
36 #include <inttypes.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <sys/queue.h>
40
41 #include <rte_common.h>
42 #include <rte_memory.h>
43 #include <rte_per_lcore.h>
44 #include <rte_launch.h>
45 #include <rte_eal.h>
46 #include <rte_lcore.h>
47 #include <rte_cycles.h>
48 #include <rte_spinlock.h>
49 #include <rte_atomic.h>
50
51 #include "test.h"
52
53 /*
54  * Spinlock test
55  * =============
56  *
57  * - There is a global spinlock and a table of spinlocks (one per lcore).
58  *
59  * - The test function takes all of these locks and launches the
60  *   ``test_spinlock_per_core()`` function on each core (except the master).
61  *
62  *   - The function takes the global lock, display something, then releases
63  *     the global lock.
64  *   - The function takes the per-lcore lock, display something, then releases
65  *     the per-core lock.
66  *
67  * - The main function unlocks the per-lcore locks sequentially and
68  *   waits between each lock. This triggers the display of a message
69  *   for each core, in the correct order. The autotest script checks that
70  *   this order is correct.
71  *
72  * - A load test is carried out, with all cores attempting to lock a single lock
73  *   multiple times
74  */
75
76 static rte_spinlock_t sl, sl_try;
77 static rte_spinlock_t sl_tab[RTE_MAX_LCORE];
78 static rte_spinlock_recursive_t slr;
79 static unsigned count = 0;
80
81 static rte_atomic32_t synchro;
82
83 static int
84 test_spinlock_per_core(__attribute__((unused)) void *arg)
85 {
86         rte_spinlock_lock(&sl);
87         printf("Global lock taken on core %u\n", rte_lcore_id());
88         rte_spinlock_unlock(&sl);
89
90         rte_spinlock_lock(&sl_tab[rte_lcore_id()]);
91         printf("Hello from core %u !\n", rte_lcore_id());
92         rte_spinlock_unlock(&sl_tab[rte_lcore_id()]);
93
94         return 0;
95 }
96
97 static int
98 test_spinlock_recursive_per_core(__attribute__((unused)) void *arg)
99 {
100         unsigned id = rte_lcore_id();
101
102         rte_spinlock_recursive_lock(&slr);
103         printf("Global recursive lock taken on core %u - count = %d\n",
104                id, slr.count);
105         rte_spinlock_recursive_lock(&slr);
106         printf("Global recursive lock taken on core %u - count = %d\n",
107                id, slr.count);
108         rte_spinlock_recursive_lock(&slr);
109         printf("Global recursive lock taken on core %u - count = %d\n",
110                id, slr.count);
111
112         printf("Hello from within recursive locks from core %u !\n", id);
113
114         rte_spinlock_recursive_unlock(&slr);
115         printf("Global recursive lock released on core %u - count = %d\n",
116                id, slr.count);
117         rte_spinlock_recursive_unlock(&slr);
118         printf("Global recursive lock released on core %u - count = %d\n",
119                id, slr.count);
120         rte_spinlock_recursive_unlock(&slr);
121         printf("Global recursive lock released on core %u - count = %d\n",
122                id, slr.count);
123
124         return 0;
125 }
126
127 static rte_spinlock_t lk = RTE_SPINLOCK_INITIALIZER;
128 static uint64_t lock_count[RTE_MAX_LCORE] = {0};
129
130 #define TIME_MS 100
131
132 static int
133 load_loop_fn(void *func_param)
134 {
135         uint64_t time_diff = 0, begin;
136         uint64_t hz = rte_get_timer_hz();
137         uint64_t lcount = 0;
138         const int use_lock = *(int*)func_param;
139         const unsigned lcore = rte_lcore_id();
140
141         /* wait synchro for slaves */
142         if (lcore != rte_get_master_lcore())
143                 while (rte_atomic32_read(&synchro) == 0);
144
145         begin = rte_get_timer_cycles();
146         while (time_diff < hz * TIME_MS / 1000) {
147                 if (use_lock)
148                         rte_spinlock_lock(&lk);
149                 lcount++;
150                 if (use_lock)
151                         rte_spinlock_unlock(&lk);
152                 /* delay to make lock duty cycle slighlty realistic */
153                 rte_delay_us(1);
154                 time_diff = rte_get_timer_cycles() - begin;
155         }
156         lock_count[lcore] = lcount;
157         return 0;
158 }
159
160 static int
161 test_spinlock_perf(void)
162 {
163         unsigned int i;
164         uint64_t total = 0;
165         int lock = 0;
166         const unsigned lcore = rte_lcore_id();
167
168         printf("\nTest with no lock on single core...\n");
169         load_loop_fn(&lock);
170         printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
171         memset(lock_count, 0, sizeof(lock_count));
172
173         printf("\nTest with lock on single core...\n");
174         lock = 1;
175         load_loop_fn(&lock);
176         printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
177         memset(lock_count, 0, sizeof(lock_count));
178
179         printf("\nTest with lock on %u cores...\n", rte_lcore_count());
180
181         /* Clear synchro and start slaves */
182         rte_atomic32_set(&synchro, 0);
183         rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
184
185         /* start synchro and launch test on master */
186         rte_atomic32_set(&synchro, 1);
187         load_loop_fn(&lock);
188
189         rte_eal_mp_wait_lcore();
190
191         RTE_LCORE_FOREACH(i) {
192                 printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
193                 total += lock_count[i];
194         }
195
196         printf("Total count = %"PRIu64"\n", total);
197
198         return 0;
199 }
200
201 /*
202  * Use rte_spinlock_trylock() to trylock a spinlock object,
203  * If it could not lock the object successfully, it would
204  * return immediately and the variable of "count" would be
205  * increased by one per times. the value of "count" could be
206  * checked as the result later.
207  */
208 static int
209 test_spinlock_try(__attribute__((unused)) void *arg)
210 {
211         if (rte_spinlock_trylock(&sl_try) == 0) {
212                 rte_spinlock_lock(&sl);
213                 count ++;
214                 rte_spinlock_unlock(&sl);
215         }
216
217         return 0;
218 }
219
220
221 /*
222  * Test rte_eal_get_lcore_state() in addition to spinlocks
223  * as we have "waiting" then "running" lcores.
224  */
225 static int
226 test_spinlock(void)
227 {
228         int ret = 0;
229         int i;
230
231         /* slave cores should be waiting: print it */
232         RTE_LCORE_FOREACH_SLAVE(i) {
233                 printf("lcore %d state: %d\n", i,
234                        (int) rte_eal_get_lcore_state(i));
235         }
236
237         rte_spinlock_init(&sl);
238         rte_spinlock_init(&sl_try);
239         rte_spinlock_recursive_init(&slr);
240         for (i=0; i<RTE_MAX_LCORE; i++)
241                 rte_spinlock_init(&sl_tab[i]);
242
243         rte_spinlock_lock(&sl);
244
245         RTE_LCORE_FOREACH_SLAVE(i) {
246                 rte_spinlock_lock(&sl_tab[i]);
247                 rte_eal_remote_launch(test_spinlock_per_core, NULL, i);
248         }
249
250         /* slave cores should be busy: print it */
251         RTE_LCORE_FOREACH_SLAVE(i) {
252                 printf("lcore %d state: %d\n", i,
253                        (int) rte_eal_get_lcore_state(i));
254         }
255         rte_spinlock_unlock(&sl);
256
257         RTE_LCORE_FOREACH_SLAVE(i) {
258                 rte_spinlock_unlock(&sl_tab[i]);
259                 rte_delay_ms(10);
260         }
261
262         rte_eal_mp_wait_lcore();
263
264         rte_spinlock_recursive_lock(&slr);
265
266         /*
267          * Try to acquire a lock that we already own
268          */
269         if(!rte_spinlock_recursive_trylock(&slr)) {
270                 printf("rte_spinlock_recursive_trylock failed on a lock that "
271                        "we already own\n");
272                 ret = -1;
273         } else
274                 rte_spinlock_recursive_unlock(&slr);
275
276         RTE_LCORE_FOREACH_SLAVE(i) {
277                 rte_eal_remote_launch(test_spinlock_recursive_per_core, NULL, i);
278         }
279         rte_spinlock_recursive_unlock(&slr);
280         rte_eal_mp_wait_lcore();
281
282         /*
283          * Test if it could return immediately from try-locking a locked object.
284          * Here it will lock the spinlock object first, then launch all the slave
285          * lcores to trylock the same spinlock object.
286          * All the slave lcores should give up try-locking a locked object and
287          * return immediately, and then increase the "count" initialized with zero
288          * by one per times.
289          * We can check if the "count" is finally equal to the number of all slave
290          * lcores to see if the behavior of try-locking a locked spinlock object
291          * is correct.
292          */
293         if (rte_spinlock_trylock(&sl_try) == 0) {
294                 return -1;
295         }
296         count = 0;
297         RTE_LCORE_FOREACH_SLAVE(i) {
298                 rte_eal_remote_launch(test_spinlock_try, NULL, i);
299         }
300         rte_eal_mp_wait_lcore();
301         rte_spinlock_unlock(&sl_try);
302         if (rte_spinlock_is_locked(&sl)) {
303                 printf("spinlock is locked but it should not be\n");
304                 return -1;
305         }
306         rte_spinlock_lock(&sl);
307         if (count != ( rte_lcore_count() - 1)) {
308                 ret = -1;
309         }
310         rte_spinlock_unlock(&sl);
311
312         /*
313          * Test if it can trylock recursively.
314          * Use rte_spinlock_recursive_trylock() to check if it can lock a spinlock
315          * object recursively. Here it will try to lock a spinlock object twice.
316          */
317         if (rte_spinlock_recursive_trylock(&slr) == 0) {
318                 printf("It failed to do the first spinlock_recursive_trylock but it should able to do\n");
319                 return -1;
320         }
321         if (rte_spinlock_recursive_trylock(&slr) == 0) {
322                 printf("It failed to do the second spinlock_recursive_trylock but it should able to do\n");
323                 return -1;
324         }
325         rte_spinlock_recursive_unlock(&slr);
326         rte_spinlock_recursive_unlock(&slr);
327
328         if (test_spinlock_perf() < 0)
329                 return -1;
330
331         return ret;
332 }
333
334 REGISTER_TEST_COMMAND(spinlock_autotest, test_spinlock);