LCOV - code coverage report
Current view: top level - app/test - test_fib6.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 373 388 96.1 %
Date: 2026-08-01 17:54:00 Functions: 19 21 90.5 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 140 242 57.9 %

           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);

Generated by: LCOV version 1.14