test/threads: add unit test
[dpdk.git] / app / test-pmd / cmd_flex_item.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2021 NVIDIA Corporation & Affiliates
3  */
4
5 #include <stddef.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <errno.h>
9 #include <string.h>
10
11 #include <rte_common.h>
12 #include <rte_ethdev.h>
13 #include <cmdline_parse.h>
14 #include <cmdline_parse_string.h>
15 #include <cmdline_parse_num.h>
16 #include <rte_flow.h>
17
18 #include "testpmd.h"
19
20 struct flex_item *flex_items[RTE_MAX_ETHPORTS][FLEX_MAX_PARSERS_NUM];
21 struct flex_pattern flex_patterns[FLEX_MAX_PATTERNS_NUM];
22
23 static struct flex_item *
24 flex_parser_fetch(uint16_t port_id, uint16_t flex_id)
25 {
26         if (port_id >= RTE_MAX_ETHPORTS) {
27                 printf("Invalid port_id: %u\n", port_id);
28                 return FLEX_PARSER_ERR;
29         }
30         if (flex_id >= FLEX_MAX_PARSERS_NUM) {
31                 printf("Invalid flex item flex_id: %u\n", flex_id);
32                 return FLEX_PARSER_ERR;
33         }
34         return flex_items[port_id][flex_id];
35 }
36
37 #ifdef RTE_HAS_JANSSON
38 static __rte_always_inline bool
39 match_strkey(const char *key, const char *pattern)
40 {
41         return strncmp(key, pattern, strlen(key)) == 0;
42 }
43
44 static int
45 flex_tunnel_parse(json_t *jtun, enum rte_flow_item_flex_tunnel_mode *tunnel)
46 {
47         int tun = -1;
48
49         if (json_is_integer(jtun))
50                 tun = (int)json_integer_value(jtun);
51         else if (json_is_real(jtun))
52                 tun = (int)json_real_value(jtun);
53         else if (json_is_string(jtun)) {
54                 const char *mode = json_string_value(jtun);
55
56                 if (match_strkey(mode, "FLEX_TUNNEL_MODE_SINGLE"))
57                         tun = FLEX_TUNNEL_MODE_SINGLE;
58                 else if (match_strkey(mode, "FLEX_TUNNEL_MODE_OUTER"))
59                         tun = FLEX_TUNNEL_MODE_OUTER;
60                 else if (match_strkey(mode, "FLEX_TUNNEL_MODE_INNER"))
61                         tun = FLEX_TUNNEL_MODE_INNER;
62                 else if (match_strkey(mode, "FLEX_TUNNEL_MODE_MULTI"))
63                         tun = FLEX_TUNNEL_MODE_MULTI;
64                 else if (match_strkey(mode, "FLEX_TUNNEL_MODE_TUNNEL"))
65                         tun = FLEX_TUNNEL_MODE_TUNNEL;
66                 else
67                         return -EINVAL;
68         } else
69                 return -EINVAL;
70         *tunnel = (enum rte_flow_item_flex_tunnel_mode)tun;
71         return 0;
72 }
73
74 static int
75 flex_field_parse(json_t *jfld, struct rte_flow_item_flex_field *fld)
76 {
77         const char *key;
78         json_t *je;
79
80 #define FLEX_FIELD_GET(fm, t) \
81 do {                  \
82         if (!strncmp(key, # fm, strlen(# fm))) { \
83                 if (json_is_real(je))   \
84                         fld->fm = (t) json_real_value(je); \
85                 else if (json_is_integer(je))   \
86                         fld->fm = (t) json_integer_value(je); \
87                 else   \
88                         return -EINVAL; \
89         }         \
90 } while (0)
91
92         json_object_foreach(jfld, key, je) {
93                 FLEX_FIELD_GET(field_size, uint32_t);
94                 FLEX_FIELD_GET(field_base, int32_t);
95                 FLEX_FIELD_GET(offset_base, uint32_t);
96                 FLEX_FIELD_GET(offset_mask, uint32_t);
97                 FLEX_FIELD_GET(offset_shift, int32_t);
98                 FLEX_FIELD_GET(field_id, uint16_t);
99                 if (match_strkey(key, "field_mode")) {
100                         const char *mode;
101                         if (!json_is_string(je))
102                                 return -EINVAL;
103                         mode = json_string_value(je);
104                         if (match_strkey(mode, "FIELD_MODE_DUMMY"))
105                                 fld->field_mode = FIELD_MODE_DUMMY;
106                         else if (match_strkey(mode, "FIELD_MODE_FIXED"))
107                                 fld->field_mode = FIELD_MODE_FIXED;
108                         else if (match_strkey(mode, "FIELD_MODE_OFFSET"))
109                                 fld->field_mode = FIELD_MODE_OFFSET;
110                         else if (match_strkey(mode, "FIELD_MODE_BITMASK"))
111                                 fld->field_mode = FIELD_MODE_BITMASK;
112                         else
113                                 return -EINVAL;
114                 }
115         }
116         return 0;
117 }
118
119 enum flex_link_type {
120         FLEX_LINK_IN = 0,
121         FLEX_LINK_OUT = 1
122 };
123
124 static int
125 flex_link_item_parse(const char *src, struct rte_flow_item *item)
126 {
127 #define  FLEX_PARSE_DATA_SIZE 1024
128
129         int ret;
130         uint8_t *ptr, data[FLEX_PARSE_DATA_SIZE] = {0,};
131         char flow_rule[256];
132         struct rte_flow_attr *attr;
133         struct rte_flow_item *pattern;
134         struct rte_flow_action *actions;
135
136         sprintf(flow_rule,
137                 "flow create 0 pattern %s / end actions drop / end", src);
138         src = flow_rule;
139         ret = flow_parse(src, (void *)data, sizeof(data),
140                          &attr, &pattern, &actions);
141         if (ret)
142                 return ret;
143         item->type = pattern->type;
144         if (pattern->spec) {
145                 ptr = (void *)(uintptr_t)item->spec;
146                 memcpy(ptr, pattern->spec, FLEX_MAX_FLOW_PATTERN_LENGTH);
147         } else {
148                 item->spec = NULL;
149         }
150         if (pattern->mask) {
151                 ptr = (void *)(uintptr_t)item->mask;
152                 memcpy(ptr, pattern->mask, FLEX_MAX_FLOW_PATTERN_LENGTH);
153         } else {
154                 item->mask = NULL;
155         }
156         if (pattern->last) {
157                 ptr = (void *)(uintptr_t)item->last;
158                 memcpy(ptr, pattern->last, FLEX_MAX_FLOW_PATTERN_LENGTH);
159         } else {
160                 item->last = NULL;
161         }
162         return 0;
163 }
164
165 static int
166 flex_link_parse(json_t *jobj, struct rte_flow_item_flex_link *link,
167                 enum flex_link_type link_type)
168 {
169         const char *key;
170         json_t *je;
171         int ret;
172         json_object_foreach(jobj, key, je) {
173                 if (match_strkey(key, "item")) {
174                         if (!json_is_string(je))
175                                 return -EINVAL;
176                         ret = flex_link_item_parse(json_string_value(je),
177                                                    &link->item);
178                         if (ret)
179                                 return -EINVAL;
180                         if (link_type == FLEX_LINK_IN) {
181                                 if (!link->item.spec || !link->item.mask)
182                                         return -EINVAL;
183                                 if (link->item.last)
184                                         return -EINVAL;
185                         }
186                 }
187                 if (match_strkey(key, "next")) {
188                         if (json_is_integer(je))
189                                 link->next = (typeof(link->next))
190                                              json_integer_value(je);
191                         else if (json_is_real(je))
192                                 link->next = (typeof(link->next))
193                                              json_real_value(je);
194                         else
195                                 return -EINVAL;
196                 }
197         }
198         return 0;
199 }
200
201 static int flex_item_config(json_t *jroot,
202                             struct rte_flow_item_flex_conf *flex_conf)
203 {
204         const char *key;
205         json_t *jobj = NULL;
206         int ret = 0;
207
208         json_object_foreach(jroot, key, jobj) {
209                 if (match_strkey(key, "tunnel")) {
210                         ret = flex_tunnel_parse(jobj, &flex_conf->tunnel);
211                         if (ret) {
212                                 printf("Can't parse tunnel value\n");
213                                 goto out;
214                         }
215                 } else if (match_strkey(key, "next_header")) {
216                         ret = flex_field_parse(jobj, &flex_conf->next_header);
217                         if (ret) {
218                                 printf("Can't parse next_header field\n");
219                                 goto out;
220                         }
221                 } else if (match_strkey(key, "next_protocol")) {
222                         ret = flex_field_parse(jobj,
223                                                &flex_conf->next_protocol);
224                         if (ret) {
225                                 printf("Can't parse next_protocol field\n");
226                                 goto out;
227                         }
228                 } else if (match_strkey(key, "sample_data")) {
229                         json_t *ji;
230                         uint32_t i, size = json_array_size(jobj);
231                         for (i = 0; i < size; i++) {
232                                 ji = json_array_get(jobj, i);
233                                 ret = flex_field_parse
234                                         (ji, flex_conf->sample_data + i);
235                                 if (ret) {
236                                         printf("Can't parse sample_data field(s)\n");
237                                         goto out;
238                                 }
239                         }
240                         flex_conf->nb_samples = size;
241                 } else if (match_strkey(key, "input_link")) {
242                         json_t *ji;
243                         uint32_t i, size = json_array_size(jobj);
244                         for (i = 0; i < size; i++) {
245                                 ji = json_array_get(jobj, i);
246                                 ret = flex_link_parse(ji,
247                                                       flex_conf->input_link + i,
248                                                       FLEX_LINK_IN);
249                                 if (ret) {
250                                         printf("Can't parse input_link(s)\n");
251                                         goto out;
252                                 }
253                         }
254                         flex_conf->nb_inputs = size;
255                 } else if (match_strkey(key, "output_link")) {
256                         json_t *ji;
257                         uint32_t i, size = json_array_size(jobj);
258                         for (i = 0; i < size; i++) {
259                                 ji = json_array_get(jobj, i);
260                                 ret = flex_link_parse
261                                         (ji, flex_conf->output_link + i,
262                                          FLEX_LINK_OUT);
263                                 if (ret) {
264                                         printf("Can't parse output_link(s)\n");
265                                         goto out;
266                                 }
267                         }
268                         flex_conf->nb_outputs = size;
269                 }
270         }
271 out:
272         return ret;
273 }
274
275 static struct flex_item *
276 flex_item_init(void)
277 {
278         size_t base_size, samples_size, links_size, spec_size;
279         struct rte_flow_item_flex_conf *conf;
280         struct flex_item *fp;
281         uint8_t (*pattern)[FLEX_MAX_FLOW_PATTERN_LENGTH];
282         int i;
283
284         base_size = RTE_ALIGN(sizeof(*conf), sizeof(uintptr_t));
285         samples_size = RTE_ALIGN(FLEX_ITEM_MAX_SAMPLES_NUM *
286                                  sizeof(conf->sample_data[0]),
287                                  sizeof(uintptr_t));
288         links_size = RTE_ALIGN(FLEX_ITEM_MAX_LINKS_NUM *
289                                sizeof(conf->input_link[0]),
290                                sizeof(uintptr_t));
291         /* spec & mask for all input links */
292         spec_size = 2 * FLEX_MAX_FLOW_PATTERN_LENGTH * FLEX_ITEM_MAX_LINKS_NUM;
293         fp = calloc(1, base_size + samples_size + 2 * links_size + spec_size);
294         if (fp == NULL) {
295                 printf("Can't allocate memory for flex item\n");
296                 return NULL;
297         }
298         conf = &fp->flex_conf;
299         conf->sample_data = (typeof(conf->sample_data))
300                             ((uint8_t *)fp + base_size);
301         conf->input_link = (typeof(conf->input_link))
302                            ((uint8_t *)conf->sample_data + samples_size);
303         conf->output_link = (typeof(conf->output_link))
304                             ((uint8_t *)conf->input_link + links_size);
305         pattern = (typeof(pattern))((uint8_t *)conf->output_link + links_size);
306         for (i = 0; i < FLEX_ITEM_MAX_LINKS_NUM; i++) {
307                 struct rte_flow_item_flex_link *in = conf->input_link + i;
308                 in->item.spec = pattern++;
309                 in->item.mask = pattern++;
310         }
311         return fp;
312 }
313
314 static int
315 flex_item_build_config(struct flex_item *fp, const char *filename)
316 {
317         int ret;
318         json_error_t json_error;
319         json_t *jroot = json_load_file(filename, 0, &json_error);
320
321         if (!jroot) {
322                 printf("Bad JSON file \"%s\": %s\n", filename, json_error.text);
323                 return -1;
324         }
325         ret = flex_item_config(jroot, &fp->flex_conf);
326         json_decref(jroot);
327         return ret;
328 }
329
330 void
331 flex_item_create(portid_t port_id, uint16_t flex_id, const char *filename)
332 {
333         struct rte_flow_error flow_error;
334         struct flex_item *fp = flex_parser_fetch(port_id, flex_id);
335         int ret;
336
337         if (fp == FLEX_PARSER_ERR) {
338                 printf("Bad parameters: port_id=%u flex_id=%u\n",
339                        port_id, flex_id);
340                 return;
341         }
342         if (fp) {
343                 printf("port-%u: flex item #%u is already in use\n",
344                        port_id, flex_id);
345                 return;
346         }
347         fp = flex_item_init();
348         if (!fp) {
349                 printf("Could not allocate flex item\n");
350                 goto out;
351         }
352         ret = flex_item_build_config(fp, filename);
353         if (ret)
354                 goto out;
355         fp->flex_handle = rte_flow_flex_item_create(port_id,
356                                                     &fp->flex_conf,
357                                                     &flow_error);
358         if (fp->flex_handle) {
359                 flex_items[port_id][flex_id] = fp;
360                 printf("port-%u: created flex item #%u\n", port_id, flex_id);
361                 fp = NULL;
362         } else {
363                 printf("port-%u: flex item #%u creation failed: %s\n",
364                        port_id, flex_id,
365                        flow_error.message ? flow_error.message : "");
366         }
367 out:
368         free(fp);
369 }
370
371 #else /* RTE_HAS_JANSSON */
372 void flex_item_create(__rte_unused portid_t port_id,
373                       __rte_unused uint16_t flex_id,
374                       __rte_unused const char *filename)
375 {
376         printf("cannot create flex item - no JSON library configured\n");
377 }
378 #endif /* RTE_HAS_JANSSON */
379
380 void
381 flex_item_destroy(portid_t port_id, uint16_t flex_id)
382 {
383         int ret;
384         struct rte_flow_error error;
385         struct flex_item *fp = flex_parser_fetch(port_id, flex_id);
386         if (!flex_id)
387                 return;
388         if (fp == FLEX_PARSER_ERR) {
389                 printf("Bad parameters: port_id=%u flex_id=%u\n",
390                        port_id, flex_id);
391                 return;
392         }
393         if (!fp)
394                 return;
395         ret = rte_flow_flex_item_release(port_id, fp->flex_handle, &error);
396         if (!ret) {
397                 free(fp);
398                 flex_items[port_id][flex_id] = NULL;
399                 printf("port-%u: released flex item #%u\n",
400                        port_id, flex_id);
401
402         } else {
403                 printf("port-%u: cannot release flex item #%u: %s\n",
404                        port_id, flex_id, error.message);
405         }
406 }
407
408 void
409 port_flex_item_flush(portid_t port_id)
410 {
411         uint16_t i;
412
413         for (i = 0; i < FLEX_MAX_PARSERS_NUM; i++) {
414                 if (flex_items[port_id][i] != NULL) {
415                         flex_item_destroy(port_id, i);
416                         flex_items[port_id][i] = NULL;
417                 }
418         }
419 }
420
421 struct flex_pattern_set {
422         cmdline_fixed_string_t set, flex_pattern;
423         cmdline_fixed_string_t is_spec, mask;
424         cmdline_fixed_string_t spec_data, mask_data;
425         uint16_t id;
426 };
427
428 static cmdline_parse_token_string_t flex_pattern_set_token =
429         TOKEN_STRING_INITIALIZER(struct flex_pattern_set, set, "set");
430 static cmdline_parse_token_string_t flex_pattern_token =
431         TOKEN_STRING_INITIALIZER(struct flex_pattern_set,
432 flex_pattern, "flex_pattern");
433 static cmdline_parse_token_string_t flex_pattern_is_token =
434         TOKEN_STRING_INITIALIZER(struct flex_pattern_set,
435 is_spec, "is");
436 static cmdline_parse_token_string_t flex_pattern_spec_token =
437         TOKEN_STRING_INITIALIZER(struct flex_pattern_set,
438 is_spec, "spec");
439 static cmdline_parse_token_string_t flex_pattern_mask_token =
440         TOKEN_STRING_INITIALIZER(struct flex_pattern_set, mask, "mask");
441 static cmdline_parse_token_string_t flex_pattern_spec_data_token =
442         TOKEN_STRING_INITIALIZER(struct flex_pattern_set, spec_data, NULL);
443 static cmdline_parse_token_string_t flex_pattern_mask_data_token =
444         TOKEN_STRING_INITIALIZER(struct flex_pattern_set, mask_data, NULL);
445 static cmdline_parse_token_num_t flex_pattern_id_token =
446         TOKEN_NUM_INITIALIZER(struct flex_pattern_set, id, RTE_UINT16);
447
448 /*
449  * flex pattern data - spec or mask is a string representation of byte array
450  * in hexadecimal format. Each byte in data string must have 2 characters:
451  * 0x15 - "15"
452  * 0x1  - "01"
453  * Bytes in data array are in network order.
454  */
455 static uint32_t
456 flex_pattern_data(const char *str, uint8_t *data)
457 {
458         uint32_t i, len = strlen(str);
459         char b[3], *endptr;
460
461         if (len & 01)
462                 return 0;
463         len /= 2;
464         if (len >= FLEX_MAX_FLOW_PATTERN_LENGTH)
465                 return 0;
466         for (i = 0, b[2] = '\0'; i < len; i++) {
467                 b[0] = str[2 * i];
468                 b[1] = str[2 * i + 1];
469                 data[i] = strtoul(b, &endptr, 16);
470                 if (endptr != &b[2])
471                         return 0;
472         }
473         return len;
474 }
475
476 static void
477 flex_pattern_parsed_fn(void *parsed_result,
478                        __rte_unused struct cmdline *cl,
479                        __rte_unused void *data)
480 {
481         struct flex_pattern_set *res = parsed_result;
482         struct flex_pattern *fp;
483         bool full_spec;
484
485         if (res->id >= FLEX_MAX_PATTERNS_NUM) {
486                 printf("Bad flex pattern id\n");
487                 return;
488         }
489         fp = flex_patterns + res->id;
490         memset(fp->spec_pattern, 0, sizeof(fp->spec_pattern));
491         memset(fp->mask_pattern, 0, sizeof(fp->mask_pattern));
492         fp->spec.length = flex_pattern_data(res->spec_data, fp->spec_pattern);
493         if (!fp->spec.length) {
494                 printf("Bad flex pattern spec\n");
495                 return;
496         }
497         full_spec = strncmp(res->is_spec, "spec", strlen("spec")) == 0;
498         if (full_spec) {
499                 fp->mask.length = flex_pattern_data(res->mask_data,
500                                                     fp->mask_pattern);
501                 if (!fp->mask.length) {
502                         printf("Bad flex pattern mask\n");
503                         return;
504                 }
505         } else {
506                 memset(fp->mask_pattern, 0xFF, fp->spec.length);
507                 fp->mask.length = fp->spec.length;
508         }
509         if (fp->mask.length != fp->spec.length) {
510                 printf("Spec length do not match mask length\n");
511                 return;
512         }
513         fp->spec.pattern = fp->spec_pattern;
514         fp->mask.pattern = fp->mask_pattern;
515         printf("created pattern #%u\n", res->id);
516 }
517
518 cmdline_parse_inst_t cmd_set_flex_is_pattern = {
519         .f = flex_pattern_parsed_fn,
520         .data = NULL,
521         .help_str = "set flex_pattern <id> is <spec_data>",
522         .tokens = {
523                 (void *)&flex_pattern_set_token,
524                 (void *)&flex_pattern_token,
525                 (void *)&flex_pattern_id_token,
526                 (void *)&flex_pattern_is_token,
527                 (void *)&flex_pattern_spec_data_token,
528                 NULL,
529         }
530 };
531
532 cmdline_parse_inst_t cmd_set_flex_spec_pattern = {
533         .f = flex_pattern_parsed_fn,
534         .data = NULL,
535         .help_str = "set flex_pattern <id> spec <spec_data> mask <mask_data>",
536         .tokens = {
537                 (void *)&flex_pattern_set_token,
538                 (void *)&flex_pattern_token,
539                 (void *)&flex_pattern_id_token,
540                 (void *)&flex_pattern_spec_token,
541                 (void *)&flex_pattern_spec_data_token,
542                 (void *)&flex_pattern_mask_token,
543                 (void *)&flex_pattern_mask_data_token,
544                 NULL,
545         }
546 };