xref: /dpdk/app/test/test_fib.c (revision 259deb73d989d91cb1ddb8c60c24de58aa09609a)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>
3  * Copyright(c) 2019 Intel Corporation
4  */
5 
6 #include <stdio.h>
7 #include <stdint.h>
8 #include <stdlib.h>
9 
10 #include <rte_ip.h>
11 #include <rte_log.h>
12 #include <rte_fib.h>
13 #include <rte_malloc.h>
14 
15 #include "test.h"
16 
17 typedef int32_t (*rte_fib_test)(void);
18 
19 static int32_t test_create_invalid(void);
20 static int32_t test_multiple_create(void);
21 static int32_t test_free_null(void);
22 static int32_t test_add_del_invalid(void);
23 static int32_t test_get_invalid(void);
24 static int32_t test_lookup(void);
25 static int32_t test_invalid_rcu(void);
26 static int32_t test_fib_rcu_sync_rw(void);
27 
28 #define MAX_ROUTES	(1 << 16)
29 #define MAX_TBL8	(1 << 15)
30 
31 /*
32  * Check that rte_fib_create fails gracefully for incorrect user input
33  * arguments
34  */
35 int32_t
36 test_create_invalid(void)
37 {
38 	struct rte_fib *fib = NULL;
39 	struct rte_fib_conf config = { 0 };
40 
41 	config.max_routes = MAX_ROUTES;
42 	config.rib_ext_sz = 0;
43 	config.default_nh = 0;
44 	config.type = RTE_FIB_DUMMY;
45 
46 	/* rte_fib_create: fib name == NULL */
47 	fib = rte_fib_create(NULL, SOCKET_ID_ANY, &config);
48 	RTE_TEST_ASSERT(fib == NULL,
49 		"Call succeeded with invalid parameters\n");
50 
51 	/* rte_fib_create: config == NULL */
52 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, NULL);
53 	RTE_TEST_ASSERT(fib == NULL,
54 		"Call succeeded with invalid parameters\n");
55 
56 	/* socket_id < -1 is invalid */
57 	fib = rte_fib_create(__func__, -2, &config);
58 	RTE_TEST_ASSERT(fib == NULL,
59 		"Call succeeded with invalid parameters\n");
60 
61 	/* rte_fib_create: max_routes = 0 */
62 	config.max_routes = 0;
63 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
64 	RTE_TEST_ASSERT(fib == NULL,
65 		"Call succeeded with invalid parameters\n");
66 	config.max_routes = MAX_ROUTES;
67 
68 	config.type = RTE_FIB_DIR24_8 + 1;
69 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
70 	RTE_TEST_ASSERT(fib == NULL,
71 		"Call succeeded with invalid parameters\n");
72 
73 	config.type = RTE_FIB_DIR24_8;
74 	config.dir24_8.num_tbl8 = MAX_TBL8;
75 
76 	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_8B + 1;
77 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
78 	RTE_TEST_ASSERT(fib == NULL,
79 		"Call succeeded with invalid parameters\n");
80 	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_8B;
81 
82 	config.dir24_8.num_tbl8 = 0;
83 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
84 	RTE_TEST_ASSERT(fib == NULL,
85 		"Call succeeded with invalid parameters\n");
86 
87 	return TEST_SUCCESS;
88 }
89 
90 /*
91  * Create fib table then delete fib table 10 times
92  * Use a slightly different rules size each time
93  */
94 int32_t
95 test_multiple_create(void)
96 {
97 	struct rte_fib *fib = NULL;
98 	struct rte_fib_conf config = { 0 };
99 	int32_t i;
100 
101 	config.rib_ext_sz = 0;
102 	config.default_nh = 0;
103 	config.type = RTE_FIB_DUMMY;
104 
105 	for (i = 0; i < 100; i++) {
106 		config.max_routes = MAX_ROUTES - i;
107 		fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
108 		RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
109 		rte_fib_free(fib);
110 	}
111 	/* Can not test free so return success */
112 	return TEST_SUCCESS;
113 }
114 
115 /*
116  * Call rte_fib_free for NULL pointer user input. Note: free has no return and
117  * therefore it is impossible to check for failure but this test is added to
118  * increase function coverage metrics and to validate that freeing null does
119  * not crash.
120  */
121 int32_t
122 test_free_null(void)
123 {
124 	struct rte_fib *fib = NULL;
125 	struct rte_fib_conf config = { 0 };
126 
127 	config.max_routes = MAX_ROUTES;
128 	config.rib_ext_sz = 0;
129 	config.default_nh = 0;
130 	config.type = RTE_FIB_DUMMY;
131 
132 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
133 	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
134 
135 	rte_fib_free(fib);
136 	rte_fib_free(NULL);
137 	return TEST_SUCCESS;
138 }
139 
140 /*
141  * Check that rte_fib_add and rte_fib_delete fails gracefully
142  * for incorrect user input arguments
143  */
144 int32_t
145 test_add_del_invalid(void)
146 {
147 	struct rte_fib *fib = NULL;
148 	struct rte_fib_conf config = { 0 };
149 	uint64_t nh = 100;
150 	uint32_t ip = RTE_IPV4(0, 0, 0, 0);
151 	int ret;
152 	uint8_t depth = 24;
153 
154 	config.max_routes = MAX_ROUTES;
155 	config.rib_ext_sz = 0;
156 	config.default_nh = 0;
157 	config.type = RTE_FIB_DUMMY;
158 
159 	/* rte_fib_add: fib == NULL */
160 	ret = rte_fib_add(NULL, ip, depth, nh);
161 	RTE_TEST_ASSERT(ret < 0,
162 		"Call succeeded with invalid parameters\n");
163 
164 	/* rte_fib_delete: fib == NULL */
165 	ret = rte_fib_delete(NULL, ip, depth);
166 	RTE_TEST_ASSERT(ret < 0,
167 		"Call succeeded with invalid parameters\n");
168 
169 	/*Create valid fib to use in rest of test. */
170 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
171 	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
172 
173 	/* rte_fib_add: depth > RTE_FIB_MAXDEPTH */
174 	ret = rte_fib_add(fib, ip, RTE_FIB_MAXDEPTH + 1, nh);
175 	RTE_TEST_ASSERT(ret < 0,
176 		"Call succeeded with invalid parameters\n");
177 
178 	/* rte_fib_delete: depth > RTE_FIB_MAXDEPTH */
179 	ret = rte_fib_delete(fib, ip, RTE_FIB_MAXDEPTH + 1);
180 	RTE_TEST_ASSERT(ret < 0,
181 		"Call succeeded with invalid parameters\n");
182 
183 	rte_fib_free(fib);
184 
185 	return TEST_SUCCESS;
186 }
187 
188 /*
189  * Check that rte_fib_get_dp and rte_fib_get_rib fails gracefully
190  * for incorrect user input arguments
191  */
192 int32_t
193 test_get_invalid(void)
194 {
195 	void *p;
196 
197 	p = rte_fib_get_dp(NULL);
198 	RTE_TEST_ASSERT(p == NULL,
199 		"Call succeeded with invalid parameters\n");
200 
201 	p = rte_fib_get_rib(NULL);
202 	RTE_TEST_ASSERT(p == NULL,
203 		"Call succeeded with invalid parameters\n");
204 
205 	return TEST_SUCCESS;
206 }
207 
208 /*
209  * Add routes for one supernet with all possible depths and do lookup
210  * on each step
211  * After delete routes with doing lookup on each step
212  */
213 static int
214 lookup_and_check_asc(struct rte_fib *fib, uint32_t ip_arr[RTE_FIB_MAXDEPTH],
215 	uint32_t ip_missing, uint64_t def_nh, uint32_t n)
216 {
217 	uint64_t nh_arr[RTE_FIB_MAXDEPTH];
218 	int ret;
219 	uint32_t i = 0;
220 
221 	ret = rte_fib_lookup_bulk(fib, ip_arr, nh_arr, RTE_FIB_MAXDEPTH);
222 	RTE_TEST_ASSERT(ret == 0, "Failed to lookup\n");
223 
224 	for (; i <= RTE_FIB_MAXDEPTH - n; i++)
225 		RTE_TEST_ASSERT(nh_arr[i] == n,
226 			"Failed to get proper nexthop\n");
227 
228 	for (; i < RTE_FIB_MAXDEPTH; i++)
229 		RTE_TEST_ASSERT(nh_arr[i] == --n,
230 			"Failed to get proper nexthop\n");
231 
232 	ret = rte_fib_lookup_bulk(fib, &ip_missing, nh_arr, 1);
233 	RTE_TEST_ASSERT((ret == 0) && (nh_arr[0] == def_nh),
234 		"Failed to get proper nexthop\n");
235 
236 	return TEST_SUCCESS;
237 }
238 
239 static int
240 lookup_and_check_desc(struct rte_fib *fib, uint32_t ip_arr[RTE_FIB_MAXDEPTH],
241 	uint32_t ip_missing, uint64_t def_nh, uint32_t n)
242 {
243 	uint64_t nh_arr[RTE_FIB_MAXDEPTH];
244 	int ret;
245 	uint32_t i = 0;
246 
247 	ret = rte_fib_lookup_bulk(fib, ip_arr, nh_arr, RTE_FIB_MAXDEPTH);
248 	RTE_TEST_ASSERT(ret == 0, "Failed to lookup\n");
249 
250 	for (; i < n; i++)
251 		RTE_TEST_ASSERT(nh_arr[i] == RTE_FIB_MAXDEPTH - i,
252 			"Failed to get proper nexthop\n");
253 
254 	for (; i < RTE_FIB_MAXDEPTH; i++)
255 		RTE_TEST_ASSERT(nh_arr[i] == def_nh,
256 			"Failed to get proper nexthop\n");
257 
258 	ret = rte_fib_lookup_bulk(fib, &ip_missing, nh_arr, 1);
259 	RTE_TEST_ASSERT((ret == 0) && (nh_arr[0] == def_nh),
260 		"Failed to get proper nexthop\n");
261 
262 	return TEST_SUCCESS;
263 }
264 
265 static int
266 check_fib(struct rte_fib *fib)
267 {
268 	uint64_t def_nh = 100;
269 	uint32_t ip_arr[RTE_FIB_MAXDEPTH];
270 	uint32_t ip_add = RTE_IPV4(128, 0, 0, 0);
271 	uint32_t i, ip_missing = RTE_IPV4(127, 255, 255, 255);
272 	int ret;
273 
274 	for (i = 0; i < RTE_FIB_MAXDEPTH; i++)
275 		ip_arr[i] = ip_add + (1ULL << i) - 1;
276 
277 	ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh, 0);
278 	RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
279 
280 	for (i = 1; i <= RTE_FIB_MAXDEPTH; i++) {
281 		ret = rte_fib_add(fib, ip_add, i, i);
282 		RTE_TEST_ASSERT(ret == 0, "Failed to add a route\n");
283 		ret = lookup_and_check_asc(fib, ip_arr, ip_missing,
284 				def_nh, i);
285 		RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
286 	}
287 
288 	for (i = RTE_FIB_MAXDEPTH; i > 1; i--) {
289 		ret = rte_fib_delete(fib, ip_add, i);
290 		RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
291 		ret = lookup_and_check_asc(fib, ip_arr, ip_missing,
292 			def_nh, i - 1);
293 
294 		RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
295 	}
296 	ret = rte_fib_delete(fib, ip_add, i);
297 	RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
298 	ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh, 0);
299 	RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
300 
301 	for (i = 0; i < RTE_FIB_MAXDEPTH; i++) {
302 		ret = rte_fib_add(fib, ip_add, RTE_FIB_MAXDEPTH - i,
303 			RTE_FIB_MAXDEPTH - i);
304 		RTE_TEST_ASSERT(ret == 0, "Failed to add a route\n");
305 		ret = lookup_and_check_desc(fib, ip_arr, ip_missing,
306 			def_nh, i + 1);
307 		RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
308 	}
309 
310 	for (i = 1; i <= RTE_FIB_MAXDEPTH; i++) {
311 		ret = rte_fib_delete(fib, ip_add, i);
312 		RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
313 		ret = lookup_and_check_desc(fib, ip_arr, ip_missing, def_nh,
314 			RTE_FIB_MAXDEPTH - i);
315 		RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
316 	}
317 
318 	return TEST_SUCCESS;
319 }
320 
321 int32_t
322 test_lookup(void)
323 {
324 	struct rte_fib *fib = NULL;
325 	struct rte_fib_conf config = { 0 };
326 	uint64_t def_nh = 100;
327 	int ret;
328 
329 	config.max_routes = MAX_ROUTES;
330 	config.rib_ext_sz = 0;
331 	config.default_nh = def_nh;
332 	config.type = RTE_FIB_DUMMY;
333 
334 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
335 	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
336 	ret = check_fib(fib);
337 	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
338 		"Check_fib fails for DUMMY type\n");
339 	rte_fib_free(fib);
340 
341 	config.type = RTE_FIB_DIR24_8;
342 
343 	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_1B;
344 	config.dir24_8.num_tbl8 = 127;
345 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
346 	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
347 	ret = check_fib(fib);
348 	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
349 		"Check_fib fails for DIR24_8_1B type\n");
350 	rte_fib_free(fib);
351 
352 	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_2B;
353 	config.dir24_8.num_tbl8 = MAX_TBL8 - 1;
354 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
355 	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
356 	ret = check_fib(fib);
357 	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
358 		"Check_fib fails for DIR24_8_2B type\n");
359 	rte_fib_free(fib);
360 
361 	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_4B;
362 	config.dir24_8.num_tbl8 = MAX_TBL8;
363 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
364 	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
365 	ret = check_fib(fib);
366 	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
367 		"Check_fib fails for DIR24_8_4B type\n");
368 	rte_fib_free(fib);
369 
370 	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_8B;
371 	config.dir24_8.num_tbl8 = MAX_TBL8;
372 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
373 	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
374 	ret = check_fib(fib);
375 	RTE_TEST_ASSERT(ret == TEST_SUCCESS,
376 		"Check_fib fails for DIR24_8_8B type\n");
377 	rte_fib_free(fib);
378 
379 	return TEST_SUCCESS;
380 }
381 
382 /*
383  * rte_fib_rcu_qsbr_add positive and negative tests.
384  *  - Add RCU QSBR variable to FIB
385  *  - Add another RCU QSBR variable to FIB
386  *  - Check returns
387  */
388 int32_t
389 test_invalid_rcu(void)
390 {
391 	struct rte_fib *fib = NULL;
392 	struct rte_fib_conf config = { 0 };
393 	size_t sz;
394 	struct rte_rcu_qsbr *qsv;
395 	struct rte_rcu_qsbr *qsv2;
396 	int32_t status;
397 	struct rte_fib_rcu_config rcu_cfg = {0};
398 	uint64_t def_nh = 100;
399 
400 	config.max_routes = MAX_ROUTES;
401 	config.rib_ext_sz = 0;
402 	config.default_nh = def_nh;
403 
404 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
405 	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
406 
407 	/* Create RCU QSBR variable */
408 	sz = rte_rcu_qsbr_get_memsize(RTE_MAX_LCORE);
409 	qsv = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz, RTE_CACHE_LINE_SIZE,
410 		SOCKET_ID_ANY);
411 	RTE_TEST_ASSERT(qsv != NULL, "Can not allocate memory for RCU\n");
412 
413 	status = rte_rcu_qsbr_init(qsv, RTE_MAX_LCORE);
414 	RTE_TEST_ASSERT(status == 0, "Can not initialize RCU\n");
415 
416 	rcu_cfg.v = qsv;
417 
418 	/* adding rcu to RTE_FIB_DUMMY FIB type */
419 	config.type = RTE_FIB_DUMMY;
420 	rcu_cfg.mode = RTE_FIB_QSBR_MODE_SYNC;
421 	status = rte_fib_rcu_qsbr_add(fib, &rcu_cfg);
422 	RTE_TEST_ASSERT(status == -ENOTSUP,
423 		"rte_fib_rcu_qsbr_add returned wrong error status when called with DUMMY type FIB\n");
424 	rte_fib_free(fib);
425 
426 	config.type = RTE_FIB_DIR24_8;
427 	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_4B;
428 	config.dir24_8.num_tbl8 = MAX_TBL8;
429 	fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
430 	RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
431 
432 	/* Call rte_fib_rcu_qsbr_add without fib or config */
433 	status = rte_fib_rcu_qsbr_add(NULL, &rcu_cfg);
434 	RTE_TEST_ASSERT(status == -EINVAL, "RCU added without fib\n");
435 	status = rte_fib_rcu_qsbr_add(fib, NULL);
436 	RTE_TEST_ASSERT(status == -EINVAL, "RCU added without config\n");
437 
438 	/* Invalid QSBR mode */
439 	rcu_cfg.mode = 2;
440 	status = rte_fib_rcu_qsbr_add(fib, &rcu_cfg);
441 	RTE_TEST_ASSERT(status == -EINVAL, "RCU added with incorrect mode\n");
442 
443 	rcu_cfg.mode = RTE_FIB_QSBR_MODE_DQ;
444 
445 	/* Attach RCU QSBR to FIB to check for double attach */
446 	status = rte_fib_rcu_qsbr_add(fib, &rcu_cfg);
447 	RTE_TEST_ASSERT(status == 0, "Can not attach RCU to FIB\n");
448 
449 	/* Create and attach another RCU QSBR to FIB table */
450 	qsv2 = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz, RTE_CACHE_LINE_SIZE,
451 		SOCKET_ID_ANY);
452 	RTE_TEST_ASSERT(qsv2 != NULL, "Can not allocate memory for RCU\n");
453 
454 	rcu_cfg.v = qsv2;
455 	rcu_cfg.mode = RTE_FIB_QSBR_MODE_SYNC;
456 	status = rte_fib_rcu_qsbr_add(fib, &rcu_cfg);
457 	RTE_TEST_ASSERT(status == -EEXIST, "Secondary RCU was mistakenly attached\n");
458 
459 	rte_fib_free(fib);
460 	rte_free(qsv);
461 	rte_free(qsv2);
462 
463 	return TEST_SUCCESS;
464 }
465 
466 static struct rte_fib *g_fib;
467 static struct rte_rcu_qsbr *g_v;
468 static uint32_t g_ip = RTE_IPV4(192, 0, 2, 100);
469 static volatile uint8_t writer_done;
470 /* Report quiescent state interval every 1024 lookups. Larger critical
471  * sections in reader will result in writer polling multiple times.
472  */
473 #define QSBR_REPORTING_INTERVAL 1024
474 #define WRITER_ITERATIONS	512
475 
476 /*
477  * Reader thread using rte_fib data structure with RCU.
478  */
479 static int
480 test_fib_rcu_qsbr_reader(void *arg)
481 {
482 	int i;
483 	uint64_t next_hop_return = 0;
484 
485 	RTE_SET_USED(arg);
486 	/* Register this thread to report quiescent state */
487 	rte_rcu_qsbr_thread_register(g_v, 0);
488 	rte_rcu_qsbr_thread_online(g_v, 0);
489 
490 	do {
491 		for (i = 0; i < QSBR_REPORTING_INTERVAL; i++)
492 			rte_fib_lookup_bulk(g_fib, &g_ip, &next_hop_return, 1);
493 
494 		/* Update quiescent state */
495 		rte_rcu_qsbr_quiescent(g_v, 0);
496 	} while (!writer_done);
497 
498 	rte_rcu_qsbr_thread_offline(g_v, 0);
499 	rte_rcu_qsbr_thread_unregister(g_v, 0);
500 
501 	return 0;
502 }
503 
504 /*
505  * rte_fib_rcu_qsbr_add sync mode functional test.
506  * 1 Reader and 1 writer. They cannot be in the same thread in this test.
507  *  - Create FIB which supports 1 tbl8 group at max
508  *  - Add RCU QSBR variable with sync mode to FIB
509  *  - Register a reader thread. Reader keeps looking up a specific rule.
510  *  - Writer keeps adding and deleting a specific rule with depth=28 (> 24)
511  */
512 int32_t
513 test_fib_rcu_sync_rw(void)
514 {
515 	struct rte_fib_conf config = { 0 };
516 	size_t sz;
517 	int32_t status;
518 	uint32_t i, next_hop;
519 	uint8_t depth;
520 	struct rte_fib_rcu_config rcu_cfg = {0};
521 	uint64_t def_nh = 100;
522 
523 	if (rte_lcore_count() < 2) {
524 		printf("Not enough cores for %s, expecting at least 2\n", __func__);
525 		return TEST_SKIPPED;
526 	}
527 
528 	config.max_routes = MAX_ROUTES;
529 	config.rib_ext_sz = 0;
530 	config.default_nh = def_nh;
531 	config.type = RTE_FIB_DIR24_8;
532 	config.dir24_8.nh_sz = RTE_FIB_DIR24_8_4B;
533 	config.dir24_8.num_tbl8 = 1;
534 
535 	g_fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config);
536 	RTE_TEST_ASSERT(g_fib != NULL, "Failed to create FIB\n");
537 
538 	/* Create RCU QSBR variable */
539 	sz = rte_rcu_qsbr_get_memsize(1);
540 	g_v = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz, RTE_CACHE_LINE_SIZE,
541 		SOCKET_ID_ANY);
542 	RTE_TEST_ASSERT(g_v != NULL, "Can not allocate memory for RCU\n");
543 
544 	status = rte_rcu_qsbr_init(g_v, 1);
545 	RTE_TEST_ASSERT(status == 0, "Can not initialize RCU\n");
546 
547 	rcu_cfg.v = g_v;
548 	rcu_cfg.mode = RTE_FIB_QSBR_MODE_SYNC;
549 	/* Attach RCU QSBR to FIB table */
550 	status = rte_fib_rcu_qsbr_add(g_fib, &rcu_cfg);
551 	RTE_TEST_ASSERT(status == 0, "Can not attach RCU to FIB\n");
552 
553 	writer_done = 0;
554 	/* Launch reader thread */
555 	rte_eal_remote_launch(test_fib_rcu_qsbr_reader, NULL, rte_get_next_lcore(-1, 1, 0));
556 
557 	depth = 28;
558 	next_hop = 1;
559 	status = rte_fib_add(g_fib, g_ip, depth, next_hop);
560 	if (status != 0) {
561 		printf("%s: Failed to add rule\n", __func__);
562 		goto error;
563 	}
564 
565 	/* Writer update */
566 	for (i = 0; i < WRITER_ITERATIONS; i++) {
567 		status = rte_fib_delete(g_fib, g_ip, depth);
568 		if (status != 0) {
569 			printf("%s: Failed to delete rule at iteration %d\n", __func__, i);
570 			goto error;
571 		}
572 
573 		status = rte_fib_add(g_fib, g_ip, depth, next_hop);
574 		if (status != 0) {
575 			printf("%s: Failed to add rule at iteration %d\n", __func__, i);
576 			goto error;
577 		}
578 	}
579 
580 error:
581 	writer_done = 1;
582 	/* Wait until reader exited. */
583 	rte_eal_mp_wait_lcore();
584 
585 	rte_fib_free(g_fib);
586 	rte_free(g_v);
587 
588 	return status == 0 ? TEST_SUCCESS : TEST_FAILED;
589 }
590 
591 static struct unit_test_suite fib_fast_tests = {
592 	.suite_name = "fib autotest",
593 	.setup = NULL,
594 	.teardown = NULL,
595 	.unit_test_cases = {
596 	TEST_CASE(test_create_invalid),
597 	TEST_CASE(test_free_null),
598 	TEST_CASE(test_add_del_invalid),
599 	TEST_CASE(test_get_invalid),
600 	TEST_CASE(test_lookup),
601 	TEST_CASE(test_invalid_rcu),
602 	TEST_CASE(test_fib_rcu_sync_rw),
603 	TEST_CASES_END()
604 	}
605 };
606 
607 static struct unit_test_suite fib_slow_tests = {
608 	.suite_name = "fib slow autotest",
609 	.setup = NULL,
610 	.teardown = NULL,
611 	.unit_test_cases = {
612 	TEST_CASE(test_multiple_create),
613 	TEST_CASES_END()
614 	}
615 };
616 
617 /*
618  * Do all unit tests.
619  */
620 static int
621 test_fib(void)
622 {
623 	return unit_test_suite_runner(&fib_fast_tests);
624 }
625 
626 static int
627 test_slow_fib(void)
628 {
629 	return unit_test_suite_runner(&fib_slow_tests);
630 }
631 
632 REGISTER_FAST_TEST(fib_autotest, true, true, test_fib);
633 REGISTER_PERF_TEST(fib_slow_autotest, test_slow_fib);
634