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