LCOV - code coverage report
Current view: top level - lib/lpm - rte_lpm6.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 334 357 93.6 %
Date: 2025-01-02 22:41:34 Functions: 22 22 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 194 222 87.4 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2010-2014 Intel Corporation
       3                 :            :  */
       4                 :            : #include <string.h>
       5                 :            : #include <stdalign.h>
       6                 :            : #include <stdint.h>
       7                 :            : #include <errno.h>
       8                 :            : #include <stdio.h>
       9                 :            : #include <sys/queue.h>
      10                 :            : 
      11                 :            : #include <rte_log.h>
      12                 :            : #include <rte_common.h>
      13                 :            : #include <rte_malloc.h>
      14                 :            : #include <rte_memcpy.h>
      15                 :            : #include <rte_eal_memconfig.h>
      16                 :            : #include <rte_string_fns.h>
      17                 :            : #include <rte_errno.h>
      18                 :            : #include <rte_hash.h>
      19                 :            : #include <assert.h>
      20                 :            : #include <rte_jhash.h>
      21                 :            : #include <rte_tailq.h>
      22                 :            : 
      23                 :            : #include "rte_lpm6.h"
      24                 :            : #include "lpm_log.h"
      25                 :            : 
      26                 :            : #define RTE_LPM6_TBL24_NUM_ENTRIES        (1 << 24)
      27                 :            : #define RTE_LPM6_TBL8_GROUP_NUM_ENTRIES         256
      28                 :            : #define RTE_LPM6_TBL8_MAX_NUM_GROUPS      (1 << 21)
      29                 :            : 
      30                 :            : #define RTE_LPM6_VALID_EXT_ENTRY_BITMASK 0xA0000000
      31                 :            : #define RTE_LPM6_LOOKUP_SUCCESS          0x20000000
      32                 :            : #define RTE_LPM6_TBL8_BITMASK            0x001FFFFF
      33                 :            : 
      34                 :            : #define ADD_FIRST_BYTE                            3
      35                 :            : #define LOOKUP_FIRST_BYTE                         4
      36                 :            : #define BYTE_SIZE                                 8
      37                 :            : #define BYTES2_SIZE                              16
      38                 :            : 
      39                 :            : #define RULE_HASH_TABLE_EXTRA_SPACE              64
      40                 :            : #define TBL24_IND                        UINT32_MAX
      41                 :            : 
      42                 :            : #define lpm6_tbl8_gindex next_hop
      43                 :            : 
      44                 :            : /** Flags for setting an entry as valid/invalid. */
      45                 :            : enum valid_flag {
      46                 :            :         INVALID = 0,
      47                 :            :         VALID
      48                 :            : };
      49                 :            : 
      50                 :            : TAILQ_HEAD(rte_lpm6_list, rte_tailq_entry);
      51                 :            : 
      52                 :            : static struct rte_tailq_elem rte_lpm6_tailq = {
      53                 :            :         .name = "RTE_LPM6",
      54                 :            : };
      55         [ -  + ]:        251 : EAL_REGISTER_TAILQ(rte_lpm6_tailq)
      56                 :            : 
      57                 :            : /** Tbl entry structure. It is the same for both tbl24 and tbl8 */
      58                 :            : struct rte_lpm6_tbl_entry {
      59                 :            :         uint32_t next_hop:      21;  /**< Next hop / next table to be checked. */
      60                 :            :         uint32_t depth  :8;      /**< Rule depth. */
      61                 :            : 
      62                 :            :         /* Flags. */
      63                 :            :         uint32_t valid     :1;   /**< Validation flag. */
      64                 :            :         uint32_t valid_group :1; /**< Group validation flag. */
      65                 :            :         uint32_t ext_entry :1;   /**< External entry. */
      66                 :            : };
      67                 :            : 
      68                 :            : /** Rules tbl entry structure. */
      69                 :            : struct rte_lpm6_rule {
      70                 :            :         struct rte_ipv6_addr ip; /**< Rule IP address. */
      71                 :            :         uint32_t next_hop; /**< Rule next hop. */
      72                 :            :         uint8_t depth; /**< Rule depth. */
      73                 :            : };
      74                 :            : 
      75                 :            : /** Rules tbl entry key. */
      76                 :            : struct rte_lpm6_rule_key {
      77                 :            :         struct rte_ipv6_addr ip; /**< Rule IP address. */
      78                 :            :         uint32_t depth; /**< Rule depth. */
      79                 :            : };
      80                 :            : 
      81                 :            : /* Header of tbl8 */
      82                 :            : struct rte_lpm_tbl8_hdr {
      83                 :            :         uint32_t owner_tbl_ind; /**< owner table: TBL24_IND if owner is tbl24,
      84                 :            :                                   *  otherwise index of tbl8
      85                 :            :                                   */
      86                 :            :         uint32_t owner_entry_ind; /**< index of the owner table entry where
      87                 :            :                                     *  pointer to the tbl8 is stored
      88                 :            :                                     */
      89                 :            :         uint32_t ref_cnt; /**< table reference counter */
      90                 :            : };
      91                 :            : 
      92                 :            : /** LPM6 structure. */
      93                 :            : struct rte_lpm6 {
      94                 :            :         /* LPM metadata. */
      95                 :            :         char name[RTE_LPM6_NAMESIZE];    /**< Name of the lpm. */
      96                 :            :         uint32_t max_rules;              /**< Max number of rules. */
      97                 :            :         uint32_t used_rules;             /**< Used rules so far. */
      98                 :            :         uint32_t number_tbl8s;           /**< Number of tbl8s to allocate. */
      99                 :            : 
     100                 :            :         /* LPM Tables. */
     101                 :            :         struct rte_hash *rules_tbl; /**< LPM rules. */
     102                 :            :         alignas(RTE_CACHE_LINE_SIZE) struct rte_lpm6_tbl_entry tbl24[RTE_LPM6_TBL24_NUM_ENTRIES];
     103                 :            :                         /**< LPM tbl24 table. */
     104                 :            : 
     105                 :            :         uint32_t *tbl8_pool; /**< pool of indexes of free tbl8s */
     106                 :            :         uint32_t tbl8_pool_pos; /**< current position in the tbl8 pool */
     107                 :            : 
     108                 :            :         struct rte_lpm_tbl8_hdr *tbl8_hdrs; /* array of tbl8 headers */
     109                 :            : 
     110                 :            :         alignas(RTE_CACHE_LINE_SIZE) struct rte_lpm6_tbl_entry tbl8[];
     111                 :            :                         /**< LPM tbl8 table. */
     112                 :            : };
     113                 :            : 
     114                 :            : /*
     115                 :            :  * LPM6 rule hash function
     116                 :            :  *
     117                 :            :  * It's used as a hash function for the rte_hash
     118                 :            :  *      containing rules
     119                 :            :  */
     120                 :            : static inline uint32_t
     121                 :       7621 : rule_hash(const void *data, __rte_unused uint32_t data_len,
     122                 :            :                   uint32_t init_val)
     123                 :            : {
     124                 :       7621 :         return rte_jhash(data, sizeof(struct rte_lpm6_rule_key), init_val);
     125                 :            : }
     126                 :            : 
     127                 :            : /*
     128                 :            :  * Init pool of free tbl8 indexes
     129                 :            :  */
     130                 :            : static void
     131                 :            : tbl8_pool_init(struct rte_lpm6 *lpm)
     132                 :            : {
     133                 :            :         uint32_t i;
     134                 :            : 
     135                 :            :         /* put entire range of indexes to the tbl8 pool */
     136   [ +  +  +  +  :    4415843 :         for (i = 0; i < lpm->number_tbl8s; i++)
                   +  + ]
     137                 :    4415776 :                 lpm->tbl8_pool[i] = i;
     138                 :            : 
     139                 :         67 :         lpm->tbl8_pool_pos = 0;
     140                 :            : }
     141                 :            : 
     142                 :            : /*
     143                 :            :  * Get an index of a free tbl8 from the pool
     144                 :            :  */
     145                 :            : static inline uint32_t
     146                 :            : tbl8_get(struct rte_lpm6 *lpm, uint32_t *tbl8_ind)
     147                 :            : {
     148   [ +  -  +  - ]:       6275 :         if (lpm->tbl8_pool_pos == lpm->number_tbl8s)
     149                 :            :                 /* no more free tbl8 */
     150                 :            :                 return -ENOSPC;
     151                 :            : 
     152                 :            :         /* next index */
     153                 :       6275 :         *tbl8_ind = lpm->tbl8_pool[lpm->tbl8_pool_pos++];
     154                 :            :         return 0;
     155                 :            : }
     156                 :            : 
     157                 :            : /*
     158                 :            :  * Put an index of a free tbl8 back to the pool
     159                 :            :  */
     160                 :            : static inline uint32_t
     161                 :            : tbl8_put(struct rte_lpm6 *lpm, uint32_t tbl8_ind)
     162                 :            : {
     163         [ +  - ]:        427 :         if (lpm->tbl8_pool_pos == 0)
     164                 :            :                 /* pool is full */
     165                 :            :                 return -ENOSPC;
     166                 :            : 
     167                 :        427 :         lpm->tbl8_pool[--lpm->tbl8_pool_pos] = tbl8_ind;
     168                 :        427 :         return 0;
     169                 :            : }
     170                 :            : 
     171                 :            : /*
     172                 :            :  * Returns number of tbl8s available in the pool
     173                 :            :  */
     174                 :            : static inline uint32_t
     175                 :            : tbl8_available(struct rte_lpm6 *lpm)
     176                 :            : {
     177                 :       1490 :         return lpm->number_tbl8s - lpm->tbl8_pool_pos;
     178                 :            : }
     179                 :            : 
     180                 :            : /*
     181                 :            :  * Init a rule key.
     182                 :            :  *        note that ip must be already masked
     183                 :            :  */
     184                 :            : static inline void
     185                 :            : rule_key_init(struct rte_lpm6_rule_key *key, const struct rte_ipv6_addr *ip, uint8_t depth)
     186                 :            : {
     187                 :       1645 :         key->ip = *ip;
     188                 :       1574 :         key->depth = depth;
     189                 :         71 : }
     190                 :            : 
     191                 :            : /*
     192                 :            :  * Rebuild the entire LPM tree by reinserting all rules
     193                 :            :  */
     194                 :            : static void
     195                 :          4 : rebuild_lpm(struct rte_lpm6 *lpm)
     196                 :            : {
     197                 :            :         uint64_t next_hop;
     198                 :            :         struct rte_lpm6_rule_key *rule_key;
     199                 :          4 :         uint32_t iter = 0;
     200                 :            : 
     201                 :         10 :         while (rte_hash_iterate(lpm->rules_tbl, (void *) &rule_key,
     202         [ +  + ]:         10 :                         (void **) &next_hop, &iter) >= 0)
     203                 :          6 :                 rte_lpm6_add(lpm, &rule_key->ip, rule_key->depth,
     204                 :            :                         (uint32_t) next_hop);
     205                 :          4 : }
     206                 :            : 
     207                 :            : /*
     208                 :            :  * Allocates memory for LPM object
     209                 :            :  */
     210                 :            : struct rte_lpm6 *
     211                 :         59 : rte_lpm6_create(const char *name, int socket_id,
     212                 :            :                 const struct rte_lpm6_config *config)
     213                 :            : {
     214                 :            :         char mem_name[RTE_LPM6_NAMESIZE];
     215                 :            :         struct rte_lpm6 *lpm = NULL;
     216                 :            :         struct rte_tailq_entry *te;
     217                 :            :         uint64_t mem_size;
     218                 :            :         struct rte_lpm6_list *lpm_list;
     219                 :            :         struct rte_hash *rules_tbl = NULL;
     220                 :            :         uint32_t *tbl8_pool = NULL;
     221                 :            :         struct rte_lpm_tbl8_hdr *tbl8_hdrs = NULL;
     222                 :            : 
     223                 :         59 :         lpm_list = RTE_TAILQ_CAST(rte_lpm6_tailq.head, rte_lpm6_list);
     224                 :            : 
     225                 :            :         RTE_BUILD_BUG_ON(sizeof(struct rte_lpm6_tbl_entry) != sizeof(uint32_t));
     226                 :            :         RTE_BUILD_BUG_ON(sizeof(struct rte_lpm6_rule_key) %
     227                 :            :                 sizeof(uint32_t) != 0);
     228                 :            : 
     229                 :            :         /* Check user arguments. */
     230   [ +  +  +  + ]:         59 :         if ((name == NULL) || (socket_id < -1) || (config == NULL) ||
     231         [ +  + ]:         56 :                         (config->max_rules == 0) ||
     232         [ +  + ]:         55 :                         config->number_tbl8s > RTE_LPM6_TBL8_MAX_NUM_GROUPS) {
     233                 :          5 :                 rte_errno = EINVAL;
     234                 :          5 :                 return NULL;
     235                 :            :         }
     236                 :            : 
     237                 :            :         /* create rules hash table */
     238                 :            :         snprintf(mem_name, sizeof(mem_name), "LRH_%s", name);
     239                 :         54 :         struct rte_hash_parameters rule_hash_tbl_params = {
     240                 :         54 :                 .entries = config->max_rules * 1.2 +
     241                 :            :                         RULE_HASH_TABLE_EXTRA_SPACE,
     242                 :            :                 .key_len = sizeof(struct rte_lpm6_rule_key),
     243                 :            :                 .hash_func = rule_hash,
     244                 :            :                 .hash_func_init_val = 0,
     245                 :            :                 .name = mem_name,
     246                 :            :                 .reserved = 0,
     247                 :            :                 .socket_id = socket_id,
     248                 :            :                 .extra_flag = 0
     249                 :            :         };
     250                 :            : 
     251                 :         54 :         rules_tbl = rte_hash_create(&rule_hash_tbl_params);
     252         [ +  + ]:         54 :         if (rules_tbl == NULL) {
     253                 :          1 :                 LPM_LOG(ERR, "LPM rules hash table allocation failed: %s (%d)",
     254                 :            :                                   rte_strerror(rte_errno), rte_errno);
     255                 :          1 :                 goto fail_wo_unlock;
     256                 :            :         }
     257                 :            : 
     258                 :            :         /* allocate tbl8 indexes pool */
     259                 :         53 :         tbl8_pool = rte_malloc(NULL,
     260                 :         53 :                         sizeof(uint32_t) * config->number_tbl8s,
     261                 :            :                         RTE_CACHE_LINE_SIZE);
     262         [ -  + ]:         53 :         if (tbl8_pool == NULL) {
     263                 :          0 :                 LPM_LOG(ERR, "LPM tbl8 pool allocation failed: %s (%d)",
     264                 :            :                                   rte_strerror(rte_errno), rte_errno);
     265                 :          0 :                 rte_errno = ENOMEM;
     266                 :          0 :                 goto fail_wo_unlock;
     267                 :            :         }
     268                 :            : 
     269                 :            :         /* allocate tbl8 headers */
     270                 :         53 :         tbl8_hdrs = rte_malloc(NULL,
     271                 :         53 :                         sizeof(struct rte_lpm_tbl8_hdr) * config->number_tbl8s,
     272                 :            :                         RTE_CACHE_LINE_SIZE);
     273         [ -  + ]:         53 :         if (tbl8_hdrs == NULL) {
     274                 :          0 :                 LPM_LOG(ERR, "LPM tbl8 headers allocation failed: %s (%d)",
     275                 :            :                                   rte_strerror(rte_errno), rte_errno);
     276                 :          0 :                 rte_errno = ENOMEM;
     277                 :          0 :                 goto fail_wo_unlock;
     278                 :            :         }
     279                 :            : 
     280                 :            :         snprintf(mem_name, sizeof(mem_name), "LPM_%s", name);
     281                 :            : 
     282                 :            :         /* Determine the amount of memory to allocate. */
     283                 :         53 :         mem_size = sizeof(*lpm) + (sizeof(lpm->tbl8[0]) *
     284                 :         53 :                         RTE_LPM6_TBL8_GROUP_NUM_ENTRIES * config->number_tbl8s);
     285                 :            : 
     286                 :         53 :         rte_mcfg_tailq_write_lock();
     287                 :            : 
     288                 :            :         /* Guarantee there's no existing */
     289         [ +  + ]:         54 :         TAILQ_FOREACH(te, lpm_list, next) {
     290                 :          1 :                 lpm = (struct rte_lpm6 *) te->data;
     291         [ +  - ]:          1 :                 if (strncmp(name, lpm->name, RTE_LPM6_NAMESIZE) == 0)
     292                 :            :                         break;
     293                 :            :         }
     294                 :            :         lpm = NULL;
     295         [ -  + ]:         53 :         if (te != NULL) {
     296                 :          0 :                 rte_errno = EEXIST;
     297                 :          0 :                 goto fail;
     298                 :            :         }
     299                 :            : 
     300                 :            :         /* allocate tailq entry */
     301                 :         53 :         te = rte_zmalloc("LPM6_TAILQ_ENTRY", sizeof(*te), 0);
     302         [ -  + ]:         53 :         if (te == NULL) {
     303                 :          0 :                 LPM_LOG(ERR, "Failed to allocate tailq entry!");
     304                 :          0 :                 rte_errno = ENOMEM;
     305                 :          0 :                 goto fail;
     306                 :            :         }
     307                 :            : 
     308                 :            :         /* Allocate memory to store the LPM data structures. */
     309                 :         53 :         lpm = rte_zmalloc_socket(mem_name, (size_t)mem_size,
     310                 :            :                         RTE_CACHE_LINE_SIZE, socket_id);
     311                 :            : 
     312         [ -  + ]:         53 :         if (lpm == NULL) {
     313                 :          0 :                 LPM_LOG(ERR, "LPM memory allocation failed");
     314                 :          0 :                 rte_free(te);
     315                 :          0 :                 rte_errno = ENOMEM;
     316                 :          0 :                 goto fail;
     317                 :            :         }
     318                 :            : 
     319                 :            :         /* Save user arguments. */
     320                 :         53 :         lpm->max_rules = config->max_rules;
     321                 :         53 :         lpm->number_tbl8s = config->number_tbl8s;
     322                 :         53 :         strlcpy(lpm->name, name, sizeof(lpm->name));
     323                 :         53 :         lpm->rules_tbl = rules_tbl;
     324                 :         53 :         lpm->tbl8_pool = tbl8_pool;
     325                 :         53 :         lpm->tbl8_hdrs = tbl8_hdrs;
     326                 :            : 
     327                 :            :         /* init the stack */
     328                 :            :         tbl8_pool_init(lpm);
     329                 :            : 
     330                 :         53 :         te->data = (void *) lpm;
     331                 :            : 
     332                 :         53 :         TAILQ_INSERT_TAIL(lpm_list, te, next);
     333                 :         53 :         rte_mcfg_tailq_write_unlock();
     334                 :         53 :         return lpm;
     335                 :            : 
     336                 :          0 : fail:
     337                 :          0 :         rte_mcfg_tailq_write_unlock();
     338                 :            : 
     339                 :          1 : fail_wo_unlock:
     340                 :          1 :         rte_free(tbl8_hdrs);
     341                 :          1 :         rte_free(tbl8_pool);
     342                 :          1 :         rte_hash_free(rules_tbl);
     343                 :            : 
     344                 :          1 :         return NULL;
     345                 :            : }
     346                 :            : 
     347                 :            : /*
     348                 :            :  * Find an existing lpm table and return a pointer to it.
     349                 :            :  */
     350                 :            : struct rte_lpm6 *
     351                 :          2 : rte_lpm6_find_existing(const char *name)
     352                 :            : {
     353                 :            :         struct rte_lpm6 *l = NULL;
     354                 :            :         struct rte_tailq_entry *te;
     355                 :            :         struct rte_lpm6_list *lpm_list;
     356                 :            : 
     357                 :          2 :         lpm_list = RTE_TAILQ_CAST(rte_lpm6_tailq.head, rte_lpm6_list);
     358                 :            : 
     359                 :          2 :         rte_mcfg_tailq_read_lock();
     360         [ +  + ]:          3 :         TAILQ_FOREACH(te, lpm_list, next) {
     361                 :          2 :                 l = (struct rte_lpm6 *) te->data;
     362         [ +  + ]:          2 :                 if (strncmp(name, l->name, RTE_LPM6_NAMESIZE) == 0)
     363                 :            :                         break;
     364                 :            :         }
     365                 :          2 :         rte_mcfg_tailq_read_unlock();
     366                 :            : 
     367         [ +  + ]:          2 :         if (te == NULL) {
     368                 :          1 :                 rte_errno = ENOENT;
     369                 :          1 :                 return NULL;
     370                 :            :         }
     371                 :            : 
     372                 :            :         return l;
     373                 :            : }
     374                 :            : 
     375                 :            : /*
     376                 :            :  * Deallocates memory for given LPM table.
     377                 :            :  */
     378                 :            : void
     379                 :         54 : rte_lpm6_free(struct rte_lpm6 *lpm)
     380                 :            : {
     381                 :            :         struct rte_lpm6_list *lpm_list;
     382                 :            :         struct rte_tailq_entry *te;
     383                 :            : 
     384                 :            :         /* Check user arguments. */
     385         [ +  + ]:         54 :         if (lpm == NULL)
     386                 :            :                 return;
     387                 :            : 
     388                 :         53 :         lpm_list = RTE_TAILQ_CAST(rte_lpm6_tailq.head, rte_lpm6_list);
     389                 :            : 
     390                 :         53 :         rte_mcfg_tailq_write_lock();
     391                 :            : 
     392                 :            :         /* find our tailq entry */
     393         [ +  - ]:         53 :         TAILQ_FOREACH(te, lpm_list, next) {
     394         [ -  + ]:         53 :                 if (te->data == (void *) lpm)
     395                 :            :                         break;
     396                 :            :         }
     397                 :            : 
     398         [ +  - ]:         53 :         if (te != NULL)
     399         [ +  + ]:         53 :                 TAILQ_REMOVE(lpm_list, te, next);
     400                 :            : 
     401                 :         53 :         rte_mcfg_tailq_write_unlock();
     402                 :            : 
     403                 :         53 :         rte_free(lpm->tbl8_hdrs);
     404                 :         53 :         rte_free(lpm->tbl8_pool);
     405                 :         53 :         rte_hash_free(lpm->rules_tbl);
     406                 :         53 :         rte_free(lpm);
     407                 :         53 :         rte_free(te);
     408                 :            : }
     409                 :            : 
     410                 :            : /* Find a rule */
     411                 :            : static inline int
     412                 :            : rule_find_with_key(struct rte_lpm6 *lpm,
     413                 :            :                   const struct rte_lpm6_rule_key *rule_key,
     414                 :            :                   uint32_t *next_hop)
     415                 :            : {
     416                 :            :         uint64_t hash_val;
     417                 :            :         int ret;
     418                 :            : 
     419                 :            :         /* lookup for a rule */
     420                 :          7 :         ret = rte_hash_lookup_data(lpm->rules_tbl, (const void *) rule_key,
     421                 :            :                 (void **) &hash_val);
     422   [ +  +  +  +  :       6056 :         if (ret >= 0) {
                   +  + ]
     423                 :         22 :                 *next_hop = (uint32_t) hash_val;
     424                 :          3 :                 return 1;
     425                 :            :         }
     426                 :            : 
     427                 :            :         return 0;
     428                 :            : }
     429                 :            : 
     430                 :            : /* Find a rule */
     431                 :            : static int
     432                 :            : rule_find(struct rte_lpm6 *lpm, struct rte_ipv6_addr *ip, uint8_t depth,
     433                 :            :                   uint32_t *next_hop)
     434                 :            : {
     435                 :            :         struct rte_lpm6_rule_key rule_key;
     436                 :            : 
     437                 :            :         /* init a rule key */
     438                 :            :         rule_key_init(&rule_key, ip, depth);
     439                 :            : 
     440                 :            :         return rule_find_with_key(lpm, &rule_key, next_hop);
     441                 :            : }
     442                 :            : 
     443                 :            : /*
     444                 :            :  * Checks if a rule already exists in the rules table and updates
     445                 :            :  * the nexthop if so. Otherwise it adds a new rule if enough space is available.
     446                 :            :  *
     447                 :            :  * Returns:
     448                 :            :  *    0 - next hop of existed rule is updated
     449                 :            :  *    1 - new rule successfully added
     450                 :            :  *   <0 - error
     451                 :            :  */
     452                 :            : static inline int
     453                 :       1487 : rule_add(struct rte_lpm6 *lpm, struct rte_ipv6_addr *ip, uint8_t depth, uint32_t next_hop)
     454                 :            : {
     455                 :            :         int ret, rule_exist;
     456                 :            :         struct rte_lpm6_rule_key rule_key;
     457                 :            :         uint32_t unused;
     458                 :            : 
     459                 :            :         /* init a rule key */
     460                 :            :         rule_key_init(&rule_key, ip, depth);
     461                 :            : 
     462                 :            :         /* Scan through rule list to see if rule already exists. */
     463                 :            :         rule_exist = rule_find_with_key(lpm, &rule_key, &unused);
     464                 :            : 
     465                 :            :         /*
     466                 :            :          * If rule does not exist check if there is space to add a new rule to
     467                 :            :          * this rule group. If there is no space return error.
     468                 :            :          */
     469         [ +  + ]:       1460 :         if (!rule_exist && lpm->used_rules == lpm->max_rules)
     470                 :            :                 return -ENOSPC;
     471                 :            : 
     472                 :            :         /* add the rule or update rules next hop */
     473                 :       1485 :         ret = rte_hash_add_key_data(lpm->rules_tbl, &rule_key,
     474                 :       1485 :                 (void *)(uintptr_t) next_hop);
     475         [ +  - ]:       1485 :         if (ret < 0)
     476                 :            :                 return ret;
     477                 :            : 
     478                 :            :         /* Increment the used rules counter for this rule group. */
     479         [ +  + ]:       1485 :         if (!rule_exist) {
     480                 :       1458 :                 lpm->used_rules++;
     481                 :       1458 :                 return 1;
     482                 :            :         }
     483                 :            : 
     484                 :            :         return 0;
     485                 :            : }
     486                 :            : 
     487                 :            : /*
     488                 :            :  * Function that expands a rule across the data structure when a less-generic
     489                 :            :  * one has been added before. It assures that every possible combination of bits
     490                 :            :  * in the IP address returns a match.
     491                 :            :  */
     492                 :            : static void
     493                 :      18333 : expand_rule(struct rte_lpm6 *lpm, uint32_t tbl8_gindex, uint8_t old_depth,
     494                 :            :                 uint8_t new_depth, uint32_t next_hop, uint8_t valid)
     495                 :            : {
     496                 :            :         uint32_t tbl8_group_end, tbl8_gindex_next, j;
     497                 :            : 
     498                 :      18333 :         tbl8_group_end = tbl8_gindex + RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
     499                 :            : 
     500                 :      18333 :         struct rte_lpm6_tbl_entry new_tbl8_entry = {
     501                 :            :                 .valid = valid,
     502                 :            :                 .valid_group = valid,
     503                 :            :                 .depth = new_depth,
     504                 :            :                 .next_hop = next_hop,
     505                 :            :                 .ext_entry = 0,
     506                 :            :         };
     507                 :            : 
     508         [ +  + ]:    4711581 :         for (j = tbl8_gindex; j < tbl8_group_end; j++) {
     509   [ +  +  +  + ]:    4693248 :                 if (!lpm->tbl8[j].valid || (lpm->tbl8[j].ext_entry == 0
     510         [ +  + ]:    4498626 :                                 && lpm->tbl8[j].depth <= old_depth)) {
     511                 :            : 
     512                 :    1722425 :                         lpm->tbl8[j] = new_tbl8_entry;
     513                 :            : 
     514         [ +  + ]:    2970823 :                 } else if (lpm->tbl8[j].ext_entry == 1) {
     515                 :            : 
     516                 :      15615 :                         tbl8_gindex_next = lpm->tbl8[j].lpm6_tbl8_gindex
     517                 :      15615 :                                         * RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
     518                 :      15615 :                         expand_rule(lpm, tbl8_gindex_next, old_depth, new_depth,
     519                 :            :                                         next_hop, valid);
     520                 :            :                 }
     521                 :            :         }
     522                 :      18333 : }
     523                 :            : 
     524                 :            : /*
     525                 :            :  * Init a tbl8 header
     526                 :            :  */
     527                 :            : static inline void
     528                 :            : init_tbl8_header(struct rte_lpm6 *lpm, uint32_t tbl_ind,
     529                 :            :                 uint32_t owner_tbl_ind, uint32_t owner_entry_ind)
     530                 :            : {
     531                 :       6275 :         struct rte_lpm_tbl8_hdr *tbl_hdr = &lpm->tbl8_hdrs[tbl_ind];
     532                 :       6275 :         tbl_hdr->owner_tbl_ind = owner_tbl_ind;
     533                 :       6275 :         tbl_hdr->owner_entry_ind = owner_entry_ind;
     534                 :       6275 :         tbl_hdr->ref_cnt = 0;
     535                 :            : }
     536                 :            : 
     537                 :            : /*
     538                 :            :  * Calculate index to the table based on the number and position
     539                 :            :  * of the bytes being inspected in this step.
     540                 :            :  */
     541                 :            : static uint32_t
     542                 :            : get_bitshift(const struct rte_ipv6_addr *ip, uint8_t first_byte, uint8_t bytes)
     543                 :            : {
     544                 :            :         uint32_t entry_ind, i;
     545                 :            :         int8_t bitshift;
     546                 :            : 
     547                 :            :         entry_ind = 0;
     548   [ +  +  +  + ]:      27506 :         for (i = first_byte; i < (uint32_t)(first_byte + bytes); i++) {
     549                 :      16728 :                 bitshift = (int8_t)((bytes - i)*BYTE_SIZE);
     550                 :            : 
     551                 :            :                 if (bitshift < 0)
     552                 :            :                         bitshift = 0;
     553                 :      16728 :                 entry_ind = entry_ind | ip->a[i-1] << bitshift;
     554                 :            :         }
     555                 :            : 
     556                 :            :         return entry_ind;
     557                 :            : }
     558                 :            : 
     559                 :            : /*
     560                 :            :  * Simulate adding a new route to the LPM counting number
     561                 :            :  * of new tables that will be needed
     562                 :            :  *
     563                 :            :  * It returns 0 on success, or 1 if
     564                 :            :  * the process needs to be continued by calling the function again.
     565                 :            :  */
     566                 :            : static inline int
     567                 :       2262 : simulate_add_step(struct rte_lpm6 *lpm, struct rte_lpm6_tbl_entry *tbl,
     568                 :            :                 struct rte_lpm6_tbl_entry **next_tbl, const struct rte_ipv6_addr *ip,
     569                 :            :                 uint8_t bytes, uint8_t first_byte, uint8_t depth,
     570                 :            :                 uint32_t *need_tbl_nb)
     571                 :            : {
     572                 :            :         uint32_t entry_ind;
     573                 :            :         uint8_t bits_covered;
     574                 :            :         uint32_t next_tbl_ind;
     575                 :            : 
     576                 :            :         /*
     577                 :            :          * Calculate index to the table based on the number and position
     578                 :            :          * of the bytes being inspected in this step.
     579                 :            :          */
     580                 :       2262 :         entry_ind = get_bitshift(ip, first_byte, bytes);
     581                 :            : 
     582                 :            :         /* Number of bits covered in this step */
     583                 :       2262 :         bits_covered = (uint8_t)((bytes+first_byte-1)*BYTE_SIZE);
     584                 :            : 
     585         [ +  + ]:       2262 :         if (depth <= bits_covered) {
     586                 :        359 :                 *need_tbl_nb = 0;
     587                 :        359 :                 return 0;
     588                 :            :         }
     589                 :            : 
     590         [ +  + ]:       1903 :         if (tbl[entry_ind].valid == 0 || tbl[entry_ind].ext_entry == 0) {
     591                 :            :                 /* from this point on a new table is needed on each level
     592                 :            :                  * that is not covered yet
     593                 :            :                  */
     594                 :       1131 :                 depth -= bits_covered;
     595                 :       1131 :                 uint32_t cnt = depth >> 3; /* depth / BYTE_SIZE */
     596         [ +  + ]:       1131 :                 if (depth & 7) /* 0b00000111 */
     597                 :            :                         /* if depth % 8 > 0 then one more table is needed
     598                 :            :                          * for those last bits
     599                 :            :                          */
     600                 :        978 :                         cnt++;
     601                 :            : 
     602                 :       1131 :                 *need_tbl_nb = cnt;
     603                 :       1131 :                 return 0;
     604                 :            :         }
     605                 :            : 
     606                 :        772 :         next_tbl_ind = tbl[entry_ind].lpm6_tbl8_gindex;
     607                 :        772 :         *next_tbl = &(lpm->tbl8[next_tbl_ind *
     608                 :            :                 RTE_LPM6_TBL8_GROUP_NUM_ENTRIES]);
     609                 :        772 :         *need_tbl_nb = 0;
     610                 :        772 :         return 1;
     611                 :            : }
     612                 :            : 
     613                 :            : /*
     614                 :            :  * Partially adds a new route to the data structure (tbl24+tbl8s).
     615                 :            :  * It returns 0 on success, a negative number on failure, or 1 if
     616                 :            :  * the process needs to be continued by calling the function again.
     617                 :            :  */
     618                 :            : static inline int
     619                 :       8516 : add_step(struct rte_lpm6 *lpm, struct rte_lpm6_tbl_entry *tbl,
     620                 :            :                 uint32_t tbl_ind, struct rte_lpm6_tbl_entry **next_tbl,
     621                 :            :                 uint32_t *next_tbl_ind, struct rte_ipv6_addr *ip, uint8_t bytes,
     622                 :            :                 uint8_t first_byte, uint8_t depth, uint32_t next_hop,
     623                 :            :                 uint8_t is_new_rule)
     624                 :            : {
     625                 :            :         uint32_t entry_ind, tbl_range, tbl8_group_start, tbl8_group_end, i;
     626                 :            :         uint32_t tbl8_gindex;
     627                 :            :         uint8_t bits_covered;
     628                 :            :         int ret;
     629                 :            : 
     630                 :            :         /*
     631                 :            :          * Calculate index to the table based on the number and position
     632                 :            :          * of the bytes being inspected in this step.
     633                 :            :          */
     634                 :       8516 :         entry_ind = get_bitshift(ip, first_byte, bytes);
     635                 :            : 
     636                 :            :         /* Number of bits covered in this step */
     637                 :       8516 :         bits_covered = (uint8_t)((bytes+first_byte-1)*BYTE_SIZE);
     638                 :            : 
     639                 :            :         /*
     640                 :            :          * If depth if smaller than this number (ie this is the last step)
     641                 :            :          * expand the rule across the relevant positions in the table.
     642                 :            :          */
     643         [ +  + ]:       8516 :         if (depth <= bits_covered) {
     644                 :       1485 :                 tbl_range = 1 << (bits_covered - depth);
     645                 :            : 
     646         [ +  + ]:  160450456 :                 for (i = entry_ind; i < (entry_ind + tbl_range); i++) {
     647   [ +  +  +  + ]:  160448971 :                         if (!tbl[i].valid || (tbl[i].ext_entry == 0 &&
     648         [ +  + ]:  118464127 :                                         tbl[i].depth <= depth)) {
     649                 :            : 
     650                 :   99764208 :                                 struct rte_lpm6_tbl_entry new_tbl_entry = {
     651                 :            :                                         .next_hop = next_hop,
     652                 :            :                                         .depth = depth,
     653                 :            :                                         .valid = VALID,
     654                 :            :                                         .valid_group = VALID,
     655                 :            :                                         .ext_entry = 0,
     656                 :            :                                 };
     657                 :            : 
     658                 :   99764208 :                                 tbl[i] = new_tbl_entry;
     659                 :            : 
     660         [ +  + ]:   60684763 :                         } else if (tbl[i].ext_entry == 1) {
     661                 :            : 
     662                 :            :                                 /*
     663                 :            :                                  * If tbl entry is valid and extended calculate the index
     664                 :            :                                  * into next tbl8 and expand the rule across the data structure.
     665                 :            :                                  */
     666                 :       2716 :                                 tbl8_gindex = tbl[i].lpm6_tbl8_gindex *
     667                 :            :                                                 RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
     668                 :       2716 :                                 expand_rule(lpm, tbl8_gindex, depth, depth,
     669                 :            :                                                 next_hop, VALID);
     670                 :            :                         }
     671                 :            :                 }
     672                 :            : 
     673                 :            :                 /* update tbl8 rule reference counter */
     674         [ +  + ]:       1485 :                 if (tbl_ind != TBL24_IND && is_new_rule)
     675                 :       1225 :                         lpm->tbl8_hdrs[tbl_ind].ref_cnt++;
     676                 :            : 
     677                 :       1485 :                 return 0;
     678                 :            :         }
     679                 :            :         /*
     680                 :            :          * If this is not the last step just fill one position
     681                 :            :          * and calculate the index to the next table.
     682                 :            :          */
     683                 :            :         else {
     684                 :            :                 /* If it's invalid a new tbl8 is needed */
     685         [ +  + ]:       7031 :                 if (!tbl[entry_ind].valid) {
     686                 :            :                         /* get a new table */
     687                 :            :                         ret = tbl8_get(lpm, &tbl8_gindex);
     688                 :            :                         if (ret != 0)
     689                 :            :                                 return -ENOSPC;
     690                 :            : 
     691                 :            :                         /* invalidate all new tbl8 entries */
     692                 :       1445 :                         tbl8_group_start = tbl8_gindex *
     693                 :            :                                         RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
     694         [ +  + ]:       1445 :                         memset(&lpm->tbl8[tbl8_group_start], 0,
     695                 :            :                                         RTE_LPM6_TBL8_GROUP_NUM_ENTRIES *
     696                 :            :                                         sizeof(struct rte_lpm6_tbl_entry));
     697                 :            : 
     698                 :            :                         /* init the new table's header:
     699                 :            :                          *   save the reference to the owner table
     700                 :            :                          */
     701                 :            :                         init_tbl8_header(lpm, tbl8_gindex, tbl_ind, entry_ind);
     702                 :            : 
     703                 :            :                         /* reference to a new tbl8 */
     704                 :       1445 :                         struct rte_lpm6_tbl_entry new_tbl_entry = {
     705                 :            :                                 .lpm6_tbl8_gindex = tbl8_gindex,
     706                 :            :                                 .depth = 0,
     707                 :            :                                 .valid = VALID,
     708                 :            :                                 .valid_group = VALID,
     709                 :            :                                 .ext_entry = 1,
     710                 :            :                         };
     711                 :            : 
     712                 :       1445 :                         tbl[entry_ind] = new_tbl_entry;
     713                 :            : 
     714                 :            :                         /* update the current table's reference counter */
     715         [ +  + ]:       1445 :                         if (tbl_ind != TBL24_IND)
     716                 :       1027 :                                 lpm->tbl8_hdrs[tbl_ind].ref_cnt++;
     717                 :            :                 }
     718                 :            :                 /*
     719                 :            :                  * If it's valid but not extended the rule that was stored
     720                 :            :                  * here needs to be moved to the next table.
     721                 :            :                  */
     722         [ +  + ]:       5586 :                 else if (tbl[entry_ind].ext_entry == 0) {
     723                 :            :                         /* get a new tbl8 index */
     724                 :            :                         ret = tbl8_get(lpm, &tbl8_gindex);
     725                 :            :                         if (ret != 0)
     726                 :            :                                 return -ENOSPC;
     727                 :            : 
     728                 :       4830 :                         tbl8_group_start = tbl8_gindex *
     729                 :            :                                         RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
     730                 :       4830 :                         tbl8_group_end = tbl8_group_start +
     731                 :            :                                         RTE_LPM6_TBL8_GROUP_NUM_ENTRIES;
     732                 :            : 
     733                 :            :                         struct rte_lpm6_tbl_entry tbl_entry = {
     734                 :       4830 :                                 .next_hop = tbl[entry_ind].next_hop,
     735                 :       4830 :                                 .depth = tbl[entry_ind].depth,
     736                 :            :                                 .valid = VALID,
     737                 :            :                                 .valid_group = VALID,
     738                 :            :                                 .ext_entry = 0
     739                 :            :                         };
     740                 :            : 
     741                 :            :                         /* Populate new tbl8 with tbl value. */
     742         [ +  + ]:    1241310 :                         for (i = tbl8_group_start; i < tbl8_group_end; i++)
     743                 :    1236480 :                                 lpm->tbl8[i] = tbl_entry;
     744                 :            : 
     745                 :            :                         /* init the new table's header:
     746                 :            :                          *   save the reference to the owner table
     747                 :            :                          */
     748                 :            :                         init_tbl8_header(lpm, tbl8_gindex, tbl_ind, entry_ind);
     749                 :            : 
     750                 :            :                         /*
     751                 :            :                          * Update tbl entry to point to new tbl8 entry. Note: The
     752                 :            :                          * ext_flag and tbl8_index need to be updated simultaneously,
     753                 :            :                          * so assign whole structure in one go.
     754                 :            :                          */
     755                 :       4830 :                         struct rte_lpm6_tbl_entry new_tbl_entry = {
     756                 :            :                                 .lpm6_tbl8_gindex = tbl8_gindex,
     757                 :            :                                 .depth = 0,
     758                 :            :                                 .valid = VALID,
     759                 :            :                                 .valid_group = VALID,
     760                 :            :                                 .ext_entry = 1,
     761                 :            :                         };
     762                 :            : 
     763                 :       4830 :                         tbl[entry_ind] = new_tbl_entry;
     764                 :            : 
     765                 :            :                         /* update the current table's reference counter */
     766         [ +  + ]:       4830 :                         if (tbl_ind != TBL24_IND)
     767                 :       4132 :                                 lpm->tbl8_hdrs[tbl_ind].ref_cnt++;
     768                 :            :                 }
     769                 :            : 
     770                 :       7031 :                 *next_tbl_ind = tbl[entry_ind].lpm6_tbl8_gindex;
     771                 :       7031 :                 *next_tbl = &(lpm->tbl8[*next_tbl_ind *
     772                 :            :                                   RTE_LPM6_TBL8_GROUP_NUM_ENTRIES]);
     773                 :            :         }
     774                 :            : 
     775                 :       7031 :         return 1;
     776                 :            : }
     777                 :            : 
     778                 :            : /*
     779                 :            :  * Simulate adding a route to LPM
     780                 :            :  *
     781                 :            :  *      Returns:
     782                 :            :  *    0 on success
     783                 :            :  *    -ENOSPC not enough tbl8 left
     784                 :            :  */
     785                 :            : static int
     786                 :       1490 : simulate_add(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *masked_ip, uint8_t depth)
     787                 :            : {
     788                 :            :         struct rte_lpm6_tbl_entry *tbl;
     789                 :       1490 :         struct rte_lpm6_tbl_entry *tbl_next = NULL;
     790                 :            :         int ret, i;
     791                 :            : 
     792                 :            :         /* number of new tables needed for a step */
     793                 :            :         uint32_t need_tbl_nb;
     794                 :            :         /* total number of new tables needed */
     795                 :            :         uint32_t total_need_tbl_nb;
     796                 :            : 
     797                 :            :         /* Inspect the first three bytes through tbl24 on the first step. */
     798                 :       1490 :         ret = simulate_add_step(lpm, lpm->tbl24, &tbl_next, masked_ip,
     799                 :            :                 ADD_FIRST_BYTE, 1, depth, &need_tbl_nb);
     800                 :       1490 :         total_need_tbl_nb = need_tbl_nb;
     801                 :            :         /*
     802                 :            :          * Inspect one by one the rest of the bytes until
     803                 :            :          * the process is completed.
     804                 :            :          */
     805         [ +  + ]:       2262 :         for (i = ADD_FIRST_BYTE; i < RTE_IPV6_ADDR_SIZE && ret == 1; i++) {
     806                 :        772 :                 tbl = tbl_next;
     807                 :        772 :                 ret = simulate_add_step(lpm, tbl, &tbl_next, masked_ip, 1,
     808                 :        772 :                         (uint8_t)(i + 1), depth, &need_tbl_nb);
     809                 :        772 :                 total_need_tbl_nb += need_tbl_nb;
     810                 :            :         }
     811                 :            : 
     812         [ +  + ]:       1490 :         if (tbl8_available(lpm) < total_need_tbl_nb)
     813                 :            :                 /* not enough tbl8 to add a rule */
     814                 :          3 :                 return -ENOSPC;
     815                 :            : 
     816                 :            :         return 0;
     817                 :            : }
     818                 :            : 
     819                 :            : /*
     820                 :            :  * Add a route
     821                 :            :  */
     822                 :            : int
     823                 :       1493 : rte_lpm6_add(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip, uint8_t depth,
     824                 :            :              uint32_t next_hop)
     825                 :            : {
     826                 :            :         struct rte_lpm6_tbl_entry *tbl;
     827                 :       1493 :         struct rte_lpm6_tbl_entry *tbl_next = NULL;
     828                 :            :         /* init to avoid compiler warning */
     829                 :       1493 :         uint32_t tbl_next_num = 123456;
     830                 :            :         int status;
     831                 :            :         struct rte_ipv6_addr masked_ip;
     832                 :            :         int i;
     833                 :            : 
     834                 :            :         /* Check user arguments. */
     835   [ +  +  +  + ]:       1493 :         if ((lpm == NULL) || (depth < 1) || (depth > RTE_IPV6_MAX_DEPTH))
     836                 :            :                 return -EINVAL;
     837                 :            : 
     838                 :            :         /* Copy the IP and mask it to avoid modifying user's input data. */
     839                 :       1490 :         masked_ip = *ip;
     840         [ +  + ]:       1490 :         rte_ipv6_addr_mask(&masked_ip, depth);
     841                 :            : 
     842                 :            :         /* Simulate adding a new route */
     843                 :       1490 :         int ret = simulate_add(lpm, &masked_ip, depth);
     844         [ +  + ]:       1490 :         if (ret < 0)
     845                 :            :                 return ret;
     846                 :            : 
     847                 :            :         /* Add the rule to the rule table. */
     848                 :       1487 :         int is_new_rule = rule_add(lpm, &masked_ip, depth, next_hop);
     849                 :            :         /* If there is no space available for new rule return error. */
     850         [ +  + ]:       1487 :         if (is_new_rule < 0)
     851                 :            :                 return is_new_rule;
     852                 :            : 
     853                 :            :         /* Inspect the first three bytes through tbl24 on the first step. */
     854                 :       1485 :         tbl = lpm->tbl24;
     855                 :       1485 :         status = add_step(lpm, tbl, TBL24_IND, &tbl_next, &tbl_next_num,
     856                 :            :                 &masked_ip, ADD_FIRST_BYTE, 1, depth, next_hop,
     857                 :            :                 is_new_rule);
     858         [ -  + ]:       1485 :         assert(status >= 0);
     859                 :            : 
     860                 :            :         /*
     861                 :            :          * Inspect one by one the rest of the bytes until
     862                 :            :          * the process is completed.
     863                 :            :          */
     864         [ +  + ]:       8516 :         for (i = ADD_FIRST_BYTE; i < RTE_IPV6_ADDR_SIZE && status == 1; i++) {
     865                 :       7031 :                 tbl = tbl_next;
     866                 :       7031 :                 status = add_step(lpm, tbl, tbl_next_num, &tbl_next,
     867                 :       7031 :                         &tbl_next_num, &masked_ip, 1, (uint8_t)(i + 1),
     868                 :            :                         depth, next_hop, is_new_rule);
     869         [ -  + ]:       7031 :                 assert(status >= 0);
     870                 :            :         }
     871                 :            : 
     872                 :            :         return status;
     873                 :            : }
     874                 :            : 
     875                 :            : /*
     876                 :            :  * Takes a pointer to a table entry and inspect one level.
     877                 :            :  * The function returns 0 on lookup success, ENOENT if no match was found
     878                 :            :  * or 1 if the process needs to be continued by calling the function again.
     879                 :            :  */
     880                 :            : static inline int
     881                 :            : lookup_step(const struct rte_lpm6 *lpm, const struct rte_lpm6_tbl_entry *tbl,
     882                 :            :                 const struct rte_lpm6_tbl_entry **tbl_next, const struct rte_ipv6_addr *ip,
     883                 :            :                 uint8_t first_byte, uint32_t *next_hop)
     884                 :            : {
     885                 :            :         uint32_t tbl8_index, tbl_entry;
     886                 :            : 
     887                 :            :         /* Take the integer value from the pointer. */
     888                 :    1505327 :         tbl_entry = *(const uint32_t *)tbl;
     889                 :            : 
     890                 :            :         /* If it is valid and extended we calculate the new pointer to return. */
     891                 :    1505327 :         if ((tbl_entry & RTE_LPM6_VALID_EXT_ENTRY_BITMASK) ==
     892                 :            :                         RTE_LPM6_VALID_EXT_ENTRY_BITMASK) {
     893                 :            : 
     894                 :    1338820 :                 tbl8_index = ip->a[first_byte-1] +
     895                 :    1338820 :                                 ((tbl_entry & RTE_LPM6_TBL8_BITMASK) *
     896                 :            :                                 RTE_LPM6_TBL8_GROUP_NUM_ENTRIES);
     897                 :            : 
     898                 :    1338748 :                 *tbl_next = &lpm->tbl8[tbl8_index];
     899                 :            : 
     900                 :         72 :                 return 1;
     901                 :            :         } else {
     902                 :            :                 /* If not extended then we can have a match. */
     903                 :     166507 :                 *next_hop = ((uint32_t)tbl_entry & RTE_LPM6_TBL8_BITMASK);
     904   [ +  +  +  + ]:     166507 :                 return (tbl_entry & RTE_LPM6_LOOKUP_SUCCESS) ? 0 : -ENOENT;
     905                 :            :         }
     906                 :            : }
     907                 :            : 
     908                 :            : /*
     909                 :            :  * Looks up an IP
     910                 :            :  */
     911                 :            : int
     912                 :     166481 : rte_lpm6_lookup(const struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip,
     913                 :            :                 uint32_t *next_hop)
     914                 :            : {
     915                 :            :         const struct rte_lpm6_tbl_entry *tbl;
     916                 :            :         const struct rte_lpm6_tbl_entry *tbl_next = NULL;
     917                 :            :         int status;
     918                 :            :         uint8_t first_byte;
     919                 :            :         uint32_t tbl24_index;
     920                 :            : 
     921                 :            :         /* DEBUG: Check user input arguments. */
     922   [ +  +  +  + ]:     166481 :         if ((lpm == NULL) || (ip == NULL) || (next_hop == NULL))
     923                 :            :                 return -EINVAL;
     924                 :            : 
     925                 :            :         first_byte = LOOKUP_FIRST_BYTE;
     926                 :     166478 :         tbl24_index = (ip->a[0] << BYTES2_SIZE) | (ip->a[1] << BYTE_SIZE) | ip->a[2];
     927                 :            : 
     928                 :            :         /* Calculate pointer to the first entry to be inspected */
     929                 :     166478 :         tbl = &lpm->tbl24[tbl24_index];
     930                 :            : 
     931                 :            :         do {
     932                 :            :                 /* Continue inspecting following levels until success or failure */
     933         [ +  + ]:    1505226 :                 status = lookup_step(lpm, tbl, &tbl_next, ip, first_byte++, next_hop);
     934                 :            :                 tbl = tbl_next;
     935                 :            :         } while (status == 1);
     936                 :            : 
     937                 :            :         return status;
     938                 :            : }
     939                 :            : 
     940                 :            : /*
     941                 :            :  * Looks up a group of IP addresses
     942                 :            :  */
     943                 :            : int
     944                 :          9 : rte_lpm6_lookup_bulk_func(const struct rte_lpm6 *lpm,
     945                 :            :                 struct rte_ipv6_addr *ips,
     946                 :            :                 int32_t *next_hops, unsigned int n)
     947                 :            : {
     948                 :            :         unsigned int i;
     949                 :            :         const struct rte_lpm6_tbl_entry *tbl;
     950                 :            :         const struct rte_lpm6_tbl_entry *tbl_next = NULL;
     951                 :            :         uint32_t tbl24_index, next_hop;
     952                 :            :         uint8_t first_byte;
     953                 :            :         int status;
     954                 :            : 
     955                 :            :         /* DEBUG: Check user input arguments. */
     956   [ +  +  +  + ]:          9 :         if ((lpm == NULL) || (ips == NULL) || (next_hops == NULL))
     957                 :            :                 return -EINVAL;
     958                 :            : 
     959         [ +  + ]:         35 :         for (i = 0; i < n; i++) {
     960                 :            :                 first_byte = LOOKUP_FIRST_BYTE;
     961                 :         29 :                 tbl24_index = (ips[i].a[0] << BYTES2_SIZE) |
     962                 :         29 :                                 (ips[i].a[1] << BYTE_SIZE) | ips[i].a[2];
     963                 :            : 
     964                 :            :                 /* Calculate pointer to the first entry to be inspected */
     965                 :         29 :                 tbl = &lpm->tbl24[tbl24_index];
     966                 :            : 
     967                 :            :                 do {
     968                 :            :                         /* Continue inspecting following levels
     969                 :            :                          * until success or failure
     970                 :            :                          */
     971                 :        101 :                         status = lookup_step(lpm, tbl, &tbl_next, &ips[i],
     972         [ +  + ]:        101 :                                         first_byte++, &next_hop);
     973                 :            :                         tbl = tbl_next;
     974         [ +  + ]:        101 :                 } while (status == 1);
     975                 :            : 
     976         [ +  + ]:         29 :                 if (status < 0)
     977                 :         15 :                         next_hops[i] = -1;
     978                 :            :                 else
     979                 :         14 :                         next_hops[i] = (int32_t)next_hop;
     980                 :            :         }
     981                 :            : 
     982                 :            :         return 0;
     983                 :            : }
     984                 :            : 
     985                 :            : /*
     986                 :            :  * Look for a rule in the high-level rules table
     987                 :            :  */
     988                 :            : int
     989                 :          7 : rte_lpm6_is_rule_present(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip, uint8_t depth,
     990                 :            :                          uint32_t *next_hop)
     991                 :            : {
     992                 :            :         struct rte_ipv6_addr masked_ip;
     993                 :            : 
     994                 :            :         /* Check user arguments. */
     995         [ +  - ]:          7 :         if ((lpm == NULL) || next_hop == NULL || ip == NULL ||
     996   [ +  -  +  - ]:          7 :                         (depth < 1) || (depth > RTE_IPV6_MAX_DEPTH))
     997                 :            :                 return -EINVAL;
     998                 :            : 
     999                 :            :         /* Copy the IP and mask it to avoid modifying user's input data. */
    1000         [ +  - ]:          7 :         masked_ip = *ip;
    1001                 :            :         rte_ipv6_addr_mask(&masked_ip, depth);
    1002                 :            : 
    1003                 :          7 :         return rule_find(lpm, &masked_ip, depth, next_hop);
    1004                 :            : }
    1005                 :            : 
    1006                 :            : /*
    1007                 :            :  * Delete a rule from the rule table.
    1008                 :            :  * NOTE: Valid range for depth parameter is 1 .. 128 inclusive.
    1009                 :            :  * return
    1010                 :            :  *        0 on success
    1011                 :            :  *   <0 on failure
    1012                 :            :  */
    1013                 :            : static inline int
    1014                 :          6 : rule_delete(struct rte_lpm6 *lpm, struct rte_ipv6_addr *ip, uint8_t depth)
    1015                 :            : {
    1016                 :            :         int ret;
    1017                 :            :         struct rte_lpm6_rule_key rule_key;
    1018                 :            : 
    1019                 :            :         /* init rule key */
    1020                 :            :         rule_key_init(&rule_key, ip, depth);
    1021                 :            : 
    1022                 :            :         /* delete the rule */
    1023                 :         80 :         ret = rte_hash_del_key(lpm->rules_tbl, (void *) &rule_key);
    1024   [ +  +  +  + ]:         80 :         if (ret >= 0)
    1025                 :         77 :                 lpm->used_rules--;
    1026                 :            : 
    1027                 :          6 :         return ret;
    1028                 :            : }
    1029                 :            : 
    1030                 :            : /*
    1031                 :            :  * Deletes a group of rules
    1032                 :            :  *
    1033                 :            :  * Note that the function rebuilds the lpm table,
    1034                 :            :  * rather than doing incremental updates like
    1035                 :            :  * the regular delete function
    1036                 :            :  */
    1037                 :            : int
    1038                 :          7 : rte_lpm6_delete_bulk_func(struct rte_lpm6 *lpm,
    1039                 :            :                 struct rte_ipv6_addr *ips, uint8_t *depths,
    1040                 :            :                 unsigned n)
    1041                 :            : {
    1042                 :            :         struct rte_ipv6_addr masked_ip;
    1043                 :            :         unsigned i;
    1044                 :            : 
    1045                 :            :         /* Check input arguments. */
    1046   [ +  +  +  + ]:          7 :         if ((lpm == NULL) || (ips == NULL) || (depths == NULL))
    1047                 :            :                 return -EINVAL;
    1048                 :            : 
    1049         [ +  + ]:         10 :         for (i = 0; i < n; i++) {
    1050                 :          6 :                 masked_ip = ips[i];
    1051         [ +  - ]:          6 :                 rte_ipv6_addr_mask(&masked_ip, depths[i]);
    1052                 :          6 :                 rule_delete(lpm, &masked_ip, depths[i]);
    1053                 :            :         }
    1054                 :            : 
    1055                 :            :         /*
    1056                 :            :          * Set all the table entries to 0 (ie delete every rule
    1057                 :            :          * from the data structure.
    1058                 :            :          */
    1059                 :          4 :         memset(lpm->tbl24, 0, sizeof(lpm->tbl24));
    1060                 :          4 :         memset(lpm->tbl8, 0, sizeof(lpm->tbl8[0])
    1061                 :          4 :                         * RTE_LPM6_TBL8_GROUP_NUM_ENTRIES * lpm->number_tbl8s);
    1062                 :            :         tbl8_pool_init(lpm);
    1063                 :            : 
    1064                 :            :         /*
    1065                 :            :          * Add every rule again (except for the ones that were removed from
    1066                 :            :          * the rules table).
    1067                 :            :          */
    1068                 :          4 :         rebuild_lpm(lpm);
    1069                 :            : 
    1070                 :          4 :         return 0;
    1071                 :            : }
    1072                 :            : 
    1073                 :            : /*
    1074                 :            :  * Delete all rules from the LPM table.
    1075                 :            :  */
    1076                 :            : void
    1077                 :         10 : rte_lpm6_delete_all(struct rte_lpm6 *lpm)
    1078                 :            : {
    1079                 :            :         /* Zero used rules counter. */
    1080                 :         10 :         lpm->used_rules = 0;
    1081                 :            : 
    1082                 :            :         /* Zero tbl24. */
    1083                 :         10 :         memset(lpm->tbl24, 0, sizeof(lpm->tbl24));
    1084                 :            : 
    1085                 :            :         /* Zero tbl8. */
    1086                 :         10 :         memset(lpm->tbl8, 0, sizeof(lpm->tbl8[0]) *
    1087                 :         10 :                         RTE_LPM6_TBL8_GROUP_NUM_ENTRIES * lpm->number_tbl8s);
    1088                 :            : 
    1089                 :            :         /* init pool of free tbl8 indexes */
    1090                 :            :         tbl8_pool_init(lpm);
    1091                 :            : 
    1092                 :            :         /* Delete all rules form the rules table. */
    1093                 :         10 :         rte_hash_reset(lpm->rules_tbl);
    1094                 :         10 : }
    1095                 :            : 
    1096                 :            : /*
    1097                 :            :  * Convert a depth to a one byte long mask
    1098                 :            :  *   Example: 4 will be converted to 0xF0
    1099                 :            :  */
    1100                 :            : static uint8_t __rte_pure
    1101                 :            : depth_to_mask_1b(uint8_t depth)
    1102                 :            : {
    1103                 :            :         /* To calculate a mask start with a 1 on the left hand side and right
    1104                 :            :          * shift while populating the left hand side with 1's
    1105                 :            :          */
    1106                 :       4077 :         return (signed char)0x80 >> (depth - 1);
    1107                 :            : }
    1108                 :            : 
    1109                 :            : /*
    1110                 :            :  * Find a less specific rule
    1111                 :            :  */
    1112                 :            : static int
    1113                 :         72 : rule_find_less_specific(struct rte_lpm6 *lpm, struct rte_ipv6_addr *ip, uint8_t depth,
    1114                 :            :         struct rte_lpm6_rule *rule)
    1115                 :            : {
    1116                 :            :         int ret;
    1117                 :            :         uint32_t next_hop;
    1118                 :            :         uint8_t mask;
    1119                 :            :         struct rte_lpm6_rule_key rule_key;
    1120                 :            : 
    1121         [ +  + ]:         72 :         if (depth == 1)
    1122                 :            :                 return 0;
    1123                 :            : 
    1124                 :            :         rule_key_init(&rule_key, ip, depth);
    1125                 :            : 
    1126         [ +  + ]:       4614 :         while (depth > 1) {
    1127                 :       4562 :                 depth--;
    1128                 :            : 
    1129                 :            :                 /* each iteration zero one more bit of the key */
    1130                 :       4562 :                 mask = depth & 7; /* depth % BYTE_SIZE */
    1131         [ +  + ]:       4562 :                 if (mask > 0)
    1132                 :       4034 :                         mask = depth_to_mask_1b(mask);
    1133                 :            : 
    1134                 :       4562 :                 rule_key.depth = depth;
    1135                 :       4562 :                 rule_key.ip.a[depth >> 3] &= mask;
    1136                 :            : 
    1137                 :            :                 ret = rule_find_with_key(lpm, &rule_key, &next_hop);
    1138                 :            :                 if (ret) {
    1139                 :         19 :                         rule->depth = depth;
    1140                 :         19 :                         rule->ip = rule_key.ip;
    1141                 :         19 :                         rule->next_hop = next_hop;
    1142                 :         19 :                         return 1;
    1143                 :            :                 }
    1144                 :            :         }
    1145                 :            : 
    1146                 :            :         return 0;
    1147                 :            : }
    1148                 :            : 
    1149                 :            : /*
    1150                 :            :  * Find range of tbl8 cells occupied by a rule
    1151                 :            :  */
    1152                 :            : static void
    1153                 :         72 : rule_find_range(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip, uint8_t depth,
    1154                 :            :                   struct rte_lpm6_tbl_entry **from,
    1155                 :            :                   struct rte_lpm6_tbl_entry **to,
    1156                 :            :                   uint32_t *out_tbl_ind)
    1157                 :            : {
    1158                 :            :         uint32_t ind;
    1159                 :         72 :         uint32_t first_3bytes = (uint32_t)ip->a[0] << 16 |
    1160                 :         72 :                         ip->a[1] << 8 | ip->a[2];
    1161                 :            : 
    1162         [ +  + ]:         72 :         if (depth <= 24) {
    1163                 :            :                 /* rule is within the top level */
    1164                 :            :                 ind = first_3bytes;
    1165                 :         29 :                 *from = &lpm->tbl24[ind];
    1166                 :         29 :                 ind += (1 << (24 - depth)) - 1;
    1167                 :         29 :                 *to = &lpm->tbl24[ind];
    1168                 :         29 :                 *out_tbl_ind = TBL24_IND;
    1169                 :            :         } else {
    1170                 :            :                 /* top level entry */
    1171                 :            :                 struct rte_lpm6_tbl_entry *tbl = &lpm->tbl24[first_3bytes];
    1172         [ -  + ]:         43 :                 assert(tbl->ext_entry == 1);
    1173                 :            :                 /* first tbl8 */
    1174                 :         43 :                 uint32_t tbl_ind = tbl->lpm6_tbl8_gindex;
    1175                 :         43 :                 tbl = &lpm->tbl8[tbl_ind *
    1176                 :            :                                 RTE_LPM6_TBL8_GROUP_NUM_ENTRIES];
    1177                 :            :                 /* current ip byte, the top level is already behind */
    1178                 :            :                 uint8_t byte = 3;
    1179                 :            :                 /* minus top level */
    1180                 :         43 :                 depth -= 24;
    1181                 :            : 
    1182                 :            :                 /* iterate through levels (tbl8s)
    1183                 :            :                  * until we reach the last one
    1184                 :            :                  */
    1185         [ +  + ]:        442 :                 while (depth > 8) {
    1186                 :        399 :                         tbl += ip->a[byte];
    1187         [ -  + ]:        399 :                         assert(tbl->ext_entry == 1);
    1188                 :            :                         /* go to the next level/tbl8 */
    1189                 :        399 :                         tbl_ind = tbl->lpm6_tbl8_gindex;
    1190                 :        399 :                         tbl = &lpm->tbl8[tbl_ind *
    1191                 :            :                                         RTE_LPM6_TBL8_GROUP_NUM_ENTRIES];
    1192                 :        399 :                         byte += 1;
    1193                 :        399 :                         depth -= 8;
    1194                 :            :                 }
    1195                 :            : 
    1196                 :            :                 /* last level/tbl8 */
    1197                 :         43 :                 ind = ip->a[byte] & depth_to_mask_1b(depth);
    1198                 :         43 :                 *from = &tbl[ind];
    1199                 :         43 :                 ind += (1 << (8 - depth)) - 1;
    1200                 :         43 :                 *to = &tbl[ind];
    1201                 :         43 :                 *out_tbl_ind = tbl_ind;
    1202                 :            :         }
    1203                 :         72 : }
    1204                 :            : 
    1205                 :            : /*
    1206                 :            :  * Remove a table from the LPM tree
    1207                 :            :  */
    1208                 :            : static void
    1209                 :        427 : remove_tbl(struct rte_lpm6 *lpm, struct rte_lpm_tbl8_hdr *tbl_hdr,
    1210                 :            :                   uint32_t tbl_ind, struct rte_lpm6_rule *lsp_rule)
    1211                 :            : {
    1212                 :            :         struct rte_lpm6_tbl_entry *owner_entry;
    1213                 :            : 
    1214         [ +  + ]:        427 :         if (tbl_hdr->owner_tbl_ind == TBL24_IND)
    1215                 :         40 :                 owner_entry = &lpm->tbl24[tbl_hdr->owner_entry_ind];
    1216                 :            :         else {
    1217                 :            :                 uint32_t owner_tbl_ind = tbl_hdr->owner_tbl_ind;
    1218                 :        387 :                 owner_entry = &lpm->tbl8[
    1219                 :        387 :                         owner_tbl_ind * RTE_LPM6_TBL8_GROUP_NUM_ENTRIES +
    1220                 :        387 :                         tbl_hdr->owner_entry_ind];
    1221                 :            : 
    1222                 :        387 :                 struct rte_lpm_tbl8_hdr *owner_tbl_hdr =
    1223                 :        387 :                         &lpm->tbl8_hdrs[owner_tbl_ind];
    1224         [ +  - ]:        387 :                 if (--owner_tbl_hdr->ref_cnt == 0)
    1225                 :        387 :                         remove_tbl(lpm, owner_tbl_hdr, owner_tbl_ind, lsp_rule);
    1226                 :            :         }
    1227                 :            : 
    1228         [ -  + ]:        427 :         assert(owner_entry->ext_entry == 1);
    1229                 :            : 
    1230                 :            :         /* unlink the table */
    1231         [ -  + ]:        427 :         if (lsp_rule != NULL) {
    1232                 :            :                 struct rte_lpm6_tbl_entry new_tbl_entry = {
    1233                 :          0 :                         .next_hop = lsp_rule->next_hop,
    1234                 :          0 :                         .depth = lsp_rule->depth,
    1235                 :            :                         .valid = VALID,
    1236                 :            :                         .valid_group = VALID,
    1237                 :            :                         .ext_entry = 0
    1238                 :            :                 };
    1239                 :            : 
    1240                 :          0 :                 *owner_entry = new_tbl_entry;
    1241                 :            :         } else {
    1242                 :            :                 struct rte_lpm6_tbl_entry new_tbl_entry = {
    1243                 :            :                         .next_hop = 0,
    1244                 :            :                         .depth = 0,
    1245                 :            :                         .valid = INVALID,
    1246                 :            :                         .valid_group = INVALID,
    1247                 :            :                         .ext_entry = 0
    1248                 :            :                 };
    1249                 :            : 
    1250                 :        427 :                 *owner_entry = new_tbl_entry;
    1251                 :            :         }
    1252                 :            : 
    1253                 :            :         /* return the table to the pool */
    1254                 :            :         tbl8_put(lpm, tbl_ind);
    1255                 :        427 : }
    1256                 :            : 
    1257                 :            : /*
    1258                 :            :  * Deletes a rule
    1259                 :            :  */
    1260                 :            : int
    1261                 :         77 : rte_lpm6_delete(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip, uint8_t depth)
    1262                 :            : {
    1263                 :            :         struct rte_ipv6_addr masked_ip;
    1264                 :            :         struct rte_lpm6_rule lsp_rule_obj;
    1265                 :            :         struct rte_lpm6_rule *lsp_rule;
    1266                 :            :         int ret;
    1267                 :            :         uint32_t tbl_ind;
    1268                 :            :         struct rte_lpm6_tbl_entry *from, *to;
    1269                 :            : 
    1270                 :            :         /* Check input arguments. */
    1271   [ +  +  +  + ]:         77 :         if ((lpm == NULL) || (depth < 1) || (depth > RTE_IPV6_MAX_DEPTH))
    1272                 :            :                 return -EINVAL;
    1273                 :            : 
    1274                 :            :         /* Copy the IP and mask it to avoid modifying user's input data. */
    1275                 :         74 :         masked_ip = *ip;
    1276         [ +  + ]:         74 :         rte_ipv6_addr_mask(&masked_ip, depth);
    1277                 :            : 
    1278                 :            :         /* Delete the rule from the rule table. */
    1279                 :            :         ret = rule_delete(lpm, &masked_ip, depth);
    1280         [ +  + ]:         74 :         if (ret < 0)
    1281                 :            :                 return -ENOENT;
    1282                 :            : 
    1283                 :            :         /* find rule cells */
    1284                 :         72 :         rule_find_range(lpm, &masked_ip, depth, &from, &to, &tbl_ind);
    1285                 :            : 
    1286                 :            :         /* find a less specific rule (a rule with smaller depth)
    1287                 :            :          * note: masked_ip will be modified, don't use it anymore
    1288                 :            :          */
    1289                 :         72 :         ret = rule_find_less_specific(lpm, &masked_ip, depth,
    1290                 :            :                         &lsp_rule_obj);
    1291         [ +  + ]:         72 :         lsp_rule = ret ? &lsp_rule_obj : NULL;
    1292                 :            : 
    1293                 :            :         /* decrement the table rule counter,
    1294                 :            :          * note that tbl24 doesn't have a header
    1295                 :            :          */
    1296         [ +  + ]:         72 :         if (tbl_ind != TBL24_IND) {
    1297                 :         43 :                 struct rte_lpm_tbl8_hdr *tbl_hdr = &lpm->tbl8_hdrs[tbl_ind];
    1298         [ +  + ]:         43 :                 if (--tbl_hdr->ref_cnt == 0) {
    1299                 :            :                         /* remove the table */
    1300                 :         40 :                         remove_tbl(lpm, tbl_hdr, tbl_ind, lsp_rule);
    1301                 :         40 :                         return 0;
    1302                 :            :                 }
    1303                 :            :         }
    1304                 :            : 
    1305                 :            :         /* iterate rule cells */
    1306         [ +  + ]:   20972588 :         for (; from <= to; from++)
    1307         [ +  + ]:   20972556 :                 if (from->ext_entry == 1) {
    1308                 :            :                         /* reference to a more specific space
    1309                 :            :                          * of the prefix/rule. Entries in a more
    1310                 :            :                          * specific space that are not used by
    1311                 :            :                          * a more specific prefix must be occupied
    1312                 :            :                          * by the prefix
    1313                 :            :                          */
    1314         [ -  + ]:          2 :                         if (lsp_rule != NULL)
    1315                 :          0 :                                 expand_rule(lpm,
    1316                 :          0 :                                         from->lpm6_tbl8_gindex *
    1317                 :            :                                         RTE_LPM6_TBL8_GROUP_NUM_ENTRIES,
    1318                 :          0 :                                         depth, lsp_rule->depth,
    1319                 :            :                                         lsp_rule->next_hop, VALID);
    1320                 :            :                         else
    1321                 :            :                                 /* since the prefix has no less specific prefix,
    1322                 :            :                                  * its more specific space must be invalidated
    1323                 :            :                                  */
    1324                 :          2 :                                 expand_rule(lpm,
    1325                 :          2 :                                         from->lpm6_tbl8_gindex *
    1326                 :            :                                         RTE_LPM6_TBL8_GROUP_NUM_ENTRIES,
    1327                 :            :                                         depth, 0, 0, INVALID);
    1328         [ +  - ]:   20972554 :                 } else if (from->depth == depth) {
    1329                 :            :                         /* entry is not a reference and belongs to the prefix */
    1330         [ +  + ]:   20972554 :                         if (lsp_rule != NULL) {
    1331                 :            :                                 struct rte_lpm6_tbl_entry new_tbl_entry = {
    1332                 :   12582660 :                                         .next_hop = lsp_rule->next_hop,
    1333                 :   12582660 :                                         .depth = lsp_rule->depth,
    1334                 :            :                                         .valid = VALID,
    1335                 :            :                                         .valid_group = VALID,
    1336                 :            :                                         .ext_entry = 0
    1337                 :            :                                 };
    1338                 :            : 
    1339                 :   12582660 :                                 *from = new_tbl_entry;
    1340                 :            :                         } else {
    1341                 :            :                                 struct rte_lpm6_tbl_entry new_tbl_entry = {
    1342                 :            :                                         .next_hop = 0,
    1343                 :            :                                         .depth = 0,
    1344                 :            :                                         .valid = INVALID,
    1345                 :            :                                         .valid_group = INVALID,
    1346                 :            :                                         .ext_entry = 0
    1347                 :            :                                 };
    1348                 :            : 
    1349                 :    8389894 :                                 *from = new_tbl_entry;
    1350                 :            :                         }
    1351                 :            :                 }
    1352                 :            : 
    1353                 :            :         return 0;
    1354                 :            : }

Generated by: LCOV version 1.14