]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * Copyright(c) 2017 Intel Corporation | |
3 | */ | |
4 | ||
5 | #include <rte_common.h> | |
6 | #include <rte_hexdump.h> | |
7 | #include <rte_mbuf.h> | |
8 | #include <rte_malloc.h> | |
9 | #include <rte_memcpy.h> | |
10 | #include <rte_cycles.h> | |
11 | ||
12 | #include <rte_service.h> | |
13 | #include <rte_service_component.h> | |
14 | ||
15 | #include "test.h" | |
16 | ||
17 | /* used as the service core ID */ | |
18 | static uint32_t slcore_id; | |
19 | /* used as timestamp to detect if a service core is running */ | |
20 | static uint64_t service_tick; | |
21 | /* used as a flag to check if a function was run */ | |
22 | static uint32_t service_remote_launch_flag; | |
23 | ||
24 | #define SERVICE_DELAY 1 | |
25 | ||
26 | #define DUMMY_SERVICE_NAME "dummy_service" | |
27 | #define MT_SAFE_SERVICE_NAME "mt_safe_service" | |
28 | ||
29 | static int | |
30 | testsuite_setup(void) | |
31 | { | |
32 | slcore_id = rte_get_next_lcore(/* start core */ -1, | |
33 | /* skip master */ 1, | |
34 | /* wrap */ 0); | |
35 | ||
36 | return TEST_SUCCESS; | |
37 | } | |
38 | ||
39 | static void | |
40 | testsuite_teardown(void) | |
41 | { | |
42 | /* release service cores? */ | |
43 | } | |
44 | ||
45 | static int32_t dummy_cb(void *args) | |
46 | { | |
47 | RTE_SET_USED(args); | |
48 | service_tick++; | |
49 | rte_delay_ms(SERVICE_DELAY); | |
50 | return 0; | |
51 | } | |
52 | ||
53 | static int32_t dummy_mt_unsafe_cb(void *args) | |
54 | { | |
55 | /* before running test, the initialization has set pass_test to 1. | |
56 | * If the cmpset in service-cores is working correctly, the code here | |
57 | * should never fail to take the lock. If the lock *is* taken, fail the | |
58 | * test, because two threads are concurrently in a non-MT safe callback. | |
59 | */ | |
60 | uint32_t *test_params = args; | |
61 | uint32_t *atomic_lock = &test_params[0]; | |
62 | uint32_t *pass_test = &test_params[1]; | |
63 | int lock_taken = rte_atomic32_cmpset(atomic_lock, 0, 1); | |
64 | if (lock_taken) { | |
65 | /* delay with the lock held */ | |
66 | rte_delay_ms(250); | |
67 | rte_atomic32_clear((rte_atomic32_t *)atomic_lock); | |
68 | } else { | |
69 | /* 2nd thread will fail to take lock, so set pass flag */ | |
70 | *pass_test = 0; | |
71 | } | |
72 | ||
73 | return 0; | |
74 | } | |
75 | ||
76 | ||
77 | static int32_t dummy_mt_safe_cb(void *args) | |
78 | { | |
79 | /* Atomic checks to ensure MT safe services allow > 1 thread to | |
80 | * concurrently run the callback. The concept is as follows; | |
81 | * 1) if lock is available, take the lock then delay | |
82 | * 2) if first lock is taken, and a thread arrives in the CB, we know | |
83 | * that 2 threads are running the callback at the same time: MT safe | |
84 | */ | |
85 | uint32_t *test_params = args; | |
86 | uint32_t *atomic_lock = &test_params[0]; | |
87 | uint32_t *pass_test = &test_params[1]; | |
88 | int lock_taken = rte_atomic32_cmpset(atomic_lock, 0, 1); | |
89 | if (lock_taken) { | |
90 | /* delay with the lock held */ | |
91 | rte_delay_ms(250); | |
92 | rte_atomic32_clear((rte_atomic32_t *)atomic_lock); | |
93 | } else { | |
94 | /* 2nd thread will fail to take lock, so set pass flag */ | |
95 | *pass_test = 1; | |
96 | } | |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
101 | /* unregister all services */ | |
102 | static int | |
103 | unregister_all(void) | |
104 | { | |
105 | uint32_t i; | |
106 | ||
107 | TEST_ASSERT_EQUAL(-EINVAL, rte_service_component_unregister(1000), | |
108 | "Unregistered invalid service id"); | |
109 | ||
110 | uint32_t c = rte_service_get_count(); | |
111 | for (i = 0; i < c; i++) { | |
112 | TEST_ASSERT_EQUAL(0, rte_service_component_unregister(i), | |
113 | "Error unregistering a valid service"); | |
114 | } | |
115 | ||
116 | rte_service_lcore_reset_all(); | |
117 | ||
118 | return TEST_SUCCESS; | |
119 | } | |
120 | ||
121 | /* register a single dummy service */ | |
122 | static int | |
123 | dummy_register(void) | |
124 | { | |
125 | /* make sure there are no remains from previous tests */ | |
126 | unregister_all(); | |
127 | ||
128 | struct rte_service_spec service; | |
129 | memset(&service, 0, sizeof(struct rte_service_spec)); | |
130 | ||
131 | TEST_ASSERT_EQUAL(-EINVAL, | |
132 | rte_service_component_register(&service, NULL), | |
133 | "Invalid callback"); | |
134 | service.callback = dummy_cb; | |
135 | ||
136 | TEST_ASSERT_EQUAL(-EINVAL, | |
137 | rte_service_component_register(&service, NULL), | |
138 | "Invalid name"); | |
139 | snprintf(service.name, sizeof(service.name), DUMMY_SERVICE_NAME); | |
140 | ||
141 | uint32_t id; | |
142 | TEST_ASSERT_EQUAL(0, rte_service_component_register(&service, &id), | |
143 | "Failed to register valid service"); | |
144 | ||
145 | rte_service_component_runstate_set(id, 1); | |
146 | ||
147 | return TEST_SUCCESS; | |
148 | } | |
149 | ||
150 | /* verify get_by_name() service lookup */ | |
151 | static int | |
152 | service_get_by_name(void) | |
153 | { | |
154 | unregister_all(); | |
155 | ||
156 | uint32_t sid; | |
157 | TEST_ASSERT_EQUAL(-ENODEV, | |
158 | rte_service_get_by_name(DUMMY_SERVICE_NAME, &sid), | |
159 | "get by name with invalid name should return -ENODEV"); | |
160 | TEST_ASSERT_EQUAL(-EINVAL, | |
161 | rte_service_get_by_name(DUMMY_SERVICE_NAME, 0x0), | |
162 | "get by name with NULL ptr should return -ENODEV"); | |
163 | ||
164 | /* register service */ | |
165 | struct rte_service_spec service; | |
166 | memset(&service, 0, sizeof(struct rte_service_spec)); | |
167 | TEST_ASSERT_EQUAL(-EINVAL, | |
168 | rte_service_component_register(&service, NULL), | |
169 | "Invalid callback"); | |
170 | service.callback = dummy_cb; | |
171 | TEST_ASSERT_EQUAL(-EINVAL, | |
172 | rte_service_component_register(&service, NULL), | |
173 | "Invalid name"); | |
174 | snprintf(service.name, sizeof(service.name), DUMMY_SERVICE_NAME); | |
175 | TEST_ASSERT_EQUAL(0, rte_service_component_register(&service, NULL), | |
176 | "Failed to register valid service"); | |
177 | ||
178 | /* we unregistered all service, now registering 1, should be id 0 */ | |
179 | uint32_t service_id_as_expected = 0; | |
180 | TEST_ASSERT_EQUAL(0, rte_service_get_by_name(DUMMY_SERVICE_NAME, &sid), | |
181 | "Service get_by_name should return 0 on valid inputs"); | |
182 | TEST_ASSERT_EQUAL(service_id_as_expected, sid, | |
183 | "Service get_by_name should equal expected id"); | |
184 | ||
185 | unregister_all(); | |
186 | ||
187 | /* ensure after unregister, get_by_name returns NULL */ | |
188 | TEST_ASSERT_EQUAL(-ENODEV, | |
189 | rte_service_get_by_name(DUMMY_SERVICE_NAME, &sid), | |
190 | "get by name should return -ENODEV after unregister"); | |
191 | ||
192 | return TEST_SUCCESS; | |
193 | } | |
194 | ||
195 | /* verify probe of capabilities */ | |
196 | static int | |
197 | service_probe_capability(void) | |
198 | { | |
199 | unregister_all(); | |
200 | ||
201 | struct rte_service_spec service; | |
202 | memset(&service, 0, sizeof(struct rte_service_spec)); | |
203 | service.callback = dummy_cb; | |
204 | snprintf(service.name, sizeof(service.name), DUMMY_SERVICE_NAME); | |
205 | service.capabilities |= RTE_SERVICE_CAP_MT_SAFE; | |
206 | TEST_ASSERT_EQUAL(0, rte_service_component_register(&service, NULL), | |
207 | "Register of MT SAFE service failed"); | |
208 | ||
209 | /* verify flag is enabled */ | |
210 | const uint32_t sid = 0; | |
211 | int32_t mt = rte_service_probe_capability(sid, RTE_SERVICE_CAP_MT_SAFE); | |
212 | TEST_ASSERT_EQUAL(1, mt, "MT SAFE capability flag not set."); | |
213 | ||
214 | ||
215 | unregister_all(); | |
216 | ||
217 | memset(&service, 0, sizeof(struct rte_service_spec)); | |
218 | service.callback = dummy_cb; | |
219 | snprintf(service.name, sizeof(service.name), DUMMY_SERVICE_NAME); | |
220 | TEST_ASSERT_EQUAL(0, rte_service_component_register(&service, NULL), | |
221 | "Register of non-MT safe service failed"); | |
222 | ||
223 | /* verify flag is enabled */ | |
224 | mt = rte_service_probe_capability(sid, RTE_SERVICE_CAP_MT_SAFE); | |
225 | TEST_ASSERT_EQUAL(0, mt, "MT SAFE cap flag set on non MT SAFE service"); | |
226 | ||
227 | return unregister_all(); | |
228 | } | |
229 | ||
230 | /* verify the service name */ | |
231 | static int | |
232 | service_name(void) | |
233 | { | |
234 | const char *name = rte_service_get_name(0); | |
235 | int equal = strcmp(name, DUMMY_SERVICE_NAME); | |
236 | TEST_ASSERT_EQUAL(0, equal, "Error: Service name not correct"); | |
237 | ||
238 | return unregister_all(); | |
239 | } | |
240 | ||
241 | /* verify service attr get */ | |
242 | static int | |
243 | service_attr_get(void) | |
244 | { | |
245 | /* ensure all services unregistered so cycle counts are zero */ | |
246 | unregister_all(); | |
247 | ||
248 | struct rte_service_spec service; | |
249 | memset(&service, 0, sizeof(struct rte_service_spec)); | |
250 | service.callback = dummy_cb; | |
251 | snprintf(service.name, sizeof(service.name), DUMMY_SERVICE_NAME); | |
252 | service.capabilities |= RTE_SERVICE_CAP_MT_SAFE; | |
253 | uint32_t id; | |
254 | TEST_ASSERT_EQUAL(0, rte_service_component_register(&service, &id), | |
255 | "Register of service failed"); | |
256 | rte_service_component_runstate_set(id, 1); | |
257 | TEST_ASSERT_EQUAL(0, rte_service_runstate_set(id, 1), | |
258 | "Error: Service start returned non-zero"); | |
259 | rte_service_set_stats_enable(id, 1); | |
260 | ||
261 | uint32_t attr_id = UINT32_MAX; | |
9f95a23c | 262 | uint64_t attr_value = 0xdead; |
11fdf7f2 TL |
263 | /* check error return values */ |
264 | TEST_ASSERT_EQUAL(-EINVAL, rte_service_attr_get(id, attr_id, | |
265 | &attr_value), | |
266 | "Invalid attr_id didn't return -EINVAL"); | |
267 | ||
268 | attr_id = RTE_SERVICE_ATTR_CYCLES; | |
269 | TEST_ASSERT_EQUAL(-EINVAL, rte_service_attr_get(UINT32_MAX, attr_id, | |
270 | &attr_value), | |
271 | "Invalid service id didn't return -EINVAL"); | |
272 | ||
273 | TEST_ASSERT_EQUAL(-EINVAL, rte_service_attr_get(id, attr_id, NULL), | |
274 | "Invalid attr_value pointer id didn't return -EINVAL"); | |
275 | ||
276 | /* check correct (zero) return value and correct value (zero) */ | |
277 | TEST_ASSERT_EQUAL(0, rte_service_attr_get(id, attr_id, &attr_value), | |
278 | "Valid attr_get() call didn't return success"); | |
279 | TEST_ASSERT_EQUAL(0, attr_value, | |
280 | "attr_get() call didn't set correct cycles (zero)"); | |
281 | /* check correct call count */ | |
282 | const int attr_calls = RTE_SERVICE_ATTR_CALL_COUNT; | |
283 | TEST_ASSERT_EQUAL(0, rte_service_attr_get(id, attr_calls, &attr_value), | |
284 | "Valid attr_get() call didn't return success"); | |
285 | TEST_ASSERT_EQUAL(0, attr_value, | |
286 | "attr_get() call didn't get call count (zero)"); | |
287 | ||
288 | /* Call service to increment cycle count */ | |
289 | TEST_ASSERT_EQUAL(0, rte_service_lcore_add(slcore_id), | |
290 | "Service core add did not return zero"); | |
291 | TEST_ASSERT_EQUAL(0, rte_service_map_lcore_set(id, slcore_id, 1), | |
292 | "Enabling valid service and core failed"); | |
293 | TEST_ASSERT_EQUAL(0, rte_service_lcore_start(slcore_id), | |
294 | "Starting service core failed"); | |
295 | ||
296 | /* wait for the service lcore to run */ | |
297 | rte_delay_ms(200); | |
298 | ||
299 | TEST_ASSERT_EQUAL(0, rte_service_attr_get(id, attr_id, &attr_value), | |
300 | "Valid attr_get() call didn't return success"); | |
301 | int cycles_gt_zero = attr_value > 0; | |
302 | TEST_ASSERT_EQUAL(1, cycles_gt_zero, | |
303 | "attr_get() failed to get cycles (expected > zero)"); | |
304 | ||
305 | rte_service_lcore_stop(slcore_id); | |
306 | ||
307 | TEST_ASSERT_EQUAL(0, rte_service_attr_get(id, attr_calls, &attr_value), | |
308 | "Valid attr_get() call didn't return success"); | |
309 | TEST_ASSERT_EQUAL(1, (attr_value > 0), | |
310 | "attr_get() call didn't get call count (zero)"); | |
311 | ||
312 | TEST_ASSERT_EQUAL(0, rte_service_attr_reset_all(id), | |
313 | "Valid attr_reset_all() return success"); | |
314 | ||
315 | TEST_ASSERT_EQUAL(0, rte_service_attr_get(id, attr_id, &attr_value), | |
316 | "Valid attr_get() call didn't return success"); | |
317 | TEST_ASSERT_EQUAL(0, attr_value, | |
318 | "attr_get() call didn't set correct cycles (zero)"); | |
319 | /* ensure call count > zero */ | |
320 | TEST_ASSERT_EQUAL(0, rte_service_attr_get(id, attr_calls, &attr_value), | |
321 | "Valid attr_get() call didn't return success"); | |
322 | TEST_ASSERT_EQUAL(0, (attr_value > 0), | |
323 | "attr_get() call didn't get call count (zero)"); | |
324 | ||
325 | return unregister_all(); | |
326 | } | |
327 | ||
328 | /* verify service lcore attr get */ | |
329 | static int | |
330 | service_lcore_attr_get(void) | |
331 | { | |
332 | /* ensure all services unregistered so cycle counts are zero */ | |
333 | unregister_all(); | |
334 | ||
335 | struct rte_service_spec service; | |
336 | memset(&service, 0, sizeof(struct rte_service_spec)); | |
337 | service.callback = dummy_cb; | |
338 | snprintf(service.name, sizeof(service.name), DUMMY_SERVICE_NAME); | |
339 | service.capabilities |= RTE_SERVICE_CAP_MT_SAFE; | |
340 | uint32_t id; | |
341 | TEST_ASSERT_EQUAL(0, rte_service_component_register(&service, &id), | |
342 | "Register of service failed"); | |
343 | rte_service_component_runstate_set(id, 1); | |
344 | TEST_ASSERT_EQUAL(0, rte_service_runstate_set(id, 1), | |
345 | "Error: Service start returned non-zero"); | |
346 | rte_service_set_stats_enable(id, 1); | |
347 | ||
348 | uint64_t lcore_attr_value = 0xdead; | |
349 | uint32_t lcore_attr_id = UINT32_MAX; | |
350 | ||
351 | /* check error return values */ | |
352 | TEST_ASSERT_EQUAL(-EINVAL, rte_service_lcore_attr_get(UINT32_MAX, | |
353 | lcore_attr_id, &lcore_attr_value), | |
354 | "Invalid lcore_id didn't return -EINVAL"); | |
355 | TEST_ASSERT_EQUAL(-ENOTSUP, rte_service_lcore_attr_get(rte_lcore_id(), | |
356 | lcore_attr_id, &lcore_attr_value), | |
357 | "Non-service core didn't return -ENOTSUP"); | |
358 | ||
359 | /* Start service core to increment loop count */ | |
360 | TEST_ASSERT_EQUAL(0, rte_service_lcore_add(slcore_id), | |
361 | "Service core add did not return zero"); | |
362 | TEST_ASSERT_EQUAL(0, rte_service_map_lcore_set(id, slcore_id, 1), | |
363 | "Enabling valid service and core failed"); | |
364 | TEST_ASSERT_EQUAL(0, rte_service_lcore_start(slcore_id), | |
365 | "Starting service core failed"); | |
366 | ||
367 | /* wait for the service lcore to run */ | |
368 | rte_delay_ms(200); | |
369 | ||
370 | lcore_attr_id = RTE_SERVICE_LCORE_ATTR_LOOPS; | |
371 | TEST_ASSERT_EQUAL(0, rte_service_lcore_attr_get(slcore_id, | |
372 | lcore_attr_id, &lcore_attr_value), | |
373 | "Valid lcore_attr_get() call didn't return success"); | |
374 | int loops_gt_zero = lcore_attr_value > 0; | |
375 | TEST_ASSERT_EQUAL(1, loops_gt_zero, | |
376 | "lcore_attr_get() failed to get loops " | |
377 | "(expected > zero)"); | |
378 | ||
379 | lcore_attr_id++; // invalid lcore attr id | |
380 | TEST_ASSERT_EQUAL(-EINVAL, rte_service_lcore_attr_get(slcore_id, | |
381 | lcore_attr_id, &lcore_attr_value), | |
382 | "Invalid lcore attr didn't return -EINVAL"); | |
383 | ||
384 | rte_service_lcore_stop(slcore_id); | |
385 | ||
386 | TEST_ASSERT_EQUAL(0, rte_service_lcore_attr_reset_all(slcore_id), | |
387 | "Valid lcore_attr_reset_all() didn't return success"); | |
388 | ||
389 | lcore_attr_id = RTE_SERVICE_LCORE_ATTR_LOOPS; | |
390 | TEST_ASSERT_EQUAL(0, rte_service_lcore_attr_get(slcore_id, | |
391 | lcore_attr_id, &lcore_attr_value), | |
392 | "Valid lcore_attr_get() call didn't return success"); | |
393 | TEST_ASSERT_EQUAL(0, lcore_attr_value, | |
394 | "lcore_attr_get() didn't get correct loop count " | |
395 | "(zero)"); | |
396 | ||
397 | return unregister_all(); | |
398 | } | |
399 | ||
400 | /* verify service dump */ | |
401 | static int | |
402 | service_dump(void) | |
403 | { | |
404 | const uint32_t sid = 0; | |
405 | rte_service_set_stats_enable(sid, 1); | |
406 | rte_service_dump(stdout, 0); | |
407 | rte_service_set_stats_enable(sid, 0); | |
408 | rte_service_dump(stdout, 0); | |
409 | return unregister_all(); | |
410 | } | |
411 | ||
412 | /* start and stop a service */ | |
413 | static int | |
414 | service_start_stop(void) | |
415 | { | |
416 | const uint32_t sid = 0; | |
417 | ||
418 | /* runstate_get() returns if service is running and slcore is mapped */ | |
419 | TEST_ASSERT_EQUAL(0, rte_service_lcore_add(slcore_id), | |
420 | "Service core add did not return zero"); | |
421 | int ret = rte_service_map_lcore_set(sid, slcore_id, 1); | |
422 | TEST_ASSERT_EQUAL(0, ret, | |
423 | "Enabling service core, expected 0 got %d", ret); | |
424 | ||
425 | TEST_ASSERT_EQUAL(0, rte_service_runstate_get(sid), | |
426 | "Error: Service should be stopped"); | |
427 | ||
428 | TEST_ASSERT_EQUAL(0, rte_service_runstate_set(sid, 0), | |
429 | "Error: Service stopped returned non-zero"); | |
430 | ||
431 | TEST_ASSERT_EQUAL(0, rte_service_runstate_get(sid), | |
432 | "Error: Service is running - should be stopped"); | |
433 | ||
434 | TEST_ASSERT_EQUAL(0, rte_service_runstate_set(sid, 1), | |
435 | "Error: Service start returned non-zero"); | |
436 | ||
437 | TEST_ASSERT_EQUAL(1, rte_service_runstate_get(sid), | |
438 | "Error: Service is not running"); | |
439 | ||
440 | return unregister_all(); | |
441 | } | |
442 | ||
443 | ||
444 | static int | |
445 | service_remote_launch_func(void *arg) | |
446 | { | |
447 | RTE_SET_USED(arg); | |
448 | service_remote_launch_flag = 1; | |
449 | return 0; | |
450 | } | |
451 | ||
452 | /* enable and disable a lcore for a service */ | |
453 | static int | |
454 | service_lcore_en_dis_able(void) | |
455 | { | |
456 | const uint32_t sid = 0; | |
457 | ||
458 | /* expected failure cases */ | |
459 | TEST_ASSERT_EQUAL(-EINVAL, rte_service_map_lcore_set(sid, 100000, 1), | |
460 | "Enable on invalid core did not fail"); | |
461 | TEST_ASSERT_EQUAL(-EINVAL, rte_service_map_lcore_set(sid, 100000, 0), | |
462 | "Disable on invalid core did not fail"); | |
463 | ||
464 | /* add service core to allow enabling */ | |
465 | TEST_ASSERT_EQUAL(0, rte_service_lcore_add(slcore_id), | |
466 | "Add service core failed when not in use before"); | |
467 | ||
468 | /* valid enable */ | |
469 | TEST_ASSERT_EQUAL(0, rte_service_map_lcore_set(sid, slcore_id, 1), | |
470 | "Enabling valid service and core failed"); | |
471 | TEST_ASSERT_EQUAL(1, rte_service_map_lcore_get(sid, slcore_id), | |
472 | "Enabled core returned not-enabled"); | |
473 | ||
474 | /* valid disable */ | |
475 | TEST_ASSERT_EQUAL(0, rte_service_map_lcore_set(sid, slcore_id, 0), | |
476 | "Disabling valid service and lcore failed"); | |
477 | TEST_ASSERT_EQUAL(0, rte_service_map_lcore_get(sid, slcore_id), | |
478 | "Disabled core returned enabled"); | |
479 | ||
480 | /* call remote_launch to verify that app can launch ex-service lcore */ | |
481 | service_remote_launch_flag = 0; | |
482 | rte_eal_wait_lcore(slcore_id); | |
483 | int ret = rte_eal_remote_launch(service_remote_launch_func, NULL, | |
484 | slcore_id); | |
485 | TEST_ASSERT_EQUAL(0, ret, "Ex-service core remote launch failed."); | |
f67539c2 | 486 | rte_eal_wait_lcore(slcore_id); |
11fdf7f2 TL |
487 | TEST_ASSERT_EQUAL(1, service_remote_launch_flag, |
488 | "Ex-service core function call had no effect."); | |
489 | ||
490 | return unregister_all(); | |
491 | } | |
492 | ||
493 | static int | |
494 | service_lcore_running_check(void) | |
495 | { | |
496 | uint64_t tick = service_tick; | |
497 | rte_delay_ms(SERVICE_DELAY * 100); | |
498 | /* if (tick != service_tick) we know the lcore as polled the service */ | |
499 | return tick != service_tick; | |
500 | } | |
501 | ||
502 | static int | |
503 | service_lcore_add_del(void) | |
504 | { | |
f67539c2 TL |
505 | if (!rte_lcore_is_enabled(0) || !rte_lcore_is_enabled(1) || |
506 | !rte_lcore_is_enabled(2) || !rte_lcore_is_enabled(3)) | |
507 | return TEST_SKIPPED; | |
508 | ||
11fdf7f2 TL |
509 | /* check initial count */ |
510 | TEST_ASSERT_EQUAL(0, rte_service_lcore_count(), | |
511 | "Service lcore count has value before adding a lcore"); | |
512 | ||
513 | /* check service lcore add */ | |
514 | TEST_ASSERT_EQUAL(0, rte_service_lcore_add(slcore_id), | |
515 | "Add service core failed when not in use before"); | |
516 | TEST_ASSERT_EQUAL(-EALREADY, rte_service_lcore_add(slcore_id), | |
517 | "Add service core failed to refuse in-use lcore"); | |
518 | ||
519 | /* check count */ | |
520 | TEST_ASSERT_EQUAL(1, rte_service_lcore_count(), | |
521 | "Service core count not equal to one"); | |
522 | ||
523 | /* retrieve core list, checking lcore ids */ | |
524 | const uint32_t size = 4; | |
525 | uint32_t service_core_ids[size]; | |
526 | int32_t n = rte_service_lcore_list(service_core_ids, size); | |
527 | TEST_ASSERT_EQUAL(1, n, "Service core list return should equal 1"); | |
528 | TEST_ASSERT_EQUAL(slcore_id, service_core_ids[0], | |
529 | "Service core list lcore must equal slcore_id"); | |
530 | ||
531 | /* recheck count, add more cores, and check count */ | |
532 | TEST_ASSERT_EQUAL(1, rte_service_lcore_count(), | |
533 | "Service core count not equal to one"); | |
534 | uint32_t slcore_1 = rte_get_next_lcore(/* start core */ -1, | |
535 | /* skip master */ 1, | |
536 | /* wrap */ 0); | |
537 | TEST_ASSERT_EQUAL(0, rte_service_lcore_add(slcore_1), | |
538 | "Service core add did not return zero"); | |
539 | uint32_t slcore_2 = rte_get_next_lcore(/* start core */ slcore_1, | |
540 | /* skip master */ 1, | |
541 | /* wrap */ 0); | |
542 | TEST_ASSERT_EQUAL(0, rte_service_lcore_add(slcore_2), | |
543 | "Service core add did not return zero"); | |
544 | ||
545 | uint32_t count = rte_service_lcore_count(); | |
546 | const uint32_t cores_at_this_point = 3; | |
547 | TEST_ASSERT_EQUAL(cores_at_this_point, count, | |
548 | "Service core count %d, expected %d", count, | |
549 | cores_at_this_point); | |
550 | ||
551 | /* check longer service core list */ | |
552 | n = rte_service_lcore_list(service_core_ids, size); | |
553 | TEST_ASSERT_EQUAL(3, n, "Service core list return should equal 3"); | |
554 | TEST_ASSERT_EQUAL(slcore_id, service_core_ids[0], | |
555 | "Service core list[0] lcore must equal 1"); | |
556 | TEST_ASSERT_EQUAL(slcore_1, service_core_ids[1], | |
557 | "Service core list[1] lcore must equal 2"); | |
558 | TEST_ASSERT_EQUAL(slcore_2, service_core_ids[2], | |
559 | "Service core list[2] lcore must equal 3"); | |
560 | ||
561 | /* recheck count, remove lcores, check remaining lcore_id is correct */ | |
562 | TEST_ASSERT_EQUAL(3, rte_service_lcore_count(), | |
563 | "Service core count not equal to three"); | |
564 | TEST_ASSERT_EQUAL(0, rte_service_lcore_del(slcore_1), | |
565 | "Service core add did not return zero"); | |
566 | TEST_ASSERT_EQUAL(0, rte_service_lcore_del(slcore_2), | |
567 | "Service core add did not return zero"); | |
568 | TEST_ASSERT_EQUAL(1, rte_service_lcore_count(), | |
569 | "Service core count not equal to one"); | |
570 | n = rte_service_lcore_list(service_core_ids, size); | |
571 | TEST_ASSERT_EQUAL(1, n, "Service core list return should equal one"); | |
572 | TEST_ASSERT_EQUAL(slcore_id, service_core_ids[0], | |
573 | "Service core list[0] lcore must equal %d", | |
574 | slcore_id); | |
575 | ||
576 | return unregister_all(); | |
577 | } | |
578 | ||
579 | static int | |
580 | service_threaded_test(int mt_safe) | |
581 | { | |
582 | unregister_all(); | |
583 | ||
584 | /* add next 2 cores */ | |
585 | uint32_t slcore_1 = rte_get_next_lcore(/* start core */ -1, | |
586 | /* skip master */ 1, | |
587 | /* wrap */ 0); | |
588 | TEST_ASSERT_EQUAL(0, rte_service_lcore_add(slcore_1), | |
589 | "mt safe lcore add fail"); | |
590 | uint32_t slcore_2 = rte_get_next_lcore(/* start core */ slcore_1, | |
591 | /* skip master */ 1, | |
592 | /* wrap */ 0); | |
593 | TEST_ASSERT_EQUAL(0, rte_service_lcore_add(slcore_2), | |
594 | "mt safe lcore add fail"); | |
595 | ||
596 | /* Use atomic locks to verify that two threads are in the same function | |
597 | * at the same time. These are passed to the unit tests through the | |
598 | * callback userdata parameter | |
599 | */ | |
600 | uint32_t test_params[2]; | |
601 | memset(test_params, 0, sizeof(uint32_t) * 2); | |
602 | ||
603 | /* register MT safe service. */ | |
604 | struct rte_service_spec service; | |
605 | memset(&service, 0, sizeof(struct rte_service_spec)); | |
606 | service.callback_userdata = test_params; | |
607 | snprintf(service.name, sizeof(service.name), MT_SAFE_SERVICE_NAME); | |
608 | ||
609 | if (mt_safe) { | |
610 | service.callback = dummy_mt_safe_cb; | |
611 | service.capabilities |= RTE_SERVICE_CAP_MT_SAFE; | |
612 | } else | |
613 | service.callback = dummy_mt_unsafe_cb; | |
614 | ||
615 | uint32_t id; | |
616 | TEST_ASSERT_EQUAL(0, rte_service_component_register(&service, &id), | |
617 | "Register of MT SAFE service failed"); | |
618 | ||
619 | const uint32_t sid = 0; | |
620 | TEST_ASSERT_EQUAL(0, rte_service_runstate_set(sid, 1), | |
621 | "Starting valid service failed"); | |
622 | TEST_ASSERT_EQUAL(0, rte_service_map_lcore_set(sid, slcore_1, 1), | |
623 | "Failed to enable lcore 1 on mt safe service"); | |
624 | TEST_ASSERT_EQUAL(0, rte_service_map_lcore_set(sid, slcore_2, 1), | |
625 | "Failed to enable lcore 2 on mt safe service"); | |
626 | rte_service_lcore_start(slcore_1); | |
627 | rte_service_lcore_start(slcore_2); | |
628 | ||
629 | /* wait for the worker threads to run */ | |
630 | rte_delay_ms(500); | |
631 | rte_service_lcore_stop(slcore_1); | |
632 | rte_service_lcore_stop(slcore_2); | |
633 | ||
634 | TEST_ASSERT_EQUAL(0, test_params[1], | |
635 | "Service run with component runstate = 0"); | |
636 | ||
637 | /* enable backend runstate: the service should run after this */ | |
638 | rte_service_component_runstate_set(id, 1); | |
639 | ||
640 | /* initialize to pass, see callback comment for details */ | |
641 | if (!mt_safe) | |
642 | test_params[1] = 1; | |
643 | ||
644 | /* wait for lcores before start() */ | |
645 | rte_eal_wait_lcore(slcore_1); | |
646 | rte_eal_wait_lcore(slcore_2); | |
647 | ||
648 | rte_service_lcore_start(slcore_1); | |
649 | rte_service_lcore_start(slcore_2); | |
650 | ||
651 | /* wait for the worker threads to run */ | |
652 | rte_delay_ms(500); | |
653 | rte_service_lcore_stop(slcore_1); | |
654 | rte_service_lcore_stop(slcore_2); | |
655 | ||
656 | TEST_ASSERT_EQUAL(1, test_params[1], | |
657 | "MT Safe service not run by two cores concurrently"); | |
658 | TEST_ASSERT_EQUAL(0, rte_service_runstate_set(sid, 0), | |
659 | "Failed to stop MT Safe service"); | |
660 | ||
661 | rte_eal_wait_lcore(slcore_1); | |
662 | rte_eal_wait_lcore(slcore_2); | |
663 | unregister_all(); | |
664 | ||
665 | /* return the value of the callback pass_test variable to caller */ | |
666 | return test_params[1]; | |
667 | } | |
668 | ||
669 | /* tests an MT SAFE service with two cores. The callback function ensures that | |
670 | * two threads access the callback concurrently. | |
671 | */ | |
672 | static int | |
673 | service_mt_safe_poll(void) | |
674 | { | |
675 | int mt_safe = 1; | |
f67539c2 TL |
676 | |
677 | if (!rte_lcore_is_enabled(0) || !rte_lcore_is_enabled(1) || | |
678 | !rte_lcore_is_enabled(2)) | |
679 | return TEST_SKIPPED; | |
680 | ||
11fdf7f2 TL |
681 | TEST_ASSERT_EQUAL(1, service_threaded_test(mt_safe), |
682 | "Error: MT Safe service not run by two cores concurrently"); | |
683 | return TEST_SUCCESS; | |
684 | } | |
685 | ||
686 | /* tests a NON mt safe service with two cores, the callback is serialized | |
687 | * using the atomic cmpset. | |
688 | */ | |
689 | static int | |
690 | service_mt_unsafe_poll(void) | |
691 | { | |
692 | int mt_safe = 0; | |
f67539c2 TL |
693 | |
694 | if (!rte_lcore_is_enabled(0) || !rte_lcore_is_enabled(1) || | |
695 | !rte_lcore_is_enabled(2)) | |
696 | return TEST_SKIPPED; | |
697 | ||
11fdf7f2 TL |
698 | TEST_ASSERT_EQUAL(1, service_threaded_test(mt_safe), |
699 | "Error: NON MT Safe service run by two cores concurrently"); | |
700 | return TEST_SUCCESS; | |
701 | } | |
702 | ||
703 | static int32_t | |
704 | delay_as_a_mt_safe_service(void *args) | |
705 | { | |
706 | RTE_SET_USED(args); | |
707 | uint32_t *params = args; | |
708 | ||
709 | /* retrieve done flag and atomic lock to inc/dec */ | |
710 | uint32_t *done = ¶ms[0]; | |
711 | rte_atomic32_t *lock = (rte_atomic32_t *)¶ms[1]; | |
712 | ||
713 | while (!*done) { | |
714 | rte_atomic32_inc(lock); | |
715 | rte_delay_us(500); | |
716 | if (rte_atomic32_read(lock) > 1) | |
717 | /* pass: second core has simultaneously incremented */ | |
718 | *done = 1; | |
719 | rte_atomic32_dec(lock); | |
720 | } | |
721 | ||
722 | return 0; | |
723 | } | |
724 | ||
725 | static int32_t | |
726 | delay_as_a_service(void *args) | |
727 | { | |
728 | uint32_t *done = (uint32_t *)args; | |
729 | while (!*done) | |
730 | rte_delay_ms(5); | |
731 | return 0; | |
732 | } | |
733 | ||
734 | static int | |
735 | service_run_on_app_core_func(void *arg) | |
736 | { | |
737 | uint32_t *delay_service_id = (uint32_t *)arg; | |
738 | return rte_service_run_iter_on_app_lcore(*delay_service_id, 1); | |
739 | } | |
740 | ||
741 | static int | |
742 | service_app_lcore_poll_impl(const int mt_safe) | |
743 | { | |
744 | uint32_t params[2] = {0}; | |
745 | ||
746 | struct rte_service_spec service; | |
747 | memset(&service, 0, sizeof(struct rte_service_spec)); | |
748 | snprintf(service.name, sizeof(service.name), MT_SAFE_SERVICE_NAME); | |
749 | if (mt_safe) { | |
750 | service.callback = delay_as_a_mt_safe_service; | |
751 | service.callback_userdata = params; | |
752 | service.capabilities |= RTE_SERVICE_CAP_MT_SAFE; | |
753 | } else { | |
754 | service.callback = delay_as_a_service; | |
755 | service.callback_userdata = ¶ms; | |
756 | } | |
757 | ||
758 | uint32_t id; | |
759 | TEST_ASSERT_EQUAL(0, rte_service_component_register(&service, &id), | |
760 | "Register of app lcore delay service failed"); | |
761 | ||
762 | rte_service_component_runstate_set(id, 1); | |
763 | rte_service_runstate_set(id, 1); | |
764 | ||
765 | uint32_t app_core2 = rte_get_next_lcore(slcore_id, 1, 1); | |
766 | rte_eal_wait_lcore(app_core2); | |
767 | int app_core2_ret = rte_eal_remote_launch(service_run_on_app_core_func, | |
768 | &id, app_core2); | |
769 | ||
770 | rte_delay_ms(100); | |
771 | ||
772 | int app_core1_ret = service_run_on_app_core_func(&id); | |
773 | ||
774 | /* flag done, then wait for the spawned 2nd core to return */ | |
775 | params[0] = 1; | |
776 | rte_eal_mp_wait_lcore(); | |
777 | ||
778 | /* core two gets launched first - and should hold the service lock */ | |
779 | TEST_ASSERT_EQUAL(0, app_core2_ret, | |
780 | "App core2 : run service didn't return zero"); | |
781 | ||
782 | if (mt_safe) { | |
783 | /* mt safe should have both cores return 0 for success */ | |
784 | TEST_ASSERT_EQUAL(0, app_core1_ret, | |
785 | "MT Safe: App core1 didn't return 0"); | |
786 | } else { | |
787 | /* core one attempts to run later - should be blocked */ | |
788 | TEST_ASSERT_EQUAL(-EBUSY, app_core1_ret, | |
789 | "MT Unsafe: App core1 didn't return -EBUSY"); | |
790 | } | |
791 | ||
f67539c2 TL |
792 | /* Performance test: call in a loop, and measure tsc() */ |
793 | const uint32_t perf_iters = (1 << 12); | |
794 | uint64_t start = rte_rdtsc(); | |
795 | uint32_t i; | |
796 | for (i = 0; i < perf_iters; i++) { | |
797 | int err = service_run_on_app_core_func(&id); | |
798 | TEST_ASSERT_EQUAL(0, err, "perf test: returned run failure"); | |
799 | } | |
800 | uint64_t end = rte_rdtsc(); | |
801 | printf("perf test for %s: %0.1f cycles per call\n", mt_safe ? | |
802 | "MT Safe" : "MT Unsafe", (end - start)/(float)perf_iters); | |
11fdf7f2 | 803 | |
f67539c2 | 804 | unregister_all(); |
11fdf7f2 TL |
805 | return TEST_SUCCESS; |
806 | } | |
807 | ||
808 | static int | |
809 | service_app_lcore_mt_safe(void) | |
810 | { | |
811 | const int mt_safe = 1; | |
812 | return service_app_lcore_poll_impl(mt_safe); | |
813 | } | |
814 | ||
815 | static int | |
816 | service_app_lcore_mt_unsafe(void) | |
817 | { | |
818 | const int mt_safe = 0; | |
819 | return service_app_lcore_poll_impl(mt_safe); | |
820 | } | |
821 | ||
822 | /* start and stop a service core - ensuring it goes back to sleep */ | |
823 | static int | |
824 | service_lcore_start_stop(void) | |
825 | { | |
826 | /* start service core and service, create mapping so tick() runs */ | |
827 | const uint32_t sid = 0; | |
828 | TEST_ASSERT_EQUAL(0, rte_service_runstate_set(sid, 1), | |
829 | "Starting valid service failed"); | |
830 | TEST_ASSERT_EQUAL(-EINVAL, rte_service_map_lcore_set(sid, slcore_id, 1), | |
831 | "Enabling valid service on non-service core must fail"); | |
832 | ||
833 | /* core start */ | |
834 | TEST_ASSERT_EQUAL(-EINVAL, rte_service_lcore_start(slcore_id), | |
835 | "Service core start without add should return EINVAL"); | |
836 | TEST_ASSERT_EQUAL(0, rte_service_lcore_add(slcore_id), | |
837 | "Service core add did not return zero"); | |
838 | TEST_ASSERT_EQUAL(0, rte_service_map_lcore_set(sid, slcore_id, 1), | |
839 | "Enabling valid service on valid core failed"); | |
840 | TEST_ASSERT_EQUAL(0, rte_service_lcore_start(slcore_id), | |
841 | "Service core start after add failed"); | |
842 | TEST_ASSERT_EQUAL(-EALREADY, rte_service_lcore_start(slcore_id), | |
843 | "Service core expected as running but was stopped"); | |
844 | ||
845 | /* ensures core really is running the service function */ | |
846 | TEST_ASSERT_EQUAL(1, service_lcore_running_check(), | |
847 | "Service core expected to poll service but it didn't"); | |
848 | ||
849 | /* core stop */ | |
850 | TEST_ASSERT_EQUAL(-EBUSY, rte_service_lcore_stop(slcore_id), | |
851 | "Service core running a service should return -EBUSY"); | |
852 | TEST_ASSERT_EQUAL(0, rte_service_runstate_set(sid, 0), | |
853 | "Stopping valid service failed"); | |
854 | TEST_ASSERT_EQUAL(-EINVAL, rte_service_lcore_stop(100000), | |
855 | "Invalid Service core stop should return -EINVAL"); | |
856 | TEST_ASSERT_EQUAL(0, rte_service_lcore_stop(slcore_id), | |
857 | "Service core stop expected to return 0"); | |
858 | TEST_ASSERT_EQUAL(-EALREADY, rte_service_lcore_stop(slcore_id), | |
859 | "Already stopped service core should return -EALREADY"); | |
860 | ||
861 | /* ensure service is not longer running */ | |
862 | TEST_ASSERT_EQUAL(0, service_lcore_running_check(), | |
863 | "Service core expected to poll service but it didn't"); | |
864 | ||
865 | TEST_ASSERT_EQUAL(0, rte_service_lcore_del(slcore_id), | |
866 | "Service core del did not return zero"); | |
867 | ||
868 | return unregister_all(); | |
869 | } | |
870 | ||
871 | /* stop a service and wait for it to become inactive */ | |
872 | static int | |
873 | service_may_be_active(void) | |
874 | { | |
875 | const uint32_t sid = 0; | |
876 | int i; | |
877 | ||
878 | /* expected failure cases */ | |
879 | TEST_ASSERT_EQUAL(-EINVAL, rte_service_may_be_active(10000), | |
880 | "Invalid service may be active check did not fail"); | |
881 | ||
882 | /* start the service */ | |
883 | TEST_ASSERT_EQUAL(0, rte_service_runstate_set(sid, 1), | |
884 | "Starting valid service failed"); | |
885 | TEST_ASSERT_EQUAL(0, rte_service_lcore_add(slcore_id), | |
886 | "Add service core failed when not in use before"); | |
887 | TEST_ASSERT_EQUAL(0, rte_service_map_lcore_set(sid, slcore_id, 1), | |
888 | "Enabling valid service on valid core failed"); | |
889 | TEST_ASSERT_EQUAL(0, rte_service_lcore_start(slcore_id), | |
890 | "Service core start after add failed"); | |
891 | ||
892 | /* ensures core really is running the service function */ | |
893 | TEST_ASSERT_EQUAL(1, service_lcore_running_check(), | |
894 | "Service core expected to poll service but it didn't"); | |
895 | ||
896 | /* stop the service */ | |
897 | TEST_ASSERT_EQUAL(0, rte_service_runstate_set(sid, 0), | |
898 | "Error: Service stop returned non-zero"); | |
899 | ||
900 | /* give the service 100ms to stop running */ | |
901 | for (i = 0; i < 100; i++) { | |
902 | if (!rte_service_may_be_active(sid)) | |
903 | break; | |
904 | rte_delay_ms(SERVICE_DELAY); | |
905 | } | |
906 | ||
907 | TEST_ASSERT_EQUAL(0, rte_service_may_be_active(sid), | |
908 | "Error: Service not stopped after 100ms"); | |
909 | ||
910 | return unregister_all(); | |
911 | } | |
912 | ||
913 | static struct unit_test_suite service_tests = { | |
914 | .suite_name = "service core test suite", | |
915 | .setup = testsuite_setup, | |
916 | .teardown = testsuite_teardown, | |
917 | .unit_test_cases = { | |
918 | TEST_CASE_ST(dummy_register, NULL, unregister_all), | |
919 | TEST_CASE_ST(dummy_register, NULL, service_name), | |
920 | TEST_CASE_ST(dummy_register, NULL, service_get_by_name), | |
921 | TEST_CASE_ST(dummy_register, NULL, service_dump), | |
922 | TEST_CASE_ST(dummy_register, NULL, service_attr_get), | |
923 | TEST_CASE_ST(dummy_register, NULL, service_lcore_attr_get), | |
924 | TEST_CASE_ST(dummy_register, NULL, service_probe_capability), | |
925 | TEST_CASE_ST(dummy_register, NULL, service_start_stop), | |
926 | TEST_CASE_ST(dummy_register, NULL, service_lcore_add_del), | |
927 | TEST_CASE_ST(dummy_register, NULL, service_lcore_start_stop), | |
928 | TEST_CASE_ST(dummy_register, NULL, service_lcore_en_dis_able), | |
929 | TEST_CASE_ST(dummy_register, NULL, service_mt_unsafe_poll), | |
930 | TEST_CASE_ST(dummy_register, NULL, service_mt_safe_poll), | |
931 | TEST_CASE_ST(dummy_register, NULL, service_app_lcore_mt_safe), | |
932 | TEST_CASE_ST(dummy_register, NULL, service_app_lcore_mt_unsafe), | |
933 | TEST_CASE_ST(dummy_register, NULL, service_may_be_active), | |
934 | TEST_CASES_END() /**< NULL terminate unit test array */ | |
935 | } | |
936 | }; | |
937 | ||
938 | static int | |
939 | test_service_common(void) | |
940 | { | |
941 | return unit_test_suite_runner(&service_tests); | |
942 | } | |
943 | ||
944 | REGISTER_TEST_COMMAND(service_autotest, test_service_common); |