app/test: rework command registration
[dpdk.git] / app / 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_memzone.h>
44 #include <rte_per_lcore.h>
45 #include <rte_launch.h>
46 #include <rte_eal.h>
47 #include <rte_per_lcore.h>
48 #include <rte_lcore.h>
49 #include <rte_cycles.h>
50 #include <rte_spinlock.h>
51 #include <rte_atomic.h>
52
53 #include "test.h"
54
55 /*
56  * Spinlock test
57  * =============
58  *
59  * - There is a global spinlock and a table of spinlocks (one per lcore).
60  *
61  * - The test function takes all of these locks and launches the
62  *   ``test_spinlock_per_core()`` function on each core (except the master).
63  *
64  *   - The function takes the global lock, display something, then releases
65  *     the global lock.
66  *   - The function takes the per-lcore lock, display something, then releases
67  *     the per-core lock.
68  *
69  * - The main function unlocks the per-lcore locks sequentially and
70  *   waits between each lock. This triggers the display of a message
71  *   for each core, in the correct order. The autotest script checks that
72  *   this order is correct.
73  *
74  * - A load test is carried out, with all cores attempting to lock a single lock
75  *   multiple times
76  */
77
78 static rte_spinlock_t sl, sl_try;
79 static rte_spinlock_t sl_tab[RTE_MAX_LCORE];
80 static rte_spinlock_recursive_t slr;
81 static unsigned count = 0;
82
83 static rte_atomic32_t synchro;
84
85 static int
86 test_spinlock_per_core(__attribute__((unused)) void *arg)
87 {
88         rte_spinlock_lock(&sl);
89         printf("Global lock taken on core %u\n", rte_lcore_id());
90         rte_spinlock_unlock(&sl);
91
92         rte_spinlock_lock(&sl_tab[rte_lcore_id()]);
93         printf("Hello from core %u !\n", rte_lcore_id());
94         rte_spinlock_unlock(&sl_tab[rte_lcore_id()]);
95
96         return 0;
97 }
98
99 static int
100 test_spinlock_recursive_per_core(__attribute__((unused)) void *arg)
101 {
102         unsigned id = rte_lcore_id();
103
104         rte_spinlock_recursive_lock(&slr);
105         printf("Global recursive lock taken on core %u - count = %d\n",
106                id, slr.count);
107         rte_spinlock_recursive_lock(&slr);
108         printf("Global recursive lock taken on core %u - count = %d\n",
109                id, slr.count);
110         rte_spinlock_recursive_lock(&slr);
111         printf("Global recursive lock taken on core %u - count = %d\n",
112                id, slr.count);
113
114         printf("Hello from within recursive locks from core %u !\n", id);
115
116         rte_spinlock_recursive_unlock(&slr);
117         printf("Global recursive lock released on core %u - count = %d\n",
118                id, slr.count);
119         rte_spinlock_recursive_unlock(&slr);
120         printf("Global recursive lock released on core %u - count = %d\n",
121                id, slr.count);
122         rte_spinlock_recursive_unlock(&slr);
123         printf("Global recursive lock released on core %u - count = %d\n",
124                id, slr.count);
125
126         return 0;
127 }
128
129 static rte_spinlock_t lk = RTE_SPINLOCK_INITIALIZER;
130 static uint64_t lock_count[RTE_MAX_LCORE] = {0};
131
132 #define TIME_MS 100
133
134 static int
135 load_loop_fn(void *func_param)
136 {
137         uint64_t time_diff = 0, begin;
138         uint64_t hz = rte_get_timer_hz();
139         uint64_t lcount = 0;
140         const int use_lock = *(int*)func_param;
141         const unsigned lcore = rte_lcore_id();
142
143         /* wait synchro for slaves */
144         if (lcore != rte_get_master_lcore())
145                 while (rte_atomic32_read(&synchro) == 0);
146
147         begin = rte_get_timer_cycles();
148         while (time_diff < hz * TIME_MS / 1000) {
149                 if (use_lock)
150                         rte_spinlock_lock(&lk);
151                 lcount++;
152                 if (use_lock)
153                         rte_spinlock_unlock(&lk);
154                 /* delay to make lock duty cycle slighlty realistic */
155                 rte_delay_us(1);
156                 time_diff = rte_get_timer_cycles() - begin;
157         }
158         lock_count[lcore] = lcount;
159         return 0;
160 }
161
162 static int
163 test_spinlock_perf(void)
164 {
165         unsigned int i;
166         uint64_t total = 0;
167         int lock = 0;
168         const unsigned lcore = rte_lcore_id();
169
170         printf("\nTest with no lock on single core...\n");
171         load_loop_fn(&lock);
172         printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
173         memset(lock_count, 0, sizeof(lock_count));
174
175         printf("\nTest with lock on single core...\n");
176         lock = 1;
177         load_loop_fn(&lock);
178         printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
179         memset(lock_count, 0, sizeof(lock_count));
180
181         printf("\nTest with lock on %u cores...\n", rte_lcore_count());
182
183         /* Clear synchro and start slaves */
184         rte_atomic32_set(&synchro, 0);
185         rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
186
187         /* start synchro and launch test on master */
188         rte_atomic32_set(&synchro, 1);
189         load_loop_fn(&lock);
190
191         rte_eal_mp_wait_lcore();
192
193         RTE_LCORE_FOREACH(i) {
194                 printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
195                 total += lock_count[i];
196         }
197
198         printf("Total count = %"PRIu64"\n", total);
199
200         return 0;
201 }
202
203 /*
204  * Use rte_spinlock_trylock() to trylock a spinlock object,
205  * If it could not lock the object sucessfully, it would
206  * return immediately and the variable of "count" would be
207  * increased by one per times. the value of "count" could be
208  * checked as the result later.
209  */
210 static int
211 test_spinlock_try(__attribute__((unused)) void *arg)
212 {
213         if (rte_spinlock_trylock(&sl_try) == 0) {
214                 rte_spinlock_lock(&sl);
215                 count ++;
216                 rte_spinlock_unlock(&sl);
217         }
218
219         return 0;
220 }
221
222
223 /*
224  * Test rte_eal_get_lcore_state() in addition to spinlocks
225  * as we have "waiting" then "running" lcores.
226  */
227 static int
228 test_spinlock(void)
229 {
230         int ret = 0;
231         int i;
232
233         /* slave cores should be waiting: print it */
234         RTE_LCORE_FOREACH_SLAVE(i) {
235                 printf("lcore %d state: %d\n", i,
236                        (int) rte_eal_get_lcore_state(i));
237         }
238
239         rte_spinlock_init(&sl);
240         rte_spinlock_init(&sl_try);
241         rte_spinlock_recursive_init(&slr);
242         for (i=0; i<RTE_MAX_LCORE; i++)
243                 rte_spinlock_init(&sl_tab[i]);
244
245         rte_spinlock_lock(&sl);
246
247         RTE_LCORE_FOREACH_SLAVE(i) {
248                 rte_spinlock_lock(&sl_tab[i]);
249                 rte_eal_remote_launch(test_spinlock_per_core, NULL, i);
250         }
251
252         /* slave cores should be busy: print it */
253         RTE_LCORE_FOREACH_SLAVE(i) {
254                 printf("lcore %d state: %d\n", i,
255                        (int) rte_eal_get_lcore_state(i));
256         }
257         rte_spinlock_unlock(&sl);
258
259         RTE_LCORE_FOREACH_SLAVE(i) {
260                 rte_spinlock_unlock(&sl_tab[i]);
261                 rte_delay_ms(10);
262         }
263
264         rte_eal_mp_wait_lcore();
265
266         rte_spinlock_recursive_lock(&slr);
267
268         /*
269          * Try to acquire a lock that we already own
270          */
271         if(!rte_spinlock_recursive_trylock(&slr)) {
272                 printf("rte_spinlock_recursive_trylock failed on a lock that "
273                        "we already own\n");
274                 ret = -1;
275         } else
276                 rte_spinlock_recursive_unlock(&slr);
277
278         RTE_LCORE_FOREACH_SLAVE(i) {
279                 rte_eal_remote_launch(test_spinlock_recursive_per_core, NULL, i);
280         }
281         rte_spinlock_recursive_unlock(&slr);
282         rte_eal_mp_wait_lcore();
283
284         /*
285          * Test if it could return immediately from try-locking a locked object.
286          * Here it will lock the spinlock object first, then launch all the slave
287          * lcores to trylock the same spinlock object.
288          * All the slave lcores should give up try-locking a locked object and
289          * return immediately, and then increase the "count" initialized with zero
290          * by one per times.
291          * We can check if the "count" is finally equal to the number of all slave
292          * lcores to see if the behavior of try-locking a locked spinlock object
293          * is correct.
294          */
295         if (rte_spinlock_trylock(&sl_try) == 0) {
296                 return -1;
297         }
298         count = 0;
299         RTE_LCORE_FOREACH_SLAVE(i) {
300                 rte_eal_remote_launch(test_spinlock_try, NULL, i);
301         }
302         rte_eal_mp_wait_lcore();
303         rte_spinlock_unlock(&sl_try);
304         if (rte_spinlock_is_locked(&sl)) {
305                 printf("spinlock is locked but it should not be\n");
306                 return -1;
307         }
308         rte_spinlock_lock(&sl);
309         if (count != ( rte_lcore_count() - 1)) {
310                 ret = -1;
311         }
312         rte_spinlock_unlock(&sl);
313
314         /*
315          * Test if it can trylock recursively.
316          * Use rte_spinlock_recursive_trylock() to check if it can lock a spinlock
317          * object recursively. Here it will try to lock a spinlock object twice.
318          */
319         if (rte_spinlock_recursive_trylock(&slr) == 0) {
320                 printf("It failed to do the first spinlock_recursive_trylock but it should able to do\n");
321                 return -1;
322         }
323         if (rte_spinlock_recursive_trylock(&slr) == 0) {
324                 printf("It failed to do the second spinlock_recursive_trylock but it should able to do\n");
325                 return -1;
326         }
327         rte_spinlock_recursive_unlock(&slr);
328         rte_spinlock_recursive_unlock(&slr);
329
330         if (test_spinlock_perf() < 0)
331                 return -1;
332
333         return ret;
334 }
335
336 REGISTER_TEST_COMMAND(spinlock_autotest, test_spinlock);