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