test/crypto: add TTL and hop limit decrement cases
[dpdk.git] / app / test / test_lpm.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4
5 #include "test.h"
6
7 #ifdef RTE_EXEC_ENV_WINDOWS
8 static int
9 test_lpm(void)
10 {
11         printf("lpm not supported on Windows, skipping test\n");
12         return TEST_SKIPPED;
13 }
14
15 #else
16
17 #include <stdio.h>
18 #include <stdint.h>
19 #include <stdlib.h>
20
21 #include <rte_ip.h>
22 #include <rte_lpm.h>
23 #include <rte_malloc.h>
24
25 #include "test_xmmt_ops.h"
26
27 #define TEST_LPM_ASSERT(cond) do {                                            \
28         if (!(cond)) {                                                        \
29                 printf("Error at line %d: \n", __LINE__);                     \
30                 return -1;                                                    \
31         }                                                                     \
32 } while(0)
33
34 typedef int32_t (*rte_lpm_test)(void);
35
36 static int32_t test0(void);
37 static int32_t test1(void);
38 static int32_t test2(void);
39 static int32_t test3(void);
40 static int32_t test4(void);
41 static int32_t test5(void);
42 static int32_t test6(void);
43 static int32_t test7(void);
44 static int32_t test8(void);
45 static int32_t test9(void);
46 static int32_t test10(void);
47 static int32_t test11(void);
48 static int32_t test12(void);
49 static int32_t test13(void);
50 static int32_t test14(void);
51 static int32_t test15(void);
52 static int32_t test16(void);
53 static int32_t test17(void);
54 static int32_t test18(void);
55 static int32_t test19(void);
56 static int32_t test20(void);
57 static int32_t test21(void);
58
59 rte_lpm_test tests[] = {
60 /* Test Cases */
61         test0,
62         test1,
63         test2,
64         test3,
65         test4,
66         test5,
67         test6,
68         test7,
69         test8,
70         test9,
71         test10,
72         test11,
73         test12,
74         test13,
75         test14,
76         test15,
77         test16,
78         test17,
79         test18,
80         test19,
81         test20,
82         test21
83 };
84
85 #define MAX_DEPTH 32
86 #define MAX_RULES 256
87 #define NUMBER_TBL8S 256
88 #define PASS 0
89
90 /*
91  * Check that rte_lpm_create fails gracefully for incorrect user input
92  * arguments
93  */
94 int32_t
95 test0(void)
96 {
97         struct rte_lpm *lpm = NULL;
98         struct rte_lpm_config config;
99
100         config.max_rules = MAX_RULES;
101         config.number_tbl8s = NUMBER_TBL8S;
102         config.flags = 0;
103
104         /* rte_lpm_create: lpm name == NULL */
105         lpm = rte_lpm_create(NULL, SOCKET_ID_ANY, &config);
106         TEST_LPM_ASSERT(lpm == NULL);
107
108         /* rte_lpm_create: max_rules = 0 */
109         /* Note: __func__ inserts the function name, in this case "test0". */
110         config.max_rules = 0;
111         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
112         TEST_LPM_ASSERT(lpm == NULL);
113
114         /* socket_id < -1 is invalid */
115         config.max_rules = MAX_RULES;
116         lpm = rte_lpm_create(__func__, -2, &config);
117         TEST_LPM_ASSERT(lpm == NULL);
118
119         return PASS;
120 }
121
122 /*
123  * Create lpm table then delete lpm table 100 times
124  * Use a slightly different rules size each time
125  * */
126 int32_t
127 test1(void)
128 {
129         struct rte_lpm *lpm = NULL;
130         struct rte_lpm_config config;
131
132         config.number_tbl8s = NUMBER_TBL8S;
133         config.flags = 0;
134         int32_t i;
135
136         /* rte_lpm_free: Free NULL */
137         for (i = 0; i < 100; i++) {
138                 config.max_rules = MAX_RULES - i;
139                 lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
140                 TEST_LPM_ASSERT(lpm != NULL);
141
142                 rte_lpm_free(lpm);
143         }
144
145         /* Can not test free so return success */
146         return PASS;
147 }
148
149 /*
150  * Call rte_lpm_free for NULL pointer user input. Note: free has no return and
151  * therefore it is impossible to check for failure but this test is added to
152  * increase function coverage metrics and to validate that freeing null does
153  * not crash.
154  */
155 int32_t
156 test2(void)
157 {
158         struct rte_lpm *lpm = NULL;
159         struct rte_lpm_config config;
160
161         config.max_rules = MAX_RULES;
162         config.number_tbl8s = NUMBER_TBL8S;
163         config.flags = 0;
164
165         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
166         TEST_LPM_ASSERT(lpm != NULL);
167
168         rte_lpm_free(lpm);
169         rte_lpm_free(NULL);
170         return PASS;
171 }
172
173 /*
174  * Check that rte_lpm_add fails gracefully for incorrect user input arguments
175  */
176 int32_t
177 test3(void)
178 {
179         struct rte_lpm *lpm = NULL;
180         struct rte_lpm_config config;
181
182         config.max_rules = MAX_RULES;
183         config.number_tbl8s = NUMBER_TBL8S;
184         config.flags = 0;
185         uint32_t ip = RTE_IPV4(0, 0, 0, 0), next_hop = 100;
186         uint8_t depth = 24;
187         int32_t status = 0;
188
189         /* rte_lpm_add: lpm == NULL */
190         status = rte_lpm_add(NULL, ip, depth, next_hop);
191         TEST_LPM_ASSERT(status < 0);
192
193         /*Create valid lpm to use in rest of test. */
194         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
195         TEST_LPM_ASSERT(lpm != NULL);
196
197         /* rte_lpm_add: depth < 1 */
198         status = rte_lpm_add(lpm, ip, 0, next_hop);
199         TEST_LPM_ASSERT(status < 0);
200
201         /* rte_lpm_add: depth > MAX_DEPTH */
202         status = rte_lpm_add(lpm, ip, (MAX_DEPTH + 1), next_hop);
203         TEST_LPM_ASSERT(status < 0);
204
205         rte_lpm_free(lpm);
206
207         return PASS;
208 }
209
210 /*
211  * Check that rte_lpm_delete fails gracefully for incorrect user input
212  * arguments
213  */
214 int32_t
215 test4(void)
216 {
217         struct rte_lpm *lpm = NULL;
218         struct rte_lpm_config config;
219
220         config.max_rules = MAX_RULES;
221         config.number_tbl8s = NUMBER_TBL8S;
222         config.flags = 0;
223         uint32_t ip = RTE_IPV4(0, 0, 0, 0);
224         uint8_t depth = 24;
225         int32_t status = 0;
226
227         /* rte_lpm_delete: lpm == NULL */
228         status = rte_lpm_delete(NULL, ip, depth);
229         TEST_LPM_ASSERT(status < 0);
230
231         /*Create valid lpm to use in rest of test. */
232         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
233         TEST_LPM_ASSERT(lpm != NULL);
234
235         /* rte_lpm_delete: depth < 1 */
236         status = rte_lpm_delete(lpm, ip, 0);
237         TEST_LPM_ASSERT(status < 0);
238
239         /* rte_lpm_delete: depth > MAX_DEPTH */
240         status = rte_lpm_delete(lpm, ip, (MAX_DEPTH + 1));
241         TEST_LPM_ASSERT(status < 0);
242
243         rte_lpm_free(lpm);
244
245         return PASS;
246 }
247
248 /*
249  * Check that rte_lpm_lookup fails gracefully for incorrect user input
250  * arguments
251  */
252 int32_t
253 test5(void)
254 {
255 #if defined(RTE_LIBRTE_LPM_DEBUG)
256         struct rte_lpm *lpm = NULL;
257         struct rte_lpm_config config;
258
259         config.max_rules = MAX_RULES;
260         config.number_tbl8s = NUMBER_TBL8S;
261         config.flags = 0;
262         uint32_t ip = RTE_IPV4(0, 0, 0, 0), next_hop_return = 0;
263         int32_t status = 0;
264
265         /* rte_lpm_lookup: lpm == NULL */
266         status = rte_lpm_lookup(NULL, ip, &next_hop_return);
267         TEST_LPM_ASSERT(status < 0);
268
269         /*Create valid lpm to use in rest of test. */
270         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
271         TEST_LPM_ASSERT(lpm != NULL);
272
273         /* rte_lpm_lookup: depth < 1 */
274         status = rte_lpm_lookup(lpm, ip, NULL);
275         TEST_LPM_ASSERT(status < 0);
276
277         rte_lpm_free(lpm);
278 #endif
279         return PASS;
280 }
281
282
283
284 /*
285  * Call add, lookup and delete for a single rule with depth <= 24
286  */
287 int32_t
288 test6(void)
289 {
290         struct rte_lpm *lpm = NULL;
291         struct rte_lpm_config config;
292
293         config.max_rules = MAX_RULES;
294         config.number_tbl8s = NUMBER_TBL8S;
295         config.flags = 0;
296         uint32_t ip = RTE_IPV4(0, 0, 0, 0), next_hop_add = 100, next_hop_return = 0;
297         uint8_t depth = 24;
298         int32_t status = 0;
299
300         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
301         TEST_LPM_ASSERT(lpm != NULL);
302
303         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
304         TEST_LPM_ASSERT(status == 0);
305
306         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
307         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
308
309         status = rte_lpm_delete(lpm, ip, depth);
310         TEST_LPM_ASSERT(status == 0);
311
312         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
313         TEST_LPM_ASSERT(status == -ENOENT);
314
315         rte_lpm_free(lpm);
316
317         return PASS;
318 }
319
320 /*
321  * Call add, lookup and delete for a single rule with depth > 24
322  */
323
324 int32_t
325 test7(void)
326 {
327         xmm_t ipx4;
328         uint32_t hop[4];
329         struct rte_lpm *lpm = NULL;
330         struct rte_lpm_config config;
331
332         config.max_rules = MAX_RULES;
333         config.number_tbl8s = NUMBER_TBL8S;
334         config.flags = 0;
335         uint32_t ip = RTE_IPV4(0, 0, 0, 0), next_hop_add = 100, next_hop_return = 0;
336         uint8_t depth = 32;
337         int32_t status = 0;
338
339         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
340         TEST_LPM_ASSERT(lpm != NULL);
341
342         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
343         TEST_LPM_ASSERT(status == 0);
344
345         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
346         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
347
348         ipx4 = vect_set_epi32(ip, ip + 0x100, ip - 0x100, ip);
349         rte_lpm_lookupx4(lpm, ipx4, hop, UINT32_MAX);
350         TEST_LPM_ASSERT(hop[0] == next_hop_add);
351         TEST_LPM_ASSERT(hop[1] == UINT32_MAX);
352         TEST_LPM_ASSERT(hop[2] == UINT32_MAX);
353         TEST_LPM_ASSERT(hop[3] == next_hop_add);
354
355         status = rte_lpm_delete(lpm, ip, depth);
356         TEST_LPM_ASSERT(status == 0);
357
358         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
359         TEST_LPM_ASSERT(status == -ENOENT);
360
361         rte_lpm_free(lpm);
362
363         return PASS;
364 }
365
366 /*
367  * Use rte_lpm_add to add rules which effect only the second half of the lpm
368  * table. Use all possible depths ranging from 1..32. Set the next hop = to the
369  * depth. Check lookup hit for on every add and check for lookup miss on the
370  * first half of the lpm table after each add. Finally delete all rules going
371  * backwards (i.e. from depth = 32 ..1) and carry out a lookup after each
372  * delete. The lookup should return the next_hop_add value related to the
373  * previous depth value (i.e. depth -1).
374  */
375 int32_t
376 test8(void)
377 {
378         xmm_t ipx4;
379         uint32_t hop[4];
380         struct rte_lpm *lpm = NULL;
381         struct rte_lpm_config config;
382
383         config.max_rules = MAX_RULES;
384         config.number_tbl8s = NUMBER_TBL8S;
385         config.flags = 0;
386         uint32_t ip1 = RTE_IPV4(127, 255, 255, 255), ip2 = RTE_IPV4(128, 0, 0, 0);
387         uint32_t next_hop_add, next_hop_return;
388         uint8_t depth;
389         int32_t status = 0;
390
391         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
392         TEST_LPM_ASSERT(lpm != NULL);
393
394         /* Loop with rte_lpm_add. */
395         for (depth = 1; depth <= 32; depth++) {
396                 /* Let the next_hop_add value = depth. Just for change. */
397                 next_hop_add = depth;
398
399                 status = rte_lpm_add(lpm, ip2, depth, next_hop_add);
400                 TEST_LPM_ASSERT(status == 0);
401
402                 /* Check IP in first half of tbl24 which should be empty. */
403                 status = rte_lpm_lookup(lpm, ip1, &next_hop_return);
404                 TEST_LPM_ASSERT(status == -ENOENT);
405
406                 status = rte_lpm_lookup(lpm, ip2, &next_hop_return);
407                 TEST_LPM_ASSERT((status == 0) &&
408                         (next_hop_return == next_hop_add));
409
410                 ipx4 = vect_set_epi32(ip2, ip1, ip2, ip1);
411                 rte_lpm_lookupx4(lpm, ipx4, hop, UINT32_MAX);
412                 TEST_LPM_ASSERT(hop[0] == UINT32_MAX);
413                 TEST_LPM_ASSERT(hop[1] == next_hop_add);
414                 TEST_LPM_ASSERT(hop[2] == UINT32_MAX);
415                 TEST_LPM_ASSERT(hop[3] == next_hop_add);
416         }
417
418         /* Loop with rte_lpm_delete. */
419         for (depth = 32; depth >= 1; depth--) {
420                 next_hop_add = (uint8_t) (depth - 1);
421
422                 status = rte_lpm_delete(lpm, ip2, depth);
423                 TEST_LPM_ASSERT(status == 0);
424
425                 status = rte_lpm_lookup(lpm, ip2, &next_hop_return);
426
427                 if (depth != 1) {
428                         TEST_LPM_ASSERT((status == 0) &&
429                                 (next_hop_return == next_hop_add));
430                 } else {
431                         TEST_LPM_ASSERT(status == -ENOENT);
432                 }
433
434                 status = rte_lpm_lookup(lpm, ip1, &next_hop_return);
435                 TEST_LPM_ASSERT(status == -ENOENT);
436
437                 ipx4 = vect_set_epi32(ip1, ip1, ip2, ip2);
438                 rte_lpm_lookupx4(lpm, ipx4, hop, UINT32_MAX);
439                 if (depth != 1) {
440                         TEST_LPM_ASSERT(hop[0] == next_hop_add);
441                         TEST_LPM_ASSERT(hop[1] == next_hop_add);
442                 } else {
443                         TEST_LPM_ASSERT(hop[0] == UINT32_MAX);
444                         TEST_LPM_ASSERT(hop[1] == UINT32_MAX);
445                 }
446                 TEST_LPM_ASSERT(hop[2] == UINT32_MAX);
447                 TEST_LPM_ASSERT(hop[3] == UINT32_MAX);
448         }
449
450         rte_lpm_free(lpm);
451
452         return PASS;
453 }
454
455 /*
456  * - Add & lookup to hit invalid TBL24 entry
457  * - Add & lookup to hit valid TBL24 entry not extended
458  * - Add & lookup to hit valid extended TBL24 entry with invalid TBL8 entry
459  * - Add & lookup to hit valid extended TBL24 entry with valid TBL8 entry
460  *
461  */
462 int32_t
463 test9(void)
464 {
465         struct rte_lpm *lpm = NULL;
466         struct rte_lpm_config config;
467
468         config.max_rules = MAX_RULES;
469         config.number_tbl8s = NUMBER_TBL8S;
470         config.flags = 0;
471         uint32_t ip, ip_1, ip_2;
472         uint8_t depth, depth_1, depth_2;
473         uint32_t next_hop_add, next_hop_add_1, next_hop_add_2, next_hop_return;
474         int32_t status = 0;
475
476         /* Add & lookup to hit invalid TBL24 entry */
477         ip = RTE_IPV4(128, 0, 0, 0);
478         depth = 24;
479         next_hop_add = 100;
480
481         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
482         TEST_LPM_ASSERT(lpm != NULL);
483
484         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
485         TEST_LPM_ASSERT(status == 0);
486
487         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
488         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
489
490         status = rte_lpm_delete(lpm, ip, depth);
491         TEST_LPM_ASSERT(status == 0);
492
493         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
494         TEST_LPM_ASSERT(status == -ENOENT);
495
496         rte_lpm_delete_all(lpm);
497
498         /* Add & lookup to hit valid TBL24 entry not extended */
499         ip = RTE_IPV4(128, 0, 0, 0);
500         depth = 23;
501         next_hop_add = 100;
502
503         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
504         TEST_LPM_ASSERT(status == 0);
505
506         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
507         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
508
509         depth = 24;
510         next_hop_add = 101;
511
512         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
513         TEST_LPM_ASSERT(status == 0);
514
515         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
516         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
517
518         depth = 24;
519
520         status = rte_lpm_delete(lpm, ip, depth);
521         TEST_LPM_ASSERT(status == 0);
522
523         depth = 23;
524
525         status = rte_lpm_delete(lpm, ip, depth);
526         TEST_LPM_ASSERT(status == 0);
527
528         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
529         TEST_LPM_ASSERT(status == -ENOENT);
530
531         rte_lpm_delete_all(lpm);
532
533         /* Add & lookup to hit valid extended TBL24 entry with invalid TBL8
534          * entry */
535         ip = RTE_IPV4(128, 0, 0, 0);
536         depth = 32;
537         next_hop_add = 100;
538
539         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
540         TEST_LPM_ASSERT(status == 0);
541
542         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
543         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
544
545         ip = RTE_IPV4(128, 0, 0, 5);
546         depth = 32;
547         next_hop_add = 101;
548
549         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
550         TEST_LPM_ASSERT(status == 0);
551
552         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
553         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
554
555         status = rte_lpm_delete(lpm, ip, depth);
556         TEST_LPM_ASSERT(status == 0);
557
558         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
559         TEST_LPM_ASSERT(status == -ENOENT);
560
561         ip = RTE_IPV4(128, 0, 0, 0);
562         depth = 32;
563         next_hop_add = 100;
564
565         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
566         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
567
568         status = rte_lpm_delete(lpm, ip, depth);
569         TEST_LPM_ASSERT(status == 0);
570
571         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
572         TEST_LPM_ASSERT(status == -ENOENT);
573
574         rte_lpm_delete_all(lpm);
575
576         /* Add & lookup to hit valid extended TBL24 entry with valid TBL8
577          * entry */
578         ip_1 = RTE_IPV4(128, 0, 0, 0);
579         depth_1 = 25;
580         next_hop_add_1 = 101;
581
582         ip_2 = RTE_IPV4(128, 0, 0, 5);
583         depth_2 = 32;
584         next_hop_add_2 = 102;
585
586         next_hop_return = 0;
587
588         status = rte_lpm_add(lpm, ip_1, depth_1, next_hop_add_1);
589         TEST_LPM_ASSERT(status == 0);
590
591         status = rte_lpm_lookup(lpm, ip_1, &next_hop_return);
592         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add_1));
593
594         status = rte_lpm_add(lpm, ip_2, depth_2, next_hop_add_2);
595         TEST_LPM_ASSERT(status == 0);
596
597         status = rte_lpm_lookup(lpm, ip_2, &next_hop_return);
598         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add_2));
599
600         status = rte_lpm_delete(lpm, ip_2, depth_2);
601         TEST_LPM_ASSERT(status == 0);
602
603         status = rte_lpm_lookup(lpm, ip_2, &next_hop_return);
604         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add_1));
605
606         status = rte_lpm_delete(lpm, ip_1, depth_1);
607         TEST_LPM_ASSERT(status == 0);
608
609         status = rte_lpm_lookup(lpm, ip_1, &next_hop_return);
610         TEST_LPM_ASSERT(status == -ENOENT);
611
612         rte_lpm_free(lpm);
613
614         return PASS;
615 }
616
617
618 /*
619  * - Add rule that covers a TBL24 range previously invalid & lookup (& delete &
620  *   lookup)
621  * - Add rule that extends a TBL24 invalid entry & lookup (& delete & lookup)
622  * - Add rule that extends a TBL24 valid entry & lookup for both rules (&
623  *   delete & lookup)
624  * - Add rule that updates the next hop in TBL24 & lookup (& delete & lookup)
625  * - Add rule that updates the next hop in TBL8 & lookup (& delete & lookup)
626  * - Delete a rule that is not present in the TBL24 & lookup
627  * - Delete a rule that is not present in the TBL8 & lookup
628  *
629  */
630 int32_t
631 test10(void)
632 {
633
634         struct rte_lpm *lpm = NULL;
635         struct rte_lpm_config config;
636
637         config.max_rules = MAX_RULES;
638         config.number_tbl8s = NUMBER_TBL8S;
639         config.flags = 0;
640         uint32_t ip, next_hop_add, next_hop_return;
641         uint8_t depth;
642         int32_t status = 0;
643
644         /* Add rule that covers a TBL24 range previously invalid & lookup
645          * (& delete & lookup) */
646         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
647         TEST_LPM_ASSERT(lpm != NULL);
648
649         ip = RTE_IPV4(128, 0, 0, 0);
650         depth = 16;
651         next_hop_add = 100;
652
653         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
654         TEST_LPM_ASSERT(status == 0);
655
656         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
657         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
658
659         status = rte_lpm_delete(lpm, ip, depth);
660         TEST_LPM_ASSERT(status == 0);
661
662         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
663         TEST_LPM_ASSERT(status == -ENOENT);
664
665         rte_lpm_delete_all(lpm);
666
667         ip = RTE_IPV4(128, 0, 0, 0);
668         depth = 25;
669         next_hop_add = 100;
670
671         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
672         TEST_LPM_ASSERT(status == 0);
673
674         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
675         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
676
677         status = rte_lpm_delete(lpm, ip, depth);
678         TEST_LPM_ASSERT(status == 0);
679
680         rte_lpm_delete_all(lpm);
681
682         /* Add rule that extends a TBL24 valid entry & lookup for both rules
683          * (& delete & lookup) */
684
685         ip = RTE_IPV4(128, 0, 0, 0);
686         depth = 24;
687         next_hop_add = 100;
688
689         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
690         TEST_LPM_ASSERT(status == 0);
691
692         ip = RTE_IPV4(128, 0, 0, 10);
693         depth = 32;
694         next_hop_add = 101;
695
696         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
697         TEST_LPM_ASSERT(status == 0);
698
699         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
700         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
701
702         ip = RTE_IPV4(128, 0, 0, 0);
703         next_hop_add = 100;
704
705         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
706         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
707
708         ip = RTE_IPV4(128, 0, 0, 0);
709         depth = 24;
710
711         status = rte_lpm_delete(lpm, ip, depth);
712         TEST_LPM_ASSERT(status == 0);
713
714         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
715         TEST_LPM_ASSERT(status == -ENOENT);
716
717         ip = RTE_IPV4(128, 0, 0, 10);
718         depth = 32;
719
720         status = rte_lpm_delete(lpm, ip, depth);
721         TEST_LPM_ASSERT(status == 0);
722
723         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
724         TEST_LPM_ASSERT(status == -ENOENT);
725
726         rte_lpm_delete_all(lpm);
727
728         /* Add rule that updates the next hop in TBL24 & lookup
729          * (& delete & lookup) */
730
731         ip = RTE_IPV4(128, 0, 0, 0);
732         depth = 24;
733         next_hop_add = 100;
734
735         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
736         TEST_LPM_ASSERT(status == 0);
737
738         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
739         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
740
741         next_hop_add = 101;
742
743         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
744         TEST_LPM_ASSERT(status == 0);
745
746         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
747         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
748
749         status = rte_lpm_delete(lpm, ip, depth);
750         TEST_LPM_ASSERT(status == 0);
751
752         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
753         TEST_LPM_ASSERT(status == -ENOENT);
754
755         rte_lpm_delete_all(lpm);
756
757         /* Add rule that updates the next hop in TBL8 & lookup
758          * (& delete & lookup) */
759
760         ip = RTE_IPV4(128, 0, 0, 0);
761         depth = 32;
762         next_hop_add = 100;
763
764         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
765         TEST_LPM_ASSERT(status == 0);
766
767         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
768         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
769
770         next_hop_add = 101;
771
772         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
773         TEST_LPM_ASSERT(status == 0);
774
775         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
776         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
777
778         status = rte_lpm_delete(lpm, ip, depth);
779         TEST_LPM_ASSERT(status == 0);
780
781         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
782         TEST_LPM_ASSERT(status == -ENOENT);
783
784         rte_lpm_delete_all(lpm);
785
786         /* Delete a rule that is not present in the TBL24 & lookup */
787
788         ip = RTE_IPV4(128, 0, 0, 0);
789         depth = 24;
790
791         status = rte_lpm_delete(lpm, ip, depth);
792         TEST_LPM_ASSERT(status < 0);
793
794         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
795         TEST_LPM_ASSERT(status == -ENOENT);
796
797         rte_lpm_delete_all(lpm);
798
799         /* Delete a rule that is not present in the TBL8 & lookup */
800
801         ip = RTE_IPV4(128, 0, 0, 0);
802         depth = 32;
803
804         status = rte_lpm_delete(lpm, ip, depth);
805         TEST_LPM_ASSERT(status < 0);
806
807         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
808         TEST_LPM_ASSERT(status == -ENOENT);
809
810         rte_lpm_free(lpm);
811
812         return PASS;
813 }
814
815 /*
816  * Add two rules, lookup to hit the more specific one, lookup to hit the less
817  * specific one delete the less specific rule and lookup previous values again;
818  * add a more specific rule than the existing rule, lookup again
819  *
820  * */
821 int32_t
822 test11(void)
823 {
824
825         struct rte_lpm *lpm = NULL;
826         struct rte_lpm_config config;
827
828         config.max_rules = MAX_RULES;
829         config.number_tbl8s = NUMBER_TBL8S;
830         config.flags = 0;
831         uint32_t ip, next_hop_add, next_hop_return;
832         uint8_t depth;
833         int32_t status = 0;
834
835         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
836         TEST_LPM_ASSERT(lpm != NULL);
837
838         ip = RTE_IPV4(128, 0, 0, 0);
839         depth = 24;
840         next_hop_add = 100;
841
842         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
843         TEST_LPM_ASSERT(status == 0);
844
845         ip = RTE_IPV4(128, 0, 0, 10);
846         depth = 32;
847         next_hop_add = 101;
848
849         status = rte_lpm_add(lpm, ip, depth, next_hop_add);
850         TEST_LPM_ASSERT(status == 0);
851
852         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
853         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
854
855         ip = RTE_IPV4(128, 0, 0, 0);
856         next_hop_add = 100;
857
858         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
859         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add));
860
861         ip = RTE_IPV4(128, 0, 0, 0);
862         depth = 24;
863
864         status = rte_lpm_delete(lpm, ip, depth);
865         TEST_LPM_ASSERT(status == 0);
866
867         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
868         TEST_LPM_ASSERT(status == -ENOENT);
869
870         ip = RTE_IPV4(128, 0, 0, 10);
871         depth = 32;
872
873         status = rte_lpm_delete(lpm, ip, depth);
874         TEST_LPM_ASSERT(status == 0);
875
876         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
877         TEST_LPM_ASSERT(status == -ENOENT);
878
879         rte_lpm_free(lpm);
880
881         return PASS;
882 }
883
884 /*
885  * Add an extended rule (i.e. depth greater than 24, lookup (hit), delete,
886  * lookup (miss) in a for loop of 1000 times. This will check tbl8 extension
887  * and contraction.
888  *
889  * */
890
891 int32_t
892 test12(void)
893 {
894         xmm_t ipx4;
895         uint32_t hop[4];
896         struct rte_lpm *lpm = NULL;
897         struct rte_lpm_config config;
898
899         config.max_rules = MAX_RULES;
900         config.number_tbl8s = NUMBER_TBL8S;
901         config.flags = 0;
902         uint32_t ip, i, next_hop_add, next_hop_return;
903         uint8_t depth;
904         int32_t status = 0;
905
906         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
907         TEST_LPM_ASSERT(lpm != NULL);
908
909         ip = RTE_IPV4(128, 0, 0, 0);
910         depth = 32;
911         next_hop_add = 100;
912
913         for (i = 0; i < 1000; i++) {
914                 status = rte_lpm_add(lpm, ip, depth, next_hop_add);
915                 TEST_LPM_ASSERT(status == 0);
916
917                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
918                 TEST_LPM_ASSERT((status == 0) &&
919                                 (next_hop_return == next_hop_add));
920
921                 ipx4 = vect_set_epi32(ip, ip + 1, ip, ip - 1);
922                 rte_lpm_lookupx4(lpm, ipx4, hop, UINT32_MAX);
923                 TEST_LPM_ASSERT(hop[0] == UINT32_MAX);
924                 TEST_LPM_ASSERT(hop[1] == next_hop_add);
925                 TEST_LPM_ASSERT(hop[2] == UINT32_MAX);
926                 TEST_LPM_ASSERT(hop[3] == next_hop_add);
927
928                 status = rte_lpm_delete(lpm, ip, depth);
929                 TEST_LPM_ASSERT(status == 0);
930
931                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
932                 TEST_LPM_ASSERT(status == -ENOENT);
933         }
934
935         rte_lpm_free(lpm);
936
937         return PASS;
938 }
939
940 /*
941  * Add a rule to tbl24, lookup (hit), then add a rule that will extend this
942  * tbl24 entry, lookup (hit). delete the rule that caused the tbl24 extension,
943  * lookup (miss) and repeat for loop of 1000 times. This will check tbl8
944  * extension and contraction.
945  *
946  * */
947
948 int32_t
949 test13(void)
950 {
951         struct rte_lpm *lpm = NULL;
952         struct rte_lpm_config config;
953
954         config.max_rules = MAX_RULES;
955         config.number_tbl8s = NUMBER_TBL8S;
956         config.flags = 0;
957         uint32_t ip, i, next_hop_add_1, next_hop_add_2, next_hop_return;
958         uint8_t depth;
959         int32_t status = 0;
960
961         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
962         TEST_LPM_ASSERT(lpm != NULL);
963
964         ip = RTE_IPV4(128, 0, 0, 0);
965         depth = 24;
966         next_hop_add_1 = 100;
967
968         status = rte_lpm_add(lpm, ip, depth, next_hop_add_1);
969         TEST_LPM_ASSERT(status == 0);
970
971         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
972         TEST_LPM_ASSERT((status == 0) && (next_hop_return == next_hop_add_1));
973
974         depth = 32;
975         next_hop_add_2 = 101;
976
977         for (i = 0; i < 1000; i++) {
978                 status = rte_lpm_add(lpm, ip, depth, next_hop_add_2);
979                 TEST_LPM_ASSERT(status == 0);
980
981                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
982                 TEST_LPM_ASSERT((status == 0) &&
983                                 (next_hop_return == next_hop_add_2));
984
985                 status = rte_lpm_delete(lpm, ip, depth);
986                 TEST_LPM_ASSERT(status == 0);
987
988                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
989                 TEST_LPM_ASSERT((status == 0) &&
990                                 (next_hop_return == next_hop_add_1));
991         }
992
993         depth = 24;
994
995         status = rte_lpm_delete(lpm, ip, depth);
996         TEST_LPM_ASSERT(status == 0);
997
998         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
999         TEST_LPM_ASSERT(status == -ENOENT);
1000
1001         rte_lpm_free(lpm);
1002
1003         return PASS;
1004 }
1005
1006 /*
1007  * For TBL8 extension exhaustion. Add 512 rules that require a tbl8 extension.
1008  * No more tbl8 extensions will be allowed. Now add one more rule that required
1009  * a tbl8 extension and get fail.
1010  * */
1011 int32_t
1012 test14(void)
1013 {
1014
1015         /* We only use depth = 32 in the loop below so we must make sure
1016          * that we have enough storage for all rules at that depth*/
1017
1018         struct rte_lpm *lpm = NULL;
1019         struct rte_lpm_config config;
1020
1021         config.max_rules = 256 * 32;
1022         config.number_tbl8s = 512;
1023         config.flags = 0;
1024         uint32_t ip, next_hop_base, next_hop_return;
1025         uint8_t depth;
1026         int32_t status = 0;
1027         xmm_t ipx4;
1028         uint32_t hop[4];
1029
1030         /* Add enough space for 256 rules for every depth */
1031         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
1032         TEST_LPM_ASSERT(lpm != NULL);
1033
1034         depth = 32;
1035         next_hop_base = 100;
1036         ip = RTE_IPV4(0, 0, 0, 0);
1037
1038         /* Add 256 rules that require a tbl8 extension */
1039         for (; ip <= RTE_IPV4(0, 1, 255, 0); ip += 256) {
1040                 status = rte_lpm_add(lpm, ip, depth, next_hop_base + ip);
1041                 TEST_LPM_ASSERT(status == 0);
1042
1043                 status = rte_lpm_lookup(lpm, ip, &next_hop_return);
1044                 TEST_LPM_ASSERT((status == 0) &&
1045                                 (next_hop_return == next_hop_base + ip));
1046
1047                 ipx4 = vect_set_epi32(ip + 3, ip + 2, ip + 1, ip);
1048                 rte_lpm_lookupx4(lpm, ipx4, hop, UINT32_MAX);
1049                 TEST_LPM_ASSERT(hop[0] == next_hop_base + ip);
1050                 TEST_LPM_ASSERT(hop[1] == UINT32_MAX);
1051                 TEST_LPM_ASSERT(hop[2] == UINT32_MAX);
1052                 TEST_LPM_ASSERT(hop[3] == UINT32_MAX);
1053         }
1054
1055         /* All tbl8 extensions have been used above. Try to add one more and
1056          * we get a fail */
1057         ip = RTE_IPV4(1, 0, 0, 0);
1058         depth = 32;
1059
1060         status = rte_lpm_add(lpm, ip, depth, next_hop_base + ip);
1061         TEST_LPM_ASSERT(status < 0);
1062
1063         rte_lpm_free(lpm);
1064
1065         return PASS;
1066 }
1067
1068 /*
1069  * Sequence of operations for find existing lpm table
1070  *
1071  *  - create table
1072  *  - find existing table: hit
1073  *  - find non-existing table: miss
1074  *
1075  */
1076 int32_t
1077 test15(void)
1078 {
1079         struct rte_lpm *lpm = NULL, *result = NULL;
1080         struct rte_lpm_config config;
1081
1082         config.max_rules = 256 * 32;
1083         config.number_tbl8s = NUMBER_TBL8S;
1084         config.flags = 0;
1085
1086         /* Create lpm  */
1087         lpm = rte_lpm_create("lpm_find_existing", SOCKET_ID_ANY, &config);
1088         TEST_LPM_ASSERT(lpm != NULL);
1089
1090         /* Try to find existing lpm */
1091         result = rte_lpm_find_existing("lpm_find_existing");
1092         TEST_LPM_ASSERT(result == lpm);
1093
1094         /* Try to find non-existing lpm */
1095         result = rte_lpm_find_existing("lpm_find_non_existing");
1096         TEST_LPM_ASSERT(result == NULL);
1097
1098         /* Cleanup. */
1099         rte_lpm_delete_all(lpm);
1100         rte_lpm_free(lpm);
1101
1102         return PASS;
1103 }
1104
1105 /*
1106  * test failure condition of overloading the tbl8 so no more will fit
1107  * Check we get an error return value in that case
1108  */
1109 int32_t
1110 test16(void)
1111 {
1112         uint32_t ip;
1113         struct rte_lpm_config config;
1114
1115         config.max_rules = 256 * 32;
1116         config.number_tbl8s = NUMBER_TBL8S;
1117         config.flags = 0;
1118         struct rte_lpm *lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
1119
1120         /* ip loops through all possibilities for top 24 bits of address */
1121         for (ip = 0; ip < 0xFFFFFF; ip++) {
1122                 /* add an entry within a different tbl8 each time, since
1123                  * depth >24 and the top 24 bits are different */
1124                 if (rte_lpm_add(lpm, (ip << 8) + 0xF0, 30, 0) < 0)
1125                         break;
1126         }
1127
1128         if (ip != NUMBER_TBL8S) {
1129                 printf("Error, unexpected failure with filling tbl8 groups\n");
1130                 printf("Failed after %u additions, expected after %u\n",
1131                                 (unsigned)ip, (unsigned)NUMBER_TBL8S);
1132         }
1133
1134         rte_lpm_free(lpm);
1135         return 0;
1136 }
1137
1138 /*
1139  * Test for overwriting of tbl8:
1140  *  - add rule /32 and lookup
1141  *  - add new rule /24 and lookup
1142  *      - add third rule /25 and lookup
1143  *      - lookup /32 and /24 rule to ensure the table has not been overwritten.
1144  */
1145 int32_t
1146 test17(void)
1147 {
1148         struct rte_lpm *lpm = NULL;
1149         struct rte_lpm_config config;
1150
1151         config.max_rules = MAX_RULES;
1152         config.number_tbl8s = NUMBER_TBL8S;
1153         config.flags = 0;
1154         const uint32_t ip_10_32 = RTE_IPV4(10, 10, 10, 2);
1155         const uint32_t ip_10_24 = RTE_IPV4(10, 10, 10, 0);
1156         const uint32_t ip_20_25 = RTE_IPV4(10, 10, 20, 2);
1157         const uint8_t d_ip_10_32 = 32,
1158                         d_ip_10_24 = 24,
1159                         d_ip_20_25 = 25;
1160         const uint32_t next_hop_ip_10_32 = 100,
1161                         next_hop_ip_10_24 = 105,
1162                         next_hop_ip_20_25 = 111;
1163         uint32_t next_hop_return = 0;
1164         int32_t status = 0;
1165
1166         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
1167         TEST_LPM_ASSERT(lpm != NULL);
1168
1169         if ((status = rte_lpm_add(lpm, ip_10_32, d_ip_10_32,
1170                         next_hop_ip_10_32)) < 0)
1171                 return -1;
1172
1173         status = rte_lpm_lookup(lpm, ip_10_32, &next_hop_return);
1174         uint32_t test_hop_10_32 = next_hop_return;
1175         TEST_LPM_ASSERT(status == 0);
1176         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_10_32);
1177
1178         if ((status = rte_lpm_add(lpm, ip_10_24, d_ip_10_24,
1179                         next_hop_ip_10_24)) < 0)
1180                         return -1;
1181
1182         status = rte_lpm_lookup(lpm, ip_10_24, &next_hop_return);
1183         uint32_t test_hop_10_24 = next_hop_return;
1184         TEST_LPM_ASSERT(status == 0);
1185         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_10_24);
1186
1187         if ((status = rte_lpm_add(lpm, ip_20_25, d_ip_20_25,
1188                         next_hop_ip_20_25)) < 0)
1189                 return -1;
1190
1191         status = rte_lpm_lookup(lpm, ip_20_25, &next_hop_return);
1192         uint32_t test_hop_20_25 = next_hop_return;
1193         TEST_LPM_ASSERT(status == 0);
1194         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_20_25);
1195
1196         if (test_hop_10_32 == test_hop_10_24) {
1197                 printf("Next hop return equal\n");
1198                 return -1;
1199         }
1200
1201         if (test_hop_10_24 == test_hop_20_25) {
1202                 printf("Next hop return equal\n");
1203                 return -1;
1204         }
1205
1206         status = rte_lpm_lookup(lpm, ip_10_32, &next_hop_return);
1207         TEST_LPM_ASSERT(status == 0);
1208         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_10_32);
1209
1210         status = rte_lpm_lookup(lpm, ip_10_24, &next_hop_return);
1211         TEST_LPM_ASSERT(status == 0);
1212         TEST_LPM_ASSERT(next_hop_return == next_hop_ip_10_24);
1213
1214         rte_lpm_free(lpm);
1215
1216         return PASS;
1217 }
1218
1219 /*
1220  * Test for recycle of tbl8
1221  *  - step 1: add a rule with depth=28 (> 24)
1222  *  - step 2: add a rule with same 24-bit prefix and depth=23 (< 24)
1223  *  - step 3: delete the first rule
1224  *  - step 4: check tbl8 is freed
1225  *  - step 5: add a rule same as the first one (depth=28)
1226  *  - step 6: check same tbl8 is allocated
1227  *  - step 7: add a rule with same 24-bit prefix and depth=24
1228  *  - step 8: delete the rule (depth=28) added in step 5
1229  *  - step 9: check tbl8 is freed
1230  *  - step 10: add a rule with same 24-bit prefix and depth = 28
1231  *  - setp 11: check same tbl8 is allocated again
1232  */
1233 int32_t
1234 test18(void)
1235 {
1236 #define group_idx next_hop
1237         struct rte_lpm *lpm = NULL;
1238         struct rte_lpm_config config;
1239         uint32_t ip, next_hop;
1240         uint8_t depth;
1241         uint32_t tbl8_group_index;
1242
1243         config.max_rules = MAX_RULES;
1244         config.number_tbl8s = NUMBER_TBL8S;
1245         config.flags = 0;
1246
1247         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
1248         TEST_LPM_ASSERT(lpm != NULL);
1249
1250         ip = RTE_IPV4(192, 168, 100, 100);
1251         depth = 28;
1252         next_hop = 1;
1253         rte_lpm_add(lpm, ip, depth, next_hop);
1254
1255         TEST_LPM_ASSERT(lpm->tbl24[ip>>8].valid_group);
1256         tbl8_group_index = lpm->tbl24[ip>>8].group_idx;
1257
1258         depth = 23;
1259         next_hop = 2;
1260         rte_lpm_add(lpm, ip, depth, next_hop);
1261         TEST_LPM_ASSERT(lpm->tbl24[ip>>8].valid_group);
1262
1263         depth = 28;
1264         rte_lpm_delete(lpm, ip, depth);
1265
1266         TEST_LPM_ASSERT(!lpm->tbl24[ip>>8].valid_group);
1267
1268         next_hop = 3;
1269         rte_lpm_add(lpm, ip, depth, next_hop);
1270
1271         TEST_LPM_ASSERT(lpm->tbl24[ip>>8].valid_group);
1272         TEST_LPM_ASSERT(tbl8_group_index == lpm->tbl24[ip>>8].group_idx);
1273
1274         depth = 24;
1275         next_hop = 4;
1276         rte_lpm_add(lpm, ip, depth, next_hop);
1277         TEST_LPM_ASSERT(lpm->tbl24[ip>>8].valid_group);
1278
1279         depth = 28;
1280         rte_lpm_delete(lpm, ip, depth);
1281
1282         TEST_LPM_ASSERT(!lpm->tbl24[ip>>8].valid_group);
1283
1284         next_hop = 5;
1285         rte_lpm_add(lpm, ip, depth, next_hop);
1286
1287         TEST_LPM_ASSERT(lpm->tbl24[ip>>8].valid_group);
1288         TEST_LPM_ASSERT(tbl8_group_index == lpm->tbl24[ip>>8].group_idx);
1289
1290         rte_lpm_free(lpm);
1291 #undef group_idx
1292         return PASS;
1293 }
1294
1295 /*
1296  * rte_lpm_rcu_qsbr_add positive and negative tests.
1297  *  - Add RCU QSBR variable to LPM
1298  *  - Add another RCU QSBR variable to LPM
1299  *  - Check returns
1300  */
1301 int32_t
1302 test19(void)
1303 {
1304         struct rte_lpm *lpm = NULL;
1305         struct rte_lpm_config config;
1306         size_t sz;
1307         struct rte_rcu_qsbr *qsv;
1308         struct rte_rcu_qsbr *qsv2;
1309         int32_t status;
1310         struct rte_lpm_rcu_config rcu_cfg = {0};
1311
1312         config.max_rules = MAX_RULES;
1313         config.number_tbl8s = NUMBER_TBL8S;
1314         config.flags = 0;
1315
1316         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
1317         TEST_LPM_ASSERT(lpm != NULL);
1318
1319         /* Create RCU QSBR variable */
1320         sz = rte_rcu_qsbr_get_memsize(RTE_MAX_LCORE);
1321         qsv = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz,
1322                                         RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY);
1323         TEST_LPM_ASSERT(qsv != NULL);
1324
1325         status = rte_rcu_qsbr_init(qsv, RTE_MAX_LCORE);
1326         TEST_LPM_ASSERT(status == 0);
1327
1328         rcu_cfg.v = qsv;
1329         /* Invalid QSBR mode */
1330         rcu_cfg.mode = 2;
1331         status = rte_lpm_rcu_qsbr_add(lpm, &rcu_cfg);
1332         TEST_LPM_ASSERT(status != 0);
1333
1334         rcu_cfg.mode = RTE_LPM_QSBR_MODE_DQ;
1335         /* Attach RCU QSBR to LPM table */
1336         status = rte_lpm_rcu_qsbr_add(lpm, &rcu_cfg);
1337         TEST_LPM_ASSERT(status == 0);
1338
1339         /* Create and attach another RCU QSBR to LPM table */
1340         qsv2 = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz,
1341                                         RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY);
1342         TEST_LPM_ASSERT(qsv2 != NULL);
1343
1344         rcu_cfg.v = qsv2;
1345         rcu_cfg.mode = RTE_LPM_QSBR_MODE_SYNC;
1346         status = rte_lpm_rcu_qsbr_add(lpm, &rcu_cfg);
1347         TEST_LPM_ASSERT(status != 0);
1348
1349         rte_lpm_free(lpm);
1350         rte_free(qsv);
1351         rte_free(qsv2);
1352
1353         return PASS;
1354 }
1355
1356 /*
1357  * rte_lpm_rcu_qsbr_add DQ mode functional test.
1358  * Reader and writer are in the same thread in this test.
1359  *  - Create LPM which supports 1 tbl8 group at max
1360  *  - Add RCU QSBR variable to LPM
1361  *  - Add a rule with depth=28 (> 24)
1362  *  - Register a reader thread (not a real thread)
1363  *  - Reader lookup existing rule
1364  *  - Writer delete the rule
1365  *  - Reader lookup the rule
1366  *  - Writer re-add the rule (no available tbl8 group)
1367  *  - Reader report quiescent state and unregister
1368  *  - Writer re-add the rule
1369  *  - Reader lookup the rule
1370  */
1371 int32_t
1372 test20(void)
1373 {
1374         struct rte_lpm *lpm = NULL;
1375         struct rte_lpm_config config;
1376         size_t sz;
1377         struct rte_rcu_qsbr *qsv;
1378         int32_t status;
1379         uint32_t ip, next_hop, next_hop_return;
1380         uint8_t depth;
1381         struct rte_lpm_rcu_config rcu_cfg = {0};
1382
1383         config.max_rules = MAX_RULES;
1384         config.number_tbl8s = 1;
1385         config.flags = 0;
1386
1387         lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
1388         TEST_LPM_ASSERT(lpm != NULL);
1389
1390         /* Create RCU QSBR variable */
1391         sz = rte_rcu_qsbr_get_memsize(1);
1392         qsv = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz,
1393                                 RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY);
1394         TEST_LPM_ASSERT(qsv != NULL);
1395
1396         status = rte_rcu_qsbr_init(qsv, 1);
1397         TEST_LPM_ASSERT(status == 0);
1398
1399         rcu_cfg.v = qsv;
1400         rcu_cfg.mode = RTE_LPM_QSBR_MODE_DQ;
1401         /* Attach RCU QSBR to LPM table */
1402         status = rte_lpm_rcu_qsbr_add(lpm, &rcu_cfg);
1403         TEST_LPM_ASSERT(status == 0);
1404
1405         ip = RTE_IPV4(192, 0, 2, 100);
1406         depth = 28;
1407         next_hop = 1;
1408         status = rte_lpm_add(lpm, ip, depth, next_hop);
1409         TEST_LPM_ASSERT(status == 0);
1410         TEST_LPM_ASSERT(lpm->tbl24[ip>>8].valid_group);
1411
1412         /* Register pseudo reader */
1413         status = rte_rcu_qsbr_thread_register(qsv, 0);
1414         TEST_LPM_ASSERT(status == 0);
1415         rte_rcu_qsbr_thread_online(qsv, 0);
1416
1417         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
1418         TEST_LPM_ASSERT(status == 0);
1419         TEST_LPM_ASSERT(next_hop_return == next_hop);
1420
1421         /* Writer update */
1422         status = rte_lpm_delete(lpm, ip, depth);
1423         TEST_LPM_ASSERT(status == 0);
1424         TEST_LPM_ASSERT(!lpm->tbl24[ip>>8].valid);
1425
1426         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
1427         TEST_LPM_ASSERT(status != 0);
1428
1429         status = rte_lpm_add(lpm, ip, depth, next_hop);
1430         TEST_LPM_ASSERT(status != 0);
1431
1432         /* Reader quiescent */
1433         rte_rcu_qsbr_quiescent(qsv, 0);
1434
1435         status = rte_lpm_add(lpm, ip, depth, next_hop);
1436         TEST_LPM_ASSERT(status == 0);
1437
1438         rte_rcu_qsbr_thread_offline(qsv, 0);
1439         status = rte_rcu_qsbr_thread_unregister(qsv, 0);
1440         TEST_LPM_ASSERT(status == 0);
1441
1442         status = rte_lpm_lookup(lpm, ip, &next_hop_return);
1443         TEST_LPM_ASSERT(status == 0);
1444         TEST_LPM_ASSERT(next_hop_return == next_hop);
1445
1446         rte_lpm_free(lpm);
1447         rte_free(qsv);
1448
1449         return PASS;
1450 }
1451
1452 static struct rte_lpm *g_lpm;
1453 static struct rte_rcu_qsbr *g_v;
1454 static uint32_t g_ip = RTE_IPV4(192, 0, 2, 100);
1455 static volatile uint8_t writer_done;
1456 /* Report quiescent state interval every 1024 lookups. Larger critical
1457  * sections in reader will result in writer polling multiple times.
1458  */
1459 #define QSBR_REPORTING_INTERVAL 1024
1460 #define WRITER_ITERATIONS       512
1461
1462 /*
1463  * Reader thread using rte_lpm data structure with RCU.
1464  */
1465 static int
1466 test_lpm_rcu_qsbr_reader(void *arg)
1467 {
1468         int i;
1469         uint32_t next_hop_return = 0;
1470
1471         RTE_SET_USED(arg);
1472         /* Register this thread to report quiescent state */
1473         rte_rcu_qsbr_thread_register(g_v, 0);
1474         rte_rcu_qsbr_thread_online(g_v, 0);
1475
1476         do {
1477                 for (i = 0; i < QSBR_REPORTING_INTERVAL; i++)
1478                         rte_lpm_lookup(g_lpm, g_ip, &next_hop_return);
1479
1480                 /* Update quiescent state */
1481                 rte_rcu_qsbr_quiescent(g_v, 0);
1482         } while (!writer_done);
1483
1484         rte_rcu_qsbr_thread_offline(g_v, 0);
1485         rte_rcu_qsbr_thread_unregister(g_v, 0);
1486
1487         return 0;
1488 }
1489
1490 /*
1491  * rte_lpm_rcu_qsbr_add sync mode functional test.
1492  * 1 Reader and 1 writer. They cannot be in the same thread in this test.
1493  *  - Create LPM which supports 1 tbl8 group at max
1494  *  - Add RCU QSBR variable with sync mode to LPM
1495  *  - Register a reader thread. Reader keeps looking up a specific rule.
1496  *  - Writer keeps adding and deleting a specific rule with depth=28 (> 24)
1497  */
1498 int32_t
1499 test21(void)
1500 {
1501         struct rte_lpm_config config;
1502         size_t sz;
1503         int32_t status;
1504         uint32_t i, next_hop;
1505         uint8_t depth;
1506         struct rte_lpm_rcu_config rcu_cfg = {0};
1507
1508         if (rte_lcore_count() < 2) {
1509                 printf("Not enough cores for %s, expecting at least 2\n",
1510                         __func__);
1511                 return TEST_SKIPPED;
1512         }
1513
1514         config.max_rules = MAX_RULES;
1515         config.number_tbl8s = 1;
1516         config.flags = 0;
1517
1518         g_lpm = rte_lpm_create(__func__, SOCKET_ID_ANY, &config);
1519         TEST_LPM_ASSERT(g_lpm != NULL);
1520
1521         /* Create RCU QSBR variable */
1522         sz = rte_rcu_qsbr_get_memsize(1);
1523         g_v = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz,
1524                                 RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY);
1525         TEST_LPM_ASSERT(g_v != NULL);
1526
1527         status = rte_rcu_qsbr_init(g_v, 1);
1528         TEST_LPM_ASSERT(status == 0);
1529
1530         rcu_cfg.v = g_v;
1531         rcu_cfg.mode = RTE_LPM_QSBR_MODE_SYNC;
1532         /* Attach RCU QSBR to LPM table */
1533         status = rte_lpm_rcu_qsbr_add(g_lpm, &rcu_cfg);
1534         TEST_LPM_ASSERT(status == 0);
1535
1536         writer_done = 0;
1537         /* Launch reader thread */
1538         rte_eal_remote_launch(test_lpm_rcu_qsbr_reader, NULL,
1539                                 rte_get_next_lcore(-1, 1, 0));
1540
1541         depth = 28;
1542         next_hop = 1;
1543         status = rte_lpm_add(g_lpm, g_ip, depth, next_hop);
1544         if (status != 0) {
1545                 printf("%s: Failed to add rule\n", __func__);
1546                 goto error;
1547         }
1548
1549         /* Writer update */
1550         for (i = 0; i < WRITER_ITERATIONS; i++) {
1551                 status = rte_lpm_delete(g_lpm, g_ip, depth);
1552                 if (status != 0) {
1553                         printf("%s: Failed to delete rule at iteration %d\n",
1554                                 __func__, i);
1555                         goto error;
1556                 }
1557
1558                 status = rte_lpm_add(g_lpm, g_ip, depth, next_hop);
1559                 if (status != 0) {
1560                         printf("%s: Failed to add rule at iteration %d\n",
1561                                 __func__, i);
1562                         goto error;
1563                 }
1564         }
1565
1566 error:
1567         writer_done = 1;
1568         /* Wait until reader exited. */
1569         rte_eal_mp_wait_lcore();
1570
1571         rte_lpm_free(g_lpm);
1572         rte_free(g_v);
1573
1574         return (status == 0) ? PASS : -1;
1575 }
1576
1577 /*
1578  * Do all unit tests.
1579  */
1580
1581 static int
1582 test_lpm(void)
1583 {
1584         unsigned i;
1585         int status, global_status = 0;
1586
1587         for (i = 0; i < RTE_DIM(tests); i++) {
1588                 status = tests[i]();
1589                 if (status < 0) {
1590                         printf("ERROR: LPM Test %u: FAIL\n", i);
1591                         global_status = status;
1592                 }
1593         }
1594
1595         return global_status;
1596 }
1597
1598 #endif /* !RTE_EXEC_ENV_WINDOWS */
1599
1600 REGISTER_TEST_COMMAND(lpm_autotest, test_lpm);