LCOV - code coverage report
Current view: top level - lib/lpm - rte_lpm.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 331 363 91.2 %
Date: 2025-08-01 17:49:26 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         [ -  + ]:        253 : 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         [ -  + ]:        253 : 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         [ +  + ]:         70 :         TAILQ_FOREACH(te, lpm_list, next) {
     133                 :         44 :                 i_lpm = te->data;
     134         [ +  + ]:         44 :                 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                 :        148 : 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                 :        148 :         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   [ +  +  +  + ]:        148 :         if ((name == NULL) || (socket_id < -1) || (config->max_rules == 0)
     168         [ -  + ]:        143 :                         || 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                 :        143 :         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 :         rte_atomic_store_explicit(&tbl8[tbl8_group_index].val,
     302                 :            :                         zero_tbl8_entry.val, rte_memory_order_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 :                         rte_atomic_store_explicit(&tbl8_entry->val, new_tbl8_entry.val,
     524                 :            :                                         rte_memory_order_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 :                 struct rte_lpm_tbl_entry *tbl8_entry =
     560                 :       2010 :                                 &i_lpm->lpm.tbl8[tbl8_group_start];
     561                 :       2010 :                 rte_atomic_store_explicit(&tbl8_entry->val, zero_tbl8_entry.val,
     562                 :            :                                 rte_memory_order_relaxed);
     563         [ +  + ]:        513 :         } else if (i_lpm->rcu_mode == RTE_LPM_QSBR_MODE_SYNC) {
     564                 :            :                 /* Wait for quiescent state change. */
     565                 :        512 :                 rte_rcu_qsbr_synchronize(i_lpm->v,
     566                 :            :                         RTE_QSBR_THRID_INVALID);
     567                 :            :                 /* Set tbl8 group invalid*/
     568                 :        512 :                 struct rte_lpm_tbl_entry *tbl8_entry =
     569                 :        512 :                                 &i_lpm->lpm.tbl8[tbl8_group_start];
     570                 :        512 :                 rte_atomic_store_explicit(&tbl8_entry->val, zero_tbl8_entry.val,
     571                 :            :                                 rte_memory_order_relaxed);
     572         [ +  - ]:          1 :         } else if (i_lpm->rcu_mode == RTE_LPM_QSBR_MODE_DQ) {
     573                 :            :                 /* Push into QSBR defer queue. */
     574                 :          1 :                 status = rte_rcu_qsbr_dq_enqueue(i_lpm->dq,
     575                 :            :                                 (void *)&tbl8_group_start);
     576         [ -  + ]:          1 :                 if (status == 1) {
     577                 :          0 :                         LPM_LOG(ERR, "Failed to push QSBR FIFO");
     578                 :          0 :                         return -rte_errno;
     579                 :            :                 }
     580                 :            :         }
     581                 :            : 
     582                 :            :         return 0;
     583                 :            : }
     584                 :            : 
     585                 :            : static __rte_noinline int32_t
     586                 :         41 : add_depth_small(struct __rte_lpm *i_lpm, uint32_t ip, uint8_t depth,
     587                 :            :                 uint32_t next_hop)
     588                 :            : {
     589                 :            : #define group_idx next_hop
     590                 :            :         uint32_t tbl24_index, tbl24_range, tbl8_index, tbl8_group_end, i, j;
     591                 :            : 
     592                 :            :         /* Calculate the index into Table24. */
     593                 :         41 :         tbl24_index = ip >> 8;
     594         [ +  - ]:         41 :         tbl24_range = depth_to_range(depth);
     595                 :            : 
     596         [ +  + ]:   16778550 :         for (i = tbl24_index; i < (tbl24_index + tbl24_range); i++) {
     597                 :            :                 /*
     598                 :            :                  * For invalid OR valid and non-extended tbl 24 entries set
     599                 :            :                  * entry.
     600                 :            :                  */
     601   [ +  +  +  + ]:   16778509 :                 if (!i_lpm->lpm.tbl24[i].valid || (i_lpm->lpm.tbl24[i].valid_group == 0 &&
     602         [ +  - ]:    8388865 :                                 i_lpm->lpm.tbl24[i].depth <= depth)) {
     603                 :            : 
     604                 :   16778506 :                         struct rte_lpm_tbl_entry new_tbl24_entry = {
     605                 :            :                                 .next_hop = next_hop,
     606                 :            :                                 .valid = VALID,
     607                 :            :                                 .valid_group = 0,
     608                 :            :                                 .depth = depth,
     609                 :            :                         };
     610                 :            : 
     611                 :            :                         /* Setting tbl24 entry in one go to avoid race
     612                 :            :                          * conditions
     613                 :            :                          */
     614                 :            :                         struct rte_lpm_tbl_entry *tbl24_entry =
     615                 :            :                                         &i_lpm->lpm.tbl24[i];
     616                 :   16778506 :                         rte_atomic_store_explicit(&tbl24_entry->val, new_tbl24_entry.val,
     617                 :            :                                         rte_memory_order_release);
     618                 :            : 
     619                 :            :                         continue;
     620                 :            :                 }
     621                 :            : 
     622         [ +  - ]:          3 :                 if (i_lpm->lpm.tbl24[i].valid_group == 1) {
     623                 :            :                         /* If tbl24 entry is valid and extended calculate the
     624                 :            :                          *  index into tbl8.
     625                 :            :                          */
     626                 :          3 :                         tbl8_index = i_lpm->lpm.tbl24[i].group_idx *
     627                 :            :                                         RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
     628                 :          3 :                         tbl8_group_end = tbl8_index +
     629                 :            :                                         RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
     630                 :            : 
     631         [ +  + ]:        771 :                         for (j = tbl8_index; j < tbl8_group_end; j++) {
     632         [ +  + ]:        768 :                                 if (!i_lpm->lpm.tbl8[j].valid ||
     633         [ +  + ]:        273 :                                                 i_lpm->lpm.tbl8[j].depth <= depth) {
     634                 :            :                                         struct rte_lpm_tbl_entry
     635                 :        735 :                                                 new_tbl8_entry = {
     636                 :            :                                                 .valid = VALID,
     637                 :            :                                                 .valid_group = VALID,
     638                 :            :                                                 .depth = depth,
     639                 :            :                                                 .next_hop = next_hop,
     640                 :            :                                         };
     641                 :            : 
     642                 :            :                                         /*
     643                 :            :                                          * Setting tbl8 entry in one go to avoid
     644                 :            :                                          * race conditions
     645                 :            :                                          */
     646                 :            :                                         struct rte_lpm_tbl_entry *tbl8_entry =
     647                 :            :                                                         &i_lpm->lpm.tbl8[j];
     648                 :        735 :                                         rte_atomic_store_explicit(&tbl8_entry->val,
     649                 :            :                                                         new_tbl8_entry.val,
     650                 :            :                                                         rte_memory_order_relaxed);
     651                 :            : 
     652                 :            :                                         continue;
     653                 :            :                                 }
     654                 :            :                         }
     655                 :            :                 }
     656                 :            :         }
     657                 :            : #undef group_idx
     658                 :         41 :         return 0;
     659                 :            : }
     660                 :            : 
     661                 :            : static __rte_noinline int32_t
     662                 :       3309 : add_depth_big(struct __rte_lpm *i_lpm, uint32_t ip_masked, uint8_t depth,
     663                 :            :                 uint32_t next_hop)
     664                 :            : {
     665                 :            : #define group_idx next_hop
     666                 :            :         uint32_t tbl24_index;
     667                 :            :         int32_t tbl8_group_index, tbl8_group_start, tbl8_group_end, tbl8_index,
     668                 :            :                 tbl8_range, i;
     669                 :            : 
     670                 :       3309 :         tbl24_index = (ip_masked >> 8);
     671         [ -  + ]:       3309 :         tbl8_range = depth_to_range(depth);
     672                 :            : 
     673         [ +  + ]:       3309 :         if (!i_lpm->lpm.tbl24[tbl24_index].valid) {
     674                 :            :                 /* Search for a free tbl8 group. */
     675                 :       2294 :                 tbl8_group_index = tbl8_alloc(i_lpm);
     676                 :            : 
     677                 :            :                 /* Check tbl8 allocation was successful. */
     678         [ +  + ]:       2294 :                 if (tbl8_group_index < 0) {
     679                 :          3 :                         return tbl8_group_index;
     680                 :            :                 }
     681                 :            : 
     682                 :            :                 /* Find index into tbl8 and range. */
     683                 :       2291 :                 tbl8_index = (tbl8_group_index *
     684                 :       2291 :                                 RTE_LPM_TBL8_GROUP_NUM_ENTRIES) +
     685                 :       2291 :                                 (ip_masked & 0xFF);
     686                 :            : 
     687                 :            :                 /* Set tbl8 entry. */
     688         [ +  + ]:      13471 :                 for (i = tbl8_index; i < (tbl8_index + tbl8_range); i++) {
     689                 :      11180 :                         struct rte_lpm_tbl_entry new_tbl8_entry = {
     690                 :            :                                 .valid = VALID,
     691                 :            :                                 .depth = depth,
     692                 :      11180 :                                 .valid_group = i_lpm->lpm.tbl8[i].valid_group,
     693                 :            :                                 .next_hop = next_hop,
     694                 :            :                         };
     695                 :            :                         struct rte_lpm_tbl_entry *tbl8_entry =
     696                 :            :                                         &i_lpm->lpm.tbl8[i];
     697                 :      11180 :                         rte_atomic_store_explicit(&tbl8_entry->val, new_tbl8_entry.val,
     698                 :            :                                         rte_memory_order_relaxed);
     699                 :            :                 }
     700                 :            : 
     701                 :            :                 /*
     702                 :            :                  * Update tbl24 entry to point to new tbl8 entry. Note: The
     703                 :            :                  * ext_flag and tbl8_index need to be updated simultaneously,
     704                 :            :                  * so assign whole structure in one go
     705                 :            :                  */
     706                 :            : 
     707                 :       2291 :                 struct rte_lpm_tbl_entry new_tbl24_entry = {
     708                 :            :                         .group_idx = tbl8_group_index,
     709                 :            :                         .valid = VALID,
     710                 :            :                         .valid_group = 1,
     711                 :            :                         .depth = 0,
     712                 :            :                 };
     713                 :            : 
     714                 :            :                 /* The tbl24 entry must be written only after the
     715                 :            :                  * tbl8 entries are written.
     716                 :            :                  */
     717                 :            :                 struct rte_lpm_tbl_entry *tbl24_entry =
     718                 :            :                                 &i_lpm->lpm.tbl24[tbl24_index];
     719                 :       2291 :                 rte_atomic_store_explicit(&tbl24_entry->val, new_tbl24_entry.val,
     720                 :            :                                 rte_memory_order_release);
     721                 :            : 
     722                 :            :         } /* If valid entry but not extended calculate the index into Table8. */
     723         [ +  + ]:       1015 :         else if (i_lpm->lpm.tbl24[tbl24_index].valid_group == 0) {
     724                 :            :                 /* Search for free tbl8 group. */
     725                 :       1005 :                 tbl8_group_index = tbl8_alloc(i_lpm);
     726                 :            : 
     727         [ -  + ]:       1005 :                 if (tbl8_group_index < 0) {
     728                 :          0 :                         return tbl8_group_index;
     729                 :            :                 }
     730                 :            : 
     731                 :       1005 :                 tbl8_group_start = tbl8_group_index *
     732                 :            :                                 RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
     733                 :       1005 :                 tbl8_group_end = tbl8_group_start +
     734                 :            :                                 RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
     735                 :            : 
     736                 :            :                 /* Populate new tbl8 with tbl24 value. */
     737         [ +  + ]:     258285 :                 for (i = tbl8_group_start; i < tbl8_group_end; i++) {
     738                 :     257280 :                         struct rte_lpm_tbl_entry new_tbl8_entry = {
     739                 :            :                                 .valid = VALID,
     740                 :     257280 :                                 .depth = i_lpm->lpm.tbl24[tbl24_index].depth,
     741                 :     257280 :                                 .valid_group = i_lpm->lpm.tbl8[i].valid_group,
     742                 :     257280 :                                 .next_hop = i_lpm->lpm.tbl24[tbl24_index].next_hop,
     743                 :            :                         };
     744                 :            :                         struct rte_lpm_tbl_entry *tbl8_entry =
     745                 :            :                                         &i_lpm->lpm.tbl8[i];
     746                 :     257280 :                         rte_atomic_store_explicit(&tbl8_entry->val, new_tbl8_entry.val,
     747                 :            :                                         rte_memory_order_relaxed);
     748                 :            :                 }
     749                 :            : 
     750                 :       1005 :                 tbl8_index = tbl8_group_start + (ip_masked & 0xFF);
     751                 :            : 
     752                 :            :                 /* Insert new rule into the tbl8 entry. */
     753         [ +  + ]:       2167 :                 for (i = tbl8_index; i < tbl8_index + tbl8_range; i++) {
     754                 :       1162 :                         struct rte_lpm_tbl_entry new_tbl8_entry = {
     755                 :            :                                 .valid = VALID,
     756                 :            :                                 .depth = depth,
     757                 :       1162 :                                 .valid_group = i_lpm->lpm.tbl8[i].valid_group,
     758                 :            :                                 .next_hop = next_hop,
     759                 :            :                         };
     760                 :            :                         struct rte_lpm_tbl_entry *tbl8_entry =
     761                 :            :                                         &i_lpm->lpm.tbl8[i];
     762                 :       1162 :                         rte_atomic_store_explicit(&tbl8_entry->val, new_tbl8_entry.val,
     763                 :            :                                         rte_memory_order_relaxed);
     764                 :            :                 }
     765                 :            : 
     766                 :            :                 /*
     767                 :            :                  * Update tbl24 entry to point to new tbl8 entry. Note: The
     768                 :            :                  * ext_flag and tbl8_index need to be updated simultaneously,
     769                 :            :                  * so assign whole structure in one go.
     770                 :            :                  */
     771                 :            : 
     772                 :       1005 :                 struct rte_lpm_tbl_entry new_tbl24_entry = {
     773                 :            :                                 .group_idx = tbl8_group_index,
     774                 :            :                                 .valid = VALID,
     775                 :            :                                 .valid_group = 1,
     776                 :            :                                 .depth = 0,
     777                 :            :                 };
     778                 :            : 
     779                 :            :                 /* The tbl24 entry must be written only after the
     780                 :            :                  * tbl8 entries are written.
     781                 :            :                  */
     782                 :            :                 struct rte_lpm_tbl_entry *tbl24_entry =
     783                 :            :                                 &i_lpm->lpm.tbl24[tbl24_index];
     784                 :       1005 :                 rte_atomic_store_explicit(&tbl24_entry->val, new_tbl24_entry.val,
     785                 :            :                                 rte_memory_order_release);
     786                 :            : 
     787                 :            :         } else { /*
     788                 :            :                 * If it is valid, extended entry calculate the index into tbl8.
     789                 :            :                 */
     790                 :         10 :                 tbl8_group_index = i_lpm->lpm.tbl24[tbl24_index].group_idx;
     791                 :         10 :                 tbl8_group_start = tbl8_group_index *
     792                 :            :                                 RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
     793                 :         10 :                 tbl8_index = tbl8_group_start + (ip_masked & 0xFF);
     794                 :            : 
     795         [ +  + ]:        140 :                 for (i = tbl8_index; i < (tbl8_index + tbl8_range); i++) {
     796                 :            : 
     797         [ +  + ]:        130 :                         if (!i_lpm->lpm.tbl8[i].valid ||
     798         [ +  - ]:        129 :                                         i_lpm->lpm.tbl8[i].depth <= depth) {
     799                 :        130 :                                 struct rte_lpm_tbl_entry new_tbl8_entry = {
     800                 :            :                                         .valid = VALID,
     801                 :            :                                         .depth = depth,
     802                 :            :                                         .next_hop = next_hop,
     803                 :        130 :                                         .valid_group = i_lpm->lpm.tbl8[i].valid_group,
     804                 :            :                                 };
     805                 :            : 
     806                 :            :                                 /*
     807                 :            :                                  * Setting tbl8 entry in one go to avoid race
     808                 :            :                                  * condition
     809                 :            :                                  */
     810                 :            :                                 struct rte_lpm_tbl_entry *tbl8_entry =
     811                 :            :                                                 &i_lpm->lpm.tbl8[i];
     812                 :        130 :                                 rte_atomic_store_explicit(&tbl8_entry->val,
     813                 :            :                                                 new_tbl8_entry.val, rte_memory_order_relaxed);
     814                 :            : 
     815                 :            :                                 continue;
     816                 :            :                         }
     817                 :            :                 }
     818                 :            :         }
     819                 :            : #undef group_idx
     820                 :            :         return 0;
     821                 :            : }
     822                 :            : 
     823                 :            : /*
     824                 :            :  * Add a route
     825                 :            :  */
     826                 :            : RTE_EXPORT_SYMBOL(rte_lpm_add)
     827                 :            : int
     828                 :       3353 : rte_lpm_add(struct rte_lpm *lpm, uint32_t ip, uint8_t depth,
     829                 :            :                 uint32_t next_hop)
     830                 :            : {
     831                 :            :         int32_t rule_index, status = 0;
     832                 :            :         struct __rte_lpm *i_lpm;
     833                 :            :         uint32_t ip_masked;
     834                 :            : 
     835                 :            :         /* Check user arguments. */
     836   [ +  +  +  + ]:       3353 :         if ((lpm == NULL) || (depth < 1) || (depth > RTE_LPM_MAX_DEPTH))
     837                 :            :                 return -EINVAL;
     838                 :            : 
     839                 :            :         i_lpm = container_of(lpm, struct __rte_lpm, lpm);
     840                 :       3350 :         ip_masked = ip & depth_to_mask(depth);
     841                 :            : 
     842                 :            :         /* Add the rule to the rule table. */
     843                 :       3350 :         rule_index = rule_add(i_lpm, ip_masked, depth, next_hop);
     844                 :            : 
     845                 :            :         /* Skip table entries update if The rule is the same as
     846                 :            :          * the rule in the rules table.
     847                 :            :          */
     848         [ +  - ]:       3350 :         if (rule_index == -EEXIST)
     849                 :            :                 return 0;
     850                 :            : 
     851                 :            :         /* If the is no space available for new rule return error. */
     852         [ +  - ]:       3350 :         if (rule_index < 0) {
     853                 :            :                 return rule_index;
     854                 :            :         }
     855                 :            : 
     856         [ +  + ]:       3350 :         if (depth <= MAX_DEPTH_TBL24) {
     857                 :         41 :                 status = add_depth_small(i_lpm, ip_masked, depth, next_hop);
     858                 :            :         } else { /* If depth > RTE_LPM_MAX_DEPTH_TBL24 */
     859                 :       3309 :                 status = add_depth_big(i_lpm, ip_masked, depth, next_hop);
     860                 :            : 
     861                 :            :                 /*
     862                 :            :                  * If add fails due to exhaustion of tbl8 extensions delete
     863                 :            :                  * rule that was added to rule table.
     864                 :            :                  */
     865         [ +  + ]:       3309 :                 if (status < 0) {
     866                 :          3 :                         rule_delete(i_lpm, rule_index, depth);
     867                 :            : 
     868                 :          3 :                         return status;
     869                 :            :                 }
     870                 :            :         }
     871                 :            : 
     872                 :            :         return 0;
     873                 :            : }
     874                 :            : 
     875                 :            : /*
     876                 :            :  * Look for a rule in the high-level rules table
     877                 :            :  */
     878                 :            : RTE_EXPORT_SYMBOL(rte_lpm_is_rule_present)
     879                 :            : int
     880                 :          7 : rte_lpm_is_rule_present(struct rte_lpm *lpm, uint32_t ip, uint8_t depth,
     881                 :            : uint32_t *next_hop)
     882                 :            : {
     883                 :            :         struct __rte_lpm *i_lpm;
     884                 :            :         uint32_t ip_masked;
     885                 :            :         int32_t rule_index;
     886                 :            : 
     887                 :            :         /* Check user arguments. */
     888                 :          7 :         if ((lpm == NULL) ||
     889         [ +  - ]:          7 :                 (next_hop == NULL) ||
     890         [ +  - ]:          7 :                 (depth < 1) || (depth > RTE_LPM_MAX_DEPTH))
     891                 :            :                 return -EINVAL;
     892                 :            : 
     893                 :            :         /* Look for the rule using rule_find. */
     894                 :            :         i_lpm = container_of(lpm, struct __rte_lpm, lpm);
     895                 :          7 :         ip_masked = ip & depth_to_mask(depth);
     896                 :            :         rule_index = rule_find(i_lpm, ip_masked, depth);
     897                 :            : 
     898         [ +  + ]:          7 :         if (rule_index >= 0) {
     899                 :          3 :                 *next_hop = i_lpm->rules_tbl[rule_index].next_hop;
     900                 :          3 :                 return 1;
     901                 :            :         }
     902                 :            : 
     903                 :            :         /* If rule is not found return 0. */
     904                 :            :         return 0;
     905                 :            : }
     906                 :            : 
     907                 :            : static int32_t
     908                 :       2567 : find_previous_rule(struct __rte_lpm *i_lpm, uint32_t ip, uint8_t depth,
     909                 :            :                 uint8_t *sub_rule_depth)
     910                 :            : {
     911                 :            :         int32_t rule_index;
     912                 :            :         uint32_t ip_masked;
     913                 :            :         uint8_t prev_depth;
     914                 :            : 
     915         [ +  + ]:      54870 :         for (prev_depth = (uint8_t)(depth - 1); prev_depth > 0; prev_depth--) {
     916                 :      53338 :                 ip_masked = ip & depth_to_mask(prev_depth);
     917                 :            : 
     918                 :            :                 rule_index = rule_find(i_lpm, ip_masked, prev_depth);
     919                 :            : 
     920         [ +  + ]:      53338 :                 if (rule_index >= 0) {
     921                 :       1035 :                         *sub_rule_depth = prev_depth;
     922                 :       1035 :                         return rule_index;
     923                 :            :                 }
     924                 :            :         }
     925                 :            : 
     926                 :            :         return -1;
     927                 :            : }
     928                 :            : 
     929                 :            : static int32_t
     930                 :         35 : delete_depth_small(struct __rte_lpm *i_lpm, uint32_t ip_masked,
     931                 :            :         uint8_t depth, int32_t sub_rule_index, uint8_t sub_rule_depth)
     932                 :            : {
     933                 :            : #define group_idx next_hop
     934                 :            :         uint32_t tbl24_range, tbl24_index, tbl8_group_index, tbl8_index, i, j;
     935                 :            : 
     936                 :            :         /* Calculate the range and index into Table24. */
     937         [ +  - ]:         35 :         tbl24_range = depth_to_range(depth);
     938                 :         35 :         tbl24_index = (ip_masked >> 8);
     939                 :            :         struct rte_lpm_tbl_entry zero_tbl24_entry = {0};
     940                 :            : 
     941                 :            :         /*
     942                 :            :          * Firstly check the sub_rule_index. A -1 indicates no replacement rule
     943                 :            :          * and a positive number indicates a sub_rule_index.
     944                 :            :          */
     945         [ +  + ]:         35 :         if (sub_rule_index < 0) {
     946                 :            :                 /*
     947                 :            :                  * If no replacement rule exists then invalidate entries
     948                 :            :                  * associated with this rule.
     949                 :            :                  */
     950         [ +  + ]:    8389395 :                 for (i = tbl24_index; i < (tbl24_index + tbl24_range); i++) {
     951                 :            : 
     952         [ +  + ]:    8389384 :                         if (i_lpm->lpm.tbl24[i].valid_group == 0 &&
     953         [ +  - ]:    8389382 :                                         i_lpm->lpm.tbl24[i].depth <= depth) {
     954                 :            :                                 struct rte_lpm_tbl_entry *tbl24_entry =
     955                 :            :                                                 &i_lpm->lpm.tbl24[i];
     956                 :    8389382 :                                 rte_atomic_store_explicit(&tbl24_entry->val,
     957                 :            :                                                 zero_tbl24_entry.val, rte_memory_order_release);
     958         [ +  - ]:          2 :                         } else if (i_lpm->lpm.tbl24[i].valid_group == 1) {
     959                 :            :                                 /*
     960                 :            :                                  * If TBL24 entry is extended, then there has
     961                 :            :                                  * to be a rule with depth >= 25 in the
     962                 :            :                                  * associated TBL8 group.
     963                 :            :                                  */
     964                 :            : 
     965                 :          2 :                                 tbl8_group_index = i_lpm->lpm.tbl24[i].group_idx;
     966                 :          2 :                                 tbl8_index = tbl8_group_index *
     967                 :            :                                                 RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
     968                 :            : 
     969         [ +  + ]:        514 :                                 for (j = tbl8_index; j < (tbl8_index +
     970                 :        512 :                                         RTE_LPM_TBL8_GROUP_NUM_ENTRIES); j++) {
     971                 :            : 
     972         [ +  + ]:        512 :                                         if (i_lpm->lpm.tbl8[j].depth <= depth)
     973                 :        510 :                                                 i_lpm->lpm.tbl8[j].valid = INVALID;
     974                 :            :                                 }
     975                 :            :                         }
     976                 :            :                 }
     977                 :            :         } else {
     978                 :            :                 /*
     979                 :            :                  * If a replacement rule exists then modify entries
     980                 :            :                  * associated with this rule.
     981                 :            :                  */
     982                 :            : 
     983                 :         24 :                 struct rte_lpm_tbl_entry new_tbl24_entry = {
     984                 :         24 :                         .next_hop = i_lpm->rules_tbl[sub_rule_index].next_hop,
     985                 :            :                         .valid = VALID,
     986                 :            :                         .valid_group = 0,
     987                 :            :                         .depth = sub_rule_depth,
     988                 :            :                 };
     989                 :            : 
     990                 :         24 :                 struct rte_lpm_tbl_entry new_tbl8_entry = {
     991                 :            :                         .valid = VALID,
     992                 :            :                         .valid_group = VALID,
     993                 :            :                         .depth = sub_rule_depth,
     994                 :            :                         .next_hop = i_lpm->rules_tbl
     995                 :            :                         [sub_rule_index].next_hop,
     996                 :            :                 };
     997                 :            : 
     998         [ +  + ]:    8388632 :                 for (i = tbl24_index; i < (tbl24_index + tbl24_range); i++) {
     999                 :            : 
    1000         [ +  - ]:    8388608 :                         if (i_lpm->lpm.tbl24[i].valid_group == 0 &&
    1001         [ +  - ]:    8388608 :                                         i_lpm->lpm.tbl24[i].depth <= depth) {
    1002                 :            :                                 struct rte_lpm_tbl_entry *tbl24_entry =
    1003                 :            :                                                 &i_lpm->lpm.tbl24[i];
    1004                 :    8388608 :                                 rte_atomic_store_explicit(&tbl24_entry->val,
    1005                 :            :                                                 new_tbl24_entry.val, rte_memory_order_release);
    1006         [ #  # ]:          0 :                         } else  if (i_lpm->lpm.tbl24[i].valid_group == 1) {
    1007                 :            :                                 /*
    1008                 :            :                                  * If TBL24 entry is extended, then there has
    1009                 :            :                                  * to be a rule with depth >= 25 in the
    1010                 :            :                                  * associated TBL8 group.
    1011                 :            :                                  */
    1012                 :            : 
    1013                 :          0 :                                 tbl8_group_index = i_lpm->lpm.tbl24[i].group_idx;
    1014                 :          0 :                                 tbl8_index = tbl8_group_index *
    1015                 :            :                                                 RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
    1016                 :            : 
    1017         [ #  # ]:          0 :                                 for (j = tbl8_index; j < (tbl8_index +
    1018                 :          0 :                                         RTE_LPM_TBL8_GROUP_NUM_ENTRIES); j++) {
    1019                 :            : 
    1020         [ #  # ]:          0 :                                         if (i_lpm->lpm.tbl8[j].depth <= depth) {
    1021                 :            :                                                 struct rte_lpm_tbl_entry *tbl8_entry =
    1022                 :            :                                                                 &i_lpm->lpm.tbl8[j];
    1023                 :          0 :                                                 rte_atomic_store_explicit(&tbl8_entry->val,
    1024                 :            :                                                                 new_tbl8_entry.val,
    1025                 :            :                                                                 rte_memory_order_relaxed);
    1026                 :            :                                         }
    1027                 :            :                                 }
    1028                 :            :                         }
    1029                 :            :                 }
    1030                 :            :         }
    1031                 :            : #undef group_idx
    1032                 :         35 :         return 0;
    1033                 :            : }
    1034                 :            : 
    1035                 :            : /*
    1036                 :            :  * Checks if table 8 group can be recycled.
    1037                 :            :  *
    1038                 :            :  * Return of -EEXIST means tbl8 is in use and thus can not be recycled.
    1039                 :            :  * Return of -EINVAL means tbl8 is empty and thus can be recycled
    1040                 :            :  * Return of value > -1 means tbl8 is in use but has all the same values and
    1041                 :            :  * thus can be recycled
    1042                 :            :  */
    1043                 :            : static int32_t
    1044                 :       2532 : tbl8_recycle_check(struct rte_lpm_tbl_entry *tbl8,
    1045                 :            :                 uint32_t tbl8_group_start)
    1046                 :            : {
    1047                 :            :         uint32_t tbl8_group_end, i;
    1048                 :       2532 :         tbl8_group_end = tbl8_group_start + RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
    1049                 :            : 
    1050                 :            :         /*
    1051                 :            :          * Check the first entry of the given tbl8. If it is invalid we know
    1052                 :            :          * this tbl8 does not contain any rule with a depth < RTE_LPM_MAX_DEPTH
    1053                 :            :          *  (As they would affect all entries in a tbl8) and thus this table
    1054                 :            :          *  can not be recycled.
    1055                 :            :          */
    1056         [ +  + ]:       2532 :         if (tbl8[tbl8_group_start].valid) {
    1057                 :            :                 /*
    1058                 :            :                  * If first entry is valid check if the depth is less than 24
    1059                 :            :                  * and if so check the rest of the entries to verify that they
    1060                 :            :                  * are all of this depth.
    1061                 :            :                  */
    1062         [ +  + ]:       1012 :                 if (tbl8[tbl8_group_start].depth <= MAX_DEPTH_TBL24) {
    1063         [ +  + ]:     256768 :                         for (i = (tbl8_group_start + 1); i < tbl8_group_end;
    1064                 :     255765 :                                         i++) {
    1065                 :            : 
    1066         [ +  - ]:     255765 :                                 if (tbl8[i].depth !=
    1067                 :            :                                                 tbl8[tbl8_group_start].depth) {
    1068                 :            : 
    1069                 :            :                                         return -EEXIST;
    1070                 :            :                                 }
    1071                 :            :                         }
    1072                 :            :                         /* If all entries are the same return the tb8 index */
    1073                 :       1003 :                         return tbl8_group_start;
    1074                 :            :                 }
    1075                 :            : 
    1076                 :            :                 return -EEXIST;
    1077                 :            :         }
    1078                 :            :         /*
    1079                 :            :          * If the first entry is invalid check if the rest of the entries in
    1080                 :            :          * the tbl8 are invalid.
    1081                 :            :          */
    1082         [ +  + ]:     389120 :         for (i = (tbl8_group_start + 1); i < tbl8_group_end; i++) {
    1083         [ +  - ]:     387600 :                 if (tbl8[i].valid)
    1084                 :            :                         return -EEXIST;
    1085                 :            :         }
    1086                 :            :         /* If no valid entries are found then return -EINVAL. */
    1087                 :            :         return -EINVAL;
    1088                 :            : }
    1089                 :            : 
    1090                 :            : static int32_t
    1091                 :       2532 : delete_depth_big(struct __rte_lpm *i_lpm, uint32_t ip_masked,
    1092                 :            :         uint8_t depth, int32_t sub_rule_index, uint8_t sub_rule_depth)
    1093                 :            : {
    1094                 :            : #define group_idx next_hop
    1095                 :            :         uint32_t tbl24_index, tbl8_group_index, tbl8_group_start, tbl8_index,
    1096                 :            :                         tbl8_range, i;
    1097                 :            :         int32_t tbl8_recycle_index, status = 0;
    1098                 :            : 
    1099                 :            :         /*
    1100                 :            :          * Calculate the index into tbl24 and range. Note: All depths larger
    1101                 :            :          * than MAX_DEPTH_TBL24 are associated with only one tbl24 entry.
    1102                 :            :          */
    1103                 :       2532 :         tbl24_index = ip_masked >> 8;
    1104                 :            : 
    1105                 :            :         /* Calculate the index into tbl8 and range. */
    1106                 :       2532 :         tbl8_group_index = i_lpm->lpm.tbl24[tbl24_index].group_idx;
    1107                 :       2532 :         tbl8_group_start = tbl8_group_index * RTE_LPM_TBL8_GROUP_NUM_ENTRIES;
    1108                 :       2532 :         tbl8_index = tbl8_group_start + (ip_masked & 0xFF);
    1109         [ -  + ]:       2532 :         tbl8_range = depth_to_range(depth);
    1110                 :            : 
    1111         [ +  + ]:       2532 :         if (sub_rule_index < 0) {
    1112                 :            :                 /*
    1113                 :            :                  * Loop through the range of entries on tbl8 for which the
    1114                 :            :                  * rule_to_delete must be removed or modified.
    1115                 :            :                  */
    1116         [ +  + ]:      10991 :                 for (i = tbl8_index; i < (tbl8_index + tbl8_range); i++) {
    1117         [ +  - ]:       9470 :                         if (i_lpm->lpm.tbl8[i].depth <= depth)
    1118                 :       9470 :                                 i_lpm->lpm.tbl8[i].valid = INVALID;
    1119                 :            :                 }
    1120                 :            :         } else {
    1121                 :            :                 /* Set new tbl8 entry. */
    1122                 :       1011 :                 struct rte_lpm_tbl_entry new_tbl8_entry = {
    1123                 :            :                         .valid = VALID,
    1124                 :            :                         .depth = sub_rule_depth,
    1125                 :       1011 :                         .valid_group = i_lpm->lpm.tbl8[tbl8_group_start].valid_group,
    1126                 :       1011 :                         .next_hop = i_lpm->rules_tbl[sub_rule_index].next_hop,
    1127                 :            :                 };
    1128                 :            : 
    1129                 :            :                 /*
    1130                 :            :                  * Loop through the range of entries on tbl8 for which the
    1131                 :            :                  * rule_to_delete must be modified.
    1132                 :            :                  */
    1133         [ +  + ]:       2299 :                 for (i = tbl8_index; i < (tbl8_index + tbl8_range); i++) {
    1134         [ +  - ]:       1288 :                         if (i_lpm->lpm.tbl8[i].depth <= depth) {
    1135                 :            :                                 struct rte_lpm_tbl_entry *tbl8_entry =
    1136                 :            :                                                 &i_lpm->lpm.tbl8[i];
    1137                 :       1288 :                                 rte_atomic_store_explicit(&tbl8_entry->val,
    1138                 :            :                                                 new_tbl8_entry.val, rte_memory_order_relaxed);
    1139                 :            :                         }
    1140                 :            :                 }
    1141                 :            :         }
    1142                 :            : 
    1143                 :            :         /*
    1144                 :            :          * Check if there are any valid entries in this tbl8 group. If all
    1145                 :            :          * tbl8 entries are invalid we can free the tbl8 and invalidate the
    1146                 :            :          * associated tbl24 entry.
    1147                 :            :          */
    1148                 :            : 
    1149                 :       2532 :         tbl8_recycle_index = tbl8_recycle_check(i_lpm->lpm.tbl8, tbl8_group_start);
    1150                 :            : 
    1151         [ +  + ]:       2532 :         if (tbl8_recycle_index == -EINVAL) {
    1152                 :            :                 /* Set tbl24 before freeing tbl8 to avoid race condition.
    1153                 :            :                  * Prevent the free of the tbl8 group from hoisting.
    1154                 :            :                  */
    1155                 :       1520 :                 i_lpm->lpm.tbl24[tbl24_index].valid = 0;
    1156                 :            :                 rte_atomic_thread_fence(rte_memory_order_release);
    1157                 :       1520 :                 status = tbl8_free(i_lpm, tbl8_group_start);
    1158         [ +  + ]:       1012 :         } else if (tbl8_recycle_index > -1) {
    1159                 :            :                 /* Update tbl24 entry. */
    1160                 :       1003 :                 struct rte_lpm_tbl_entry new_tbl24_entry = {
    1161                 :       1003 :                         .next_hop = i_lpm->lpm.tbl8[tbl8_recycle_index].next_hop,
    1162                 :            :                         .valid = VALID,
    1163                 :            :                         .valid_group = 0,
    1164                 :       1003 :                         .depth = i_lpm->lpm.tbl8[tbl8_recycle_index].depth,
    1165                 :            :                 };
    1166                 :            : 
    1167                 :            :                 /* Set tbl24 before freeing tbl8 to avoid race condition.
    1168                 :            :                  * Prevent the free of the tbl8 group from hoisting.
    1169                 :            :                  */
    1170                 :            :                 struct rte_lpm_tbl_entry *tbl24_entry =
    1171                 :            :                                 &i_lpm->lpm.tbl24[tbl24_index];
    1172                 :       1003 :                 rte_atomic_store_explicit(&tbl24_entry->val, new_tbl24_entry.val,
    1173                 :            :                                 rte_memory_order_relaxed);
    1174                 :            :                 rte_atomic_thread_fence(rte_memory_order_release);
    1175                 :       1003 :                 status = tbl8_free(i_lpm, tbl8_group_start);
    1176                 :            :         }
    1177                 :            : #undef group_idx
    1178                 :       2532 :         return status;
    1179                 :            : }
    1180                 :            : 
    1181                 :            : /*
    1182                 :            :  * Deletes a rule
    1183                 :            :  */
    1184                 :            : RTE_EXPORT_SYMBOL(rte_lpm_delete)
    1185                 :            : int
    1186                 :       2572 : rte_lpm_delete(struct rte_lpm *lpm, uint32_t ip, uint8_t depth)
    1187                 :            : {
    1188                 :            :         int32_t rule_to_delete_index, sub_rule_index;
    1189                 :            :         struct __rte_lpm *i_lpm;
    1190                 :            :         uint32_t ip_masked;
    1191                 :            :         uint8_t sub_rule_depth;
    1192                 :            :         /*
    1193                 :            :          * Check input arguments. Note: IP must be a positive integer of 32
    1194                 :            :          * bits in length therefore it need not be checked.
    1195                 :            :          */
    1196   [ +  +  +  + ]:       2572 :         if ((lpm == NULL) || (depth < 1) || (depth > RTE_LPM_MAX_DEPTH)) {
    1197                 :            :                 return -EINVAL;
    1198                 :            :         }
    1199                 :            : 
    1200                 :            :         i_lpm = container_of(lpm, struct __rte_lpm, lpm);
    1201                 :       2569 :         ip_masked = ip & depth_to_mask(depth);
    1202                 :            : 
    1203                 :            :         /*
    1204                 :            :          * Find the index of the input rule, that needs to be deleted, in the
    1205                 :            :          * rule table.
    1206                 :            :          */
    1207                 :            :         rule_to_delete_index = rule_find(i_lpm, ip_masked, depth);
    1208                 :            : 
    1209                 :            :         /*
    1210                 :            :          * Check if rule_to_delete_index was found. If no rule was found the
    1211                 :            :          * function rule_find returns -EINVAL.
    1212                 :            :          */
    1213         [ +  + ]:       2569 :         if (rule_to_delete_index < 0)
    1214                 :            :                 return -EINVAL;
    1215                 :            : 
    1216                 :            :         /* Delete the rule from the rule table. */
    1217                 :       2567 :         rule_delete(i_lpm, rule_to_delete_index, depth);
    1218                 :            : 
    1219                 :            :         /*
    1220                 :            :          * Find rule to replace the rule_to_delete. If there is no rule to
    1221                 :            :          * replace the rule_to_delete we return -1 and invalidate the table
    1222                 :            :          * entries associated with this rule.
    1223                 :            :          */
    1224                 :       2567 :         sub_rule_depth = 0;
    1225                 :       2567 :         sub_rule_index = find_previous_rule(i_lpm, ip, depth, &sub_rule_depth);
    1226                 :            : 
    1227                 :            :         /*
    1228                 :            :          * If the input depth value is less than 25 use function
    1229                 :            :          * delete_depth_small otherwise use delete_depth_big.
    1230                 :            :          */
    1231         [ +  + ]:       2567 :         if (depth <= MAX_DEPTH_TBL24) {
    1232                 :         35 :                 return delete_depth_small(i_lpm, ip_masked, depth,
    1233                 :            :                                 sub_rule_index, sub_rule_depth);
    1234                 :            :         } else { /* If depth > MAX_DEPTH_TBL24 */
    1235                 :       2532 :                 return delete_depth_big(i_lpm, ip_masked, depth, sub_rule_index,
    1236                 :            :                                 sub_rule_depth);
    1237                 :            :         }
    1238                 :            : }
    1239                 :            : 
    1240                 :            : /*
    1241                 :            :  * Delete all rules from the LPM table.
    1242                 :            :  */
    1243                 :            : RTE_EXPORT_SYMBOL(rte_lpm_delete_all)
    1244                 :            : void
    1245                 :         10 : rte_lpm_delete_all(struct rte_lpm *lpm)
    1246                 :            : {
    1247                 :            :         struct __rte_lpm *i_lpm;
    1248                 :            : 
    1249                 :            :         i_lpm = container_of(lpm, struct __rte_lpm, lpm);
    1250                 :            :         /* Zero rule information. */
    1251                 :         10 :         memset(i_lpm->rule_info, 0, sizeof(i_lpm->rule_info));
    1252                 :            : 
    1253                 :            :         /* Zero tbl24. */
    1254                 :         10 :         memset(i_lpm->lpm.tbl24, 0, sizeof(i_lpm->lpm.tbl24));
    1255                 :            : 
    1256                 :            :         /* Zero tbl8. */
    1257                 :         10 :         memset(i_lpm->lpm.tbl8, 0, sizeof(i_lpm->lpm.tbl8[0])
    1258                 :         10 :                         * RTE_LPM_TBL8_GROUP_NUM_ENTRIES * i_lpm->number_tbl8s);
    1259                 :            : 
    1260                 :            :         /* Delete all rules form the rules table. */
    1261                 :         10 :         memset(i_lpm->rules_tbl, 0, sizeof(i_lpm->rules_tbl[0]) * i_lpm->max_rules);
    1262                 :         10 : }

Generated by: LCOV version 1.14