table: add wildcard match table type
[dpdk.git] / lib / librte_table / rte_swx_table_wm.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
3  */
4 #include <stdlib.h>
5 #include <string.h>
6 #include <stdio.h>
7 #include <errno.h>
8
9 #include <rte_common.h>
10 #include <rte_prefetch.h>
11 #include <rte_cycles.h>
12 #include <rte_acl.h>
13
14 #include "rte_swx_table_wm.h"
15
16 #ifndef RTE_SWX_TABLE_EM_USE_HUGE_PAGES
17 #define RTE_SWX_TABLE_EM_USE_HUGE_PAGES 1
18 #endif
19
20 #if RTE_SWX_TABLE_EM_USE_HUGE_PAGES
21
22 #include <rte_malloc.h>
23
24 static void *
25 env_malloc(size_t size, size_t alignment, int numa_node)
26 {
27         return rte_zmalloc_socket(NULL, size, alignment, numa_node);
28 }
29
30 static void
31 env_free(void *start, size_t size __rte_unused)
32 {
33         rte_free(start);
34 }
35
36 #else
37
38 #include <numa.h>
39
40 static void *
41 env_malloc(size_t size, size_t alignment __rte_unused, int numa_node)
42 {
43         return numa_alloc_onnode(size, numa_node);
44 }
45
46 static void
47 env_free(void *start, size_t size)
48 {
49         numa_free(start, size);
50 }
51
52 #endif
53
54 static char *get_unique_name(void)
55 {
56         char *name;
57         uint64_t *tsc;
58
59         name = calloc(7, 1);
60         if (!name)
61                 return NULL;
62
63         tsc = (uint64_t *) name;
64         *tsc = rte_get_tsc_cycles();
65         return name;
66 }
67
68 static uint32_t
69 count_entries(struct rte_swx_table_entry_list *entries)
70 {
71         struct rte_swx_table_entry *entry;
72         uint32_t n_entries = 0;
73
74         if (!entries)
75                 return 0;
76
77         TAILQ_FOREACH(entry, entries, node)
78                 n_entries++;
79
80         return n_entries;
81 }
82
83 static int
84 acl_table_cfg_get(struct rte_acl_config *cfg, struct rte_swx_table_params *p)
85 {
86         uint32_t byte_id = 0, field_id = 0;
87
88         /* cfg->num_categories. */
89         cfg->num_categories = 1;
90
91         /* cfg->defs and cfg->num_fields. */
92         for (byte_id = 0; byte_id < p->key_size; ) {
93                 uint32_t field_size = field_id ? 4 : 1;
94                 uint8_t byte = p->key_mask0 ? p->key_mask0[byte_id] : 0xFF;
95
96                 if (!byte) {
97                         byte_id++;
98                         continue;
99                 }
100
101                 if (field_id == RTE_ACL_MAX_FIELDS)
102                         return -1;
103
104                 cfg->defs[field_id].type = RTE_ACL_FIELD_TYPE_BITMASK;
105                 cfg->defs[field_id].size = field_size;
106                 cfg->defs[field_id].field_index = field_id;
107                 cfg->defs[field_id].input_index = field_id;
108                 cfg->defs[field_id].offset = p->key_offset + byte_id;
109
110                 field_id++;
111                 byte_id += field_size;
112         }
113
114         if (!field_id)
115                 return -1;
116
117         cfg->num_fields = field_id;
118
119         /* cfg->max_size. */
120         cfg->max_size = 0;
121
122         return 0;
123 }
124
125 static void
126 acl_table_rule_field8(uint8_t *value,
127         uint8_t *mask,
128         uint8_t *key_mask0,
129         uint8_t *key_mask,
130         uint8_t *key,
131         uint32_t offset)
132 {
133         uint8_t km0, km;
134
135         km0 = key_mask0 ? key_mask0[offset] : 0xFF;
136         km = key_mask ? key_mask[offset] : 0xFF;
137
138         *value = key[offset];
139         *mask = km0 & km;
140 }
141
142 static void
143 acl_table_rule_field32(uint32_t *value,
144         uint32_t *mask,
145         uint8_t *key_mask0,
146         uint8_t *key_mask,
147         uint8_t *key,
148         uint32_t key_size,
149         uint32_t offset)
150 {
151         uint32_t km0[4], km[4], k[4];
152         uint32_t byte_id;
153
154         /* Byte 0 = MSB, byte 3 = LSB. */
155         for (byte_id = 0; byte_id < 4; byte_id++) {
156                 if (offset + byte_id >= key_size) {
157                         km0[byte_id] = 0;
158                         km[byte_id] = 0;
159                         k[byte_id] = 0;
160                         continue;
161                 }
162
163                 km0[byte_id] = key_mask0 ? key_mask0[offset + byte_id] : 0xFF;
164                 km[byte_id] = key_mask ? key_mask[offset + byte_id] : 0xFF;
165                 k[byte_id] = key[offset + byte_id];
166         }
167
168         *value = (k[0] << 24) |
169                  (k[1] << 16) |
170                  (k[2] << 8) |
171                  k[3];
172
173         *mask = ((km[0] & km0[0]) << 24) |
174                 ((km[1] & km0[1]) << 16) |
175                 ((km[2] & km0[2]) << 8) |
176                 (km[3] & km0[3]);
177 }
178
179 RTE_ACL_RULE_DEF(acl_rule, RTE_ACL_MAX_FIELDS);
180
181 static struct rte_acl_rule *
182 acl_table_rules_get(struct rte_acl_config *acl_cfg,
183         struct rte_swx_table_params *p,
184         struct rte_swx_table_entry_list *entries,
185         uint32_t n_entries)
186 {
187         struct rte_swx_table_entry *entry;
188         uint8_t *memory;
189         uint32_t acl_rule_size = RTE_ACL_RULE_SZ(acl_cfg->num_fields);
190         uint32_t n_fields = acl_cfg->num_fields;
191         uint32_t rule_id;
192
193         if (!n_entries)
194                 return NULL;
195
196         memory = malloc(n_entries * acl_rule_size);
197         if (!memory)
198                 return NULL;
199
200         rule_id = 0;
201         TAILQ_FOREACH(entry, entries, node) {
202                 uint8_t *m = &memory[rule_id * acl_rule_size];
203                 struct acl_rule *acl_rule = (struct acl_rule *)m;
204                 uint32_t field_id;
205
206                 acl_rule->data.category_mask = 1;
207                 acl_rule->data.priority = RTE_ACL_MAX_PRIORITY -
208                         entry->key_priority;
209                 acl_rule->data.userdata = rule_id + 1;
210
211                 for (field_id = 0; field_id < n_fields; field_id++) {
212                         struct rte_acl_field *f = &acl_rule->field[field_id];
213                         uint32_t size = acl_cfg->defs[field_id].size;
214                         uint32_t offset = acl_cfg->defs[field_id].offset -
215                                 p->key_offset;
216
217                         if (size == 1) {
218                                 uint8_t value, mask;
219
220                                 acl_table_rule_field8(&value,
221                                                       &mask,
222                                                       p->key_mask0,
223                                                       entry->key_mask,
224                                                       entry->key,
225                                                       offset);
226
227                                 f->value.u8 = value;
228                                 f->mask_range.u8 = mask;
229                         } else {
230                                 uint32_t value, mask;
231
232                                 acl_table_rule_field32(&value,
233                                                        &mask,
234                                                        p->key_mask0,
235                                                        entry->key_mask,
236                                                        entry->key,
237                                                        p->key_size,
238                                                        offset);
239
240                                 f->value.u32 = value;
241                                 f->mask_range.u32 = mask;
242                         }
243                 }
244
245                 rule_id++;
246         }
247
248         return (struct rte_acl_rule *)memory;
249 }
250
251 /* When the table to be created has no rules, the expected behavior is to always
252  * get lookup miss for any input key. To achieve this, we add a single bogus
253  * rule to the table with the rule user data set to 0, i.e. the value returned
254  * when lookup miss takes place. Whether lookup hit (the bogus rule is hit) or
255  * miss, a user data of 0 is returned, which for the ACL library is equivalent
256  * to lookup miss.
257  */
258 static struct rte_acl_rule *
259 acl_table_rules_default_get(struct rte_acl_config *acl_cfg)
260 {
261         struct rte_acl_rule *acl_rule;
262         uint32_t acl_rule_size = RTE_ACL_RULE_SZ(acl_cfg->num_fields);
263
264         acl_rule = calloc(1, acl_rule_size);
265         if (!acl_rule)
266                 return NULL;
267
268         acl_rule->data.category_mask = 1;
269         acl_rule->data.priority = RTE_ACL_MAX_PRIORITY;
270         acl_rule->data.userdata = 0;
271
272         memset(&acl_rule[1], 0xFF, acl_rule_size - sizeof(struct rte_acl_rule));
273
274         return acl_rule;
275 }
276
277 static struct rte_acl_ctx *
278 acl_table_create(struct rte_swx_table_params *params,
279         struct rte_swx_table_entry_list *entries,
280         uint32_t n_entries,
281         int numa_node)
282 {
283         struct rte_acl_param acl_params = {0};
284         struct rte_acl_config acl_cfg = {0};
285         struct rte_acl_ctx *acl_ctx = NULL;
286         struct rte_acl_rule *acl_rules = NULL;
287         char *name = NULL;
288         int status = 0;
289
290         /* ACL config data structures. */
291         name = get_unique_name();
292         if (!name) {
293                 status = -1;
294                 goto free_resources;
295         }
296
297         status = acl_table_cfg_get(&acl_cfg, params);
298         if (status)
299                 goto free_resources;
300
301         acl_rules = n_entries ?
302                 acl_table_rules_get(&acl_cfg, params, entries, n_entries) :
303                 acl_table_rules_default_get(&acl_cfg);
304         if (!acl_rules) {
305                 status = -1;
306                 goto free_resources;
307         }
308
309         n_entries = n_entries ? n_entries : 1;
310
311         /* ACL create. */
312         acl_params.name = name;
313         acl_params.socket_id = numa_node;
314         acl_params.rule_size = RTE_ACL_RULE_SZ(acl_cfg.num_fields);
315         acl_params.max_rule_num = n_entries;
316
317         acl_ctx = rte_acl_create(&acl_params);
318         if (!acl_ctx) {
319                 status = -1;
320                 goto free_resources;
321         }
322
323         /* ACL add rules. */
324         status = rte_acl_add_rules(acl_ctx, acl_rules, n_entries);
325         if (status)
326                 goto free_resources;
327
328         /* ACL build. */
329         status = rte_acl_build(acl_ctx, &acl_cfg);
330
331 free_resources:
332         if (status && acl_ctx)
333                 rte_acl_free(acl_ctx);
334
335         free(acl_rules);
336
337         free(name);
338
339         return status ? NULL : acl_ctx;
340 }
341
342 static void
343 entry_data_copy(uint8_t *data,
344         struct rte_swx_table_entry_list *entries,
345         uint32_t n_entries,
346         uint32_t entry_data_size)
347 {
348         struct rte_swx_table_entry *entry;
349         uint32_t i = 0;
350
351         if (!n_entries)
352                 return;
353
354         TAILQ_FOREACH(entry, entries, node) {
355                 uint64_t *d = (uint64_t *)&data[i * entry_data_size];
356
357                 d[0] = entry->action_id;
358                 memcpy(&d[1], entry->action_data, entry_data_size - 8);
359
360                 i++;
361         }
362 }
363
364 struct table {
365         struct rte_acl_ctx *acl_ctx;
366         uint8_t *data;
367         size_t total_size;
368         uint32_t entry_data_size;
369 };
370
371 static void
372 table_free(void *table)
373 {
374         struct table *t = table;
375
376         if (!t)
377                 return;
378
379         if (t->acl_ctx)
380                 rte_acl_free(t->acl_ctx);
381         env_free(t, t->total_size);
382 }
383
384 static void *
385 table_create(struct rte_swx_table_params *params,
386              struct rte_swx_table_entry_list *entries,
387              const char *args __rte_unused,
388              int numa_node)
389 {
390         struct table *t = NULL;
391         size_t meta_sz, data_sz, total_size;
392         uint32_t entry_data_size;
393         uint32_t n_entries = count_entries(entries);
394
395         /* Check input arguments. */
396         if (!params || !params->key_size)
397                 goto error;
398
399         /* Memory allocation and initialization. */
400         entry_data_size = 8 + params->action_data_size;
401         meta_sz = sizeof(struct table);
402         data_sz = n_entries * entry_data_size;
403         total_size = meta_sz + data_sz;
404
405         t = env_malloc(total_size, RTE_CACHE_LINE_SIZE, numa_node);
406         if (!t)
407                 goto error;
408
409         memset(t, 0, total_size);
410         t->entry_data_size = entry_data_size;
411         t->total_size = total_size;
412         t->data = (uint8_t *)&t[1];
413
414         t->acl_ctx = acl_table_create(params, entries, n_entries, numa_node);
415         if (!t->acl_ctx)
416                 goto error;
417
418         entry_data_copy(t->data, entries, n_entries, entry_data_size);
419
420         return t;
421
422 error:
423         table_free(t);
424         return NULL;
425 }
426
427 struct mailbox {
428
429 };
430
431 static uint64_t
432 table_mailbox_size_get(void)
433 {
434         return sizeof(struct mailbox);
435 }
436
437 static int
438 table_lookup(void *table,
439              void *mailbox __rte_unused,
440              const uint8_t **key,
441              uint64_t *action_id,
442              uint8_t **action_data,
443              int *hit)
444 {
445         struct table *t = table;
446         uint8_t *data;
447         uint32_t user_data;
448
449         rte_acl_classify(t->acl_ctx, key, &user_data, 1, 1);
450         if (!user_data) {
451                 *hit = 0;
452                 return 1;
453         }
454
455         data = &t->data[(user_data - 1) * t->entry_data_size];
456         *action_id = ((uint64_t *)data)[0];
457         *action_data = &data[8];
458         *hit = 1;
459         return 1;
460 }
461
462 struct rte_swx_table_ops rte_swx_table_wildcard_match_ops = {
463         .footprint_get = NULL,
464         .mailbox_size_get = table_mailbox_size_get,
465         .create = table_create,
466         .add = NULL,
467         .del = NULL,
468         .lkp = (rte_swx_table_lookup_t)table_lookup,
469         .free = table_free,
470 };