Branch data Line data Source code
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_memory.h>
11 : : #include <rte_log.h>
12 : : #include <rte_rib6.h>
13 : : #include <rte_fib6.h>
14 : : #include <rte_malloc.h>
15 : :
16 : : #include "test.h"
17 : :
18 : : typedef int32_t (*rte_fib6_test)(void);
19 : :
20 : : static int32_t test_create_invalid(void);
21 : : static int32_t test_multiple_create(void);
22 : : static int32_t test_free_null(void);
23 : : static int32_t test_add_del_invalid(void);
24 : : static int32_t test_get_invalid(void);
25 : : static int32_t test_lookup(void);
26 : : static int32_t test_invalid_rcu(void);
27 : : static int32_t test_fib_rcu_sync_rw(void);
28 : : static int32_t test_drift(void);
29 : : static int32_t test_drift_compression(void);
30 : : static int32_t test_drift_multilevel(void);
31 : : static int32_t test_drift_stress(void);
32 : : static int32_t test_drift_tight_pool(void);
33 : :
34 : : #define MAX_ROUTES (1 << 16)
35 : : /** Maximum number of tbl8 for 2-byte entries */
36 : : #define MAX_TBL8 (1 << 15)
37 : :
38 : : /*
39 : : * Check that rte_fib6_create fails gracefully for incorrect user input
40 : : * arguments
41 : : */
42 : : int32_t
43 : 1 : test_create_invalid(void)
44 : : {
45 : : struct rte_fib6 *fib = NULL;
46 : : struct rte_fib6_conf config;
47 : :
48 : 1 : config.max_routes = MAX_ROUTES;
49 : 1 : config.rib_ext_sz = 0;
50 : 1 : config.default_nh = 0;
51 : 1 : config.type = RTE_FIB6_DUMMY;
52 : :
53 : : /* rte_fib6_create: fib name == NULL */
54 : 1 : fib = rte_fib6_create(NULL, SOCKET_ID_ANY, &config);
55 [ - + ]: 1 : RTE_TEST_ASSERT(fib == NULL,
56 : : "Call succeeded with invalid parameters\n");
57 : :
58 : : /* rte_fib6_create: config == NULL */
59 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, NULL);
60 [ - + ]: 1 : RTE_TEST_ASSERT(fib == NULL,
61 : : "Call succeeded with invalid parameters\n");
62 : :
63 : : /* socket_id < -1 is invalid */
64 : 1 : fib = rte_fib6_create(__func__, -2, &config);
65 [ - + ]: 1 : RTE_TEST_ASSERT(fib == NULL,
66 : : "Call succeeded with invalid parameters\n");
67 : :
68 : : /* rte_fib6_create: max_routes = 0 */
69 : 1 : config.max_routes = 0;
70 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
71 [ - + ]: 1 : RTE_TEST_ASSERT(fib == NULL,
72 : : "Call succeeded with invalid parameters\n");
73 : 1 : config.max_routes = MAX_ROUTES;
74 : :
75 : 1 : config.type = RTE_FIB6_TRIE + 1;
76 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
77 [ - + ]: 1 : RTE_TEST_ASSERT(fib == NULL,
78 : : "Call succeeded with invalid parameters\n");
79 : :
80 : 1 : config.type = RTE_FIB6_TRIE;
81 : 1 : config.trie.num_tbl8 = MAX_TBL8;
82 : :
83 : 1 : config.trie.nh_sz = RTE_FIB6_TRIE_8B + 1;
84 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
85 [ - + ]: 1 : RTE_TEST_ASSERT(fib == NULL,
86 : : "Call succeeded with invalid parameters\n");
87 : 1 : config.trie.nh_sz = RTE_FIB6_TRIE_8B;
88 : :
89 : 1 : config.trie.num_tbl8 = 0;
90 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
91 [ - + ]: 1 : RTE_TEST_ASSERT(fib == NULL,
92 : : "Call succeeded with invalid parameters\n");
93 : :
94 : : return TEST_SUCCESS;
95 : : }
96 : :
97 : : /*
98 : : * Create fib table then delete fib table 10 times
99 : : * Use a slightly different rules size each time
100 : : */
101 : : int32_t
102 : 0 : test_multiple_create(void)
103 : : {
104 : : struct rte_fib6 *fib = NULL;
105 : : struct rte_fib6_conf config;
106 : : int32_t i;
107 : :
108 : 0 : config.rib_ext_sz = 0;
109 : 0 : config.default_nh = 0;
110 : 0 : config.type = RTE_FIB6_DUMMY;
111 : :
112 [ # # ]: 0 : for (i = 0; i < 100; i++) {
113 : 0 : config.max_routes = MAX_ROUTES - i;
114 : 0 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
115 [ # # ]: 0 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
116 : 0 : rte_fib6_free(fib);
117 : : }
118 : : /* Can not test free so return success */
119 : : return TEST_SUCCESS;
120 : : }
121 : :
122 : : /*
123 : : * Call rte_fib6_free for NULL pointer user input. Note: free has no return and
124 : : * therefore it is impossible to check for failure but this test is added to
125 : : * increase function coverage metrics and to validate that freeing null does
126 : : * not crash.
127 : : */
128 : : int32_t
129 : 1 : test_free_null(void)
130 : : {
131 : : struct rte_fib6 *fib = NULL;
132 : : struct rte_fib6_conf config;
133 : :
134 : 1 : config.max_routes = MAX_ROUTES;
135 : 1 : config.rib_ext_sz = 0;
136 : 1 : config.default_nh = 0;
137 : 1 : config.type = RTE_FIB6_DUMMY;
138 : :
139 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
140 [ - + ]: 1 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
141 : :
142 : 1 : rte_fib6_free(fib);
143 : 1 : rte_fib6_free(NULL);
144 : :
145 : 1 : return TEST_SUCCESS;
146 : : }
147 : :
148 : : /*
149 : : * Check that rte_fib6_add and rte_fib6_delete fails gracefully
150 : : * for incorrect user input arguments
151 : : */
152 : : int32_t
153 : 1 : test_add_del_invalid(void)
154 : : {
155 : : struct rte_fib6 *fib = NULL;
156 : : struct rte_fib6_conf config;
157 : : uint64_t nh = 100;
158 : 1 : struct rte_ipv6_addr ip = RTE_IPV6_ADDR_UNSPEC;
159 : : int ret;
160 : : uint8_t depth = 24;
161 : :
162 : 1 : config.max_routes = MAX_ROUTES;
163 : 1 : config.rib_ext_sz = 0;
164 : 1 : config.default_nh = 0;
165 : 1 : config.type = RTE_FIB6_DUMMY;
166 : :
167 : : /* rte_fib6_add: fib == NULL */
168 : 1 : ret = rte_fib6_add(NULL, &ip, depth, nh);
169 [ - + ]: 1 : RTE_TEST_ASSERT(ret < 0,
170 : : "Call succeeded with invalid parameters\n");
171 : :
172 : : /* rte_fib6_delete: fib == NULL */
173 : 1 : ret = rte_fib6_delete(NULL, &ip, depth);
174 [ - + ]: 1 : RTE_TEST_ASSERT(ret < 0,
175 : : "Call succeeded with invalid parameters\n");
176 : :
177 : : /*Create valid fib to use in rest of test. */
178 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
179 [ - + ]: 1 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
180 : :
181 : : /* rte_fib6_add: depth > RTE_IPV6_MAX_DEPTH */
182 : 1 : ret = rte_fib6_add(fib, &ip, RTE_IPV6_MAX_DEPTH + 1, nh);
183 [ - + ]: 1 : RTE_TEST_ASSERT(ret < 0,
184 : : "Call succeeded with invalid parameters\n");
185 : :
186 : : /* rte_fib6_delete: depth > RTE_IPV6_MAX_DEPTH */
187 : 1 : ret = rte_fib6_delete(fib, &ip, RTE_IPV6_MAX_DEPTH + 1);
188 [ - + ]: 1 : RTE_TEST_ASSERT(ret < 0,
189 : : "Call succeeded with invalid parameters\n");
190 : :
191 : 1 : rte_fib6_free(fib);
192 : :
193 : 1 : return TEST_SUCCESS;
194 : : }
195 : :
196 : : /*
197 : : * Check that rte_fib6_get_dp and rte_fib6_get_rib fails gracefully
198 : : * for incorrect user input arguments
199 : : */
200 : : int32_t
201 : 1 : test_get_invalid(void)
202 : : {
203 : : void *p;
204 : :
205 : 1 : p = rte_fib6_get_dp(NULL);
206 [ - + ]: 1 : RTE_TEST_ASSERT(p == NULL,
207 : : "Call succeeded with invalid parameters\n");
208 : :
209 : 1 : p = rte_fib6_get_rib(NULL);
210 [ - + ]: 1 : RTE_TEST_ASSERT(p == NULL,
211 : : "Call succeeded with invalid parameters\n");
212 : :
213 : : return TEST_SUCCESS;
214 : : }
215 : :
216 : : /*
217 : : * Add routes for one supernet with all possible depths and do lookup
218 : : * on each step
219 : : * After delete routes with doing lookup on each step
220 : : */
221 : : static int
222 : 1020 : lookup_and_check_asc(struct rte_fib6 *fib,
223 : : struct rte_ipv6_addr *ip_arr,
224 : : struct rte_ipv6_addr *ip_missing, uint64_t def_nh,
225 : : uint32_t n)
226 : : {
227 : : uint64_t nh_arr[RTE_IPV6_MAX_DEPTH];
228 : : int ret;
229 : : uint32_t i = 0;
230 : :
231 : 1020 : ret = rte_fib6_lookup_bulk(fib, ip_arr, nh_arr, RTE_IPV6_MAX_DEPTH);
232 [ - + ]: 1020 : RTE_TEST_ASSERT(ret == 0, "Failed to lookup\n");
233 : :
234 [ + + ]: 67064 : for (; i <= RTE_IPV6_MAX_DEPTH - n; i++)
235 [ - + ]: 66044 : RTE_TEST_ASSERT(nh_arr[i] == n,
236 : : "Failed to get proper nexthop\n");
237 : :
238 [ + + ]: 65536 : for (; i < RTE_IPV6_MAX_DEPTH; i++)
239 [ - + ]: 64516 : RTE_TEST_ASSERT(nh_arr[i] == --n,
240 : : "Failed to get proper nexthop\n");
241 : :
242 : 1020 : ret = rte_fib6_lookup_bulk(fib, ip_missing, nh_arr, 1);
243 [ + - - + ]: 1020 : RTE_TEST_ASSERT((ret == 0) && (nh_arr[0] == def_nh),
244 : : "Failed to get proper nexthop\n");
245 : :
246 : : return TEST_SUCCESS;
247 : : }
248 : :
249 : : static int
250 : 1032 : lookup_and_check_desc(struct rte_fib6 *fib,
251 : : struct rte_ipv6_addr *ip_arr,
252 : : struct rte_ipv6_addr *ip_missing, uint64_t def_nh,
253 : : uint32_t n)
254 : : {
255 : : uint64_t nh_arr[RTE_IPV6_MAX_DEPTH];
256 : : int ret;
257 : : uint32_t i = 0;
258 : :
259 : 1032 : ret = rte_fib6_lookup_bulk(fib, ip_arr, nh_arr, RTE_IPV6_MAX_DEPTH);
260 [ - + ]: 1032 : RTE_TEST_ASSERT(ret == 0, "Failed to lookup\n");
261 : :
262 [ + + ]: 66568 : for (; i < n; i++)
263 [ - + ]: 65536 : RTE_TEST_ASSERT(nh_arr[i] == RTE_IPV6_MAX_DEPTH - i,
264 : : "Failed to get proper nexthop\n");
265 : :
266 [ + + ]: 67592 : for (; i < RTE_IPV6_MAX_DEPTH; i++)
267 [ - + ]: 66560 : RTE_TEST_ASSERT(nh_arr[i] == def_nh,
268 : : "Failed to get proper nexthop\n");
269 : :
270 : 1032 : ret = rte_fib6_lookup_bulk(fib, ip_missing, nh_arr, 1);
271 [ + - - + ]: 1032 : RTE_TEST_ASSERT((ret == 0) && (nh_arr[0] == def_nh),
272 : : "Failed to get proper nexthop\n");
273 : :
274 : : return TEST_SUCCESS;
275 : : }
276 : :
277 : : static int
278 : 4 : check_fib(struct rte_fib6 *fib)
279 : : {
280 : : uint64_t def_nh = 100;
281 : : struct rte_ipv6_addr ip_arr[RTE_IPV6_MAX_DEPTH];
282 : 4 : struct rte_ipv6_addr ip_add = RTE_IPV6(0x8000, 0, 0, 0, 0, 0, 0, 0);
283 : 4 : struct rte_ipv6_addr ip_missing =
284 : : RTE_IPV6(0x7fff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff);
285 : : uint32_t i, j;
286 : : int ret;
287 : :
288 [ + + ]: 516 : for (i = 0; i < RTE_IPV6_MAX_DEPTH; i++) {
289 : 512 : ip_arr[i] = ip_add;
290 : 512 : j = (RTE_IPV6_MAX_DEPTH - i) / CHAR_BIT;
291 [ + + ]: 512 : if (j < RTE_IPV6_ADDR_SIZE) {
292 : 508 : ip_arr[i].a[j] |= UINT8_MAX >> ((RTE_IPV6_MAX_DEPTH - i) % CHAR_BIT);
293 [ + + ]: 4288 : for (j++; j < RTE_IPV6_ADDR_SIZE; j++)
294 : 3780 : ip_arr[i].a[j] = 0xff;
295 : : }
296 : : }
297 : :
298 : 4 : ret = lookup_and_check_desc(fib, ip_arr, &ip_missing, def_nh, 0);
299 [ - + ]: 4 : RTE_TEST_ASSERT(ret == TEST_SUCCESS, "Lookup and check fails\n");
300 : :
301 [ + + ]: 516 : for (i = 1; i <= RTE_IPV6_MAX_DEPTH; i++) {
302 : 512 : ret = rte_fib6_add(fib, &ip_add, i, i);
303 [ - + ]: 512 : RTE_TEST_ASSERT(ret == 0, "Failed to add a route\n");
304 : 512 : ret = lookup_and_check_asc(fib, ip_arr, &ip_missing, def_nh, i);
305 [ - + ]: 512 : RTE_TEST_ASSERT(ret == TEST_SUCCESS,
306 : : "Lookup and check fails\n");
307 : : }
308 : :
309 [ + + ]: 512 : for (i = RTE_IPV6_MAX_DEPTH; i > 1; i--) {
310 : 508 : ret = rte_fib6_delete(fib, &ip_add, i);
311 [ - + ]: 508 : RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
312 : 508 : ret = lookup_and_check_asc(fib, ip_arr, &ip_missing,
313 : : def_nh, i - 1);
314 : :
315 [ - + ]: 508 : RTE_TEST_ASSERT(ret == TEST_SUCCESS,
316 : : "Lookup and check fails\n");
317 : : }
318 : 4 : ret = rte_fib6_delete(fib, &ip_add, i);
319 [ - + ]: 4 : RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
320 : 4 : ret = lookup_and_check_desc(fib, ip_arr, &ip_missing, def_nh, 0);
321 [ - + ]: 4 : RTE_TEST_ASSERT(ret == TEST_SUCCESS,
322 : : "Lookup and check fails\n");
323 : :
324 [ + + ]: 516 : for (i = 0; i < RTE_IPV6_MAX_DEPTH; i++) {
325 : 512 : ret = rte_fib6_add(fib, &ip_add, RTE_IPV6_MAX_DEPTH - i,
326 : 512 : RTE_IPV6_MAX_DEPTH - i);
327 [ - + ]: 512 : RTE_TEST_ASSERT(ret == 0, "Failed to add a route\n");
328 : 512 : ret = lookup_and_check_desc(fib, ip_arr, &ip_missing,
329 : : def_nh, i + 1);
330 [ - + ]: 512 : RTE_TEST_ASSERT(ret == TEST_SUCCESS,
331 : : "Lookup and check fails\n");
332 : : }
333 : :
334 [ + + ]: 516 : for (i = 1; i <= RTE_IPV6_MAX_DEPTH; i++) {
335 : 512 : ret = rte_fib6_delete(fib, &ip_add, i);
336 [ - + ]: 512 : RTE_TEST_ASSERT(ret == 0, "Failed to delete a route\n");
337 : 512 : ret = lookup_and_check_desc(fib, ip_arr, &ip_missing, def_nh,
338 : : RTE_IPV6_MAX_DEPTH - i);
339 [ - + ]: 512 : RTE_TEST_ASSERT(ret == TEST_SUCCESS,
340 : : "Lookup and check fails\n");
341 : : }
342 : :
343 : : return TEST_SUCCESS;
344 : : }
345 : :
346 : : int32_t
347 : 1 : test_lookup(void)
348 : : {
349 : : struct rte_fib6 *fib = NULL;
350 : : struct rte_fib6_conf config;
351 : : uint64_t def_nh = 100;
352 : : int ret;
353 : :
354 : 1 : config.max_routes = MAX_ROUTES;
355 : 1 : config.rib_ext_sz = 0;
356 : 1 : config.default_nh = def_nh;
357 : 1 : config.type = RTE_FIB6_DUMMY;
358 : :
359 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
360 [ - + ]: 1 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
361 : 1 : ret = check_fib(fib);
362 [ - + ]: 1 : RTE_TEST_ASSERT(ret == TEST_SUCCESS,
363 : : "Check_fib fails for DUMMY type\n");
364 : 1 : rte_fib6_free(fib);
365 : :
366 : 1 : config.type = RTE_FIB6_TRIE;
367 : :
368 : 1 : config.trie.nh_sz = RTE_FIB6_TRIE_2B;
369 : 1 : config.trie.num_tbl8 = MAX_TBL8 - 1;
370 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
371 [ - + ]: 1 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
372 : 1 : ret = check_fib(fib);
373 [ - + ]: 1 : RTE_TEST_ASSERT(ret == TEST_SUCCESS,
374 : : "Check_fib fails for TRIE_2B type\n");
375 : 1 : rte_fib6_free(fib);
376 : :
377 : 1 : config.trie.nh_sz = RTE_FIB6_TRIE_4B;
378 : 1 : config.trie.num_tbl8 = MAX_TBL8;
379 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
380 [ - + ]: 1 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
381 : 1 : ret = check_fib(fib);
382 [ - + ]: 1 : RTE_TEST_ASSERT(ret == TEST_SUCCESS,
383 : : "Check_fib fails for TRIE_4B type\n");
384 : 1 : rte_fib6_free(fib);
385 : :
386 : 1 : config.trie.nh_sz = RTE_FIB6_TRIE_8B;
387 : 1 : config.trie.num_tbl8 = MAX_TBL8;
388 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
389 [ - + ]: 1 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
390 : 1 : ret = check_fib(fib);
391 [ - + ]: 1 : RTE_TEST_ASSERT(ret == TEST_SUCCESS,
392 : : "Check_fib fails for TRIE_8B type\n");
393 : 1 : rte_fib6_free(fib);
394 : :
395 : 1 : return TEST_SUCCESS;
396 : : }
397 : :
398 : : /*
399 : : * rte_fib6_rcu_qsbr_add positive and negative tests.
400 : : * - Add RCU QSBR variable to FIB
401 : : * - Add another RCU QSBR variable to FIB
402 : : * - Check returns
403 : : */
404 : : int32_t
405 : 1 : test_invalid_rcu(void)
406 : : {
407 : : struct rte_fib6 *fib = NULL;
408 : 1 : struct rte_fib6_conf config = { 0 };
409 : : size_t sz;
410 : : struct rte_rcu_qsbr *qsv;
411 : : struct rte_rcu_qsbr *qsv2;
412 : : int32_t status;
413 : 1 : struct rte_fib6_rcu_config rcu_cfg = {0};
414 : : uint64_t def_nh = 100;
415 : :
416 : 1 : config.max_routes = MAX_ROUTES;
417 : : config.rib_ext_sz = 0;
418 : 1 : config.default_nh = def_nh;
419 : :
420 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
421 [ - + ]: 1 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
422 : :
423 : : /* Create RCU QSBR variable */
424 : 1 : sz = rte_rcu_qsbr_get_memsize(RTE_MAX_LCORE);
425 : 1 : qsv = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz, RTE_CACHE_LINE_SIZE,
426 : : SOCKET_ID_ANY);
427 [ - + ]: 1 : RTE_TEST_ASSERT(qsv != NULL, "Can not allocate memory for RCU\n");
428 : :
429 : 1 : status = rte_rcu_qsbr_init(qsv, RTE_MAX_LCORE);
430 [ - + ]: 1 : RTE_TEST_ASSERT(status == 0, "Can not initialize RCU\n");
431 : :
432 : 1 : rcu_cfg.v = qsv;
433 : :
434 : : /* adding rcu to RTE_FIB6_DUMMY FIB type */
435 : 1 : config.type = RTE_FIB6_DUMMY;
436 : 1 : rcu_cfg.mode = RTE_FIB6_QSBR_MODE_SYNC;
437 : 1 : status = rte_fib6_rcu_qsbr_add(fib, &rcu_cfg);
438 [ - + ]: 1 : RTE_TEST_ASSERT(status == -ENOTSUP,
439 : : "rte_fib6_rcu_qsbr_add returned wrong error status when called with DUMMY type FIB\n");
440 : 1 : rte_fib6_free(fib);
441 : :
442 : 1 : config.type = RTE_FIB6_TRIE;
443 : 1 : config.trie.nh_sz = RTE_FIB6_TRIE_4B;
444 : 1 : config.trie.num_tbl8 = MAX_TBL8;
445 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
446 [ - + ]: 1 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
447 : :
448 : : /* Call rte_fib6_rcu_qsbr_add without fib or config */
449 : 1 : status = rte_fib6_rcu_qsbr_add(NULL, &rcu_cfg);
450 [ - + ]: 1 : RTE_TEST_ASSERT(status == -EINVAL, "RCU added without fib\n");
451 : 1 : status = rte_fib6_rcu_qsbr_add(fib, NULL);
452 [ - + ]: 1 : RTE_TEST_ASSERT(status == -EINVAL, "RCU added without config\n");
453 : :
454 : : /* Invalid QSBR mode */
455 : 1 : rcu_cfg.mode = 2;
456 : 1 : status = rte_fib6_rcu_qsbr_add(fib, &rcu_cfg);
457 [ - + ]: 1 : RTE_TEST_ASSERT(status == -EINVAL, "RCU added with incorrect mode\n");
458 : :
459 : 1 : rcu_cfg.mode = RTE_FIB6_QSBR_MODE_DQ;
460 : :
461 : : /* Attach RCU QSBR to FIB to check for double attach */
462 : 1 : status = rte_fib6_rcu_qsbr_add(fib, &rcu_cfg);
463 [ - + ]: 1 : RTE_TEST_ASSERT(status == 0, "Can not attach RCU to FIB\n");
464 : :
465 : : /* Create and attach another RCU QSBR to FIB table */
466 : 1 : qsv2 = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz, RTE_CACHE_LINE_SIZE,
467 : : SOCKET_ID_ANY);
468 [ - + ]: 1 : RTE_TEST_ASSERT(qsv2 != NULL, "Can not allocate memory for RCU\n");
469 : :
470 : 1 : rcu_cfg.v = qsv2;
471 : 1 : rcu_cfg.mode = RTE_FIB6_QSBR_MODE_SYNC;
472 : 1 : status = rte_fib6_rcu_qsbr_add(fib, &rcu_cfg);
473 [ - + ]: 1 : RTE_TEST_ASSERT(status == -EEXIST, "Secondary RCU was mistakenly attached\n");
474 : :
475 : 1 : rte_fib6_free(fib);
476 : 1 : rte_free(qsv);
477 : 1 : rte_free(qsv2);
478 : :
479 : 1 : return TEST_SUCCESS;
480 : : }
481 : :
482 : : static struct rte_fib6 *g_fib;
483 : : static struct rte_rcu_qsbr *g_v;
484 : : static struct rte_ipv6_addr g_ip = RTE_IPV6(0x2001, 0xabcd, 0, 0, 0, 0, 0, 1);
485 : : static volatile uint8_t writer_done;
486 : : /* Report quiescent state interval every 1024 lookups. Larger critical
487 : : * sections in reader will result in writer polling multiple times.
488 : : */
489 : : #define QSBR_REPORTING_INTERVAL 1024
490 : : #define WRITER_ITERATIONS 512
491 : :
492 : : /*
493 : : * Reader thread using rte_fib6 data structure with RCU.
494 : : */
495 : : static int
496 : 1 : test_fib_rcu_qsbr_reader(void *arg)
497 : : {
498 : : int i;
499 : 1 : uint64_t next_hop_return = 0;
500 : :
501 : : RTE_SET_USED(arg);
502 : : /* Register this thread to report quiescent state */
503 : 1 : rte_rcu_qsbr_thread_register(g_v, 0);
504 : 1 : rte_rcu_qsbr_thread_online(g_v, 0);
505 : :
506 : : do {
507 [ + + ]: 527875 : for (i = 0; i < QSBR_REPORTING_INTERVAL; i++)
508 : 527360 : rte_fib6_lookup_bulk(g_fib, &g_ip, &next_hop_return, 1);
509 : :
510 : : /* Update quiescent state */
511 [ + + ]: 515 : rte_rcu_qsbr_quiescent(g_v, 0);
512 [ + + ]: 515 : } while (!writer_done);
513 : :
514 : 1 : rte_rcu_qsbr_thread_offline(g_v, 0);
515 : 1 : rte_rcu_qsbr_thread_unregister(g_v, 0);
516 : :
517 : 1 : return 0;
518 : : }
519 : :
520 : : /*
521 : : * rte_fib6_rcu_qsbr_add sync mode functional test.
522 : : * 1 Reader and 1 writer. They cannot be in the same thread in this test.
523 : : * - Create FIB which supports 1 tbl8 group at max
524 : : * - Add RCU QSBR variable with sync mode to FIB
525 : : * - Register a reader thread. Reader keeps looking up a specific rule.
526 : : * - Writer keeps adding and deleting a specific rule with depth=28 (> 24)
527 : : */
528 : : int32_t
529 : 1 : test_fib_rcu_sync_rw(void)
530 : : {
531 : 1 : struct rte_fib6_conf config = { 0 };
532 : : size_t sz;
533 : : int32_t status;
534 : : uint32_t i, next_hop;
535 : : uint8_t depth;
536 : 1 : struct rte_fib6_rcu_config rcu_cfg = {0};
537 : : uint64_t def_nh = 100;
538 : :
539 [ - + ]: 1 : if (rte_lcore_count() < 2) {
540 : : printf("Not enough cores for %s, expecting at least 2\n", __func__);
541 : 0 : return TEST_SKIPPED;
542 : : }
543 : :
544 : 1 : config.max_routes = MAX_ROUTES;
545 : 1 : config.rib_ext_sz = 0;
546 : 1 : config.default_nh = def_nh;
547 : 1 : config.type = RTE_FIB6_TRIE;
548 : 1 : config.trie.nh_sz = RTE_FIB6_TRIE_4B;
549 : 1 : config.trie.num_tbl8 = 1;
550 : :
551 : 1 : g_fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
552 [ - + ]: 1 : RTE_TEST_ASSERT(g_fib != NULL, "Failed to create FIB\n");
553 : :
554 : : /* Create RCU QSBR variable */
555 : 1 : sz = rte_rcu_qsbr_get_memsize(1);
556 : 1 : g_v = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz, RTE_CACHE_LINE_SIZE,
557 : : SOCKET_ID_ANY);
558 [ - + ]: 1 : RTE_TEST_ASSERT(g_v != NULL, "Can not allocate memory for RCU\n");
559 : :
560 : 1 : status = rte_rcu_qsbr_init(g_v, 1);
561 [ - + ]: 1 : RTE_TEST_ASSERT(status == 0, "Can not initialize RCU\n");
562 : :
563 : 1 : rcu_cfg.v = g_v;
564 : 1 : rcu_cfg.mode = RTE_FIB6_QSBR_MODE_SYNC;
565 : : /* Attach RCU QSBR to FIB table */
566 : 1 : status = rte_fib6_rcu_qsbr_add(g_fib, &rcu_cfg);
567 [ - + ]: 1 : RTE_TEST_ASSERT(status == 0, "Can not attach RCU to FIB\n");
568 : :
569 : 1 : writer_done = 0;
570 : : /* Launch reader thread */
571 : 1 : rte_eal_remote_launch(test_fib_rcu_qsbr_reader, NULL, rte_get_next_lcore(-1, 1, 0));
572 : :
573 : : depth = 28;
574 : : next_hop = 1;
575 : 1 : status = rte_fib6_add(g_fib, &g_ip, depth, next_hop);
576 [ - + ]: 1 : if (status != 0) {
577 : : printf("%s: Failed to add rule\n", __func__);
578 : 0 : goto error;
579 : : }
580 : :
581 : : /* Writer update */
582 [ + + ]: 513 : for (i = 0; i < WRITER_ITERATIONS; i++) {
583 : 512 : status = rte_fib6_delete(g_fib, &g_ip, depth);
584 [ - + ]: 512 : if (status != 0) {
585 : : printf("%s: Failed to delete rule at iteration %d\n", __func__, i);
586 : 0 : goto error;
587 : : }
588 : :
589 : 512 : status = rte_fib6_add(g_fib, &g_ip, depth, next_hop);
590 [ - + ]: 512 : if (status != 0) {
591 : : printf("%s: Failed to add rule at iteration %d\n", __func__, i);
592 : 0 : goto error;
593 : : }
594 : : }
595 : :
596 : 1 : error:
597 : 1 : writer_done = 1;
598 : : /* Wait until reader exited. */
599 : 1 : rte_eal_mp_wait_lcore();
600 : :
601 : 1 : rte_fib6_free(g_fib);
602 : 1 : rte_free(g_v);
603 : :
604 [ - + ]: 1 : return status == 0 ? TEST_SUCCESS : TEST_FAILED;
605 : : }
606 : :
607 : : /*
608 : : * Reproducer for the rsvd_tbl8s drift bug. The tbl8 reservation
609 : : * accounting must remain balanced even when a covering parent prefix
610 : : * is removed between an ADD and its later matching DEL.
611 : : *
612 : : * Layout: one /28 parent (fcde::/28) and three /48 siblings under it
613 : : * (fcde:0:6000::/48, fcde:1:6000::/48, fcde:2:6000::/48). The second
614 : : * hextet's high 12 bits are zero, so the three /48 IPs all fall inside
615 : : * the /28.
616 : : *
617 : : * One asymmetric sequence is enough to wrap the counter:
618 : : * ADD /28 rsvd_tbl8s += 1
619 : : * ADD /48 child_0,1,2 (with /28 parent) rsvd_tbl8s += 2 each (+6)
620 : : * DEL /28 (sibling /48 found) rsvd_tbl8s -= 0
621 : : * DEL /48 child_0,1,2 (no parent left) rsvd_tbl8s -= 3 each (-9)
622 : : */
623 : : static int32_t
624 : 1 : test_drift(void)
625 : : {
626 : 1 : struct rte_fib6_conf config = { 0 };
627 : : struct rte_fib6 *fib;
628 : 1 : struct rte_ipv6_addr parent =
629 : : RTE_IPV6(0xfcde, 0, 0, 0, 0, 0, 0, 0);
630 : 1 : struct rte_ipv6_addr child[3] = {
631 : : RTE_IPV6(0xfcde, 0, 0x6000, 0, 0, 0, 0, 0),
632 : : RTE_IPV6(0xfcde, 1, 0x6000, 0, 0, 0, 0, 0),
633 : : RTE_IPV6(0xfcde, 2, 0x6000, 0, 0, 0, 0, 0),
634 : : };
635 : : unsigned int c;
636 : : int ret;
637 : :
638 : 1 : config.max_routes = 1024;
639 : : config.rib_ext_sz = 0;
640 : : config.default_nh = 0;
641 : 1 : config.type = RTE_FIB6_TRIE;
642 : 1 : config.trie.nh_sz = RTE_FIB6_TRIE_2B;
643 : 1 : config.trie.num_tbl8 = 256;
644 : :
645 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
646 [ - + ]: 1 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
647 : :
648 : 1 : ret = rte_fib6_add(fib, &parent, 28, 0xa);
649 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "ADD /28 failed (ret=%d)\n", ret);
650 : :
651 [ + + ]: 4 : for (c = 0; c < 3; c++) {
652 : 3 : ret = rte_fib6_add(fib, &child[c], 48, 0xb + c);
653 [ - + ]: 3 : RTE_TEST_ASSERT(ret == 0,
654 : : "ADD /48 child %u failed (ret=%d)\n", c, ret);
655 : : }
656 : :
657 : 1 : ret = rte_fib6_delete(fib, &parent, 28);
658 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "DEL /28 failed (ret=%d)\n", ret);
659 : :
660 [ + + ]: 4 : for (c = 0; c < 3; c++) {
661 : 3 : ret = rte_fib6_delete(fib, &child[c], 48);
662 [ - + ]: 3 : RTE_TEST_ASSERT(ret == 0,
663 : : "DEL /48 child %u failed (ret=%d)\n", c, ret);
664 : : }
665 : :
666 : : /* Pre-fix: -ENOSPC. Post-fix: succeeds. */
667 : 1 : ret = rte_fib6_add(fib, &parent, 28, 0xe);
668 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0,
669 : : "Fresh ADD /28 spuriously failed (ret=%d)\n", ret);
670 : :
671 : 1 : ret = rte_fib6_delete(fib, &parent, 28);
672 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "Final DEL /28 failed (ret=%d)\n", ret);
673 : :
674 : 1 : rte_fib6_free(fib);
675 : 1 : return TEST_SUCCESS;
676 : : }
677 : :
678 : : /*
679 : : * Exercise compression (same nh as parent), forced decompression on
680 : : * DEL parent, then re-compression after re-adding the same ancestor.
681 : : * The tbl8 reservation accounting must remain balanced even though
682 : : * the child is physically decompressed/recompressed in the dataplane.
683 : : *
684 : : * Layout: parent fcde::/28 and child fcde:0:6000::/48, both nh=1.
685 : : *
686 : : * ADD /28 (no ancestor) rsvd_tbl8s += 1
687 : : * ADD /48 (compressed under /28) rsvd_tbl8s += 2
688 : : * DEL /28 (decompresses /48) rsvd unchanged (/48 keeps)
689 : : * re-ADD /28 (re-compresses /48) rsvd unchanged
690 : : * DEL /48 rsvd_tbl8s -= 2
691 : : * DEL /28 rsvd_tbl8s -= 1
692 : : */
693 : : static int32_t
694 : 1 : test_drift_compression(void)
695 : : {
696 : 1 : struct rte_fib6_conf config = { 0 };
697 : : struct rte_fib6 *fib;
698 : 1 : struct rte_ipv6_addr parent = RTE_IPV6(0xfcde, 0, 0, 0, 0, 0, 0, 0);
699 : 1 : struct rte_ipv6_addr child = RTE_IPV6(0xfcde, 0, 0x6000, 0, 0, 0, 0, 0);
700 : : int ret;
701 : :
702 : 1 : config.max_routes = 1024;
703 : : config.rib_ext_sz = 0;
704 : : config.default_nh = 0;
705 : 1 : config.type = RTE_FIB6_TRIE;
706 : 1 : config.trie.nh_sz = RTE_FIB6_TRIE_2B;
707 : 1 : config.trie.num_tbl8 = 256;
708 : :
709 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
710 [ - + ]: 1 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
711 : :
712 : : /* Compressed: child shares the parent's nh, modify_dp is skipped */
713 : 1 : ret = rte_fib6_add(fib, &parent, 28, 1);
714 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "ADD /28 failed\n");
715 : 1 : ret = rte_fib6_add(fib, &child, 48, 1);
716 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "ADD /48 (compressed) failed\n");
717 : :
718 : : /* DEL parent forces decompression: child must be materialized */
719 : 1 : ret = rte_fib6_delete(fib, &parent, 28);
720 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "DEL /28 (decompression) failed\n");
721 : :
722 : : /* Re-add parent with same nh: child becomes compressed again */
723 : 1 : ret = rte_fib6_add(fib, &parent, 28, 1);
724 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "Re-ADD /28 failed\n");
725 : :
726 : 1 : ret = rte_fib6_delete(fib, &child, 48);
727 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "DEL /48 failed\n");
728 : 1 : ret = rte_fib6_delete(fib, &parent, 28);
729 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "DEL /28 final failed\n");
730 : :
731 : 1 : rte_fib6_free(fib);
732 : 1 : return TEST_SUCCESS;
733 : : }
734 : :
735 : : /*
736 : : * Three-level nesting with compressed and non-compressed paths, then
737 : : * DEL of the middle prefix. The byte-boundary supernet accounting
738 : : * must remain balanced through the chain.
739 : : *
740 : : * Layout: grand fcde::/28 nh=1, mid fcde:0:6000::/48 nh=1 (compressed
741 : : * under grand), leaf fcde:0:6000::4000::/96 nh=2 (not compressed).
742 : : *
743 : : * ADD /28 (no ancestor) rsvd_tbl8s += 1
744 : : * ADD /48 (compressed under /28) rsvd_tbl8s += 2
745 : : * ADD /96 (not compressed under /48) rsvd_tbl8s += 6
746 : : * DEL /48 (leaf /96 still covers 32, 40) rsvd_tbl8s -= 0
747 : : * DEL /28 (only level 24 was solely /28's) rsvd_tbl8s -= 0
748 : : * DEL /96 (last route gone, all freed) rsvd_tbl8s -= 9
749 : : *
750 : : * Boundaries get refunded only on the DEL that makes them empty;
751 : : * intermediate DELs that leave a covering descendant are refund-free.
752 : : */
753 : : static int32_t
754 : 1 : test_drift_multilevel(void)
755 : : {
756 : 1 : struct rte_fib6_conf config = { 0 };
757 : : struct rte_fib6 *fib;
758 : 1 : struct rte_ipv6_addr grand = RTE_IPV6(0xfcde, 0, 0, 0, 0, 0, 0, 0);
759 : 1 : struct rte_ipv6_addr mid = RTE_IPV6(0xfcde, 0, 0x6000, 0, 0, 0, 0, 0);
760 : 1 : struct rte_ipv6_addr leaf = RTE_IPV6(0xfcde, 0, 0x6000, 0, 0, 0x4000, 0, 0);
761 : : int ret;
762 : :
763 : 1 : config.max_routes = 1024;
764 : : config.rib_ext_sz = 0;
765 : : config.default_nh = 0;
766 : 1 : config.type = RTE_FIB6_TRIE;
767 : 1 : config.trie.nh_sz = RTE_FIB6_TRIE_2B;
768 : 1 : config.trie.num_tbl8 = 256;
769 : :
770 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
771 [ - + ]: 1 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
772 : :
773 : 1 : ret = rte_fib6_add(fib, &grand, 28, 1);
774 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "ADD /28 failed\n");
775 : 1 : ret = rte_fib6_add(fib, &mid, 48, 1); /* compressed under /28 */
776 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "ADD /48 failed\n");
777 : 1 : ret = rte_fib6_add(fib, &leaf, 96, 2); /* non-compressed under /48 */
778 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "ADD /96 failed\n");
779 : :
780 : : /* DEL the middle prefix: byte-boundary accounting must stay
781 : : * coherent so the subsequent operations succeed.
782 : : */
783 : 1 : ret = rte_fib6_delete(fib, &mid, 48);
784 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "DEL /48 failed\n");
785 : :
786 : 1 : ret = rte_fib6_delete(fib, &grand, 28);
787 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "DEL /28 failed\n");
788 : 1 : ret = rte_fib6_delete(fib, &leaf, 96);
789 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "DEL /96 failed\n");
790 : :
791 : 1 : rte_fib6_free(fib);
792 : 1 : return TEST_SUCCESS;
793 : : }
794 : :
795 : : /*
796 : : * Pseudo-random ADD/DEL sequence over 8 prefixes with varying depths
797 : : * and next-hops. A hand-rolled LCG (not rte_rand) makes the sequence
798 : : * reproducible across runs and DPDK versions. After all prefixes are
799 : : * removed, a final ADD/DEL pair must succeed - it would fail under a
800 : : * leaked rsvd_tbl8s.
801 : : *
802 : : * depths[1] and depths[6] both use /36 on purpose: ips[1] and ips[6]
803 : : * are distinct prefixes, so this exercises two parallel /36 ADD/DEL
804 : : * paths that share byte boundaries 24 and 32.
805 : : */
806 : : static int32_t
807 : 1 : test_drift_stress(void)
808 : : {
809 : 1 : uint8_t depths[8] = { 28, 36, 40, 48, 64, 80, 36, 128 };
810 : 1 : struct rte_fib6_conf config = { 0 };
811 : 1 : struct rte_ipv6_addr ips[8] = {
812 : : RTE_IPV6(0xfcde, 0, 0, 0, 0, 0, 0, 0),
813 : : RTE_IPV6(0xfcde, 0x1, 0, 0, 0, 0, 0, 0),
814 : : RTE_IPV6(0xfcde, 0x2, 0, 0, 0, 0, 0, 0),
815 : : RTE_IPV6(0xfcde, 0x2, 0x4000, 0, 0, 0, 0, 0),
816 : : RTE_IPV6(0xfcde, 0x2, 0x4000, 0x1000, 0, 0, 0, 0),
817 : : RTE_IPV6(0xfcde, 0x2, 0x4000, 0x1000, 0x1, 0, 0, 0),
818 : : RTE_IPV6(0xfcde, 0x3, 0, 0, 0, 0, 0, 0),
819 : : RTE_IPV6(0xfcde, 0x3, 0, 0, 0, 0, 0, 0x1),
820 : : };
821 : 1 : uint8_t live[8] = { 0 };
822 : : struct rte_fib6 *fib;
823 : : uint32_t seed = 0x4242;
824 : : unsigned int i, idx;
825 : : int ret;
826 : :
827 : 1 : config.max_routes = 64;
828 : : config.rib_ext_sz = 0;
829 : : config.default_nh = 0;
830 : 1 : config.type = RTE_FIB6_TRIE;
831 : 1 : config.trie.nh_sz = RTE_FIB6_TRIE_2B;
832 : 1 : config.trie.num_tbl8 = 256;
833 : :
834 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
835 [ - + ]: 1 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
836 : :
837 [ + + ]: 2001 : for (i = 0; i < 2000; i++) {
838 : 2000 : seed = seed * 1103515245u + 12345u;
839 : 2000 : idx = (seed >> 8) & 7;
840 [ + + ]: 2000 : if (live[idx]) {
841 : 998 : ret = rte_fib6_delete(fib, &ips[idx], depths[idx]);
842 [ - + ]: 998 : RTE_TEST_ASSERT(ret == 0,
843 : : "DEL idx %u (depth /%u) failed (ret=%d)\n",
844 : : idx, depths[idx], ret);
845 : 998 : live[idx] = 0;
846 : : } else {
847 : 1002 : uint64_t nh = ((seed >> 16) & 0xff) + 1;
848 : 1002 : ret = rte_fib6_add(fib, &ips[idx], depths[idx], nh);
849 [ - + ]: 1002 : RTE_TEST_ASSERT(ret == 0,
850 : : "ADD idx %u (depth /%u nh=%" PRIu64 ") failed (ret=%d)\n",
851 : : idx, depths[idx], nh, ret);
852 : 1002 : live[idx] = 1;
853 : : }
854 : : }
855 : :
856 : : /* Drain everything */
857 [ + + ]: 9 : for (i = 0; i < RTE_DIM(live); i++) {
858 [ + + ]: 8 : if (live[i]) {
859 : 4 : ret = rte_fib6_delete(fib, &ips[i], depths[i]);
860 [ - + ]: 4 : RTE_TEST_ASSERT(ret == 0,
861 : : "final drain DEL idx %u failed (ret=%d)\n",
862 : : i, ret);
863 : : }
864 : : }
865 : :
866 : : /* If rsvd_tbl8s had leaked, this fresh ADD would fail */
867 : 1 : ret = rte_fib6_add(fib, &ips[0], depths[0], 0xff);
868 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0,
869 : : "post-drain ADD failed (rsvd leaked?) (ret=%d)\n", ret);
870 : 1 : ret = rte_fib6_delete(fib, &ips[0], depths[0]);
871 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "post-drain DEL failed\n");
872 : :
873 : 1 : rte_fib6_free(fib);
874 : 1 : return TEST_SUCCESS;
875 : : }
876 : :
877 : : /* Tight-pool re-compression scenario. Pool sized to exactly the
878 : : * highest legitimate envelope: an ADD that becomes a closer ancestor
879 : : * of an existing descendant must succeed because the byte-boundary
880 : : * supernet accounting reports the same envelope post-operation.
881 : : *
882 : : * num_tbl8 = 3
883 : : * ADD /28 nh=1 rsvd = 1
884 : : * ADD /48 nh=1 (compr.) rsvd = 3 (/48 reserves 2 new boundaries)
885 : : * DEL /28 rsvd unchanged (/48 still holds them)
886 : : * RE-ADD /28 nh=1 rsvd unchanged (already reserved)
887 : : * (pre-fix: pre-check rejects)
888 : : */
889 : : static int32_t
890 : 1 : test_drift_tight_pool(void)
891 : : {
892 : 1 : struct rte_fib6_conf config = { 0 };
893 : : struct rte_fib6 *fib;
894 : 1 : struct rte_ipv6_addr parent = RTE_IPV6(0xfcde, 0, 0, 0, 0, 0, 0, 0);
895 : 1 : struct rte_ipv6_addr child = RTE_IPV6(0xfcde, 0, 0x6000, 0, 0, 0, 0, 0);
896 : : int ret;
897 : :
898 : 1 : config.max_routes = 16;
899 : : config.rib_ext_sz = 0;
900 : : config.default_nh = 0;
901 : 1 : config.type = RTE_FIB6_TRIE;
902 : 1 : config.trie.nh_sz = RTE_FIB6_TRIE_2B;
903 : 1 : config.trie.num_tbl8 = 3;
904 : :
905 : 1 : fib = rte_fib6_create(__func__, SOCKET_ID_ANY, &config);
906 [ - + ]: 1 : RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n");
907 : :
908 : 1 : ret = rte_fib6_add(fib, &parent, 28, 1);
909 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "ADD /28 failed (ret=%d)\n", ret);
910 : 1 : ret = rte_fib6_add(fib, &child, 48, 1);
911 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "ADD /48 failed (ret=%d)\n", ret);
912 : 1 : ret = rte_fib6_delete(fib, &parent, 28);
913 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "DEL /28 failed (ret=%d)\n", ret);
914 : :
915 : : /* Re-add /28: byte boundary 24 is already occupied by the /48,
916 : : * so the re-added /28 introduces no new reservation. The
917 : : * envelope stays at 3 and still fits the pool of 3.
918 : : */
919 : 1 : ret = rte_fib6_add(fib, &parent, 28, 1);
920 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0,
921 : : "Re-ADD /28 spuriously failed (ret=%d)\n", ret);
922 : :
923 : 1 : ret = rte_fib6_delete(fib, &child, 48);
924 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "DEL /48 failed (ret=%d)\n", ret);
925 : 1 : ret = rte_fib6_delete(fib, &parent, 28);
926 [ - + ]: 1 : RTE_TEST_ASSERT(ret == 0, "Final DEL /28 failed (ret=%d)\n", ret);
927 : :
928 : 1 : rte_fib6_free(fib);
929 : 1 : return TEST_SUCCESS;
930 : : }
931 : :
932 : : static struct unit_test_suite fib6_fast_tests = {
933 : : .suite_name = "fib6 autotest",
934 : : .setup = NULL,
935 : : .teardown = NULL,
936 : : .unit_test_cases = {
937 : : TEST_CASE(test_create_invalid),
938 : : TEST_CASE(test_free_null),
939 : : TEST_CASE(test_add_del_invalid),
940 : : TEST_CASE(test_get_invalid),
941 : : TEST_CASE(test_lookup),
942 : : TEST_CASE(test_invalid_rcu),
943 : : TEST_CASE(test_fib_rcu_sync_rw),
944 : : TEST_CASE(test_drift),
945 : : TEST_CASE(test_drift_compression),
946 : : TEST_CASE(test_drift_multilevel),
947 : : TEST_CASE(test_drift_stress),
948 : : TEST_CASE(test_drift_tight_pool),
949 : : TEST_CASES_END()
950 : : }
951 : : };
952 : :
953 : : static struct unit_test_suite fib6_slow_tests = {
954 : : .suite_name = "fib6 slow autotest",
955 : : .setup = NULL,
956 : : .teardown = NULL,
957 : : .unit_test_cases = {
958 : : TEST_CASE(test_multiple_create),
959 : : TEST_CASES_END()
960 : : }
961 : : };
962 : :
963 : : /*
964 : : * Do all unit tests.
965 : : */
966 : : static int
967 : 1 : test_fib6(void)
968 : : {
969 : 1 : return unit_test_suite_runner(&fib6_fast_tests);
970 : : }
971 : :
972 : : static int
973 : 0 : test_slow_fib6(void)
974 : : {
975 : 0 : return unit_test_suite_runner(&fib6_slow_tests);
976 : : }
977 : :
978 : 303 : REGISTER_FAST_TEST(fib6_autotest, NOHUGE_OK, ASAN_OK, test_fib6);
979 : 303 : REGISTER_PERF_TEST(fib6_slow_autotest, test_slow_fib6);
|