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