LCOV - code coverage report
Current view: top level - lib/lpm - rte_lpm.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 323 354 91.2 %
Date: 2025-05-01 17:49:45 Functions: 22 22 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 190 240 79.2 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2010-2014 Intel Corporation
       3                 :            :  * Copyright(c) 2020 Arm Limited
       4                 :            :  */
       5                 :            : 
       6                 :            : #include <string.h>
       7                 :            : #include <stdint.h>
       8                 :            : #include <errno.h>
       9                 :            : #include <stdio.h>
      10                 :            : #include <sys/queue.h>
      11                 :            : 
      12                 :            : #include <eal_export.h>
      13                 :            : #include <rte_log.h>
      14                 :            : #include <rte_common.h>
      15                 :            : #include <rte_malloc.h>
      16                 :            : #include <rte_eal_memconfig.h>
      17                 :            : #include <rte_string_fns.h>
      18                 :            : #include <rte_errno.h>
      19                 :            : #include <rte_tailq.h>
      20                 :            : 
      21                 :            : #include "rte_lpm.h"
      22                 :            : #include "lpm_log.h"
      23                 :            : 
      24         [ -  + ]:        252 : RTE_LOG_REGISTER_DEFAULT(lpm_logtype, INFO);
      25                 :            : 
      26                 :            : TAILQ_HEAD(rte_lpm_list, rte_tailq_entry);
      27                 :            : 
      28                 :            : static struct rte_tailq_elem rte_lpm_tailq = {
      29                 :            :         .name = "RTE_LPM",
      30                 :            : };
      31         [ -  + ]:        252 : EAL_REGISTER_TAILQ(rte_lpm_tailq)
      32                 :            : 
      33                 :            : #define MAX_DEPTH_TBL24 24
      34                 :            : 
      35                 :            : enum valid_flag {
      36                 :            :         INVALID = 0,
      37                 :            :         VALID
      38                 :            : };
      39                 :            : 
      40                 :            : /** @internal Rule structure. */
      41                 :            : struct rte_lpm_rule {
      42                 :            :         uint32_t ip; /**< Rule IP address. */
      43                 :            :         uint32_t next_hop; /**< Rule next hop. */
      44                 :            : };
      45                 :            : 
      46                 :            : /** @internal Contains metadata about the rules table. */
      47                 :            : struct rte_lpm_rule_info {
      48                 :            :         uint32_t used_rules; /**< Used rules so far. */
      49                 :            :         uint32_t first_rule; /**< Indexes the first rule of a given depth. */
      50                 :            : };
      51                 :            : 
      52                 :            : /** @internal LPM structure. */
      53                 :            : struct __rte_lpm {
      54                 :            :         /* Exposed LPM data. */
      55                 :            :         struct rte_lpm lpm;
      56                 :            : 
      57                 :            :         /* LPM metadata. */
      58                 :            :         char name[RTE_LPM_NAMESIZE];        /**< Name of the lpm. */
      59                 :            :         uint32_t max_rules; /**< Max. balanced rules per lpm. */
      60                 :            :         uint32_t number_tbl8s; /**< Number of tbl8s. */
      61                 :            :         /**< Rule info table. */
      62                 :            :         struct rte_lpm_rule_info rule_info[RTE_LPM_MAX_DEPTH];
      63                 :            :         struct rte_lpm_rule *rules_tbl; /**< LPM rules. */
      64                 :            : 
      65                 :            :         /* RCU config. */
      66                 :            :         struct rte_rcu_qsbr *v;         /* RCU QSBR variable. */
      67                 :            :         enum rte_lpm_qsbr_mode rcu_mode;/* Blocking, defer queue. */
      68                 :            :         struct rte_rcu_qsbr_dq *dq;     /* RCU QSBR defer queue. */
      69                 :            : };
      70                 :            : 
      71                 :            : /* Macro to enable/disable run-time checks. */
      72                 :            : #if defined(RTE_LIBRTE_LPM_DEBUG)
      73                 :            : #include <rte_debug.h>
      74                 :            : #define VERIFY_DEPTH(depth) do {                                \
      75                 :            :         if ((depth == 0) || (depth > RTE_LPM_MAX_DEPTH))        \
      76                 :            :                 rte_panic("LPM: Invalid depth (%u) at line %d", \
      77                 :            :                                 (unsigned)(depth), __LINE__);   \
      78                 :            : } while (0)
      79                 :            : #else
      80                 :            : #define VERIFY_DEPTH(depth)
      81                 :            : #endif
      82                 :            : 
      83                 :            : /*
      84                 :            :  * Converts a given depth value to its corresponding mask value.
      85                 :            :  *
      86                 :            :  * depth  (IN)          : range = 1 - 32
      87                 :            :  * mask   (OUT)         : 32bit mask
      88                 :            :  */
      89                 :            : static uint32_t __rte_pure
      90                 :            : depth_to_mask(uint8_t depth)
      91                 :            : {
      92                 :            :         VERIFY_DEPTH(depth);
      93                 :            : 
      94                 :            :         /* To calculate a mask start with a 1 on the left hand side and right
      95                 :            :          * shift while populating the left hand side with 1's
      96                 :            :          */
      97                 :      59264 :         return (int)0x80000000 >> (depth - 1);
      98                 :            : }
      99                 :            : 
     100                 :            : /*
     101                 :            :  * Converts given depth value to its corresponding range value.
     102                 :            :  */
     103                 :            : static uint32_t __rte_pure
     104                 :            : depth_to_range(uint8_t depth)
     105                 :            : {
     106                 :            :         VERIFY_DEPTH(depth);
     107                 :            : 
     108                 :            :         /*
     109                 :            :          * Calculate tbl24 range. (Note: 2^depth = 1 << depth)
     110                 :            :          */
     111                 :       5917 :         if (depth <= MAX_DEPTH_TBL24)
     112                 :         76 :                 return 1 << (MAX_DEPTH_TBL24 - depth);
     113                 :            : 
     114                 :            :         /* Else if depth is greater than 24 */
     115                 :       5841 :         return 1 << (RTE_LPM_MAX_DEPTH - depth);
     116                 :            : }
     117                 :            : 
     118                 :            : /*
     119                 :            :  * Find an existing lpm table and return a pointer to it.
     120                 :            :  */
     121                 :            : RTE_EXPORT_SYMBOL(rte_lpm_find_existing)
     122                 :            : struct rte_lpm *
     123                 :         40 : rte_lpm_find_existing(const char *name)
     124                 :            : {
     125                 :            :         struct __rte_lpm *i_lpm = NULL;
     126                 :            :         struct rte_tailq_entry *te;
     127                 :            :         struct rte_lpm_list *lpm_list;
     128                 :            : 
     129                 :         40 :         lpm_list = RTE_TAILQ_CAST(rte_lpm_tailq.head, rte_lpm_list);
     130                 :            : 
     131                 :         40 :         rte_mcfg_tailq_read_lock();
     132         [ +  + ]:         74 :         TAILQ_FOREACH(te, lpm_list, next) {
     133                 :         48 :                 i_lpm = te->data;
     134         [ +  + ]:         48 :                 if (strncmp(name, i_lpm->name, RTE_LPM_NAMESIZE) == 0)
     135                 :            :                         break;
     136                 :            :         }
     137                 :         40 :         rte_mcfg_tailq_read_unlock();
     138                 :            : 
     139         [ +  + ]:         40 :         if (te == NULL) {
     140                 :         26 :                 rte_errno = ENOENT;
     141                 :         26 :                 return NULL;
     142                 :            :         }
     143                 :            : 
     144                 :         14 :         return &i_lpm->lpm;
     145                 :            : }
     146                 :            : 
     147                 :            : /*
     148                 :            :  * Allocates memory for LPM object
     149                 :            :  */
     150                 :            : RTE_EXPORT_SYMBOL(rte_lpm_create)
     151                 :            : struct rte_lpm *
     152                 :        149 : rte_lpm_create(const char *name, int socket_id,
     153                 :            :                 const struct rte_lpm_config *config)
     154                 :            : {
     155                 :            :         char mem_name[RTE_LPM_NAMESIZE];
     156                 :            :         struct __rte_lpm *i_lpm;
     157                 :            :         struct rte_lpm *lpm = NULL;
     158                 :            :         struct rte_tailq_entry *te;
     159                 :            :         uint32_t mem_size, rules_size, tbl8s_size;
     160                 :            :         struct rte_lpm_list *lpm_list;
     161                 :            : 
     162                 :        149 :         lpm_list = RTE_TAILQ_CAST(rte_lpm_tailq.head, rte_lpm_list);
     163                 :            : 
     164                 :            :         RTE_BUILD_BUG_ON(sizeof(struct rte_lpm_tbl_entry) != 4);
     165                 :            : 
     166                 :            :         /* Check user arguments. */
     167   [ +  +  +  + ]:        149 :         if ((name == NULL) || (socket_id < -1) || (config->max_rules == 0)
     168         [ -  + ]:        144 :                         || config->number_tbl8s > RTE_LPM_MAX_TBL8_NUM_GROUPS) {
     169                 :          5 :                 rte_errno = EINVAL;
     170                 :          5 :                 return NULL;
     171                 :            :         }
     172                 :            : 
     173                 :            :         snprintf(mem_name, sizeof(mem_name), "LPM_%s", name);
     174                 :            : 
     175                 :        144 :         rte_mcfg_tailq_write_lock();
     176                 :            : 
     177                 :            :         /* guarantee there's no existing */
     178         [ +  + ]:        156 :         TAILQ_FOREACH(te, lpm_list, next) {
     179                 :         19 :                 i_lpm = te->data;
     180         [ +  + ]:         19 :                 if (strncmp(name, i_lpm->name, RTE_LPM_NAMESIZE) == 0)
     181                 :            :                         break;
     182                 :            :         }
     183                 :            : 
     184         [ +  + ]:        144 :         if (te != NULL) {
     185                 :          7 :                 rte_errno = EEXIST;
     186                 :          7 :                 goto exit;
     187                 :            :         }
     188                 :            : 
     189                 :            :         /* Determine the amount of memory to allocate. */
     190                 :            :         mem_size = sizeof(*i_lpm);
     191                 :        137 :         rules_size = sizeof(struct rte_lpm_rule) * config->max_rules;
     192                 :        137 :         tbl8s_size = sizeof(struct rte_lpm_tbl_entry) *
     193                 :        137 :                         RTE_LPM_TBL8_GROUP_NUM_ENTRIES * config->number_tbl8s;
     194                 :            : 
     195                 :            :         /* allocate tailq entry */
     196                 :        137 :         te = rte_zmalloc("LPM_TAILQ_ENTRY", sizeof(*te), 0);
     197         [ -  + ]:        137 :         if (te == NULL) {
     198                 :          0 :                 LPM_LOG(ERR, "Failed to allocate tailq entry");
     199                 :          0 :                 rte_errno = ENOMEM;
     200                 :          0 :                 goto exit;
     201                 :            :         }
     202                 :            : 
     203                 :            :         /* Allocate memory to store the LPM data structures. */
     204                 :        137 :         i_lpm = rte_zmalloc_socket(mem_name, mem_size,
     205                 :            :                         RTE_CACHE_LINE_SIZE, socket_id);
     206         [ -  + ]:        137 :         if (i_lpm == NULL) {
     207                 :          0 :                 LPM_LOG(ERR, "LPM memory allocation failed");
     208                 :          0 :                 rte_free(te);
     209                 :          0 :                 rte_errno = ENOMEM;
     210                 :          0 :                 goto exit;
     211                 :            :         }
     212                 :            : 
     213                 :        137 :         i_lpm->rules_tbl = rte_zmalloc_socket(NULL,
     214                 :            :                         (size_t)rules_size, RTE_CACHE_LINE_SIZE, socket_id);
     215                 :            : 
     216         [ -  + ]:        137 :         if (i_lpm->rules_tbl == NULL) {
     217                 :          0 :                 LPM_LOG(ERR, "LPM rules_tbl memory allocation failed");
     218                 :          0 :                 rte_free(i_lpm);
     219                 :            :                 i_lpm = NULL;
     220                 :          0 :                 rte_free(te);
     221                 :          0 :                 rte_errno = ENOMEM;
     222                 :          0 :                 goto exit;
     223                 :            :         }
     224                 :            : 
     225                 :        137 :         i_lpm->lpm.tbl8 = rte_zmalloc_socket(NULL,
     226                 :            :                         (size_t)tbl8s_size, RTE_CACHE_LINE_SIZE, socket_id);
     227                 :            : 
     228         [ -  + ]:        137 :         if (i_lpm->lpm.tbl8 == NULL) {
     229                 :          0 :                 LPM_LOG(ERR, "LPM tbl8 memory allocation failed");
     230                 :          0 :                 rte_free(i_lpm->rules_tbl);
     231                 :          0 :                 rte_free(i_lpm);
     232                 :            :                 i_lpm = NULL;
     233                 :          0 :                 rte_free(te);
     234                 :          0 :                 rte_errno = ENOMEM;
     235                 :          0 :                 goto exit;
     236                 :            :         }
     237                 :            : 
     238                 :            :         /* Save user arguments. */
     239                 :        137 :         i_lpm->max_rules = config->max_rules;
     240                 :        137 :         i_lpm->number_tbl8s = config->number_tbl8s;
     241                 :        137 :         strlcpy(i_lpm->name, name, sizeof(i_lpm->name));
     242                 :            : 
     243                 :        137 :         te->data = i_lpm;
     244                 :        137 :         lpm = &i_lpm->lpm;
     245                 :            : 
     246                 :        137 :         TAILQ_INSERT_TAIL(lpm_list, te, next);
     247                 :            : 
     248                 :        144 : exit:
     249                 :        144 :         rte_mcfg_tailq_write_unlock();
     250                 :            : 
     251                 :        144 :         return lpm;
     252                 :            : }
     253                 :            : 
     254                 :            : /*
     255                 :            :  * Deallocates memory for given LPM table.
     256                 :            :  */
     257                 :            : RTE_EXPORT_SYMBOL(rte_lpm_free)
     258                 :            : void
     259                 :        139 : rte_lpm_free(struct rte_lpm *lpm)
     260                 :            : {
     261                 :            :         struct rte_lpm_list *lpm_list;
     262                 :            :         struct rte_tailq_entry *te;
     263                 :            :         struct __rte_lpm *i_lpm;
     264                 :            : 
     265                 :            :         /* Check user arguments. */
     266         [ +  + ]:        139 :         if (lpm == NULL)
     267                 :            :                 return;
     268                 :            :         i_lpm = container_of(lpm, struct __rte_lpm, lpm);
     269                 :            : 
     270                 :        137 :         lpm_list = RTE_TAILQ_CAST(rte_lpm_tailq.head, rte_lpm_list);
     271                 :            : 
     272                 :        137 :         rte_mcfg_tailq_write_lock();
     273                 :            : 
     274                 :            :         /* find our tailq entry */
     275         [ +  - ]:        149 :         TAILQ_FOREACH(te, lpm_list, next) {
     276         [ +  + ]:        149 :                 if (te->data == (void *)i_lpm)
     277                 :            :                         break;
     278                 :            :         }
     279         [ +  - ]:        137 :         if (te != NULL)
     280         [ -  + ]:        137 :                 TAILQ_REMOVE(lpm_list, te, next);
     281                 :            : 
     282                 :        137 :         rte_mcfg_tailq_write_unlock();
     283                 :            : 
     284         [ +  + ]:        137 :         if (i_lpm->dq != NULL)
     285                 :          2 :                 rte_rcu_qsbr_dq_delete(i_lpm->dq);
     286                 :        137 :         rte_free(i_lpm->lpm.tbl8);
     287                 :        137 :         rte_free(i_lpm->rules_tbl);
     288                 :        137 :         rte_free(i_lpm);
     289                 :        137 :         rte_free(te);
     290                 :            : }
     291                 :            : 
     292                 :            : static void
     293                 :          1 : __lpm_rcu_qsbr_free_resource(void *p, void *data, unsigned int n)
     294                 :            : {
     295                 :          1 :         struct rte_lpm_tbl_entry *tbl8 = ((struct __rte_lpm *)p)->lpm.tbl8;
     296                 :            :         struct rte_lpm_tbl_entry zero_tbl8_entry = {0};
     297                 :          1 :         uint32_t tbl8_group_index = *(uint32_t *)data;
     298                 :            : 
     299                 :            :         RTE_SET_USED(n);
     300                 :            :         /* Set tbl8 group invalid */
     301                 :          1 :         __atomic_store(&tbl8[tbl8_group_index], &zero_tbl8_entry,
     302                 :            :                 __ATOMIC_RELAXED);
     303                 :          1 : }
     304                 :            : 
     305                 :            : /* Associate QSBR variable with an LPM object.
     306                 :            :  */
     307                 :            : RTE_EXPORT_SYMBOL(rte_lpm_rcu_qsbr_add)
     308                 :            : int
     309                 :          5 : rte_lpm_rcu_qsbr_add(struct rte_lpm *lpm, struct rte_lpm_rcu_config *cfg)
     310                 :            : {
     311                 :          5 :         struct rte_rcu_qsbr_dq_parameters params = {0};
     312                 :            :         char rcu_dq_name[RTE_RCU_QSBR_DQ_NAMESIZE];
     313                 :            :         struct __rte_lpm *i_lpm;
     314                 :            : 
     315         [ -  + ]:          5 :         if (lpm == NULL || cfg == NULL) {
     316                 :          0 :                 rte_errno = EINVAL;
     317                 :          0 :                 return 1;
     318                 :            :         }
     319                 :            : 
     320                 :            :         i_lpm = container_of(lpm, struct __rte_lpm, lpm);
     321         [ +  + ]:          5 :         if (i_lpm->v != NULL) {
     322                 :          1 :                 rte_errno = EEXIST;
     323                 :          1 :                 return 1;
     324                 :            :         }
     325                 :            : 
     326         [ +  + ]:          4 :         if (cfg->mode == RTE_LPM_QSBR_MODE_SYNC) {
     327                 :            :                 /* No other things to do. */
     328         [ +  + ]:          3 :         } else if (cfg->mode == RTE_LPM_QSBR_MODE_DQ) {
     329                 :            :                 /* Init QSBR defer queue. */
     330                 :            :                 snprintf(rcu_dq_name, sizeof(rcu_dq_name),
     331         [ +  - ]:          2 :                                 "LPM_RCU_%s", i_lpm->name);
     332                 :          2 :                 params.name = rcu_dq_name;
     333                 :          2 :                 params.size = cfg->dq_size;
     334         [ +  - ]:          2 :                 if (params.size == 0)
     335                 :          2 :                         params.size = i_lpm->number_tbl8s;
     336                 :          2 :                 params.trigger_reclaim_limit = cfg->reclaim_thd;
     337                 :          2 :                 params.max_reclaim_size = cfg->reclaim_max;
     338         [ +  - ]:          2 :                 if (params.max_reclaim_size == 0)
     339                 :          2 :                         params.max_reclaim_size = RTE_LPM_RCU_DQ_RECLAIM_MAX;
     340                 :          2 :                 params.esize = sizeof(uint32_t);        /* tbl8 group index */
     341                 :          2 :                 params.free_fn = __lpm_rcu_qsbr_free_resource;
     342                 :          2 :                 params.p = i_lpm;
     343                 :          2 :                 params.v = cfg->v;
     344                 :          2 :                 i_lpm->dq = rte_rcu_qsbr_dq_create(&params);
     345         [ -  + ]:          2 :                 if (i_lpm->dq == NULL) {
     346                 :          0 :                         LPM_LOG(ERR, "LPM defer queue creation failed");
     347                 :          0 :                         return 1;
     348                 :            :                 }
     349                 :            :         } else {
     350                 :          1 :                 rte_errno = EINVAL;
     351                 :          1 :                 return 1;
     352                 :            :         }
     353                 :          3 :         i_lpm->rcu_mode = cfg->mode;
     354                 :          3 :         i_lpm->v = cfg->v;
     355                 :            : 
     356                 :          3 :         return 0;
     357                 :            : }
     358                 :            : 
     359                 :            : /*
     360                 :            :  * Adds a rule to the rule table.
     361                 :            :  *
     362                 :            :  * NOTE: The rule table is split into 32 groups. Each group contains rules that
     363                 :            :  * apply to a specific prefix depth (i.e. group 1 contains rules that apply to
     364                 :            :  * prefixes with a depth of 1 etc.). In the following code (depth - 1) is used
     365                 :            :  * to refer to depth 1 because even though the depth range is 1 - 32, depths
     366                 :            :  * are stored in the rule table from 0 - 31.
     367                 :            :  * NOTE: Valid range for depth parameter is 1 .. 32 inclusive.
     368                 :            :  */
     369                 :            : static int32_t
     370                 :       3350 : rule_add(struct __rte_lpm *i_lpm, uint32_t ip_masked, uint8_t depth,
     371                 :            :         uint32_t next_hop)
     372                 :            : {
     373                 :            :         uint32_t rule_gindex, rule_index, last_rule;
     374                 :            :         int i;
     375                 :            : 
     376                 :            :         VERIFY_DEPTH(depth);
     377                 :            : 
     378                 :            :         /* Scan through rule group to see if rule already exists. */
     379         [ +  + ]:       3350 :         if (i_lpm->rule_info[depth - 1].used_rules > 0) {
     380                 :            : 
     381                 :            :                 /* rule_gindex stands for rule group index. */
     382                 :        772 :                 rule_gindex = i_lpm->rule_info[depth - 1].first_rule;
     383                 :            :                 /* Initialise rule_index to point to start of rule group. */
     384                 :            :                 rule_index = rule_gindex;
     385                 :            :                 /* Last rule = Last used rule in this rule group. */
     386                 :        772 :                 last_rule = rule_gindex + i_lpm->rule_info[depth - 1].used_rules;
     387                 :            : 
     388         [ +  + ]:     164997 :                 for (; rule_index < last_rule; rule_index++) {
     389                 :            : 
     390                 :            :                         /* If rule already exists update next hop and return. */
     391         [ +  + ]:     164228 :                         if (i_lpm->rules_tbl[rule_index].ip == ip_masked) {
     392                 :            : 
     393         [ +  - ]:          3 :                                 if (i_lpm->rules_tbl[rule_index].next_hop
     394                 :            :                                                 == next_hop)
     395                 :            :                                         return -EEXIST;
     396                 :          3 :                                 i_lpm->rules_tbl[rule_index].next_hop = next_hop;
     397                 :            : 
     398                 :          3 :                                 return rule_index;
     399                 :            :                         }
     400                 :            :                 }
     401                 :            : 
     402         [ +  - ]:        769 :                 if (rule_index == i_lpm->max_rules)
     403                 :            :                         return -ENOSPC;
     404                 :            :         } else {
     405                 :            :                 /* Calculate the position in which the rule will be stored. */
     406                 :            :                 rule_index = 0;
     407                 :            : 
     408         [ +  + ]:      55061 :                 for (i = depth - 1; i > 0; i--) {
     409         [ +  + ]:      53522 :                         if (i_lpm->rule_info[i - 1].used_rules > 0) {
     410                 :       1039 :                                 rule_index = i_lpm->rule_info[i - 1].first_rule
     411                 :            :                                                 + i_lpm->rule_info[i - 1].used_rules;
     412                 :       1039 :                                 break;
     413                 :            :                         }
     414                 :            :                 }
     415         [ +  - ]:       2578 :                 if (rule_index == i_lpm->max_rules)
     416                 :            :                         return -ENOSPC;
     417                 :            : 
     418                 :       2578 :                 i_lpm->rule_info[depth - 1].first_rule = rule_index;
     419                 :            :         }
     420                 :            : 
     421                 :            :         /* Make room for the new rule in the array. */
     422         [ +  + ]:       6608 :         for (i = RTE_LPM_MAX_DEPTH; i > depth; i--) {
     423                 :       3261 :                 if (i_lpm->rule_info[i - 1].first_rule
     424         [ +  - ]:       3261 :                                 + i_lpm->rule_info[i - 1].used_rules == i_lpm->max_rules)
     425                 :            :                         return -ENOSPC;
     426                 :            : 
     427         [ +  + ]:       3261 :                 if (i_lpm->rule_info[i - 1].used_rules > 0) {
     428                 :          4 :                         i_lpm->rules_tbl[i_lpm->rule_info[i - 1].first_rule
     429                 :          4 :                                 + i_lpm->rule_info[i - 1].used_rules]
     430                 :          4 :                                         = i_lpm->rules_tbl[i_lpm->rule_info[i - 1].first_rule];
     431                 :          4 :                         i_lpm->rule_info[i - 1].first_rule++;
     432                 :            :                 }
     433                 :            :         }
     434                 :            : 
     435                 :            :         /* Add the new rule. */
     436                 :       3347 :         i_lpm->rules_tbl[rule_index].ip = ip_masked;
     437                 :       3347 :         i_lpm->rules_tbl[rule_index].next_hop = next_hop;
     438                 :            : 
     439                 :            :         /* Increment the used rules counter for this rule group. */
     440                 :       3347 :         i_lpm->rule_info[depth - 1].used_rules++;
     441                 :            : 
     442                 :       3347 :         return rule_index;
     443                 :            : }
     444                 :            : 
     445                 :            : /*
     446                 :            :  * Delete a rule from the rule table.
     447                 :            :  * NOTE: Valid range for depth parameter is 1 .. 32 inclusive.
     448                 :            :  */
     449                 :            : static void
     450                 :       2570 : rule_delete(struct __rte_lpm *i_lpm, int32_t rule_index, uint8_t depth)
     451                 :            : {
     452                 :            :         int i;
     453                 :            : 
     454                 :            :         VERIFY_DEPTH(depth);
     455                 :            : 
     456                 :       2570 :         i_lpm->rules_tbl[rule_index] =
     457                 :       2570 :                         i_lpm->rules_tbl[i_lpm->rule_info[depth - 1].first_rule
     458                 :       2570 :                         + i_lpm->rule_info[depth - 1].used_rules - 1];
     459                 :            : 
     460         [ +  + ]:       5259 :         for (i = depth; i < RTE_LPM_MAX_DEPTH; i++) {
     461         [ +  + ]:       2689 :                 if (i_lpm->rule_info[i].used_rules > 0) {
     462                 :          2 :                         i_lpm->rules_tbl[i_lpm->rule_info[i].first_rule - 1] =
     463                 :          2 :                                         i_lpm->rules_tbl[i_lpm->rule_info[i].first_rule
     464                 :          2 :                                                 + i_lpm->rule_info[i].used_rules - 1];
     465                 :          2 :                         i_lpm->rule_info[i].first_rule--;
     466                 :            :                 }
     467                 :            :         }
     468                 :            : 
     469                 :       2570 :         i_lpm->rule_info[depth - 1].used_rules--;
     470                 :       2570 : }
     471                 :            : 
     472                 :            : /*
     473                 :            :  * Finds a rule in rule table.
     474                 :            :  * NOTE: Valid range for depth parameter is 1 .. 32 inclusive.
     475                 :            :  */
     476                 :            : static int32_t
     477                 :            : rule_find(struct __rte_lpm *i_lpm, uint32_t ip_masked, uint8_t depth)
     478                 :            : {
     479                 :            :         uint32_t rule_gindex, last_rule, rule_index;
     480                 :            : 
     481                 :            :         VERIFY_DEPTH(depth);
     482                 :            : 
     483                 :      55914 :         rule_gindex = i_lpm->rule_info[depth - 1].first_rule;
     484                 :      55914 :         last_rule = rule_gindex + i_lpm->rule_info[depth - 1].used_rules;
     485                 :            : 
     486                 :            :         /* Scan used rules at given depth to find rule. */
     487   [ +  +  +  +  :      55915 :         for (rule_index = rule_gindex; rule_index < last_rule; rule_index++) {
                   +  + ]
     488                 :            :                 /* If rule is found return the rule index. */
     489   [ +  +  +  -  :       3606 :                 if (i_lpm->rules_tbl[rule_index].ip == ip_masked)
                   +  - ]
     490                 :       3605 :                         return rule_index;
     491                 :            :         }
     492                 :            : 
     493                 :            :         /* If rule is not found return -EINVAL. */
     494                 :            :         return -EINVAL;
     495                 :            : }
     496                 :            : 
     497                 :            : /*
     498                 :            :  * Find, clean and allocate a tbl8.
     499                 :            :  */
     500                 :            : static int32_t
     501                 :       3301 : _tbl8_alloc(struct __rte_lpm *i_lpm)
     502                 :            : {
     503                 :            :         uint32_t group_idx; /* tbl8 group index. */
     504                 :            :         struct rte_lpm_tbl_entry *tbl8_entry;
     505                 :            : 
     506                 :            :         /* Scan through tbl8 to find a free (i.e. INVALID) tbl8 group. */
     507         [ +  + ]:     167529 :         for (group_idx = 0; group_idx < i_lpm->number_tbl8s; group_idx++) {
     508                 :     167524 :                 tbl8_entry = &i_lpm->lpm.tbl8[group_idx *
     509                 :            :                                         RTE_LPM_TBL8_GROUP_NUM_ENTRIES];
     510                 :            :                 /* If a free tbl8 group is found clean it and set as VALID. */
     511         [ +  + ]:     167524 :                 if (!tbl8_entry->valid_group) {
     512                 :            :                         struct rte_lpm_tbl_entry new_tbl8_entry = {
     513                 :            :                                 .next_hop = 0,
     514                 :            :                                 .valid = INVALID,
     515                 :            :                                 .depth = 0,
     516                 :            :                                 .valid_group = VALID,
     517                 :            :                         };
     518                 :            : 
     519                 :            :                         memset(&tbl8_entry[0], 0,
     520                 :            :                                         RTE_LPM_TBL8_GROUP_NUM_ENTRIES *
     521                 :            :                                         sizeof(tbl8_entry[0]));
     522                 :            : 
     523                 :       3296 :                         __atomic_store(tbl8_entry, &new_tbl8_entry,
     524                 :            :                                         __ATOMIC_RELAXED);
     525                 :            : 
     526                 :            :                         /* Return group index for allocated tbl8 group. */
     527                 :       3296 :                         return group_idx;
     528                 :            :                 }
     529                 :            :         }
     530                 :            : 
     531                 :            :         /* If there are no tbl8 groups free then return error. */
     532                 :            :         return -ENOSPC;
     533                 :            : }
     534                 :            : 
     535                 :            : static int32_t
     536                 :       3299 : tbl8_alloc(struct __rte_lpm *i_lpm)
     537                 :            : {
     538                 :            :         int32_t group_idx; /* tbl8 group index. */
     539                 :            : 
     540                 :       3299 :         group_idx = _tbl8_alloc(i_lpm);
     541   [ +  +  +  + ]:       3299 :         if (group_idx == -ENOSPC && i_lpm->dq != NULL) {
     542                 :            :                 /* If there are no tbl8 groups try to reclaim one. */
     543         [ +  - ]:          2 :                 if (rte_rcu_qsbr_dq_reclaim(i_lpm->dq, 1,
     544                 :            :                                 NULL, NULL, NULL) == 0)
     545                 :          2 :                         group_idx = _tbl8_alloc(i_lpm);
     546                 :            :         }
     547                 :            : 
     548                 :       3299 :         return group_idx;
     549                 :            : }
     550                 :            : 
     551                 :            : static int32_t
     552                 :       2523 : tbl8_free(struct __rte_lpm *i_lpm, uint32_t tbl8_group_start)
     553                 :            : {
     554                 :            :         struct rte_lpm_tbl_entry zero_tbl8_entry = {0};
     555                 :            :         int status;
     556                 :            : 
     557         [ +  + ]:       2523 :         if (i_lpm->v == NULL) {
     558                 :            :                 /* Set tbl8 group invalid*/
     559                 :       2010 :                 __atomic_store(&i_lpm->lpm.tbl8[tbl8_group_start], &zero_tbl8_entry,
     560                 :            :                                 __ATOMIC_RELAXED);
     561         [ +  + ]:        513 :         } else if (i_lpm->rcu_mode == RTE_LPM_QSBR_MODE_SYNC) {
     562                 :            :                 /* Wait for quiescent state change. */
     563                 :        512 :                 rte_rcu_qsbr_synchronize(i_lpm->v,
     564                 :            :                         RTE_QSBR_THRID_INVALID);
     565                 :            :                 /* Set tbl8 group invalid*/
     566                 :        512 :                 __atomic_store(&i_lpm->lpm.tbl8[tbl8_group_start], &zero_tbl8_entry,
     567                 :            :                                 __ATOMIC_RELAXED);
     568         [ +  - ]:          1 :         } else if (i_lpm->rcu_mode == RTE_LPM_QSBR_MODE_DQ) {
     569                 :            :                 /* Push into QSBR defer queue. */
     570                 :          1 :                 status = rte_rcu_qsbr_dq_enqueue(i_lpm->dq,
     571                 :            :                                 (void *)&tbl8_group_start);
     572         [ -  + ]:          1 :                 if (status == 1) {
     573                 :          0 :                         LPM_LOG(ERR, "Failed to push QSBR FIFO");
     574                 :          0 :                         return -rte_errno;
     575                 :            :                 }
     576                 :            :         }
     577                 :            : 
     578                 :            :         return 0;
     579                 :            : }
     580                 :            : 
     581                 :            : static __rte_noinline int32_t
     582                 :         41 : add_depth_small(struct __rte_lpm *i_lpm, uint32_t ip, uint8_t depth,
     583                 :            :                 uint32_t next_hop)
     584                 :            : {
     585                 :            : #define group_idx next_hop
     586                 :            :         uint32_t tbl24_index, tbl24_range, tbl8_index, tbl8_group_end, i, j;
     587                 :            : 
     588                 :            :         /* Calculate the index into Table24. */
     589                 :         41 :         tbl24_index = ip >> 8;
     590         [ +  - ]:         41 :         tbl24_range = depth_to_range(depth);
     591                 :            : 
     592         [ +  + ]:   16778550 :         for (i = tbl24_index; i < (tbl24_index + tbl24_range); i++) {
     593                 :            :                 /*
     594                 :            :                  * For invalid OR valid and non-extended tbl 24 entries set
     595                 :            :                  * entry.
     596                 :            :                  */
     597   [ +  +  +  + ]:   16778509 :                 if (!i_lpm->lpm.tbl24[i].valid || (i_lpm->lpm.tbl24[i].valid_group == 0 &&
     598         [ +  - ]:    8388865 :                                 i_lpm->lpm.tbl24[i].depth <= depth)) {
     599                 :            : 
     600                 :   16778506 :                         struct rte_lpm_tbl_entry new_tbl24_entry = {
     601                 :            :                                 .next_hop = next_hop,
     602                 :            :                                 .valid = VALID,
     603                 :            :                                 .valid_group = 0,
     604                 :            :                                 .depth = depth,
     605                 :            :                         };
     606                 :            : 
     607                 :            :                         /* Setting tbl24 entry in one go to avoid race
     608                 :            :                          * conditions
     609                 :            :                          */
     610                 :   16778506 :                         __atomic_store(&i_lpm->lpm.tbl24[i], &new_tbl24_entry,
     611                 :            :                                         __ATOMIC_RELEASE);
     612                 :            : 
     613                 :            :                         continue;
     614                 :            :                 }
     615                 :            : 
     616         [ +  - ]:          3 :                 if (i_lpm->lpm.tbl24[i].valid_group == 1) {
     617                 :            :                         /* If tbl24 entry is valid and extended calculate the
     618                 :            :                          *  index into tbl8.
     619                 :            :                          */
     620                 :          3 :                         tbl8_index = i_lpm->lpm.tbl24[i].group_idx *
     621                 :            :                                         RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
     622                 :          3 :                         tbl8_group_end = tbl8_index +
     623                 :            :                                         RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
     624                 :            : 
     625         [ +  + ]:        771 :                         for (j = tbl8_index; j < tbl8_group_end; j++) {
     626         [ +  + ]:        768 :                                 if (!i_lpm->lpm.tbl8[j].valid ||
     627         [ +  + ]:        273 :                                                 i_lpm->lpm.tbl8[j].depth <= depth) {
     628                 :            :                                         struct rte_lpm_tbl_entry
     629                 :        735 :                                                 new_tbl8_entry = {
     630                 :            :                                                 .valid = VALID,
     631                 :            :                                                 .valid_group = VALID,
     632                 :            :                                                 .depth = depth,
     633                 :            :                                                 .next_hop = next_hop,
     634                 :            :                                         };
     635                 :            : 
     636                 :            :                                         /*
     637                 :            :                                          * Setting tbl8 entry in one go to avoid
     638                 :            :                                          * race conditions
     639                 :            :                                          */
     640                 :        735 :                                         __atomic_store(&i_lpm->lpm.tbl8[j],
     641                 :            :                                                 &new_tbl8_entry,
     642                 :            :                                                 __ATOMIC_RELAXED);
     643                 :            : 
     644                 :            :                                         continue;
     645                 :            :                                 }
     646                 :            :                         }
     647                 :            :                 }
     648                 :            :         }
     649                 :            : #undef group_idx
     650                 :         41 :         return 0;
     651                 :            : }
     652                 :            : 
     653                 :            : static __rte_noinline int32_t
     654                 :       3309 : add_depth_big(struct __rte_lpm *i_lpm, uint32_t ip_masked, uint8_t depth,
     655                 :            :                 uint32_t next_hop)
     656                 :            : {
     657                 :            : #define group_idx next_hop
     658                 :            :         uint32_t tbl24_index;
     659                 :            :         int32_t tbl8_group_index, tbl8_group_start, tbl8_group_end, tbl8_index,
     660                 :            :                 tbl8_range, i;
     661                 :            : 
     662                 :       3309 :         tbl24_index = (ip_masked >> 8);
     663         [ -  + ]:       3309 :         tbl8_range = depth_to_range(depth);
     664                 :            : 
     665         [ +  + ]:       3309 :         if (!i_lpm->lpm.tbl24[tbl24_index].valid) {
     666                 :            :                 /* Search for a free tbl8 group. */
     667                 :       2294 :                 tbl8_group_index = tbl8_alloc(i_lpm);
     668                 :            : 
     669                 :            :                 /* Check tbl8 allocation was successful. */
     670         [ +  + ]:       2294 :                 if (tbl8_group_index < 0) {
     671                 :            :                         return tbl8_group_index;
     672                 :            :                 }
     673                 :            : 
     674                 :            :                 /* Find index into tbl8 and range. */
     675                 :       2291 :                 tbl8_index = (tbl8_group_index *
     676                 :       2291 :                                 RTE_LPM_TBL8_GROUP_NUM_ENTRIES) +
     677                 :       2291 :                                 (ip_masked & 0xFF);
     678                 :            : 
     679                 :            :                 /* Set tbl8 entry. */
     680         [ +  + ]:      13471 :                 for (i = tbl8_index; i < (tbl8_index + tbl8_range); i++) {
     681                 :      11180 :                         struct rte_lpm_tbl_entry new_tbl8_entry = {
     682                 :            :                                 .valid = VALID,
     683                 :            :                                 .depth = depth,
     684                 :      11180 :                                 .valid_group = i_lpm->lpm.tbl8[i].valid_group,
     685                 :            :                                 .next_hop = next_hop,
     686                 :            :                         };
     687                 :      11180 :                         __atomic_store(&i_lpm->lpm.tbl8[i], &new_tbl8_entry,
     688                 :            :                                         __ATOMIC_RELAXED);
     689                 :            :                 }
     690                 :            : 
     691                 :            :                 /*
     692                 :            :                  * Update tbl24 entry to point to new tbl8 entry. Note: The
     693                 :            :                  * ext_flag and tbl8_index need to be updated simultaneously,
     694                 :            :                  * so assign whole structure in one go
     695                 :            :                  */
     696                 :            : 
     697                 :       2291 :                 struct rte_lpm_tbl_entry new_tbl24_entry = {
     698                 :            :                         .group_idx = tbl8_group_index,
     699                 :            :                         .valid = VALID,
     700                 :            :                         .valid_group = 1,
     701                 :            :                         .depth = 0,
     702                 :            :                 };
     703                 :            : 
     704                 :            :                 /* The tbl24 entry must be written only after the
     705                 :            :                  * tbl8 entries are written.
     706                 :            :                  */
     707                 :       2291 :                 __atomic_store(&i_lpm->lpm.tbl24[tbl24_index], &new_tbl24_entry,
     708                 :            :                                 __ATOMIC_RELEASE);
     709                 :            : 
     710                 :            :         } /* If valid entry but not extended calculate the index into Table8. */
     711         [ +  + ]:       1015 :         else if (i_lpm->lpm.tbl24[tbl24_index].valid_group == 0) {
     712                 :            :                 /* Search for free tbl8 group. */
     713                 :       1005 :                 tbl8_group_index = tbl8_alloc(i_lpm);
     714                 :            : 
     715         [ +  - ]:       1005 :                 if (tbl8_group_index < 0) {
     716                 :            :                         return tbl8_group_index;
     717                 :            :                 }
     718                 :            : 
     719                 :       1005 :                 tbl8_group_start = tbl8_group_index *
     720                 :            :                                 RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
     721                 :       1005 :                 tbl8_group_end = tbl8_group_start +
     722                 :            :                                 RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
     723                 :            : 
     724                 :            :                 /* Populate new tbl8 with tbl24 value. */
     725         [ +  + ]:     258285 :                 for (i = tbl8_group_start; i < tbl8_group_end; i++) {
     726                 :            :                         struct rte_lpm_tbl_entry new_tbl8_entry = {
     727                 :            :                                 .valid = VALID,
     728                 :     257280 :                                 .depth = i_lpm->lpm.tbl24[tbl24_index].depth,
     729                 :     257280 :                                 .valid_group = i_lpm->lpm.tbl8[i].valid_group,
     730                 :     257280 :                                 .next_hop = i_lpm->lpm.tbl24[tbl24_index].next_hop,
     731                 :            :                         };
     732                 :     257280 :                         __atomic_store(&i_lpm->lpm.tbl8[i], &new_tbl8_entry,
     733                 :            :                                         __ATOMIC_RELAXED);
     734                 :            :                 }
     735                 :            : 
     736                 :       1005 :                 tbl8_index = tbl8_group_start + (ip_masked & 0xFF);
     737                 :            : 
     738                 :            :                 /* Insert new rule into the tbl8 entry. */
     739         [ +  + ]:       2167 :                 for (i = tbl8_index; i < tbl8_index + tbl8_range; i++) {
     740                 :       1162 :                         struct rte_lpm_tbl_entry new_tbl8_entry = {
     741                 :            :                                 .valid = VALID,
     742                 :            :                                 .depth = depth,
     743                 :       1162 :                                 .valid_group = i_lpm->lpm.tbl8[i].valid_group,
     744                 :            :                                 .next_hop = next_hop,
     745                 :            :                         };
     746                 :       1162 :                         __atomic_store(&i_lpm->lpm.tbl8[i], &new_tbl8_entry,
     747                 :            :                                         __ATOMIC_RELAXED);
     748                 :            :                 }
     749                 :            : 
     750                 :            :                 /*
     751                 :            :                  * Update tbl24 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                 :            : 
     756                 :       1005 :                 struct rte_lpm_tbl_entry new_tbl24_entry = {
     757                 :            :                                 .group_idx = tbl8_group_index,
     758                 :            :                                 .valid = VALID,
     759                 :            :                                 .valid_group = 1,
     760                 :            :                                 .depth = 0,
     761                 :            :                 };
     762                 :            : 
     763                 :            :                 /* The tbl24 entry must be written only after the
     764                 :            :                  * tbl8 entries are written.
     765                 :            :                  */
     766                 :       1005 :                 __atomic_store(&i_lpm->lpm.tbl24[tbl24_index], &new_tbl24_entry,
     767                 :            :                                 __ATOMIC_RELEASE);
     768                 :            : 
     769                 :            :         } else { /*
     770                 :            :                 * If it is valid, extended entry calculate the index into tbl8.
     771                 :            :                 */
     772                 :         10 :                 tbl8_group_index = i_lpm->lpm.tbl24[tbl24_index].group_idx;
     773                 :         10 :                 tbl8_group_start = tbl8_group_index *
     774                 :            :                                 RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
     775                 :         10 :                 tbl8_index = tbl8_group_start + (ip_masked & 0xFF);
     776                 :            : 
     777         [ +  + ]:        140 :                 for (i = tbl8_index; i < (tbl8_index + tbl8_range); i++) {
     778                 :            : 
     779         [ +  + ]:        130 :                         if (!i_lpm->lpm.tbl8[i].valid ||
     780         [ +  - ]:        129 :                                         i_lpm->lpm.tbl8[i].depth <= depth) {
     781                 :        130 :                                 struct rte_lpm_tbl_entry new_tbl8_entry = {
     782                 :            :                                         .valid = VALID,
     783                 :            :                                         .depth = depth,
     784                 :            :                                         .next_hop = next_hop,
     785                 :        130 :                                         .valid_group = i_lpm->lpm.tbl8[i].valid_group,
     786                 :            :                                 };
     787                 :            : 
     788                 :            :                                 /*
     789                 :            :                                  * Setting tbl8 entry in one go to avoid race
     790                 :            :                                  * condition
     791                 :            :                                  */
     792                 :        130 :                                 __atomic_store(&i_lpm->lpm.tbl8[i], &new_tbl8_entry,
     793                 :            :                                                 __ATOMIC_RELAXED);
     794                 :            : 
     795                 :            :                                 continue;
     796                 :            :                         }
     797                 :            :                 }
     798                 :            :         }
     799                 :            : #undef group_idx
     800                 :            :         return 0;
     801                 :            : }
     802                 :            : 
     803                 :            : /*
     804                 :            :  * Add a route
     805                 :            :  */
     806                 :            : RTE_EXPORT_SYMBOL(rte_lpm_add)
     807                 :            : int
     808                 :       3353 : rte_lpm_add(struct rte_lpm *lpm, uint32_t ip, uint8_t depth,
     809                 :            :                 uint32_t next_hop)
     810                 :            : {
     811                 :            :         int32_t rule_index, status = 0;
     812                 :            :         struct __rte_lpm *i_lpm;
     813                 :            :         uint32_t ip_masked;
     814                 :            : 
     815                 :            :         /* Check user arguments. */
     816   [ +  +  +  + ]:       3353 :         if ((lpm == NULL) || (depth < 1) || (depth > RTE_LPM_MAX_DEPTH))
     817                 :            :                 return -EINVAL;
     818                 :            : 
     819                 :            :         i_lpm = container_of(lpm, struct __rte_lpm, lpm);
     820                 :       3350 :         ip_masked = ip & depth_to_mask(depth);
     821                 :            : 
     822                 :            :         /* Add the rule to the rule table. */
     823                 :       3350 :         rule_index = rule_add(i_lpm, ip_masked, depth, next_hop);
     824                 :            : 
     825                 :            :         /* Skip table entries update if The rule is the same as
     826                 :            :          * the rule in the rules table.
     827                 :            :          */
     828         [ +  - ]:       3350 :         if (rule_index == -EEXIST)
     829                 :            :                 return 0;
     830                 :            : 
     831                 :            :         /* If the is no space available for new rule return error. */
     832         [ +  - ]:       3350 :         if (rule_index < 0) {
     833                 :            :                 return rule_index;
     834                 :            :         }
     835                 :            : 
     836         [ +  + ]:       3350 :         if (depth <= MAX_DEPTH_TBL24) {
     837                 :         41 :                 status = add_depth_small(i_lpm, ip_masked, depth, next_hop);
     838                 :            :         } else { /* If depth > RTE_LPM_MAX_DEPTH_TBL24 */
     839                 :       3309 :                 status = add_depth_big(i_lpm, ip_masked, depth, next_hop);
     840                 :            : 
     841                 :            :                 /*
     842                 :            :                  * If add fails due to exhaustion of tbl8 extensions delete
     843                 :            :                  * rule that was added to rule table.
     844                 :            :                  */
     845         [ +  + ]:       3309 :                 if (status < 0) {
     846                 :          3 :                         rule_delete(i_lpm, rule_index, depth);
     847                 :            : 
     848                 :          3 :                         return status;
     849                 :            :                 }
     850                 :            :         }
     851                 :            : 
     852                 :            :         return 0;
     853                 :            : }
     854                 :            : 
     855                 :            : /*
     856                 :            :  * Look for a rule in the high-level rules table
     857                 :            :  */
     858                 :            : RTE_EXPORT_SYMBOL(rte_lpm_is_rule_present)
     859                 :            : int
     860                 :          7 : rte_lpm_is_rule_present(struct rte_lpm *lpm, uint32_t ip, uint8_t depth,
     861                 :            : uint32_t *next_hop)
     862                 :            : {
     863                 :            :         struct __rte_lpm *i_lpm;
     864                 :            :         uint32_t ip_masked;
     865                 :            :         int32_t rule_index;
     866                 :            : 
     867                 :            :         /* Check user arguments. */
     868                 :          7 :         if ((lpm == NULL) ||
     869         [ +  - ]:          7 :                 (next_hop == NULL) ||
     870         [ +  - ]:          7 :                 (depth < 1) || (depth > RTE_LPM_MAX_DEPTH))
     871                 :            :                 return -EINVAL;
     872                 :            : 
     873                 :            :         /* Look for the rule using rule_find. */
     874                 :            :         i_lpm = container_of(lpm, struct __rte_lpm, lpm);
     875                 :          7 :         ip_masked = ip & depth_to_mask(depth);
     876                 :            :         rule_index = rule_find(i_lpm, ip_masked, depth);
     877                 :            : 
     878         [ +  + ]:          7 :         if (rule_index >= 0) {
     879                 :          3 :                 *next_hop = i_lpm->rules_tbl[rule_index].next_hop;
     880                 :          3 :                 return 1;
     881                 :            :         }
     882                 :            : 
     883                 :            :         /* If rule is not found return 0. */
     884                 :            :         return 0;
     885                 :            : }
     886                 :            : 
     887                 :            : static int32_t
     888                 :       2567 : find_previous_rule(struct __rte_lpm *i_lpm, uint32_t ip, uint8_t depth,
     889                 :            :                 uint8_t *sub_rule_depth)
     890                 :            : {
     891                 :            :         int32_t rule_index;
     892                 :            :         uint32_t ip_masked;
     893                 :            :         uint8_t prev_depth;
     894                 :            : 
     895         [ +  + ]:      54870 :         for (prev_depth = (uint8_t)(depth - 1); prev_depth > 0; prev_depth--) {
     896                 :      53338 :                 ip_masked = ip & depth_to_mask(prev_depth);
     897                 :            : 
     898                 :            :                 rule_index = rule_find(i_lpm, ip_masked, prev_depth);
     899                 :            : 
     900         [ +  + ]:      53338 :                 if (rule_index >= 0) {
     901                 :       1035 :                         *sub_rule_depth = prev_depth;
     902                 :       1035 :                         return rule_index;
     903                 :            :                 }
     904                 :            :         }
     905                 :            : 
     906                 :            :         return -1;
     907                 :            : }
     908                 :            : 
     909                 :            : static int32_t
     910                 :         35 : delete_depth_small(struct __rte_lpm *i_lpm, uint32_t ip_masked,
     911                 :            :         uint8_t depth, int32_t sub_rule_index, uint8_t sub_rule_depth)
     912                 :            : {
     913                 :            : #define group_idx next_hop
     914                 :            :         uint32_t tbl24_range, tbl24_index, tbl8_group_index, tbl8_index, i, j;
     915                 :            : 
     916                 :            :         /* Calculate the range and index into Table24. */
     917         [ +  - ]:         35 :         tbl24_range = depth_to_range(depth);
     918                 :         35 :         tbl24_index = (ip_masked >> 8);
     919                 :            :         struct rte_lpm_tbl_entry zero_tbl24_entry = {0};
     920                 :            : 
     921                 :            :         /*
     922                 :            :          * Firstly check the sub_rule_index. A -1 indicates no replacement rule
     923                 :            :          * and a positive number indicates a sub_rule_index.
     924                 :            :          */
     925         [ +  + ]:         35 :         if (sub_rule_index < 0) {
     926                 :            :                 /*
     927                 :            :                  * If no replacement rule exists then invalidate entries
     928                 :            :                  * associated with this rule.
     929                 :            :                  */
     930         [ +  + ]:    8389395 :                 for (i = tbl24_index; i < (tbl24_index + tbl24_range); i++) {
     931                 :            : 
     932         [ +  + ]:    8389384 :                         if (i_lpm->lpm.tbl24[i].valid_group == 0 &&
     933         [ +  - ]:    8389382 :                                         i_lpm->lpm.tbl24[i].depth <= depth) {
     934                 :    8389382 :                                 __atomic_store(&i_lpm->lpm.tbl24[i],
     935                 :            :                                         &zero_tbl24_entry, __ATOMIC_RELEASE);
     936         [ +  - ]:          2 :                         } else if (i_lpm->lpm.tbl24[i].valid_group == 1) {
     937                 :            :                                 /*
     938                 :            :                                  * If TBL24 entry is extended, then there has
     939                 :            :                                  * to be a rule with depth >= 25 in the
     940                 :            :                                  * associated TBL8 group.
     941                 :            :                                  */
     942                 :            : 
     943                 :          2 :                                 tbl8_group_index = i_lpm->lpm.tbl24[i].group_idx;
     944                 :          2 :                                 tbl8_index = tbl8_group_index *
     945                 :            :                                                 RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
     946                 :            : 
     947         [ +  + ]:        514 :                                 for (j = tbl8_index; j < (tbl8_index +
     948                 :        512 :                                         RTE_LPM_TBL8_GROUP_NUM_ENTRIES); j++) {
     949                 :            : 
     950         [ +  + ]:        512 :                                         if (i_lpm->lpm.tbl8[j].depth <= depth)
     951                 :        510 :                                                 i_lpm->lpm.tbl8[j].valid = INVALID;
     952                 :            :                                 }
     953                 :            :                         }
     954                 :            :                 }
     955                 :            :         } else {
     956                 :            :                 /*
     957                 :            :                  * If a replacement rule exists then modify entries
     958                 :            :                  * associated with this rule.
     959                 :            :                  */
     960                 :            : 
     961                 :         24 :                 struct rte_lpm_tbl_entry new_tbl24_entry = {
     962                 :         24 :                         .next_hop = i_lpm->rules_tbl[sub_rule_index].next_hop,
     963                 :            :                         .valid = VALID,
     964                 :            :                         .valid_group = 0,
     965                 :            :                         .depth = sub_rule_depth,
     966                 :            :                 };
     967                 :            : 
     968                 :            :                 struct rte_lpm_tbl_entry new_tbl8_entry = {
     969                 :            :                         .valid = VALID,
     970                 :            :                         .valid_group = VALID,
     971                 :            :                         .depth = sub_rule_depth,
     972                 :            :                         .next_hop = i_lpm->rules_tbl
     973                 :            :                         [sub_rule_index].next_hop,
     974                 :            :                 };
     975                 :            : 
     976         [ +  + ]:    8388632 :                 for (i = tbl24_index; i < (tbl24_index + tbl24_range); i++) {
     977                 :            : 
     978         [ +  - ]:    8388608 :                         if (i_lpm->lpm.tbl24[i].valid_group == 0 &&
     979         [ +  - ]:    8388608 :                                         i_lpm->lpm.tbl24[i].depth <= depth) {
     980                 :    8388608 :                                 __atomic_store(&i_lpm->lpm.tbl24[i], &new_tbl24_entry,
     981                 :            :                                                 __ATOMIC_RELEASE);
     982         [ #  # ]:          0 :                         } else  if (i_lpm->lpm.tbl24[i].valid_group == 1) {
     983                 :            :                                 /*
     984                 :            :                                  * If TBL24 entry is extended, then there has
     985                 :            :                                  * to be a rule with depth >= 25 in the
     986                 :            :                                  * associated TBL8 group.
     987                 :            :                                  */
     988                 :            : 
     989                 :          0 :                                 tbl8_group_index = i_lpm->lpm.tbl24[i].group_idx;
     990                 :          0 :                                 tbl8_index = tbl8_group_index *
     991                 :            :                                                 RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
     992                 :            : 
     993         [ #  # ]:          0 :                                 for (j = tbl8_index; j < (tbl8_index +
     994                 :          0 :                                         RTE_LPM_TBL8_GROUP_NUM_ENTRIES); j++) {
     995                 :            : 
     996         [ #  # ]:          0 :                                         if (i_lpm->lpm.tbl8[j].depth <= depth)
     997                 :          0 :                                                 __atomic_store(&i_lpm->lpm.tbl8[j],
     998                 :            :                                                         &new_tbl8_entry,
     999                 :            :                                                         __ATOMIC_RELAXED);
    1000                 :            :                                 }
    1001                 :            :                         }
    1002                 :            :                 }
    1003                 :            :         }
    1004                 :            : #undef group_idx
    1005                 :         35 :         return 0;
    1006                 :            : }
    1007                 :            : 
    1008                 :            : /*
    1009                 :            :  * Checks if table 8 group can be recycled.
    1010                 :            :  *
    1011                 :            :  * Return of -EEXIST means tbl8 is in use and thus can not be recycled.
    1012                 :            :  * Return of -EINVAL means tbl8 is empty and thus can be recycled
    1013                 :            :  * Return of value > -1 means tbl8 is in use but has all the same values and
    1014                 :            :  * thus can be recycled
    1015                 :            :  */
    1016                 :            : static int32_t
    1017                 :       2532 : tbl8_recycle_check(struct rte_lpm_tbl_entry *tbl8,
    1018                 :            :                 uint32_t tbl8_group_start)
    1019                 :            : {
    1020                 :            :         uint32_t tbl8_group_end, i;
    1021                 :       2532 :         tbl8_group_end = tbl8_group_start + RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
    1022                 :            : 
    1023                 :            :         /*
    1024                 :            :          * Check the first entry of the given tbl8. If it is invalid we know
    1025                 :            :          * this tbl8 does not contain any rule with a depth < RTE_LPM_MAX_DEPTH
    1026                 :            :          *  (As they would affect all entries in a tbl8) and thus this table
    1027                 :            :          *  can not be recycled.
    1028                 :            :          */
    1029         [ +  + ]:       2532 :         if (tbl8[tbl8_group_start].valid) {
    1030                 :            :                 /*
    1031                 :            :                  * If first entry is valid check if the depth is less than 24
    1032                 :            :                  * and if so check the rest of the entries to verify that they
    1033                 :            :                  * are all of this depth.
    1034                 :            :                  */
    1035         [ +  + ]:       1012 :                 if (tbl8[tbl8_group_start].depth <= MAX_DEPTH_TBL24) {
    1036         [ +  + ]:     256768 :                         for (i = (tbl8_group_start + 1); i < tbl8_group_end;
    1037                 :     255765 :                                         i++) {
    1038                 :            : 
    1039         [ +  - ]:     255765 :                                 if (tbl8[i].depth !=
    1040                 :            :                                                 tbl8[tbl8_group_start].depth) {
    1041                 :            : 
    1042                 :            :                                         return -EEXIST;
    1043                 :            :                                 }
    1044                 :            :                         }
    1045                 :            :                         /* If all entries are the same return the tb8 index */
    1046                 :       1003 :                         return tbl8_group_start;
    1047                 :            :                 }
    1048                 :            : 
    1049                 :            :                 return -EEXIST;
    1050                 :            :         }
    1051                 :            :         /*
    1052                 :            :          * If the first entry is invalid check if the rest of the entries in
    1053                 :            :          * the tbl8 are invalid.
    1054                 :            :          */
    1055         [ +  + ]:     389120 :         for (i = (tbl8_group_start + 1); i < tbl8_group_end; i++) {
    1056         [ +  - ]:     387600 :                 if (tbl8[i].valid)
    1057                 :            :                         return -EEXIST;
    1058                 :            :         }
    1059                 :            :         /* If no valid entries are found then return -EINVAL. */
    1060                 :            :         return -EINVAL;
    1061                 :            : }
    1062                 :            : 
    1063                 :            : static int32_t
    1064                 :       2532 : delete_depth_big(struct __rte_lpm *i_lpm, uint32_t ip_masked,
    1065                 :            :         uint8_t depth, int32_t sub_rule_index, uint8_t sub_rule_depth)
    1066                 :            : {
    1067                 :            : #define group_idx next_hop
    1068                 :            :         uint32_t tbl24_index, tbl8_group_index, tbl8_group_start, tbl8_index,
    1069                 :            :                         tbl8_range, i;
    1070                 :            :         int32_t tbl8_recycle_index, status = 0;
    1071                 :            : 
    1072                 :            :         /*
    1073                 :            :          * Calculate the index into tbl24 and range. Note: All depths larger
    1074                 :            :          * than MAX_DEPTH_TBL24 are associated with only one tbl24 entry.
    1075                 :            :          */
    1076                 :       2532 :         tbl24_index = ip_masked >> 8;
    1077                 :            : 
    1078                 :            :         /* Calculate the index into tbl8 and range. */
    1079                 :       2532 :         tbl8_group_index = i_lpm->lpm.tbl24[tbl24_index].group_idx;
    1080                 :       2532 :         tbl8_group_start = tbl8_group_index * RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
    1081                 :       2532 :         tbl8_index = tbl8_group_start + (ip_masked & 0xFF);
    1082         [ -  + ]:       2532 :         tbl8_range = depth_to_range(depth);
    1083                 :            : 
    1084         [ +  + ]:       2532 :         if (sub_rule_index < 0) {
    1085                 :            :                 /*
    1086                 :            :                  * Loop through the range of entries on tbl8 for which the
    1087                 :            :                  * rule_to_delete must be removed or modified.
    1088                 :            :                  */
    1089         [ +  + ]:      10991 :                 for (i = tbl8_index; i < (tbl8_index + tbl8_range); i++) {
    1090         [ +  - ]:       9470 :                         if (i_lpm->lpm.tbl8[i].depth <= depth)
    1091                 :       9470 :                                 i_lpm->lpm.tbl8[i].valid = INVALID;
    1092                 :            :                 }
    1093                 :            :         } else {
    1094                 :            :                 /* Set new tbl8 entry. */
    1095                 :       1011 :                 struct rte_lpm_tbl_entry new_tbl8_entry = {
    1096                 :            :                         .valid = VALID,
    1097                 :            :                         .depth = sub_rule_depth,
    1098                 :       1011 :                         .valid_group = i_lpm->lpm.tbl8[tbl8_group_start].valid_group,
    1099                 :       1011 :                         .next_hop = i_lpm->rules_tbl[sub_rule_index].next_hop,
    1100                 :            :                 };
    1101                 :            : 
    1102                 :            :                 /*
    1103                 :            :                  * Loop through the range of entries on tbl8 for which the
    1104                 :            :                  * rule_to_delete must be modified.
    1105                 :            :                  */
    1106         [ +  + ]:       2299 :                 for (i = tbl8_index; i < (tbl8_index + tbl8_range); i++) {
    1107         [ +  - ]:       1288 :                         if (i_lpm->lpm.tbl8[i].depth <= depth)
    1108                 :       1288 :                                 __atomic_store(&i_lpm->lpm.tbl8[i], &new_tbl8_entry,
    1109                 :            :                                                 __ATOMIC_RELAXED);
    1110                 :            :                 }
    1111                 :            :         }
    1112                 :            : 
    1113                 :            :         /*
    1114                 :            :          * Check if there are any valid entries in this tbl8 group. If all
    1115                 :            :          * tbl8 entries are invalid we can free the tbl8 and invalidate the
    1116                 :            :          * associated tbl24 entry.
    1117                 :            :          */
    1118                 :            : 
    1119                 :       2532 :         tbl8_recycle_index = tbl8_recycle_check(i_lpm->lpm.tbl8, tbl8_group_start);
    1120                 :            : 
    1121         [ +  + ]:       2532 :         if (tbl8_recycle_index == -EINVAL) {
    1122                 :            :                 /* Set tbl24 before freeing tbl8 to avoid race condition.
    1123                 :            :                  * Prevent the free of the tbl8 group from hoisting.
    1124                 :            :                  */
    1125                 :       1520 :                 i_lpm->lpm.tbl24[tbl24_index].valid = 0;
    1126                 :            :                 rte_atomic_thread_fence(rte_memory_order_release);
    1127                 :       1520 :                 status = tbl8_free(i_lpm, tbl8_group_start);
    1128         [ +  + ]:       1012 :         } else if (tbl8_recycle_index > -1) {
    1129                 :            :                 /* Update tbl24 entry. */
    1130                 :            :                 struct rte_lpm_tbl_entry new_tbl24_entry = {
    1131                 :       1003 :                         .next_hop = i_lpm->lpm.tbl8[tbl8_recycle_index].next_hop,
    1132                 :            :                         .valid = VALID,
    1133                 :            :                         .valid_group = 0,
    1134                 :       1003 :                         .depth = i_lpm->lpm.tbl8[tbl8_recycle_index].depth,
    1135                 :            :                 };
    1136                 :            : 
    1137                 :            :                 /* Set tbl24 before freeing tbl8 to avoid race condition.
    1138                 :            :                  * Prevent the free of the tbl8 group from hoisting.
    1139                 :            :                  */
    1140                 :       1003 :                 __atomic_store(&i_lpm->lpm.tbl24[tbl24_index], &new_tbl24_entry,
    1141                 :            :                                 __ATOMIC_RELAXED);
    1142                 :            :                 rte_atomic_thread_fence(rte_memory_order_release);
    1143                 :       1003 :                 status = tbl8_free(i_lpm, tbl8_group_start);
    1144                 :            :         }
    1145                 :            : #undef group_idx
    1146                 :       2532 :         return status;
    1147                 :            : }
    1148                 :            : 
    1149                 :            : /*
    1150                 :            :  * Deletes a rule
    1151                 :            :  */
    1152                 :            : RTE_EXPORT_SYMBOL(rte_lpm_delete)
    1153                 :            : int
    1154                 :       2572 : rte_lpm_delete(struct rte_lpm *lpm, uint32_t ip, uint8_t depth)
    1155                 :            : {
    1156                 :            :         int32_t rule_to_delete_index, sub_rule_index;
    1157                 :            :         struct __rte_lpm *i_lpm;
    1158                 :            :         uint32_t ip_masked;
    1159                 :            :         uint8_t sub_rule_depth;
    1160                 :            :         /*
    1161                 :            :          * Check input arguments. Note: IP must be a positive integer of 32
    1162                 :            :          * bits in length therefore it need not be checked.
    1163                 :            :          */
    1164   [ +  +  +  + ]:       2572 :         if ((lpm == NULL) || (depth < 1) || (depth > RTE_LPM_MAX_DEPTH)) {
    1165                 :            :                 return -EINVAL;
    1166                 :            :         }
    1167                 :            : 
    1168                 :            :         i_lpm = container_of(lpm, struct __rte_lpm, lpm);
    1169                 :       2569 :         ip_masked = ip & depth_to_mask(depth);
    1170                 :            : 
    1171                 :            :         /*
    1172                 :            :          * Find the index of the input rule, that needs to be deleted, in the
    1173                 :            :          * rule table.
    1174                 :            :          */
    1175                 :            :         rule_to_delete_index = rule_find(i_lpm, ip_masked, depth);
    1176                 :            : 
    1177                 :            :         /*
    1178                 :            :          * Check if rule_to_delete_index was found. If no rule was found the
    1179                 :            :          * function rule_find returns -EINVAL.
    1180                 :            :          */
    1181         [ +  + ]:       2569 :         if (rule_to_delete_index < 0)
    1182                 :            :                 return -EINVAL;
    1183                 :            : 
    1184                 :            :         /* Delete the rule from the rule table. */
    1185                 :       2567 :         rule_delete(i_lpm, rule_to_delete_index, depth);
    1186                 :            : 
    1187                 :            :         /*
    1188                 :            :          * Find rule to replace the rule_to_delete. If there is no rule to
    1189                 :            :          * replace the rule_to_delete we return -1 and invalidate the table
    1190                 :            :          * entries associated with this rule.
    1191                 :            :          */
    1192                 :       2567 :         sub_rule_depth = 0;
    1193                 :       2567 :         sub_rule_index = find_previous_rule(i_lpm, ip, depth, &sub_rule_depth);
    1194                 :            : 
    1195                 :            :         /*
    1196                 :            :          * If the input depth value is less than 25 use function
    1197                 :            :          * delete_depth_small otherwise use delete_depth_big.
    1198                 :            :          */
    1199         [ +  + ]:       2567 :         if (depth <= MAX_DEPTH_TBL24) {
    1200                 :         35 :                 return delete_depth_small(i_lpm, ip_masked, depth,
    1201                 :            :                                 sub_rule_index, sub_rule_depth);
    1202                 :            :         } else { /* If depth > MAX_DEPTH_TBL24 */
    1203                 :       2532 :                 return delete_depth_big(i_lpm, ip_masked, depth, sub_rule_index,
    1204                 :            :                                 sub_rule_depth);
    1205                 :            :         }
    1206                 :            : }
    1207                 :            : 
    1208                 :            : /*
    1209                 :            :  * Delete all rules from the LPM table.
    1210                 :            :  */
    1211                 :            : RTE_EXPORT_SYMBOL(rte_lpm_delete_all)
    1212                 :            : void
    1213                 :         10 : rte_lpm_delete_all(struct rte_lpm *lpm)
    1214                 :            : {
    1215                 :            :         struct __rte_lpm *i_lpm;
    1216                 :            : 
    1217                 :            :         i_lpm = container_of(lpm, struct __rte_lpm, lpm);
    1218                 :            :         /* Zero rule information. */
    1219                 :         10 :         memset(i_lpm->rule_info, 0, sizeof(i_lpm->rule_info));
    1220                 :            : 
    1221                 :            :         /* Zero tbl24. */
    1222                 :         10 :         memset(i_lpm->lpm.tbl24, 0, sizeof(i_lpm->lpm.tbl24));
    1223                 :            : 
    1224                 :            :         /* Zero tbl8. */
    1225                 :         10 :         memset(i_lpm->lpm.tbl8, 0, sizeof(i_lpm->lpm.tbl8[0])
    1226                 :         10 :                         * RTE_LPM_TBL8_GROUP_NUM_ENTRIES * i_lpm->number_tbl8s);
    1227                 :            : 
    1228                 :            :         /* Delete all rules form the rules table. */
    1229                 :         10 :         memset(i_lpm->rules_tbl, 0, sizeof(i_lpm->rules_tbl[0]) * i_lpm->max_rules);
    1230                 :         10 : }

Generated by: LCOV version 1.14