199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 299a2dd95SBruce Richardson * Copyright(c) 2010-2014 Intel Corporation 399a2dd95SBruce Richardson */ 499a2dd95SBruce Richardson #include <string.h> 5e9fd1ebfSTyler Retzlaff #include <stdalign.h> 699a2dd95SBruce Richardson #include <stdint.h> 799a2dd95SBruce Richardson #include <errno.h> 899a2dd95SBruce Richardson #include <stdio.h> 999a2dd95SBruce Richardson #include <sys/queue.h> 1099a2dd95SBruce Richardson 1199a2dd95SBruce Richardson #include <rte_log.h> 1299a2dd95SBruce Richardson #include <rte_common.h> 1399a2dd95SBruce Richardson #include <rte_malloc.h> 1499a2dd95SBruce Richardson #include <rte_memcpy.h> 1599a2dd95SBruce Richardson #include <rte_eal_memconfig.h> 1699a2dd95SBruce Richardson #include <rte_string_fns.h> 1799a2dd95SBruce Richardson #include <rte_errno.h> 1899a2dd95SBruce Richardson #include <rte_hash.h> 1999a2dd95SBruce Richardson #include <assert.h> 2099a2dd95SBruce Richardson #include <rte_jhash.h> 2199a2dd95SBruce Richardson #include <rte_tailq.h> 2299a2dd95SBruce Richardson 2399a2dd95SBruce Richardson #include "rte_lpm6.h" 24fdb83ffbSStephen Hemminger #include "lpm_log.h" 2599a2dd95SBruce Richardson 2699a2dd95SBruce Richardson #define RTE_LPM6_TBL24_NUM_ENTRIES (1 << 24) 2799a2dd95SBruce Richardson #define RTE_LPM6_TBL8_GROUP_NUM_ENTRIES 256 2899a2dd95SBruce Richardson #define RTE_LPM6_TBL8_MAX_NUM_GROUPS (1 << 21) 2999a2dd95SBruce Richardson 3099a2dd95SBruce Richardson #define RTE_LPM6_VALID_EXT_ENTRY_BITMASK 0xA0000000 3199a2dd95SBruce Richardson #define RTE_LPM6_LOOKUP_SUCCESS 0x20000000 3299a2dd95SBruce Richardson #define RTE_LPM6_TBL8_BITMASK 0x001FFFFF 3399a2dd95SBruce Richardson 3499a2dd95SBruce Richardson #define ADD_FIRST_BYTE 3 3599a2dd95SBruce Richardson #define LOOKUP_FIRST_BYTE 4 3699a2dd95SBruce Richardson #define BYTE_SIZE 8 3799a2dd95SBruce Richardson #define BYTES2_SIZE 16 3899a2dd95SBruce Richardson 3999a2dd95SBruce Richardson #define RULE_HASH_TABLE_EXTRA_SPACE 64 4099a2dd95SBruce Richardson #define TBL24_IND UINT32_MAX 4199a2dd95SBruce Richardson 4299a2dd95SBruce Richardson #define lpm6_tbl8_gindex next_hop 4399a2dd95SBruce Richardson 4499a2dd95SBruce Richardson /** Flags for setting an entry as valid/invalid. */ 4599a2dd95SBruce Richardson enum valid_flag { 4699a2dd95SBruce Richardson INVALID = 0, 4799a2dd95SBruce Richardson VALID 4899a2dd95SBruce Richardson }; 4999a2dd95SBruce Richardson 5099a2dd95SBruce Richardson TAILQ_HEAD(rte_lpm6_list, rte_tailq_entry); 5199a2dd95SBruce Richardson 5299a2dd95SBruce Richardson static struct rte_tailq_elem rte_lpm6_tailq = { 5399a2dd95SBruce Richardson .name = "RTE_LPM6", 5499a2dd95SBruce Richardson }; 5599a2dd95SBruce Richardson EAL_REGISTER_TAILQ(rte_lpm6_tailq) 5699a2dd95SBruce Richardson 5799a2dd95SBruce Richardson /** Tbl entry structure. It is the same for both tbl24 and tbl8 */ 5899a2dd95SBruce Richardson struct rte_lpm6_tbl_entry { 5999a2dd95SBruce Richardson uint32_t next_hop: 21; /**< Next hop / next table to be checked. */ 6099a2dd95SBruce Richardson uint32_t depth :8; /**< Rule depth. */ 6199a2dd95SBruce Richardson 6299a2dd95SBruce Richardson /* Flags. */ 6399a2dd95SBruce Richardson uint32_t valid :1; /**< Validation flag. */ 6499a2dd95SBruce Richardson uint32_t valid_group :1; /**< Group validation flag. */ 6599a2dd95SBruce Richardson uint32_t ext_entry :1; /**< External entry. */ 6699a2dd95SBruce Richardson }; 6799a2dd95SBruce Richardson 6899a2dd95SBruce Richardson /** Rules tbl entry structure. */ 6999a2dd95SBruce Richardson struct rte_lpm6_rule { 70*e1a06e39SRobin Jarry struct rte_ipv6_addr ip; /**< Rule IP address. */ 7199a2dd95SBruce Richardson uint32_t next_hop; /**< Rule next hop. */ 7299a2dd95SBruce Richardson uint8_t depth; /**< Rule depth. */ 7399a2dd95SBruce Richardson }; 7499a2dd95SBruce Richardson 7599a2dd95SBruce Richardson /** Rules tbl entry key. */ 7699a2dd95SBruce Richardson struct rte_lpm6_rule_key { 77*e1a06e39SRobin Jarry struct rte_ipv6_addr ip; /**< Rule IP address. */ 78b16ac536SVladimir Medvedkin uint32_t depth; /**< Rule depth. */ 7999a2dd95SBruce Richardson }; 8099a2dd95SBruce Richardson 8199a2dd95SBruce Richardson /* Header of tbl8 */ 8299a2dd95SBruce Richardson struct rte_lpm_tbl8_hdr { 8399a2dd95SBruce Richardson uint32_t owner_tbl_ind; /**< owner table: TBL24_IND if owner is tbl24, 8499a2dd95SBruce Richardson * otherwise index of tbl8 8599a2dd95SBruce Richardson */ 8699a2dd95SBruce Richardson uint32_t owner_entry_ind; /**< index of the owner table entry where 8799a2dd95SBruce Richardson * pointer to the tbl8 is stored 8899a2dd95SBruce Richardson */ 8999a2dd95SBruce Richardson uint32_t ref_cnt; /**< table reference counter */ 9099a2dd95SBruce Richardson }; 9199a2dd95SBruce Richardson 9299a2dd95SBruce Richardson /** LPM6 structure. */ 9399a2dd95SBruce Richardson struct rte_lpm6 { 9499a2dd95SBruce Richardson /* LPM metadata. */ 9599a2dd95SBruce Richardson char name[RTE_LPM6_NAMESIZE]; /**< Name of the lpm. */ 9699a2dd95SBruce Richardson uint32_t max_rules; /**< Max number of rules. */ 9799a2dd95SBruce Richardson uint32_t used_rules; /**< Used rules so far. */ 9899a2dd95SBruce Richardson uint32_t number_tbl8s; /**< Number of tbl8s to allocate. */ 9999a2dd95SBruce Richardson 10099a2dd95SBruce Richardson /* LPM Tables. */ 10199a2dd95SBruce Richardson struct rte_hash *rules_tbl; /**< LPM rules. */ 102e9fd1ebfSTyler Retzlaff alignas(RTE_CACHE_LINE_SIZE) struct rte_lpm6_tbl_entry tbl24[RTE_LPM6_TBL24_NUM_ENTRIES]; 103e9fd1ebfSTyler Retzlaff /**< LPM tbl24 table. */ 10499a2dd95SBruce Richardson 10599a2dd95SBruce Richardson uint32_t *tbl8_pool; /**< pool of indexes of free tbl8s */ 10699a2dd95SBruce Richardson uint32_t tbl8_pool_pos; /**< current position in the tbl8 pool */ 10799a2dd95SBruce Richardson 10899a2dd95SBruce Richardson struct rte_lpm_tbl8_hdr *tbl8_hdrs; /* array of tbl8 headers */ 10999a2dd95SBruce Richardson 11093112c15STyler Retzlaff alignas(RTE_CACHE_LINE_SIZE) struct rte_lpm6_tbl_entry tbl8[]; 111e9fd1ebfSTyler Retzlaff /**< LPM tbl8 table. */ 11299a2dd95SBruce Richardson }; 11399a2dd95SBruce Richardson 11499a2dd95SBruce Richardson /* 11599a2dd95SBruce Richardson * LPM6 rule hash function 11699a2dd95SBruce Richardson * 11799a2dd95SBruce Richardson * It's used as a hash function for the rte_hash 11899a2dd95SBruce Richardson * containing rules 11999a2dd95SBruce Richardson */ 12099a2dd95SBruce Richardson static inline uint32_t 12199a2dd95SBruce Richardson rule_hash(const void *data, __rte_unused uint32_t data_len, 12299a2dd95SBruce Richardson uint32_t init_val) 12399a2dd95SBruce Richardson { 12499a2dd95SBruce Richardson return rte_jhash(data, sizeof(struct rte_lpm6_rule_key), init_val); 12599a2dd95SBruce Richardson } 12699a2dd95SBruce Richardson 12799a2dd95SBruce Richardson /* 12899a2dd95SBruce Richardson * Init pool of free tbl8 indexes 12999a2dd95SBruce Richardson */ 13099a2dd95SBruce Richardson static void 13199a2dd95SBruce Richardson tbl8_pool_init(struct rte_lpm6 *lpm) 13299a2dd95SBruce Richardson { 13399a2dd95SBruce Richardson uint32_t i; 13499a2dd95SBruce Richardson 13599a2dd95SBruce Richardson /* put entire range of indexes to the tbl8 pool */ 13699a2dd95SBruce Richardson for (i = 0; i < lpm->number_tbl8s; i++) 13799a2dd95SBruce Richardson lpm->tbl8_pool[i] = i; 13899a2dd95SBruce Richardson 13999a2dd95SBruce Richardson lpm->tbl8_pool_pos = 0; 14099a2dd95SBruce Richardson } 14199a2dd95SBruce Richardson 14299a2dd95SBruce Richardson /* 14399a2dd95SBruce Richardson * Get an index of a free tbl8 from the pool 14499a2dd95SBruce Richardson */ 14599a2dd95SBruce Richardson static inline uint32_t 14699a2dd95SBruce Richardson tbl8_get(struct rte_lpm6 *lpm, uint32_t *tbl8_ind) 14799a2dd95SBruce Richardson { 14899a2dd95SBruce Richardson if (lpm->tbl8_pool_pos == lpm->number_tbl8s) 14999a2dd95SBruce Richardson /* no more free tbl8 */ 15099a2dd95SBruce Richardson return -ENOSPC; 15199a2dd95SBruce Richardson 15299a2dd95SBruce Richardson /* next index */ 15399a2dd95SBruce Richardson *tbl8_ind = lpm->tbl8_pool[lpm->tbl8_pool_pos++]; 15499a2dd95SBruce Richardson return 0; 15599a2dd95SBruce Richardson } 15699a2dd95SBruce Richardson 15799a2dd95SBruce Richardson /* 15899a2dd95SBruce Richardson * Put an index of a free tbl8 back to the pool 15999a2dd95SBruce Richardson */ 16099a2dd95SBruce Richardson static inline uint32_t 16199a2dd95SBruce Richardson tbl8_put(struct rte_lpm6 *lpm, uint32_t tbl8_ind) 16299a2dd95SBruce Richardson { 16399a2dd95SBruce Richardson if (lpm->tbl8_pool_pos == 0) 16499a2dd95SBruce Richardson /* pool is full */ 16599a2dd95SBruce Richardson return -ENOSPC; 16699a2dd95SBruce Richardson 16799a2dd95SBruce Richardson lpm->tbl8_pool[--lpm->tbl8_pool_pos] = tbl8_ind; 16899a2dd95SBruce Richardson return 0; 16999a2dd95SBruce Richardson } 17099a2dd95SBruce Richardson 17199a2dd95SBruce Richardson /* 17299a2dd95SBruce Richardson * Returns number of tbl8s available in the pool 17399a2dd95SBruce Richardson */ 17499a2dd95SBruce Richardson static inline uint32_t 17599a2dd95SBruce Richardson tbl8_available(struct rte_lpm6 *lpm) 17699a2dd95SBruce Richardson { 17799a2dd95SBruce Richardson return lpm->number_tbl8s - lpm->tbl8_pool_pos; 17899a2dd95SBruce Richardson } 17999a2dd95SBruce Richardson 18099a2dd95SBruce Richardson /* 18199a2dd95SBruce Richardson * Init a rule key. 18299a2dd95SBruce Richardson * note that ip must be already masked 18399a2dd95SBruce Richardson */ 18499a2dd95SBruce Richardson static inline void 185*e1a06e39SRobin Jarry rule_key_init(struct rte_lpm6_rule_key *key, const struct rte_ipv6_addr *ip, uint8_t depth) 18699a2dd95SBruce Richardson { 187*e1a06e39SRobin Jarry key->ip = *ip; 18899a2dd95SBruce Richardson key->depth = depth; 18999a2dd95SBruce Richardson } 19099a2dd95SBruce Richardson 19199a2dd95SBruce Richardson /* 19299a2dd95SBruce Richardson * Rebuild the entire LPM tree by reinserting all rules 19399a2dd95SBruce Richardson */ 19499a2dd95SBruce Richardson static void 19599a2dd95SBruce Richardson rebuild_lpm(struct rte_lpm6 *lpm) 19699a2dd95SBruce Richardson { 19799a2dd95SBruce Richardson uint64_t next_hop; 19899a2dd95SBruce Richardson struct rte_lpm6_rule_key *rule_key; 19999a2dd95SBruce Richardson uint32_t iter = 0; 20099a2dd95SBruce Richardson 20199a2dd95SBruce Richardson while (rte_hash_iterate(lpm->rules_tbl, (void *) &rule_key, 20299a2dd95SBruce Richardson (void **) &next_hop, &iter) >= 0) 203*e1a06e39SRobin Jarry rte_lpm6_add(lpm, &rule_key->ip, rule_key->depth, 20499a2dd95SBruce Richardson (uint32_t) next_hop); 20599a2dd95SBruce Richardson } 20699a2dd95SBruce Richardson 20799a2dd95SBruce Richardson /* 20899a2dd95SBruce Richardson * Allocates memory for LPM object 20999a2dd95SBruce Richardson */ 21099a2dd95SBruce Richardson struct rte_lpm6 * 21199a2dd95SBruce Richardson rte_lpm6_create(const char *name, int socket_id, 21299a2dd95SBruce Richardson const struct rte_lpm6_config *config) 21399a2dd95SBruce Richardson { 21499a2dd95SBruce Richardson char mem_name[RTE_LPM6_NAMESIZE]; 21599a2dd95SBruce Richardson struct rte_lpm6 *lpm = NULL; 21699a2dd95SBruce Richardson struct rte_tailq_entry *te; 21799a2dd95SBruce Richardson uint64_t mem_size; 21899a2dd95SBruce Richardson struct rte_lpm6_list *lpm_list; 21999a2dd95SBruce Richardson struct rte_hash *rules_tbl = NULL; 22099a2dd95SBruce Richardson uint32_t *tbl8_pool = NULL; 22199a2dd95SBruce Richardson struct rte_lpm_tbl8_hdr *tbl8_hdrs = NULL; 22299a2dd95SBruce Richardson 22399a2dd95SBruce Richardson lpm_list = RTE_TAILQ_CAST(rte_lpm6_tailq.head, rte_lpm6_list); 22499a2dd95SBruce Richardson 22599a2dd95SBruce Richardson RTE_BUILD_BUG_ON(sizeof(struct rte_lpm6_tbl_entry) != sizeof(uint32_t)); 226b16ac536SVladimir Medvedkin RTE_BUILD_BUG_ON(sizeof(struct rte_lpm6_rule_key) % 227b16ac536SVladimir Medvedkin sizeof(uint32_t) != 0); 22899a2dd95SBruce Richardson 22999a2dd95SBruce Richardson /* Check user arguments. */ 23099a2dd95SBruce Richardson if ((name == NULL) || (socket_id < -1) || (config == NULL) || 23199a2dd95SBruce Richardson (config->max_rules == 0) || 23299a2dd95SBruce Richardson config->number_tbl8s > RTE_LPM6_TBL8_MAX_NUM_GROUPS) { 23399a2dd95SBruce Richardson rte_errno = EINVAL; 23499a2dd95SBruce Richardson return NULL; 23599a2dd95SBruce Richardson } 23699a2dd95SBruce Richardson 23799a2dd95SBruce Richardson /* create rules hash table */ 23899a2dd95SBruce Richardson snprintf(mem_name, sizeof(mem_name), "LRH_%s", name); 23999a2dd95SBruce Richardson struct rte_hash_parameters rule_hash_tbl_params = { 24099a2dd95SBruce Richardson .entries = config->max_rules * 1.2 + 24199a2dd95SBruce Richardson RULE_HASH_TABLE_EXTRA_SPACE, 24299a2dd95SBruce Richardson .key_len = sizeof(struct rte_lpm6_rule_key), 24399a2dd95SBruce Richardson .hash_func = rule_hash, 24499a2dd95SBruce Richardson .hash_func_init_val = 0, 24599a2dd95SBruce Richardson .name = mem_name, 24699a2dd95SBruce Richardson .reserved = 0, 24799a2dd95SBruce Richardson .socket_id = socket_id, 24899a2dd95SBruce Richardson .extra_flag = 0 24999a2dd95SBruce Richardson }; 25099a2dd95SBruce Richardson 25199a2dd95SBruce Richardson rules_tbl = rte_hash_create(&rule_hash_tbl_params); 25299a2dd95SBruce Richardson if (rules_tbl == NULL) { 253ae67895bSDavid Marchand LPM_LOG(ERR, "LPM rules hash table allocation failed: %s (%d)", 25499a2dd95SBruce Richardson rte_strerror(rte_errno), rte_errno); 25599a2dd95SBruce Richardson goto fail_wo_unlock; 25699a2dd95SBruce Richardson } 25799a2dd95SBruce Richardson 25899a2dd95SBruce Richardson /* allocate tbl8 indexes pool */ 25999a2dd95SBruce Richardson tbl8_pool = rte_malloc(NULL, 26099a2dd95SBruce Richardson sizeof(uint32_t) * config->number_tbl8s, 26199a2dd95SBruce Richardson RTE_CACHE_LINE_SIZE); 26299a2dd95SBruce Richardson if (tbl8_pool == NULL) { 263ae67895bSDavid Marchand LPM_LOG(ERR, "LPM tbl8 pool allocation failed: %s (%d)", 26499a2dd95SBruce Richardson rte_strerror(rte_errno), rte_errno); 26599a2dd95SBruce Richardson rte_errno = ENOMEM; 26699a2dd95SBruce Richardson goto fail_wo_unlock; 26799a2dd95SBruce Richardson } 26899a2dd95SBruce Richardson 26999a2dd95SBruce Richardson /* allocate tbl8 headers */ 27099a2dd95SBruce Richardson tbl8_hdrs = rte_malloc(NULL, 27199a2dd95SBruce Richardson sizeof(struct rte_lpm_tbl8_hdr) * config->number_tbl8s, 27299a2dd95SBruce Richardson RTE_CACHE_LINE_SIZE); 27399a2dd95SBruce Richardson if (tbl8_hdrs == NULL) { 274ae67895bSDavid Marchand LPM_LOG(ERR, "LPM tbl8 headers allocation failed: %s (%d)", 27599a2dd95SBruce Richardson rte_strerror(rte_errno), rte_errno); 27699a2dd95SBruce Richardson rte_errno = ENOMEM; 27799a2dd95SBruce Richardson goto fail_wo_unlock; 27899a2dd95SBruce Richardson } 27999a2dd95SBruce Richardson 28099a2dd95SBruce Richardson snprintf(mem_name, sizeof(mem_name), "LPM_%s", name); 28199a2dd95SBruce Richardson 28299a2dd95SBruce Richardson /* Determine the amount of memory to allocate. */ 28399a2dd95SBruce Richardson mem_size = sizeof(*lpm) + (sizeof(lpm->tbl8[0]) * 28499a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES * config->number_tbl8s); 28599a2dd95SBruce Richardson 28699a2dd95SBruce Richardson rte_mcfg_tailq_write_lock(); 28799a2dd95SBruce Richardson 28899a2dd95SBruce Richardson /* Guarantee there's no existing */ 28999a2dd95SBruce Richardson TAILQ_FOREACH(te, lpm_list, next) { 29099a2dd95SBruce Richardson lpm = (struct rte_lpm6 *) te->data; 29199a2dd95SBruce Richardson if (strncmp(name, lpm->name, RTE_LPM6_NAMESIZE) == 0) 29299a2dd95SBruce Richardson break; 29399a2dd95SBruce Richardson } 29499a2dd95SBruce Richardson lpm = NULL; 29599a2dd95SBruce Richardson if (te != NULL) { 29699a2dd95SBruce Richardson rte_errno = EEXIST; 29799a2dd95SBruce Richardson goto fail; 29899a2dd95SBruce Richardson } 29999a2dd95SBruce Richardson 30099a2dd95SBruce Richardson /* allocate tailq entry */ 30199a2dd95SBruce Richardson te = rte_zmalloc("LPM6_TAILQ_ENTRY", sizeof(*te), 0); 30299a2dd95SBruce Richardson if (te == NULL) { 303ae67895bSDavid Marchand LPM_LOG(ERR, "Failed to allocate tailq entry!"); 30499a2dd95SBruce Richardson rte_errno = ENOMEM; 30599a2dd95SBruce Richardson goto fail; 30699a2dd95SBruce Richardson } 30799a2dd95SBruce Richardson 30899a2dd95SBruce Richardson /* Allocate memory to store the LPM data structures. */ 30999a2dd95SBruce Richardson lpm = rte_zmalloc_socket(mem_name, (size_t)mem_size, 31099a2dd95SBruce Richardson RTE_CACHE_LINE_SIZE, socket_id); 31199a2dd95SBruce Richardson 31299a2dd95SBruce Richardson if (lpm == NULL) { 313ae67895bSDavid Marchand LPM_LOG(ERR, "LPM memory allocation failed"); 31499a2dd95SBruce Richardson rte_free(te); 31599a2dd95SBruce Richardson rte_errno = ENOMEM; 31699a2dd95SBruce Richardson goto fail; 31799a2dd95SBruce Richardson } 31899a2dd95SBruce Richardson 31999a2dd95SBruce Richardson /* Save user arguments. */ 32099a2dd95SBruce Richardson lpm->max_rules = config->max_rules; 32199a2dd95SBruce Richardson lpm->number_tbl8s = config->number_tbl8s; 32299a2dd95SBruce Richardson strlcpy(lpm->name, name, sizeof(lpm->name)); 32399a2dd95SBruce Richardson lpm->rules_tbl = rules_tbl; 32499a2dd95SBruce Richardson lpm->tbl8_pool = tbl8_pool; 32599a2dd95SBruce Richardson lpm->tbl8_hdrs = tbl8_hdrs; 32699a2dd95SBruce Richardson 32799a2dd95SBruce Richardson /* init the stack */ 32899a2dd95SBruce Richardson tbl8_pool_init(lpm); 32999a2dd95SBruce Richardson 33099a2dd95SBruce Richardson te->data = (void *) lpm; 33199a2dd95SBruce Richardson 33299a2dd95SBruce Richardson TAILQ_INSERT_TAIL(lpm_list, te, next); 33399a2dd95SBruce Richardson rte_mcfg_tailq_write_unlock(); 33499a2dd95SBruce Richardson return lpm; 33599a2dd95SBruce Richardson 33699a2dd95SBruce Richardson fail: 33799a2dd95SBruce Richardson rte_mcfg_tailq_write_unlock(); 33899a2dd95SBruce Richardson 33999a2dd95SBruce Richardson fail_wo_unlock: 34099a2dd95SBruce Richardson rte_free(tbl8_hdrs); 34199a2dd95SBruce Richardson rte_free(tbl8_pool); 34299a2dd95SBruce Richardson rte_hash_free(rules_tbl); 34399a2dd95SBruce Richardson 34499a2dd95SBruce Richardson return NULL; 34599a2dd95SBruce Richardson } 34699a2dd95SBruce Richardson 34799a2dd95SBruce Richardson /* 34899a2dd95SBruce Richardson * Find an existing lpm table and return a pointer to it. 34999a2dd95SBruce Richardson */ 35099a2dd95SBruce Richardson struct rte_lpm6 * 35199a2dd95SBruce Richardson rte_lpm6_find_existing(const char *name) 35299a2dd95SBruce Richardson { 35399a2dd95SBruce Richardson struct rte_lpm6 *l = NULL; 35499a2dd95SBruce Richardson struct rte_tailq_entry *te; 35599a2dd95SBruce Richardson struct rte_lpm6_list *lpm_list; 35699a2dd95SBruce Richardson 35799a2dd95SBruce Richardson lpm_list = RTE_TAILQ_CAST(rte_lpm6_tailq.head, rte_lpm6_list); 35899a2dd95SBruce Richardson 35999a2dd95SBruce Richardson rte_mcfg_tailq_read_lock(); 36099a2dd95SBruce Richardson TAILQ_FOREACH(te, lpm_list, next) { 36199a2dd95SBruce Richardson l = (struct rte_lpm6 *) te->data; 36299a2dd95SBruce Richardson if (strncmp(name, l->name, RTE_LPM6_NAMESIZE) == 0) 36399a2dd95SBruce Richardson break; 36499a2dd95SBruce Richardson } 36599a2dd95SBruce Richardson rte_mcfg_tailq_read_unlock(); 36699a2dd95SBruce Richardson 36799a2dd95SBruce Richardson if (te == NULL) { 36899a2dd95SBruce Richardson rte_errno = ENOENT; 36999a2dd95SBruce Richardson return NULL; 37099a2dd95SBruce Richardson } 37199a2dd95SBruce Richardson 37299a2dd95SBruce Richardson return l; 37399a2dd95SBruce Richardson } 37499a2dd95SBruce Richardson 37599a2dd95SBruce Richardson /* 37699a2dd95SBruce Richardson * Deallocates memory for given LPM table. 37799a2dd95SBruce Richardson */ 37899a2dd95SBruce Richardson void 37999a2dd95SBruce Richardson rte_lpm6_free(struct rte_lpm6 *lpm) 38099a2dd95SBruce Richardson { 38199a2dd95SBruce Richardson struct rte_lpm6_list *lpm_list; 38299a2dd95SBruce Richardson struct rte_tailq_entry *te; 38399a2dd95SBruce Richardson 38499a2dd95SBruce Richardson /* Check user arguments. */ 38599a2dd95SBruce Richardson if (lpm == NULL) 38699a2dd95SBruce Richardson return; 38799a2dd95SBruce Richardson 38899a2dd95SBruce Richardson lpm_list = RTE_TAILQ_CAST(rte_lpm6_tailq.head, rte_lpm6_list); 38999a2dd95SBruce Richardson 39099a2dd95SBruce Richardson rte_mcfg_tailq_write_lock(); 39199a2dd95SBruce Richardson 39299a2dd95SBruce Richardson /* find our tailq entry */ 39399a2dd95SBruce Richardson TAILQ_FOREACH(te, lpm_list, next) { 39499a2dd95SBruce Richardson if (te->data == (void *) lpm) 39599a2dd95SBruce Richardson break; 39699a2dd95SBruce Richardson } 39799a2dd95SBruce Richardson 39899a2dd95SBruce Richardson if (te != NULL) 39999a2dd95SBruce Richardson TAILQ_REMOVE(lpm_list, te, next); 40099a2dd95SBruce Richardson 40199a2dd95SBruce Richardson rte_mcfg_tailq_write_unlock(); 40299a2dd95SBruce Richardson 40399a2dd95SBruce Richardson rte_free(lpm->tbl8_hdrs); 40499a2dd95SBruce Richardson rte_free(lpm->tbl8_pool); 40599a2dd95SBruce Richardson rte_hash_free(lpm->rules_tbl); 40699a2dd95SBruce Richardson rte_free(lpm); 40799a2dd95SBruce Richardson rte_free(te); 40899a2dd95SBruce Richardson } 40999a2dd95SBruce Richardson 41099a2dd95SBruce Richardson /* Find a rule */ 41199a2dd95SBruce Richardson static inline int 41299a2dd95SBruce Richardson rule_find_with_key(struct rte_lpm6 *lpm, 41399a2dd95SBruce Richardson const struct rte_lpm6_rule_key *rule_key, 41499a2dd95SBruce Richardson uint32_t *next_hop) 41599a2dd95SBruce Richardson { 41699a2dd95SBruce Richardson uint64_t hash_val; 41799a2dd95SBruce Richardson int ret; 41899a2dd95SBruce Richardson 41999a2dd95SBruce Richardson /* lookup for a rule */ 42099a2dd95SBruce Richardson ret = rte_hash_lookup_data(lpm->rules_tbl, (const void *) rule_key, 42199a2dd95SBruce Richardson (void **) &hash_val); 42299a2dd95SBruce Richardson if (ret >= 0) { 42399a2dd95SBruce Richardson *next_hop = (uint32_t) hash_val; 42499a2dd95SBruce Richardson return 1; 42599a2dd95SBruce Richardson } 42699a2dd95SBruce Richardson 42799a2dd95SBruce Richardson return 0; 42899a2dd95SBruce Richardson } 42999a2dd95SBruce Richardson 43099a2dd95SBruce Richardson /* Find a rule */ 43199a2dd95SBruce Richardson static int 432*e1a06e39SRobin Jarry rule_find(struct rte_lpm6 *lpm, struct rte_ipv6_addr *ip, uint8_t depth, 43399a2dd95SBruce Richardson uint32_t *next_hop) 43499a2dd95SBruce Richardson { 43599a2dd95SBruce Richardson struct rte_lpm6_rule_key rule_key; 43699a2dd95SBruce Richardson 43799a2dd95SBruce Richardson /* init a rule key */ 43899a2dd95SBruce Richardson rule_key_init(&rule_key, ip, depth); 43999a2dd95SBruce Richardson 44099a2dd95SBruce Richardson return rule_find_with_key(lpm, &rule_key, next_hop); 44199a2dd95SBruce Richardson } 44299a2dd95SBruce Richardson 44399a2dd95SBruce Richardson /* 44499a2dd95SBruce Richardson * Checks if a rule already exists in the rules table and updates 44599a2dd95SBruce Richardson * the nexthop if so. Otherwise it adds a new rule if enough space is available. 44699a2dd95SBruce Richardson * 44799a2dd95SBruce Richardson * Returns: 44899a2dd95SBruce Richardson * 0 - next hop of existed rule is updated 44999a2dd95SBruce Richardson * 1 - new rule successfully added 45099a2dd95SBruce Richardson * <0 - error 45199a2dd95SBruce Richardson */ 45299a2dd95SBruce Richardson static inline int 453*e1a06e39SRobin Jarry rule_add(struct rte_lpm6 *lpm, struct rte_ipv6_addr *ip, uint8_t depth, uint32_t next_hop) 45499a2dd95SBruce Richardson { 45599a2dd95SBruce Richardson int ret, rule_exist; 45699a2dd95SBruce Richardson struct rte_lpm6_rule_key rule_key; 45799a2dd95SBruce Richardson uint32_t unused; 45899a2dd95SBruce Richardson 45999a2dd95SBruce Richardson /* init a rule key */ 46099a2dd95SBruce Richardson rule_key_init(&rule_key, ip, depth); 46199a2dd95SBruce Richardson 46299a2dd95SBruce Richardson /* Scan through rule list to see if rule already exists. */ 46399a2dd95SBruce Richardson rule_exist = rule_find_with_key(lpm, &rule_key, &unused); 46499a2dd95SBruce Richardson 46599a2dd95SBruce Richardson /* 46699a2dd95SBruce Richardson * If rule does not exist check if there is space to add a new rule to 46799a2dd95SBruce Richardson * this rule group. If there is no space return error. 46899a2dd95SBruce Richardson */ 46999a2dd95SBruce Richardson if (!rule_exist && lpm->used_rules == lpm->max_rules) 47099a2dd95SBruce Richardson return -ENOSPC; 47199a2dd95SBruce Richardson 47299a2dd95SBruce Richardson /* add the rule or update rules next hop */ 47399a2dd95SBruce Richardson ret = rte_hash_add_key_data(lpm->rules_tbl, &rule_key, 47499a2dd95SBruce Richardson (void *)(uintptr_t) next_hop); 47599a2dd95SBruce Richardson if (ret < 0) 47699a2dd95SBruce Richardson return ret; 47799a2dd95SBruce Richardson 47899a2dd95SBruce Richardson /* Increment the used rules counter for this rule group. */ 47999a2dd95SBruce Richardson if (!rule_exist) { 48099a2dd95SBruce Richardson lpm->used_rules++; 48199a2dd95SBruce Richardson return 1; 48299a2dd95SBruce Richardson } 48399a2dd95SBruce Richardson 48499a2dd95SBruce Richardson return 0; 48599a2dd95SBruce Richardson } 48699a2dd95SBruce Richardson 48799a2dd95SBruce Richardson /* 48899a2dd95SBruce Richardson * Function that expands a rule across the data structure when a less-generic 48999a2dd95SBruce Richardson * one has been added before. It assures that every possible combination of bits 49099a2dd95SBruce Richardson * in the IP address returns a match. 49199a2dd95SBruce Richardson */ 49299a2dd95SBruce Richardson static void 49399a2dd95SBruce Richardson expand_rule(struct rte_lpm6 *lpm, uint32_t tbl8_gindex, uint8_t old_depth, 49499a2dd95SBruce Richardson uint8_t new_depth, uint32_t next_hop, uint8_t valid) 49599a2dd95SBruce Richardson { 49699a2dd95SBruce Richardson uint32_t tbl8_group_end, tbl8_gindex_next, j; 49799a2dd95SBruce Richardson 49899a2dd95SBruce Richardson tbl8_group_end = tbl8_gindex + RTE_LPM6_TBL8_GROUP_NUM_ENTRIES; 49999a2dd95SBruce Richardson 50099a2dd95SBruce Richardson struct rte_lpm6_tbl_entry new_tbl8_entry = { 50199a2dd95SBruce Richardson .valid = valid, 50299a2dd95SBruce Richardson .valid_group = valid, 50399a2dd95SBruce Richardson .depth = new_depth, 50499a2dd95SBruce Richardson .next_hop = next_hop, 50599a2dd95SBruce Richardson .ext_entry = 0, 50699a2dd95SBruce Richardson }; 50799a2dd95SBruce Richardson 50899a2dd95SBruce Richardson for (j = tbl8_gindex; j < tbl8_group_end; j++) { 50999a2dd95SBruce Richardson if (!lpm->tbl8[j].valid || (lpm->tbl8[j].ext_entry == 0 51099a2dd95SBruce Richardson && lpm->tbl8[j].depth <= old_depth)) { 51199a2dd95SBruce Richardson 51299a2dd95SBruce Richardson lpm->tbl8[j] = new_tbl8_entry; 51399a2dd95SBruce Richardson 51499a2dd95SBruce Richardson } else if (lpm->tbl8[j].ext_entry == 1) { 51599a2dd95SBruce Richardson 51699a2dd95SBruce Richardson tbl8_gindex_next = lpm->tbl8[j].lpm6_tbl8_gindex 51799a2dd95SBruce Richardson * RTE_LPM6_TBL8_GROUP_NUM_ENTRIES; 51899a2dd95SBruce Richardson expand_rule(lpm, tbl8_gindex_next, old_depth, new_depth, 51999a2dd95SBruce Richardson next_hop, valid); 52099a2dd95SBruce Richardson } 52199a2dd95SBruce Richardson } 52299a2dd95SBruce Richardson } 52399a2dd95SBruce Richardson 52499a2dd95SBruce Richardson /* 52599a2dd95SBruce Richardson * Init a tbl8 header 52699a2dd95SBruce Richardson */ 52799a2dd95SBruce Richardson static inline void 52899a2dd95SBruce Richardson init_tbl8_header(struct rte_lpm6 *lpm, uint32_t tbl_ind, 52999a2dd95SBruce Richardson uint32_t owner_tbl_ind, uint32_t owner_entry_ind) 53099a2dd95SBruce Richardson { 53199a2dd95SBruce Richardson struct rte_lpm_tbl8_hdr *tbl_hdr = &lpm->tbl8_hdrs[tbl_ind]; 53299a2dd95SBruce Richardson tbl_hdr->owner_tbl_ind = owner_tbl_ind; 53399a2dd95SBruce Richardson tbl_hdr->owner_entry_ind = owner_entry_ind; 53499a2dd95SBruce Richardson tbl_hdr->ref_cnt = 0; 53599a2dd95SBruce Richardson } 53699a2dd95SBruce Richardson 53799a2dd95SBruce Richardson /* 53899a2dd95SBruce Richardson * Calculate index to the table based on the number and position 53999a2dd95SBruce Richardson * of the bytes being inspected in this step. 54099a2dd95SBruce Richardson */ 54199a2dd95SBruce Richardson static uint32_t 542*e1a06e39SRobin Jarry get_bitshift(const struct rte_ipv6_addr *ip, uint8_t first_byte, uint8_t bytes) 54399a2dd95SBruce Richardson { 54499a2dd95SBruce Richardson uint32_t entry_ind, i; 54599a2dd95SBruce Richardson int8_t bitshift; 54699a2dd95SBruce Richardson 54799a2dd95SBruce Richardson entry_ind = 0; 54899a2dd95SBruce Richardson for (i = first_byte; i < (uint32_t)(first_byte + bytes); i++) { 54999a2dd95SBruce Richardson bitshift = (int8_t)((bytes - i)*BYTE_SIZE); 55099a2dd95SBruce Richardson 55199a2dd95SBruce Richardson if (bitshift < 0) 55299a2dd95SBruce Richardson bitshift = 0; 553*e1a06e39SRobin Jarry entry_ind = entry_ind | ip->a[i-1] << bitshift; 55499a2dd95SBruce Richardson } 55599a2dd95SBruce Richardson 55699a2dd95SBruce Richardson return entry_ind; 55799a2dd95SBruce Richardson } 55899a2dd95SBruce Richardson 55999a2dd95SBruce Richardson /* 56099a2dd95SBruce Richardson * Simulate adding a new route to the LPM counting number 56199a2dd95SBruce Richardson * of new tables that will be needed 56299a2dd95SBruce Richardson * 56399a2dd95SBruce Richardson * It returns 0 on success, or 1 if 56499a2dd95SBruce Richardson * the process needs to be continued by calling the function again. 56599a2dd95SBruce Richardson */ 56699a2dd95SBruce Richardson static inline int 56799a2dd95SBruce Richardson simulate_add_step(struct rte_lpm6 *lpm, struct rte_lpm6_tbl_entry *tbl, 568*e1a06e39SRobin Jarry struct rte_lpm6_tbl_entry **next_tbl, const struct rte_ipv6_addr *ip, 56999a2dd95SBruce Richardson uint8_t bytes, uint8_t first_byte, uint8_t depth, 57099a2dd95SBruce Richardson uint32_t *need_tbl_nb) 57199a2dd95SBruce Richardson { 57299a2dd95SBruce Richardson uint32_t entry_ind; 57399a2dd95SBruce Richardson uint8_t bits_covered; 57499a2dd95SBruce Richardson uint32_t next_tbl_ind; 57599a2dd95SBruce Richardson 57699a2dd95SBruce Richardson /* 57799a2dd95SBruce Richardson * Calculate index to the table based on the number and position 57899a2dd95SBruce Richardson * of the bytes being inspected in this step. 57999a2dd95SBruce Richardson */ 58099a2dd95SBruce Richardson entry_ind = get_bitshift(ip, first_byte, bytes); 58199a2dd95SBruce Richardson 58299a2dd95SBruce Richardson /* Number of bits covered in this step */ 58399a2dd95SBruce Richardson bits_covered = (uint8_t)((bytes+first_byte-1)*BYTE_SIZE); 58499a2dd95SBruce Richardson 58599a2dd95SBruce Richardson if (depth <= bits_covered) { 58699a2dd95SBruce Richardson *need_tbl_nb = 0; 58799a2dd95SBruce Richardson return 0; 58899a2dd95SBruce Richardson } 58999a2dd95SBruce Richardson 59099a2dd95SBruce Richardson if (tbl[entry_ind].valid == 0 || tbl[entry_ind].ext_entry == 0) { 59199a2dd95SBruce Richardson /* from this point on a new table is needed on each level 59299a2dd95SBruce Richardson * that is not covered yet 59399a2dd95SBruce Richardson */ 59499a2dd95SBruce Richardson depth -= bits_covered; 59599a2dd95SBruce Richardson uint32_t cnt = depth >> 3; /* depth / BYTE_SIZE */ 59699a2dd95SBruce Richardson if (depth & 7) /* 0b00000111 */ 59799a2dd95SBruce Richardson /* if depth % 8 > 0 then one more table is needed 59899a2dd95SBruce Richardson * for those last bits 59999a2dd95SBruce Richardson */ 60099a2dd95SBruce Richardson cnt++; 60199a2dd95SBruce Richardson 60299a2dd95SBruce Richardson *need_tbl_nb = cnt; 60399a2dd95SBruce Richardson return 0; 60499a2dd95SBruce Richardson } 60599a2dd95SBruce Richardson 60699a2dd95SBruce Richardson next_tbl_ind = tbl[entry_ind].lpm6_tbl8_gindex; 60799a2dd95SBruce Richardson *next_tbl = &(lpm->tbl8[next_tbl_ind * 60899a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES]); 60999a2dd95SBruce Richardson *need_tbl_nb = 0; 61099a2dd95SBruce Richardson return 1; 61199a2dd95SBruce Richardson } 61299a2dd95SBruce Richardson 61399a2dd95SBruce Richardson /* 61499a2dd95SBruce Richardson * Partially adds a new route to the data structure (tbl24+tbl8s). 61599a2dd95SBruce Richardson * It returns 0 on success, a negative number on failure, or 1 if 61699a2dd95SBruce Richardson * the process needs to be continued by calling the function again. 61799a2dd95SBruce Richardson */ 61899a2dd95SBruce Richardson static inline int 61999a2dd95SBruce Richardson add_step(struct rte_lpm6 *lpm, struct rte_lpm6_tbl_entry *tbl, 62099a2dd95SBruce Richardson uint32_t tbl_ind, struct rte_lpm6_tbl_entry **next_tbl, 621*e1a06e39SRobin Jarry uint32_t *next_tbl_ind, struct rte_ipv6_addr *ip, uint8_t bytes, 62299a2dd95SBruce Richardson uint8_t first_byte, uint8_t depth, uint32_t next_hop, 62399a2dd95SBruce Richardson uint8_t is_new_rule) 62499a2dd95SBruce Richardson { 62599a2dd95SBruce Richardson uint32_t entry_ind, tbl_range, tbl8_group_start, tbl8_group_end, i; 62699a2dd95SBruce Richardson uint32_t tbl8_gindex; 62799a2dd95SBruce Richardson uint8_t bits_covered; 62899a2dd95SBruce Richardson int ret; 62999a2dd95SBruce Richardson 63099a2dd95SBruce Richardson /* 63199a2dd95SBruce Richardson * Calculate index to the table based on the number and position 63299a2dd95SBruce Richardson * of the bytes being inspected in this step. 63399a2dd95SBruce Richardson */ 63499a2dd95SBruce Richardson entry_ind = get_bitshift(ip, first_byte, bytes); 63599a2dd95SBruce Richardson 63699a2dd95SBruce Richardson /* Number of bits covered in this step */ 63799a2dd95SBruce Richardson bits_covered = (uint8_t)((bytes+first_byte-1)*BYTE_SIZE); 63899a2dd95SBruce Richardson 63999a2dd95SBruce Richardson /* 64099a2dd95SBruce Richardson * If depth if smaller than this number (ie this is the last step) 64199a2dd95SBruce Richardson * expand the rule across the relevant positions in the table. 64299a2dd95SBruce Richardson */ 64399a2dd95SBruce Richardson if (depth <= bits_covered) { 64499a2dd95SBruce Richardson tbl_range = 1 << (bits_covered - depth); 64599a2dd95SBruce Richardson 64699a2dd95SBruce Richardson for (i = entry_ind; i < (entry_ind + tbl_range); i++) { 64799a2dd95SBruce Richardson if (!tbl[i].valid || (tbl[i].ext_entry == 0 && 64899a2dd95SBruce Richardson tbl[i].depth <= depth)) { 64999a2dd95SBruce Richardson 65099a2dd95SBruce Richardson struct rte_lpm6_tbl_entry new_tbl_entry = { 65199a2dd95SBruce Richardson .next_hop = next_hop, 65299a2dd95SBruce Richardson .depth = depth, 65399a2dd95SBruce Richardson .valid = VALID, 65499a2dd95SBruce Richardson .valid_group = VALID, 65599a2dd95SBruce Richardson .ext_entry = 0, 65699a2dd95SBruce Richardson }; 65799a2dd95SBruce Richardson 65899a2dd95SBruce Richardson tbl[i] = new_tbl_entry; 65999a2dd95SBruce Richardson 66099a2dd95SBruce Richardson } else if (tbl[i].ext_entry == 1) { 66199a2dd95SBruce Richardson 66299a2dd95SBruce Richardson /* 66399a2dd95SBruce Richardson * If tbl entry is valid and extended calculate the index 66499a2dd95SBruce Richardson * into next tbl8 and expand the rule across the data structure. 66599a2dd95SBruce Richardson */ 66699a2dd95SBruce Richardson tbl8_gindex = tbl[i].lpm6_tbl8_gindex * 66799a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES; 66899a2dd95SBruce Richardson expand_rule(lpm, tbl8_gindex, depth, depth, 66999a2dd95SBruce Richardson next_hop, VALID); 67099a2dd95SBruce Richardson } 67199a2dd95SBruce Richardson } 67299a2dd95SBruce Richardson 67399a2dd95SBruce Richardson /* update tbl8 rule reference counter */ 67499a2dd95SBruce Richardson if (tbl_ind != TBL24_IND && is_new_rule) 67599a2dd95SBruce Richardson lpm->tbl8_hdrs[tbl_ind].ref_cnt++; 67699a2dd95SBruce Richardson 67799a2dd95SBruce Richardson return 0; 67899a2dd95SBruce Richardson } 67999a2dd95SBruce Richardson /* 68099a2dd95SBruce Richardson * If this is not the last step just fill one position 68199a2dd95SBruce Richardson * and calculate the index to the next table. 68299a2dd95SBruce Richardson */ 68399a2dd95SBruce Richardson else { 68499a2dd95SBruce Richardson /* If it's invalid a new tbl8 is needed */ 68599a2dd95SBruce Richardson if (!tbl[entry_ind].valid) { 68699a2dd95SBruce Richardson /* get a new table */ 68799a2dd95SBruce Richardson ret = tbl8_get(lpm, &tbl8_gindex); 68899a2dd95SBruce Richardson if (ret != 0) 68999a2dd95SBruce Richardson return -ENOSPC; 69099a2dd95SBruce Richardson 69199a2dd95SBruce Richardson /* invalidate all new tbl8 entries */ 69299a2dd95SBruce Richardson tbl8_group_start = tbl8_gindex * 69399a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES; 69499a2dd95SBruce Richardson memset(&lpm->tbl8[tbl8_group_start], 0, 69599a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES * 69699a2dd95SBruce Richardson sizeof(struct rte_lpm6_tbl_entry)); 69799a2dd95SBruce Richardson 69899a2dd95SBruce Richardson /* init the new table's header: 69999a2dd95SBruce Richardson * save the reference to the owner table 70099a2dd95SBruce Richardson */ 70199a2dd95SBruce Richardson init_tbl8_header(lpm, tbl8_gindex, tbl_ind, entry_ind); 70299a2dd95SBruce Richardson 70399a2dd95SBruce Richardson /* reference to a new tbl8 */ 70499a2dd95SBruce Richardson struct rte_lpm6_tbl_entry new_tbl_entry = { 70599a2dd95SBruce Richardson .lpm6_tbl8_gindex = tbl8_gindex, 70699a2dd95SBruce Richardson .depth = 0, 70799a2dd95SBruce Richardson .valid = VALID, 70899a2dd95SBruce Richardson .valid_group = VALID, 70999a2dd95SBruce Richardson .ext_entry = 1, 71099a2dd95SBruce Richardson }; 71199a2dd95SBruce Richardson 71299a2dd95SBruce Richardson tbl[entry_ind] = new_tbl_entry; 71399a2dd95SBruce Richardson 71499a2dd95SBruce Richardson /* update the current table's reference counter */ 71599a2dd95SBruce Richardson if (tbl_ind != TBL24_IND) 71699a2dd95SBruce Richardson lpm->tbl8_hdrs[tbl_ind].ref_cnt++; 71799a2dd95SBruce Richardson } 71899a2dd95SBruce Richardson /* 71999a2dd95SBruce Richardson * If it's valid but not extended the rule that was stored 72099a2dd95SBruce Richardson * here needs to be moved to the next table. 72199a2dd95SBruce Richardson */ 72299a2dd95SBruce Richardson else if (tbl[entry_ind].ext_entry == 0) { 72399a2dd95SBruce Richardson /* get a new tbl8 index */ 72499a2dd95SBruce Richardson ret = tbl8_get(lpm, &tbl8_gindex); 72599a2dd95SBruce Richardson if (ret != 0) 72699a2dd95SBruce Richardson return -ENOSPC; 72799a2dd95SBruce Richardson 72899a2dd95SBruce Richardson tbl8_group_start = tbl8_gindex * 72999a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES; 73099a2dd95SBruce Richardson tbl8_group_end = tbl8_group_start + 73199a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES; 73299a2dd95SBruce Richardson 73399a2dd95SBruce Richardson struct rte_lpm6_tbl_entry tbl_entry = { 73499a2dd95SBruce Richardson .next_hop = tbl[entry_ind].next_hop, 73599a2dd95SBruce Richardson .depth = tbl[entry_ind].depth, 73699a2dd95SBruce Richardson .valid = VALID, 73799a2dd95SBruce Richardson .valid_group = VALID, 73899a2dd95SBruce Richardson .ext_entry = 0 73999a2dd95SBruce Richardson }; 74099a2dd95SBruce Richardson 74199a2dd95SBruce Richardson /* Populate new tbl8 with tbl value. */ 74299a2dd95SBruce Richardson for (i = tbl8_group_start; i < tbl8_group_end; i++) 74399a2dd95SBruce Richardson lpm->tbl8[i] = tbl_entry; 74499a2dd95SBruce Richardson 74599a2dd95SBruce Richardson /* init the new table's header: 74699a2dd95SBruce Richardson * save the reference to the owner table 74799a2dd95SBruce Richardson */ 74899a2dd95SBruce Richardson init_tbl8_header(lpm, tbl8_gindex, tbl_ind, entry_ind); 74999a2dd95SBruce Richardson 75099a2dd95SBruce Richardson /* 75199a2dd95SBruce Richardson * Update tbl entry to point to new tbl8 entry. Note: The 75299a2dd95SBruce Richardson * ext_flag and tbl8_index need to be updated simultaneously, 75399a2dd95SBruce Richardson * so assign whole structure in one go. 75499a2dd95SBruce Richardson */ 75599a2dd95SBruce Richardson struct rte_lpm6_tbl_entry new_tbl_entry = { 75699a2dd95SBruce Richardson .lpm6_tbl8_gindex = tbl8_gindex, 75799a2dd95SBruce Richardson .depth = 0, 75899a2dd95SBruce Richardson .valid = VALID, 75999a2dd95SBruce Richardson .valid_group = VALID, 76099a2dd95SBruce Richardson .ext_entry = 1, 76199a2dd95SBruce Richardson }; 76299a2dd95SBruce Richardson 76399a2dd95SBruce Richardson tbl[entry_ind] = new_tbl_entry; 76499a2dd95SBruce Richardson 76599a2dd95SBruce Richardson /* update the current table's reference counter */ 76699a2dd95SBruce Richardson if (tbl_ind != TBL24_IND) 76799a2dd95SBruce Richardson lpm->tbl8_hdrs[tbl_ind].ref_cnt++; 76899a2dd95SBruce Richardson } 76999a2dd95SBruce Richardson 77099a2dd95SBruce Richardson *next_tbl_ind = tbl[entry_ind].lpm6_tbl8_gindex; 77199a2dd95SBruce Richardson *next_tbl = &(lpm->tbl8[*next_tbl_ind * 77299a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES]); 77399a2dd95SBruce Richardson } 77499a2dd95SBruce Richardson 77599a2dd95SBruce Richardson return 1; 77699a2dd95SBruce Richardson } 77799a2dd95SBruce Richardson 77899a2dd95SBruce Richardson /* 77999a2dd95SBruce Richardson * Simulate adding a route to LPM 78099a2dd95SBruce Richardson * 78199a2dd95SBruce Richardson * Returns: 78299a2dd95SBruce Richardson * 0 on success 78399a2dd95SBruce Richardson * -ENOSPC not enough tbl8 left 78499a2dd95SBruce Richardson */ 78599a2dd95SBruce Richardson static int 786*e1a06e39SRobin Jarry simulate_add(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *masked_ip, uint8_t depth) 78799a2dd95SBruce Richardson { 78899a2dd95SBruce Richardson struct rte_lpm6_tbl_entry *tbl; 78999a2dd95SBruce Richardson struct rte_lpm6_tbl_entry *tbl_next = NULL; 79099a2dd95SBruce Richardson int ret, i; 79199a2dd95SBruce Richardson 79299a2dd95SBruce Richardson /* number of new tables needed for a step */ 79399a2dd95SBruce Richardson uint32_t need_tbl_nb; 79499a2dd95SBruce Richardson /* total number of new tables needed */ 79599a2dd95SBruce Richardson uint32_t total_need_tbl_nb; 79699a2dd95SBruce Richardson 79799a2dd95SBruce Richardson /* Inspect the first three bytes through tbl24 on the first step. */ 79899a2dd95SBruce Richardson ret = simulate_add_step(lpm, lpm->tbl24, &tbl_next, masked_ip, 79999a2dd95SBruce Richardson ADD_FIRST_BYTE, 1, depth, &need_tbl_nb); 80099a2dd95SBruce Richardson total_need_tbl_nb = need_tbl_nb; 80199a2dd95SBruce Richardson /* 80299a2dd95SBruce Richardson * Inspect one by one the rest of the bytes until 80399a2dd95SBruce Richardson * the process is completed. 80499a2dd95SBruce Richardson */ 805*e1a06e39SRobin Jarry for (i = ADD_FIRST_BYTE; i < RTE_IPV6_ADDR_SIZE && ret == 1; i++) { 80699a2dd95SBruce Richardson tbl = tbl_next; 80799a2dd95SBruce Richardson ret = simulate_add_step(lpm, tbl, &tbl_next, masked_ip, 1, 80899a2dd95SBruce Richardson (uint8_t)(i + 1), depth, &need_tbl_nb); 80999a2dd95SBruce Richardson total_need_tbl_nb += need_tbl_nb; 81099a2dd95SBruce Richardson } 81199a2dd95SBruce Richardson 81299a2dd95SBruce Richardson if (tbl8_available(lpm) < total_need_tbl_nb) 81399a2dd95SBruce Richardson /* not enough tbl8 to add a rule */ 81499a2dd95SBruce Richardson return -ENOSPC; 81599a2dd95SBruce Richardson 81699a2dd95SBruce Richardson return 0; 81799a2dd95SBruce Richardson } 81899a2dd95SBruce Richardson 81999a2dd95SBruce Richardson /* 82099a2dd95SBruce Richardson * Add a route 82199a2dd95SBruce Richardson */ 82299a2dd95SBruce Richardson int 823*e1a06e39SRobin Jarry rte_lpm6_add(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip, uint8_t depth, 82499a2dd95SBruce Richardson uint32_t next_hop) 82599a2dd95SBruce Richardson { 82699a2dd95SBruce Richardson struct rte_lpm6_tbl_entry *tbl; 82799a2dd95SBruce Richardson struct rte_lpm6_tbl_entry *tbl_next = NULL; 82899a2dd95SBruce Richardson /* init to avoid compiler warning */ 82999a2dd95SBruce Richardson uint32_t tbl_next_num = 123456; 83099a2dd95SBruce Richardson int status; 831*e1a06e39SRobin Jarry struct rte_ipv6_addr masked_ip; 83299a2dd95SBruce Richardson int i; 83399a2dd95SBruce Richardson 83499a2dd95SBruce Richardson /* Check user arguments. */ 835*e1a06e39SRobin Jarry if ((lpm == NULL) || (depth < 1) || (depth > RTE_IPV6_MAX_DEPTH)) 83699a2dd95SBruce Richardson return -EINVAL; 83799a2dd95SBruce Richardson 83899a2dd95SBruce Richardson /* Copy the IP and mask it to avoid modifying user's input data. */ 839*e1a06e39SRobin Jarry masked_ip = *ip; 840*e1a06e39SRobin Jarry rte_ipv6_addr_mask(&masked_ip, depth); 84199a2dd95SBruce Richardson 84299a2dd95SBruce Richardson /* Simulate adding a new route */ 843*e1a06e39SRobin Jarry int ret = simulate_add(lpm, &masked_ip, depth); 84499a2dd95SBruce Richardson if (ret < 0) 84599a2dd95SBruce Richardson return ret; 84699a2dd95SBruce Richardson 84799a2dd95SBruce Richardson /* Add the rule to the rule table. */ 848*e1a06e39SRobin Jarry int is_new_rule = rule_add(lpm, &masked_ip, depth, next_hop); 84999a2dd95SBruce Richardson /* If there is no space available for new rule return error. */ 85099a2dd95SBruce Richardson if (is_new_rule < 0) 85199a2dd95SBruce Richardson return is_new_rule; 85299a2dd95SBruce Richardson 85399a2dd95SBruce Richardson /* Inspect the first three bytes through tbl24 on the first step. */ 85499a2dd95SBruce Richardson tbl = lpm->tbl24; 85599a2dd95SBruce Richardson status = add_step(lpm, tbl, TBL24_IND, &tbl_next, &tbl_next_num, 856*e1a06e39SRobin Jarry &masked_ip, ADD_FIRST_BYTE, 1, depth, next_hop, 85799a2dd95SBruce Richardson is_new_rule); 85899a2dd95SBruce Richardson assert(status >= 0); 85999a2dd95SBruce Richardson 86099a2dd95SBruce Richardson /* 86199a2dd95SBruce Richardson * Inspect one by one the rest of the bytes until 86299a2dd95SBruce Richardson * the process is completed. 86399a2dd95SBruce Richardson */ 864*e1a06e39SRobin Jarry for (i = ADD_FIRST_BYTE; i < RTE_IPV6_ADDR_SIZE && status == 1; i++) { 86599a2dd95SBruce Richardson tbl = tbl_next; 86699a2dd95SBruce Richardson status = add_step(lpm, tbl, tbl_next_num, &tbl_next, 867*e1a06e39SRobin Jarry &tbl_next_num, &masked_ip, 1, (uint8_t)(i + 1), 86899a2dd95SBruce Richardson depth, next_hop, is_new_rule); 86999a2dd95SBruce Richardson assert(status >= 0); 87099a2dd95SBruce Richardson } 87199a2dd95SBruce Richardson 87299a2dd95SBruce Richardson return status; 87399a2dd95SBruce Richardson } 87499a2dd95SBruce Richardson 87599a2dd95SBruce Richardson /* 87699a2dd95SBruce Richardson * Takes a pointer to a table entry and inspect one level. 87799a2dd95SBruce Richardson * The function returns 0 on lookup success, ENOENT if no match was found 87899a2dd95SBruce Richardson * or 1 if the process needs to be continued by calling the function again. 87999a2dd95SBruce Richardson */ 88099a2dd95SBruce Richardson static inline int 88199a2dd95SBruce Richardson lookup_step(const struct rte_lpm6 *lpm, const struct rte_lpm6_tbl_entry *tbl, 882*e1a06e39SRobin Jarry const struct rte_lpm6_tbl_entry **tbl_next, const struct rte_ipv6_addr *ip, 88399a2dd95SBruce Richardson uint8_t first_byte, uint32_t *next_hop) 88499a2dd95SBruce Richardson { 88599a2dd95SBruce Richardson uint32_t tbl8_index, tbl_entry; 88699a2dd95SBruce Richardson 88799a2dd95SBruce Richardson /* Take the integer value from the pointer. */ 88899a2dd95SBruce Richardson tbl_entry = *(const uint32_t *)tbl; 88999a2dd95SBruce Richardson 89099a2dd95SBruce Richardson /* If it is valid and extended we calculate the new pointer to return. */ 89199a2dd95SBruce Richardson if ((tbl_entry & RTE_LPM6_VALID_EXT_ENTRY_BITMASK) == 89299a2dd95SBruce Richardson RTE_LPM6_VALID_EXT_ENTRY_BITMASK) { 89399a2dd95SBruce Richardson 894*e1a06e39SRobin Jarry tbl8_index = ip->a[first_byte-1] + 89599a2dd95SBruce Richardson ((tbl_entry & RTE_LPM6_TBL8_BITMASK) * 89699a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES); 89799a2dd95SBruce Richardson 89899a2dd95SBruce Richardson *tbl_next = &lpm->tbl8[tbl8_index]; 89999a2dd95SBruce Richardson 90099a2dd95SBruce Richardson return 1; 90199a2dd95SBruce Richardson } else { 90299a2dd95SBruce Richardson /* If not extended then we can have a match. */ 90399a2dd95SBruce Richardson *next_hop = ((uint32_t)tbl_entry & RTE_LPM6_TBL8_BITMASK); 90499a2dd95SBruce Richardson return (tbl_entry & RTE_LPM6_LOOKUP_SUCCESS) ? 0 : -ENOENT; 90599a2dd95SBruce Richardson } 90699a2dd95SBruce Richardson } 90799a2dd95SBruce Richardson 90899a2dd95SBruce Richardson /* 90999a2dd95SBruce Richardson * Looks up an IP 91099a2dd95SBruce Richardson */ 91199a2dd95SBruce Richardson int 912*e1a06e39SRobin Jarry rte_lpm6_lookup(const struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip, 91399a2dd95SBruce Richardson uint32_t *next_hop) 91499a2dd95SBruce Richardson { 91599a2dd95SBruce Richardson const struct rte_lpm6_tbl_entry *tbl; 91699a2dd95SBruce Richardson const struct rte_lpm6_tbl_entry *tbl_next = NULL; 91799a2dd95SBruce Richardson int status; 91899a2dd95SBruce Richardson uint8_t first_byte; 91999a2dd95SBruce Richardson uint32_t tbl24_index; 92099a2dd95SBruce Richardson 92199a2dd95SBruce Richardson /* DEBUG: Check user input arguments. */ 92299a2dd95SBruce Richardson if ((lpm == NULL) || (ip == NULL) || (next_hop == NULL)) 92399a2dd95SBruce Richardson return -EINVAL; 92499a2dd95SBruce Richardson 92599a2dd95SBruce Richardson first_byte = LOOKUP_FIRST_BYTE; 926*e1a06e39SRobin Jarry tbl24_index = (ip->a[0] << BYTES2_SIZE) | (ip->a[1] << BYTE_SIZE) | ip->a[2]; 92799a2dd95SBruce Richardson 92899a2dd95SBruce Richardson /* Calculate pointer to the first entry to be inspected */ 92999a2dd95SBruce Richardson tbl = &lpm->tbl24[tbl24_index]; 93099a2dd95SBruce Richardson 93199a2dd95SBruce Richardson do { 93299a2dd95SBruce Richardson /* Continue inspecting following levels until success or failure */ 93399a2dd95SBruce Richardson status = lookup_step(lpm, tbl, &tbl_next, ip, first_byte++, next_hop); 93499a2dd95SBruce Richardson tbl = tbl_next; 93599a2dd95SBruce Richardson } while (status == 1); 93699a2dd95SBruce Richardson 93799a2dd95SBruce Richardson return status; 93899a2dd95SBruce Richardson } 93999a2dd95SBruce Richardson 94099a2dd95SBruce Richardson /* 94199a2dd95SBruce Richardson * Looks up a group of IP addresses 94299a2dd95SBruce Richardson */ 94399a2dd95SBruce Richardson int 94499a2dd95SBruce Richardson rte_lpm6_lookup_bulk_func(const struct rte_lpm6 *lpm, 945*e1a06e39SRobin Jarry struct rte_ipv6_addr *ips, 94699a2dd95SBruce Richardson int32_t *next_hops, unsigned int n) 94799a2dd95SBruce Richardson { 94899a2dd95SBruce Richardson unsigned int i; 94999a2dd95SBruce Richardson const struct rte_lpm6_tbl_entry *tbl; 95099a2dd95SBruce Richardson const struct rte_lpm6_tbl_entry *tbl_next = NULL; 95199a2dd95SBruce Richardson uint32_t tbl24_index, next_hop; 95299a2dd95SBruce Richardson uint8_t first_byte; 95399a2dd95SBruce Richardson int status; 95499a2dd95SBruce Richardson 95599a2dd95SBruce Richardson /* DEBUG: Check user input arguments. */ 95699a2dd95SBruce Richardson if ((lpm == NULL) || (ips == NULL) || (next_hops == NULL)) 95799a2dd95SBruce Richardson return -EINVAL; 95899a2dd95SBruce Richardson 95999a2dd95SBruce Richardson for (i = 0; i < n; i++) { 96099a2dd95SBruce Richardson first_byte = LOOKUP_FIRST_BYTE; 961*e1a06e39SRobin Jarry tbl24_index = (ips[i].a[0] << BYTES2_SIZE) | 962*e1a06e39SRobin Jarry (ips[i].a[1] << BYTE_SIZE) | ips[i].a[2]; 96399a2dd95SBruce Richardson 96499a2dd95SBruce Richardson /* Calculate pointer to the first entry to be inspected */ 96599a2dd95SBruce Richardson tbl = &lpm->tbl24[tbl24_index]; 96699a2dd95SBruce Richardson 96799a2dd95SBruce Richardson do { 96899a2dd95SBruce Richardson /* Continue inspecting following levels 96999a2dd95SBruce Richardson * until success or failure 97099a2dd95SBruce Richardson */ 971*e1a06e39SRobin Jarry status = lookup_step(lpm, tbl, &tbl_next, &ips[i], 97299a2dd95SBruce Richardson first_byte++, &next_hop); 97399a2dd95SBruce Richardson tbl = tbl_next; 97499a2dd95SBruce Richardson } while (status == 1); 97599a2dd95SBruce Richardson 97699a2dd95SBruce Richardson if (status < 0) 97799a2dd95SBruce Richardson next_hops[i] = -1; 97899a2dd95SBruce Richardson else 97999a2dd95SBruce Richardson next_hops[i] = (int32_t)next_hop; 98099a2dd95SBruce Richardson } 98199a2dd95SBruce Richardson 98299a2dd95SBruce Richardson return 0; 98399a2dd95SBruce Richardson } 98499a2dd95SBruce Richardson 98599a2dd95SBruce Richardson /* 98699a2dd95SBruce Richardson * Look for a rule in the high-level rules table 98799a2dd95SBruce Richardson */ 98899a2dd95SBruce Richardson int 989*e1a06e39SRobin Jarry rte_lpm6_is_rule_present(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip, uint8_t depth, 99099a2dd95SBruce Richardson uint32_t *next_hop) 99199a2dd95SBruce Richardson { 992*e1a06e39SRobin Jarry struct rte_ipv6_addr masked_ip; 99399a2dd95SBruce Richardson 99499a2dd95SBruce Richardson /* Check user arguments. */ 99599a2dd95SBruce Richardson if ((lpm == NULL) || next_hop == NULL || ip == NULL || 996*e1a06e39SRobin Jarry (depth < 1) || (depth > RTE_IPV6_MAX_DEPTH)) 99799a2dd95SBruce Richardson return -EINVAL; 99899a2dd95SBruce Richardson 99999a2dd95SBruce Richardson /* Copy the IP and mask it to avoid modifying user's input data. */ 1000*e1a06e39SRobin Jarry masked_ip = *ip; 1001*e1a06e39SRobin Jarry rte_ipv6_addr_mask(&masked_ip, depth); 100299a2dd95SBruce Richardson 1003*e1a06e39SRobin Jarry return rule_find(lpm, &masked_ip, depth, next_hop); 100499a2dd95SBruce Richardson } 100599a2dd95SBruce Richardson 100699a2dd95SBruce Richardson /* 100799a2dd95SBruce Richardson * Delete a rule from the rule table. 100899a2dd95SBruce Richardson * NOTE: Valid range for depth parameter is 1 .. 128 inclusive. 100999a2dd95SBruce Richardson * return 101099a2dd95SBruce Richardson * 0 on success 101199a2dd95SBruce Richardson * <0 on failure 101299a2dd95SBruce Richardson */ 101399a2dd95SBruce Richardson static inline int 1014*e1a06e39SRobin Jarry rule_delete(struct rte_lpm6 *lpm, struct rte_ipv6_addr *ip, uint8_t depth) 101599a2dd95SBruce Richardson { 101699a2dd95SBruce Richardson int ret; 101799a2dd95SBruce Richardson struct rte_lpm6_rule_key rule_key; 101899a2dd95SBruce Richardson 101999a2dd95SBruce Richardson /* init rule key */ 102099a2dd95SBruce Richardson rule_key_init(&rule_key, ip, depth); 102199a2dd95SBruce Richardson 102299a2dd95SBruce Richardson /* delete the rule */ 102399a2dd95SBruce Richardson ret = rte_hash_del_key(lpm->rules_tbl, (void *) &rule_key); 102499a2dd95SBruce Richardson if (ret >= 0) 102599a2dd95SBruce Richardson lpm->used_rules--; 102699a2dd95SBruce Richardson 102799a2dd95SBruce Richardson return ret; 102899a2dd95SBruce Richardson } 102999a2dd95SBruce Richardson 103099a2dd95SBruce Richardson /* 103199a2dd95SBruce Richardson * Deletes a group of rules 103299a2dd95SBruce Richardson * 103399a2dd95SBruce Richardson * Note that the function rebuilds the lpm table, 103499a2dd95SBruce Richardson * rather than doing incremental updates like 103599a2dd95SBruce Richardson * the regular delete function 103699a2dd95SBruce Richardson */ 103799a2dd95SBruce Richardson int 103899a2dd95SBruce Richardson rte_lpm6_delete_bulk_func(struct rte_lpm6 *lpm, 1039*e1a06e39SRobin Jarry struct rte_ipv6_addr *ips, uint8_t *depths, 104099a2dd95SBruce Richardson unsigned n) 104199a2dd95SBruce Richardson { 1042*e1a06e39SRobin Jarry struct rte_ipv6_addr masked_ip; 104399a2dd95SBruce Richardson unsigned i; 104499a2dd95SBruce Richardson 104599a2dd95SBruce Richardson /* Check input arguments. */ 104699a2dd95SBruce Richardson if ((lpm == NULL) || (ips == NULL) || (depths == NULL)) 104799a2dd95SBruce Richardson return -EINVAL; 104899a2dd95SBruce Richardson 104999a2dd95SBruce Richardson for (i = 0; i < n; i++) { 1050*e1a06e39SRobin Jarry masked_ip = ips[i]; 1051*e1a06e39SRobin Jarry rte_ipv6_addr_mask(&masked_ip, depths[i]); 1052*e1a06e39SRobin Jarry rule_delete(lpm, &masked_ip, depths[i]); 105399a2dd95SBruce Richardson } 105499a2dd95SBruce Richardson 105599a2dd95SBruce Richardson /* 105699a2dd95SBruce Richardson * Set all the table entries to 0 (ie delete every rule 105799a2dd95SBruce Richardson * from the data structure. 105899a2dd95SBruce Richardson */ 105999a2dd95SBruce Richardson memset(lpm->tbl24, 0, sizeof(lpm->tbl24)); 106099a2dd95SBruce Richardson memset(lpm->tbl8, 0, sizeof(lpm->tbl8[0]) 106199a2dd95SBruce Richardson * RTE_LPM6_TBL8_GROUP_NUM_ENTRIES * lpm->number_tbl8s); 106299a2dd95SBruce Richardson tbl8_pool_init(lpm); 106399a2dd95SBruce Richardson 106499a2dd95SBruce Richardson /* 106599a2dd95SBruce Richardson * Add every rule again (except for the ones that were removed from 106699a2dd95SBruce Richardson * the rules table). 106799a2dd95SBruce Richardson */ 106899a2dd95SBruce Richardson rebuild_lpm(lpm); 106999a2dd95SBruce Richardson 107099a2dd95SBruce Richardson return 0; 107199a2dd95SBruce Richardson } 107299a2dd95SBruce Richardson 107399a2dd95SBruce Richardson /* 107499a2dd95SBruce Richardson * Delete all rules from the LPM table. 107599a2dd95SBruce Richardson */ 107699a2dd95SBruce Richardson void 107799a2dd95SBruce Richardson rte_lpm6_delete_all(struct rte_lpm6 *lpm) 107899a2dd95SBruce Richardson { 107999a2dd95SBruce Richardson /* Zero used rules counter. */ 108099a2dd95SBruce Richardson lpm->used_rules = 0; 108199a2dd95SBruce Richardson 108299a2dd95SBruce Richardson /* Zero tbl24. */ 108399a2dd95SBruce Richardson memset(lpm->tbl24, 0, sizeof(lpm->tbl24)); 108499a2dd95SBruce Richardson 108599a2dd95SBruce Richardson /* Zero tbl8. */ 108699a2dd95SBruce Richardson memset(lpm->tbl8, 0, sizeof(lpm->tbl8[0]) * 108799a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES * lpm->number_tbl8s); 108899a2dd95SBruce Richardson 108999a2dd95SBruce Richardson /* init pool of free tbl8 indexes */ 109099a2dd95SBruce Richardson tbl8_pool_init(lpm); 109199a2dd95SBruce Richardson 109299a2dd95SBruce Richardson /* Delete all rules form the rules table. */ 109399a2dd95SBruce Richardson rte_hash_reset(lpm->rules_tbl); 109499a2dd95SBruce Richardson } 109599a2dd95SBruce Richardson 109699a2dd95SBruce Richardson /* 109799a2dd95SBruce Richardson * Convert a depth to a one byte long mask 109899a2dd95SBruce Richardson * Example: 4 will be converted to 0xF0 109999a2dd95SBruce Richardson */ 1100ff933786STyler Retzlaff static uint8_t __rte_pure 110199a2dd95SBruce Richardson depth_to_mask_1b(uint8_t depth) 110299a2dd95SBruce Richardson { 110399a2dd95SBruce Richardson /* To calculate a mask start with a 1 on the left hand side and right 110499a2dd95SBruce Richardson * shift while populating the left hand side with 1's 110599a2dd95SBruce Richardson */ 110699a2dd95SBruce Richardson return (signed char)0x80 >> (depth - 1); 110799a2dd95SBruce Richardson } 110899a2dd95SBruce Richardson 110999a2dd95SBruce Richardson /* 111099a2dd95SBruce Richardson * Find a less specific rule 111199a2dd95SBruce Richardson */ 111299a2dd95SBruce Richardson static int 1113*e1a06e39SRobin Jarry rule_find_less_specific(struct rte_lpm6 *lpm, struct rte_ipv6_addr *ip, uint8_t depth, 111499a2dd95SBruce Richardson struct rte_lpm6_rule *rule) 111599a2dd95SBruce Richardson { 111699a2dd95SBruce Richardson int ret; 111799a2dd95SBruce Richardson uint32_t next_hop; 111899a2dd95SBruce Richardson uint8_t mask; 111999a2dd95SBruce Richardson struct rte_lpm6_rule_key rule_key; 112099a2dd95SBruce Richardson 112199a2dd95SBruce Richardson if (depth == 1) 112299a2dd95SBruce Richardson return 0; 112399a2dd95SBruce Richardson 112499a2dd95SBruce Richardson rule_key_init(&rule_key, ip, depth); 112599a2dd95SBruce Richardson 112699a2dd95SBruce Richardson while (depth > 1) { 112799a2dd95SBruce Richardson depth--; 112899a2dd95SBruce Richardson 112999a2dd95SBruce Richardson /* each iteration zero one more bit of the key */ 113099a2dd95SBruce Richardson mask = depth & 7; /* depth % BYTE_SIZE */ 113199a2dd95SBruce Richardson if (mask > 0) 113299a2dd95SBruce Richardson mask = depth_to_mask_1b(mask); 113399a2dd95SBruce Richardson 113499a2dd95SBruce Richardson rule_key.depth = depth; 1135*e1a06e39SRobin Jarry rule_key.ip.a[depth >> 3] &= mask; 113699a2dd95SBruce Richardson 113799a2dd95SBruce Richardson ret = rule_find_with_key(lpm, &rule_key, &next_hop); 113899a2dd95SBruce Richardson if (ret) { 113999a2dd95SBruce Richardson rule->depth = depth; 1140*e1a06e39SRobin Jarry rule->ip = rule_key.ip; 114199a2dd95SBruce Richardson rule->next_hop = next_hop; 114299a2dd95SBruce Richardson return 1; 114399a2dd95SBruce Richardson } 114499a2dd95SBruce Richardson } 114599a2dd95SBruce Richardson 114699a2dd95SBruce Richardson return 0; 114799a2dd95SBruce Richardson } 114899a2dd95SBruce Richardson 114999a2dd95SBruce Richardson /* 115099a2dd95SBruce Richardson * Find range of tbl8 cells occupied by a rule 115199a2dd95SBruce Richardson */ 115299a2dd95SBruce Richardson static void 1153*e1a06e39SRobin Jarry rule_find_range(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip, uint8_t depth, 115499a2dd95SBruce Richardson struct rte_lpm6_tbl_entry **from, 115599a2dd95SBruce Richardson struct rte_lpm6_tbl_entry **to, 115699a2dd95SBruce Richardson uint32_t *out_tbl_ind) 115799a2dd95SBruce Richardson { 115899a2dd95SBruce Richardson uint32_t ind; 1159*e1a06e39SRobin Jarry uint32_t first_3bytes = (uint32_t)ip->a[0] << 16 | 1160*e1a06e39SRobin Jarry ip->a[1] << 8 | ip->a[2]; 116199a2dd95SBruce Richardson 116299a2dd95SBruce Richardson if (depth <= 24) { 116399a2dd95SBruce Richardson /* rule is within the top level */ 116499a2dd95SBruce Richardson ind = first_3bytes; 116599a2dd95SBruce Richardson *from = &lpm->tbl24[ind]; 116699a2dd95SBruce Richardson ind += (1 << (24 - depth)) - 1; 116799a2dd95SBruce Richardson *to = &lpm->tbl24[ind]; 116899a2dd95SBruce Richardson *out_tbl_ind = TBL24_IND; 116999a2dd95SBruce Richardson } else { 117099a2dd95SBruce Richardson /* top level entry */ 117199a2dd95SBruce Richardson struct rte_lpm6_tbl_entry *tbl = &lpm->tbl24[first_3bytes]; 117299a2dd95SBruce Richardson assert(tbl->ext_entry == 1); 117399a2dd95SBruce Richardson /* first tbl8 */ 117499a2dd95SBruce Richardson uint32_t tbl_ind = tbl->lpm6_tbl8_gindex; 117599a2dd95SBruce Richardson tbl = &lpm->tbl8[tbl_ind * 117699a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES]; 117799a2dd95SBruce Richardson /* current ip byte, the top level is already behind */ 117899a2dd95SBruce Richardson uint8_t byte = 3; 117999a2dd95SBruce Richardson /* minus top level */ 118099a2dd95SBruce Richardson depth -= 24; 118199a2dd95SBruce Richardson 118299a2dd95SBruce Richardson /* iterate through levels (tbl8s) 118399a2dd95SBruce Richardson * until we reach the last one 118499a2dd95SBruce Richardson */ 118599a2dd95SBruce Richardson while (depth > 8) { 1186*e1a06e39SRobin Jarry tbl += ip->a[byte]; 118799a2dd95SBruce Richardson assert(tbl->ext_entry == 1); 118899a2dd95SBruce Richardson /* go to the next level/tbl8 */ 118999a2dd95SBruce Richardson tbl_ind = tbl->lpm6_tbl8_gindex; 119099a2dd95SBruce Richardson tbl = &lpm->tbl8[tbl_ind * 119199a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES]; 119299a2dd95SBruce Richardson byte += 1; 119399a2dd95SBruce Richardson depth -= 8; 119499a2dd95SBruce Richardson } 119599a2dd95SBruce Richardson 119699a2dd95SBruce Richardson /* last level/tbl8 */ 1197*e1a06e39SRobin Jarry ind = ip->a[byte] & depth_to_mask_1b(depth); 119899a2dd95SBruce Richardson *from = &tbl[ind]; 119999a2dd95SBruce Richardson ind += (1 << (8 - depth)) - 1; 120099a2dd95SBruce Richardson *to = &tbl[ind]; 120199a2dd95SBruce Richardson *out_tbl_ind = tbl_ind; 120299a2dd95SBruce Richardson } 120399a2dd95SBruce Richardson } 120499a2dd95SBruce Richardson 120599a2dd95SBruce Richardson /* 120699a2dd95SBruce Richardson * Remove a table from the LPM tree 120799a2dd95SBruce Richardson */ 120899a2dd95SBruce Richardson static void 120999a2dd95SBruce Richardson remove_tbl(struct rte_lpm6 *lpm, struct rte_lpm_tbl8_hdr *tbl_hdr, 121099a2dd95SBruce Richardson uint32_t tbl_ind, struct rte_lpm6_rule *lsp_rule) 121199a2dd95SBruce Richardson { 121299a2dd95SBruce Richardson struct rte_lpm6_tbl_entry *owner_entry; 121399a2dd95SBruce Richardson 121499a2dd95SBruce Richardson if (tbl_hdr->owner_tbl_ind == TBL24_IND) 121599a2dd95SBruce Richardson owner_entry = &lpm->tbl24[tbl_hdr->owner_entry_ind]; 121699a2dd95SBruce Richardson else { 121799a2dd95SBruce Richardson uint32_t owner_tbl_ind = tbl_hdr->owner_tbl_ind; 121899a2dd95SBruce Richardson owner_entry = &lpm->tbl8[ 121999a2dd95SBruce Richardson owner_tbl_ind * RTE_LPM6_TBL8_GROUP_NUM_ENTRIES + 122099a2dd95SBruce Richardson tbl_hdr->owner_entry_ind]; 122199a2dd95SBruce Richardson 122299a2dd95SBruce Richardson struct rte_lpm_tbl8_hdr *owner_tbl_hdr = 122399a2dd95SBruce Richardson &lpm->tbl8_hdrs[owner_tbl_ind]; 122499a2dd95SBruce Richardson if (--owner_tbl_hdr->ref_cnt == 0) 122599a2dd95SBruce Richardson remove_tbl(lpm, owner_tbl_hdr, owner_tbl_ind, lsp_rule); 122699a2dd95SBruce Richardson } 122799a2dd95SBruce Richardson 122899a2dd95SBruce Richardson assert(owner_entry->ext_entry == 1); 122999a2dd95SBruce Richardson 123099a2dd95SBruce Richardson /* unlink the table */ 123199a2dd95SBruce Richardson if (lsp_rule != NULL) { 123299a2dd95SBruce Richardson struct rte_lpm6_tbl_entry new_tbl_entry = { 123399a2dd95SBruce Richardson .next_hop = lsp_rule->next_hop, 123499a2dd95SBruce Richardson .depth = lsp_rule->depth, 123599a2dd95SBruce Richardson .valid = VALID, 123699a2dd95SBruce Richardson .valid_group = VALID, 123799a2dd95SBruce Richardson .ext_entry = 0 123899a2dd95SBruce Richardson }; 123999a2dd95SBruce Richardson 124099a2dd95SBruce Richardson *owner_entry = new_tbl_entry; 124199a2dd95SBruce Richardson } else { 124299a2dd95SBruce Richardson struct rte_lpm6_tbl_entry new_tbl_entry = { 124399a2dd95SBruce Richardson .next_hop = 0, 124499a2dd95SBruce Richardson .depth = 0, 124599a2dd95SBruce Richardson .valid = INVALID, 124699a2dd95SBruce Richardson .valid_group = INVALID, 124799a2dd95SBruce Richardson .ext_entry = 0 124899a2dd95SBruce Richardson }; 124999a2dd95SBruce Richardson 125099a2dd95SBruce Richardson *owner_entry = new_tbl_entry; 125199a2dd95SBruce Richardson } 125299a2dd95SBruce Richardson 125399a2dd95SBruce Richardson /* return the table to the pool */ 125499a2dd95SBruce Richardson tbl8_put(lpm, tbl_ind); 125599a2dd95SBruce Richardson } 125699a2dd95SBruce Richardson 125799a2dd95SBruce Richardson /* 125899a2dd95SBruce Richardson * Deletes a rule 125999a2dd95SBruce Richardson */ 126099a2dd95SBruce Richardson int 1261*e1a06e39SRobin Jarry rte_lpm6_delete(struct rte_lpm6 *lpm, const struct rte_ipv6_addr *ip, uint8_t depth) 126299a2dd95SBruce Richardson { 1263*e1a06e39SRobin Jarry struct rte_ipv6_addr masked_ip; 126499a2dd95SBruce Richardson struct rte_lpm6_rule lsp_rule_obj; 126599a2dd95SBruce Richardson struct rte_lpm6_rule *lsp_rule; 126699a2dd95SBruce Richardson int ret; 126799a2dd95SBruce Richardson uint32_t tbl_ind; 126899a2dd95SBruce Richardson struct rte_lpm6_tbl_entry *from, *to; 126999a2dd95SBruce Richardson 127099a2dd95SBruce Richardson /* Check input arguments. */ 1271*e1a06e39SRobin Jarry if ((lpm == NULL) || (depth < 1) || (depth > RTE_IPV6_MAX_DEPTH)) 127299a2dd95SBruce Richardson return -EINVAL; 127399a2dd95SBruce Richardson 127499a2dd95SBruce Richardson /* Copy the IP and mask it to avoid modifying user's input data. */ 1275*e1a06e39SRobin Jarry masked_ip = *ip; 1276*e1a06e39SRobin Jarry rte_ipv6_addr_mask(&masked_ip, depth); 127799a2dd95SBruce Richardson 127899a2dd95SBruce Richardson /* Delete the rule from the rule table. */ 1279*e1a06e39SRobin Jarry ret = rule_delete(lpm, &masked_ip, depth); 128099a2dd95SBruce Richardson if (ret < 0) 128199a2dd95SBruce Richardson return -ENOENT; 128299a2dd95SBruce Richardson 128399a2dd95SBruce Richardson /* find rule cells */ 1284*e1a06e39SRobin Jarry rule_find_range(lpm, &masked_ip, depth, &from, &to, &tbl_ind); 128599a2dd95SBruce Richardson 128699a2dd95SBruce Richardson /* find a less specific rule (a rule with smaller depth) 128799a2dd95SBruce Richardson * note: masked_ip will be modified, don't use it anymore 128899a2dd95SBruce Richardson */ 1289*e1a06e39SRobin Jarry ret = rule_find_less_specific(lpm, &masked_ip, depth, 129099a2dd95SBruce Richardson &lsp_rule_obj); 129199a2dd95SBruce Richardson lsp_rule = ret ? &lsp_rule_obj : NULL; 129299a2dd95SBruce Richardson 129399a2dd95SBruce Richardson /* decrement the table rule counter, 129499a2dd95SBruce Richardson * note that tbl24 doesn't have a header 129599a2dd95SBruce Richardson */ 129699a2dd95SBruce Richardson if (tbl_ind != TBL24_IND) { 129799a2dd95SBruce Richardson struct rte_lpm_tbl8_hdr *tbl_hdr = &lpm->tbl8_hdrs[tbl_ind]; 129899a2dd95SBruce Richardson if (--tbl_hdr->ref_cnt == 0) { 129999a2dd95SBruce Richardson /* remove the table */ 130099a2dd95SBruce Richardson remove_tbl(lpm, tbl_hdr, tbl_ind, lsp_rule); 130199a2dd95SBruce Richardson return 0; 130299a2dd95SBruce Richardson } 130399a2dd95SBruce Richardson } 130499a2dd95SBruce Richardson 130599a2dd95SBruce Richardson /* iterate rule cells */ 130699a2dd95SBruce Richardson for (; from <= to; from++) 130799a2dd95SBruce Richardson if (from->ext_entry == 1) { 130899a2dd95SBruce Richardson /* reference to a more specific space 130999a2dd95SBruce Richardson * of the prefix/rule. Entries in a more 131099a2dd95SBruce Richardson * specific space that are not used by 131199a2dd95SBruce Richardson * a more specific prefix must be occupied 131299a2dd95SBruce Richardson * by the prefix 131399a2dd95SBruce Richardson */ 131499a2dd95SBruce Richardson if (lsp_rule != NULL) 131599a2dd95SBruce Richardson expand_rule(lpm, 131699a2dd95SBruce Richardson from->lpm6_tbl8_gindex * 131799a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES, 131899a2dd95SBruce Richardson depth, lsp_rule->depth, 131999a2dd95SBruce Richardson lsp_rule->next_hop, VALID); 132099a2dd95SBruce Richardson else 132199a2dd95SBruce Richardson /* since the prefix has no less specific prefix, 132299a2dd95SBruce Richardson * its more specific space must be invalidated 132399a2dd95SBruce Richardson */ 132499a2dd95SBruce Richardson expand_rule(lpm, 132599a2dd95SBruce Richardson from->lpm6_tbl8_gindex * 132699a2dd95SBruce Richardson RTE_LPM6_TBL8_GROUP_NUM_ENTRIES, 132799a2dd95SBruce Richardson depth, 0, 0, INVALID); 132899a2dd95SBruce Richardson } else if (from->depth == depth) { 132999a2dd95SBruce Richardson /* entry is not a reference and belongs to the prefix */ 133099a2dd95SBruce Richardson if (lsp_rule != NULL) { 133199a2dd95SBruce Richardson struct rte_lpm6_tbl_entry new_tbl_entry = { 133299a2dd95SBruce Richardson .next_hop = lsp_rule->next_hop, 133399a2dd95SBruce Richardson .depth = lsp_rule->depth, 133499a2dd95SBruce Richardson .valid = VALID, 133599a2dd95SBruce Richardson .valid_group = VALID, 133699a2dd95SBruce Richardson .ext_entry = 0 133799a2dd95SBruce Richardson }; 133899a2dd95SBruce Richardson 133999a2dd95SBruce Richardson *from = new_tbl_entry; 134099a2dd95SBruce Richardson } else { 134199a2dd95SBruce Richardson struct rte_lpm6_tbl_entry new_tbl_entry = { 134299a2dd95SBruce Richardson .next_hop = 0, 134399a2dd95SBruce Richardson .depth = 0, 134499a2dd95SBruce Richardson .valid = INVALID, 134599a2dd95SBruce Richardson .valid_group = INVALID, 134699a2dd95SBruce Richardson .ext_entry = 0 134799a2dd95SBruce Richardson }; 134899a2dd95SBruce Richardson 134999a2dd95SBruce Richardson *from = new_tbl_entry; 135099a2dd95SBruce Richardson } 135199a2dd95SBruce Richardson } 135299a2dd95SBruce Richardson 135399a2dd95SBruce Richardson return 0; 135499a2dd95SBruce Richardson } 1355