1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #include <string.h> 6 #include <stdio.h> 7 8 #include <rte_common.h> 9 #include <rte_malloc.h> 10 #include <rte_log.h> 11 #include <rte_lpm6.h> 12 13 #include "rte_table_lpm_ipv6.h" 14 15 #include "table_log.h" 16 17 #define RTE_TABLE_LPM_MAX_NEXT_HOPS 256 18 19 #ifdef RTE_TABLE_STATS_COLLECT 20 21 #define RTE_TABLE_LPM_IPV6_STATS_PKTS_IN_ADD(table, val) \ 22 table->stats.n_pkts_in += val 23 #define RTE_TABLE_LPM_IPV6_STATS_PKTS_LOOKUP_MISS(table, val) \ 24 table->stats.n_pkts_lookup_miss += val 25 26 #else 27 28 #define RTE_TABLE_LPM_IPV6_STATS_PKTS_IN_ADD(table, val) 29 #define RTE_TABLE_LPM_IPV6_STATS_PKTS_LOOKUP_MISS(table, val) 30 31 #endif 32 33 struct rte_table_lpm_ipv6 { 34 struct rte_table_stats stats; 35 36 /* Input parameters */ 37 uint32_t entry_size; 38 uint32_t entry_unique_size; 39 uint32_t n_rules; 40 uint32_t offset; 41 42 /* Handle to low-level LPM table */ 43 struct rte_lpm6 *lpm; 44 45 /* Next Hop Table (NHT) */ 46 uint32_t nht_users[RTE_TABLE_LPM_MAX_NEXT_HOPS]; 47 uint8_t nht[0] __rte_cache_aligned; 48 }; 49 50 static void * 51 rte_table_lpm_ipv6_create(void *params, int socket_id, uint32_t entry_size) 52 { 53 struct rte_table_lpm_ipv6_params *p = 54 params; 55 struct rte_table_lpm_ipv6 *lpm; 56 struct rte_lpm6_config lpm6_config; 57 uint32_t total_size, nht_size; 58 59 /* Check input parameters */ 60 if (p == NULL) { 61 TABLE_LOG(ERR, "%s: NULL input parameters", __func__); 62 return NULL; 63 } 64 if (p->n_rules == 0) { 65 TABLE_LOG(ERR, "%s: Invalid n_rules", __func__); 66 return NULL; 67 } 68 if (p->number_tbl8s == 0) { 69 TABLE_LOG(ERR, "%s: Invalid n_rules", __func__); 70 return NULL; 71 } 72 if (p->entry_unique_size == 0) { 73 TABLE_LOG(ERR, "%s: Invalid entry_unique_size", 74 __func__); 75 return NULL; 76 } 77 if (p->entry_unique_size > entry_size) { 78 TABLE_LOG(ERR, "%s: Invalid entry_unique_size", 79 __func__); 80 return NULL; 81 } 82 if (p->name == NULL) { 83 TABLE_LOG(ERR, "%s: Table name is NULL", 84 __func__); 85 return NULL; 86 } 87 entry_size = RTE_ALIGN(entry_size, sizeof(uint64_t)); 88 89 /* Memory allocation */ 90 nht_size = RTE_TABLE_LPM_MAX_NEXT_HOPS * entry_size; 91 total_size = sizeof(struct rte_table_lpm_ipv6) + nht_size; 92 lpm = rte_zmalloc_socket("TABLE", total_size, RTE_CACHE_LINE_SIZE, 93 socket_id); 94 if (lpm == NULL) { 95 TABLE_LOG(ERR, 96 "%s: Cannot allocate %u bytes for LPM IPv6 table", 97 __func__, total_size); 98 return NULL; 99 } 100 101 /* LPM low-level table creation */ 102 lpm6_config.max_rules = p->n_rules; 103 lpm6_config.number_tbl8s = p->number_tbl8s; 104 lpm6_config.flags = 0; 105 lpm->lpm = rte_lpm6_create(p->name, socket_id, &lpm6_config); 106 if (lpm->lpm == NULL) { 107 rte_free(lpm); 108 TABLE_LOG(ERR, 109 "Unable to create low-level LPM IPv6 table"); 110 return NULL; 111 } 112 113 /* Memory initialization */ 114 lpm->entry_size = entry_size; 115 lpm->entry_unique_size = p->entry_unique_size; 116 lpm->n_rules = p->n_rules; 117 lpm->offset = p->offset; 118 119 return lpm; 120 } 121 122 static int 123 rte_table_lpm_ipv6_free(void *table) 124 { 125 struct rte_table_lpm_ipv6 *lpm = table; 126 127 /* Check input parameters */ 128 if (lpm == NULL) { 129 TABLE_LOG(ERR, "%s: table parameter is NULL", __func__); 130 return -EINVAL; 131 } 132 133 /* Free previously allocated resources */ 134 rte_lpm6_free(lpm->lpm); 135 rte_free(lpm); 136 137 return 0; 138 } 139 140 static int 141 nht_find_free(struct rte_table_lpm_ipv6 *lpm, uint32_t *pos) 142 { 143 uint32_t i; 144 145 for (i = 0; i < RTE_TABLE_LPM_MAX_NEXT_HOPS; i++) { 146 if (lpm->nht_users[i] == 0) { 147 *pos = i; 148 return 1; 149 } 150 } 151 152 return 0; 153 } 154 155 static int 156 nht_find_existing(struct rte_table_lpm_ipv6 *lpm, void *entry, uint32_t *pos) 157 { 158 uint32_t i; 159 160 for (i = 0; i < RTE_TABLE_LPM_MAX_NEXT_HOPS; i++) { 161 uint8_t *nht_entry = &lpm->nht[i * lpm->entry_size]; 162 163 if ((lpm->nht_users[i] > 0) && (memcmp(nht_entry, entry, 164 lpm->entry_unique_size) == 0)) { 165 *pos = i; 166 return 1; 167 } 168 } 169 170 return 0; 171 } 172 173 static int 174 rte_table_lpm_ipv6_entry_add( 175 void *table, 176 void *key, 177 void *entry, 178 int *key_found, 179 void **entry_ptr) 180 { 181 struct rte_table_lpm_ipv6 *lpm = table; 182 struct rte_table_lpm_ipv6_key *ip_prefix = 183 key; 184 uint32_t nht_pos = 0, nht_pos0 = 0, nht_pos0_valid = 0; 185 int status; 186 187 /* Check input parameters */ 188 if (lpm == NULL) { 189 TABLE_LOG(ERR, "%s: table parameter is NULL", __func__); 190 return -EINVAL; 191 } 192 if (ip_prefix == NULL) { 193 TABLE_LOG(ERR, "%s: ip_prefix parameter is NULL", 194 __func__); 195 return -EINVAL; 196 } 197 if (entry == NULL) { 198 TABLE_LOG(ERR, "%s: entry parameter is NULL", __func__); 199 return -EINVAL; 200 } 201 202 if ((ip_prefix->depth == 0) || (ip_prefix->depth > 128)) { 203 TABLE_LOG(ERR, "%s: invalid depth (%d)", __func__, 204 ip_prefix->depth); 205 return -EINVAL; 206 } 207 208 /* Check if rule is already present in the table */ 209 status = rte_lpm6_is_rule_present(lpm->lpm, ip_prefix->ip, 210 ip_prefix->depth, &nht_pos0); 211 nht_pos0_valid = status > 0; 212 213 /* Find existing or free NHT entry */ 214 if (nht_find_existing(lpm, entry, &nht_pos) == 0) { 215 uint8_t *nht_entry; 216 217 if (nht_find_free(lpm, &nht_pos) == 0) { 218 TABLE_LOG(ERR, "%s: NHT full", __func__); 219 return -1; 220 } 221 222 nht_entry = &lpm->nht[nht_pos * lpm->entry_size]; 223 memcpy(nht_entry, entry, lpm->entry_size); 224 } 225 226 /* Add rule to low level LPM table */ 227 if (rte_lpm6_add(lpm->lpm, ip_prefix->ip, ip_prefix->depth, 228 nht_pos) < 0) { 229 TABLE_LOG(ERR, "%s: LPM IPv6 rule add failed", __func__); 230 return -1; 231 } 232 233 /* Commit NHT changes */ 234 lpm->nht_users[nht_pos]++; 235 lpm->nht_users[nht_pos0] -= nht_pos0_valid; 236 237 *key_found = nht_pos0_valid; 238 *entry_ptr = (void *) &lpm->nht[nht_pos * lpm->entry_size]; 239 return 0; 240 } 241 242 static int 243 rte_table_lpm_ipv6_entry_delete( 244 void *table, 245 void *key, 246 int *key_found, 247 void *entry) 248 { 249 struct rte_table_lpm_ipv6 *lpm = table; 250 struct rte_table_lpm_ipv6_key *ip_prefix = 251 key; 252 uint32_t nht_pos; 253 int status; 254 255 /* Check input parameters */ 256 if (lpm == NULL) { 257 TABLE_LOG(ERR, "%s: table parameter is NULL", __func__); 258 return -EINVAL; 259 } 260 if (ip_prefix == NULL) { 261 TABLE_LOG(ERR, "%s: ip_prefix parameter is NULL", 262 __func__); 263 return -EINVAL; 264 } 265 if ((ip_prefix->depth == 0) || (ip_prefix->depth > 128)) { 266 TABLE_LOG(ERR, "%s: invalid depth (%d)", __func__, 267 ip_prefix->depth); 268 return -EINVAL; 269 } 270 271 /* Return if rule is not present in the table */ 272 status = rte_lpm6_is_rule_present(lpm->lpm, ip_prefix->ip, 273 ip_prefix->depth, &nht_pos); 274 if (status < 0) { 275 TABLE_LOG(ERR, "%s: LPM IPv6 algorithmic error", 276 __func__); 277 return -1; 278 } 279 if (status == 0) { 280 *key_found = 0; 281 return 0; 282 } 283 284 /* Delete rule from the low-level LPM table */ 285 status = rte_lpm6_delete(lpm->lpm, ip_prefix->ip, ip_prefix->depth); 286 if (status) { 287 TABLE_LOG(ERR, "%s: LPM IPv6 rule delete failed", 288 __func__); 289 return -1; 290 } 291 292 /* Commit NHT changes */ 293 lpm->nht_users[nht_pos]--; 294 295 *key_found = 1; 296 if (entry) 297 memcpy(entry, &lpm->nht[nht_pos * lpm->entry_size], 298 lpm->entry_size); 299 300 return 0; 301 } 302 303 static int 304 rte_table_lpm_ipv6_lookup( 305 void *table, 306 struct rte_mbuf **pkts, 307 uint64_t pkts_mask, 308 uint64_t *lookup_hit_mask, 309 void **entries) 310 { 311 struct rte_table_lpm_ipv6 *lpm = (struct rte_table_lpm_ipv6 *) table; 312 uint64_t pkts_out_mask = 0; 313 uint32_t i; 314 315 __rte_unused uint32_t n_pkts_in = rte_popcount64(pkts_mask); 316 RTE_TABLE_LPM_IPV6_STATS_PKTS_IN_ADD(lpm, n_pkts_in); 317 318 pkts_out_mask = 0; 319 for (i = 0; i < (uint32_t)(RTE_PORT_IN_BURST_SIZE_MAX - 320 rte_clz64(pkts_mask)); i++) { 321 uint64_t pkt_mask = 1LLU << i; 322 323 if (pkt_mask & pkts_mask) { 324 struct rte_mbuf *pkt = pkts[i]; 325 uint8_t *ip = RTE_MBUF_METADATA_UINT8_PTR(pkt, 326 lpm->offset); 327 int status; 328 uint32_t nht_pos; 329 330 status = rte_lpm6_lookup(lpm->lpm, ip, &nht_pos); 331 if (status == 0) { 332 pkts_out_mask |= pkt_mask; 333 entries[i] = (void *) &lpm->nht[nht_pos * 334 lpm->entry_size]; 335 } 336 } 337 } 338 339 *lookup_hit_mask = pkts_out_mask; 340 RTE_TABLE_LPM_IPV6_STATS_PKTS_LOOKUP_MISS(lpm, n_pkts_in - rte_popcount64(pkts_out_mask)); 341 return 0; 342 } 343 344 static int 345 rte_table_lpm_ipv6_stats_read(void *table, struct rte_table_stats *stats, int clear) 346 { 347 struct rte_table_lpm_ipv6 *t = table; 348 349 if (stats != NULL) 350 memcpy(stats, &t->stats, sizeof(t->stats)); 351 352 if (clear) 353 memset(&t->stats, 0, sizeof(t->stats)); 354 355 return 0; 356 } 357 358 struct rte_table_ops rte_table_lpm_ipv6_ops = { 359 .f_create = rte_table_lpm_ipv6_create, 360 .f_free = rte_table_lpm_ipv6_free, 361 .f_add = rte_table_lpm_ipv6_entry_add, 362 .f_delete = rte_table_lpm_ipv6_entry_delete, 363 .f_add_bulk = NULL, 364 .f_delete_bulk = NULL, 365 .f_lookup = rte_table_lpm_ipv6_lookup, 366 .f_stats = rte_table_lpm_ipv6_stats_read, 367 }; 368