app/regex: add RegEx test application
[dpdk.git] / app / test-regex / main.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2020 Mellanox Technologies, Ltd
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdint.h>
9 #include <stdbool.h>
10 #include <stdarg.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <getopt.h>
14 #include <signal.h>
15
16 #include <rte_eal.h>
17 #include <rte_common.h>
18 #include <rte_malloc.h>
19 #include <rte_mempool.h>
20 #include <rte_mbuf.h>
21 #include <rte_cycles.h>
22 #include <rte_regexdev.h>
23
24 #define MAX_FILE_NAME 255
25 #define MBUF_CACHE_SIZE 256
26 #define MBUF_SIZE (1 << 8)
27 #define START_BURST_SIZE 32u
28
29 enum app_args {
30         ARG_HELP,
31         ARG_RULES_FILE_NAME,
32         ARG_DATA_FILE_NAME,
33         ARG_NUM_OF_JOBS,
34         ARG_PERF_MODE,
35         ARG_NUM_OF_ITERATIONS,
36 };
37
38 static void
39 usage(const char *prog_name)
40 {
41         printf("%s [EAL options] --\n"
42                 " --rules NAME: precompiled rules file\n"
43                 " --data NAME: data file to use\n"
44                 " --nb_jobs: number of jobs to use\n"
45                 " --perf N: only outputs the performance data\n"
46                 " --nb_iter N: number of iteration to run\n",
47                 prog_name);
48 }
49
50 static void
51 args_parse(int argc, char **argv, char *rules_file, char *data_file,
52            uint32_t *nb_jobs, bool *perf_mode, uint32_t *nb_iterations)
53 {
54         char **argvopt;
55         int opt;
56         int opt_idx;
57         size_t len;
58         static struct option lgopts[] = {
59                 { "help",  0, 0, ARG_HELP},
60                 /* Rules database file to load. */
61                 { "rules",  1, 0, ARG_RULES_FILE_NAME},
62                 /* Data file to load. */
63                 { "data",  1, 0, ARG_DATA_FILE_NAME},
64                 /* Number of jobs to create. */
65                 { "nb_jobs",  1, 0, ARG_NUM_OF_JOBS},
66                 /* Perf test only */
67                 { "perf", 0, 0, ARG_PERF_MODE},
68                 /* Number of iterations to run with perf test */
69                 { "nb_iter", 1, 0, ARG_NUM_OF_ITERATIONS}
70         };
71
72         argvopt = argv;
73         while ((opt = getopt_long(argc, argvopt, "",
74                                 lgopts, &opt_idx)) != EOF) {
75                 switch (opt) {
76                 case ARG_RULES_FILE_NAME:
77                         len = strnlen(optarg, MAX_FILE_NAME - 1);
78                         if (len == MAX_FILE_NAME)
79                                 rte_exit(EXIT_FAILURE,
80                                          "Rule file name to long max %d\n",
81                                          MAX_FILE_NAME - 1);
82                         strncpy(rules_file, optarg, MAX_FILE_NAME - 1);
83                         break;
84                 case ARG_DATA_FILE_NAME:
85                         len = strnlen(optarg, MAX_FILE_NAME - 1);
86                         if (len == MAX_FILE_NAME)
87                                 rte_exit(EXIT_FAILURE,
88                                          "Data file name to long max %d\n",
89                                          MAX_FILE_NAME - 1);
90                         strncpy(data_file, optarg, MAX_FILE_NAME - 1);
91                         break;
92                 case ARG_NUM_OF_JOBS:
93                         *nb_jobs = atoi(optarg);
94                         break;
95                 case ARG_PERF_MODE:
96                         *perf_mode = true;
97                         break;
98                 case ARG_NUM_OF_ITERATIONS:
99                         *nb_iterations = atoi(optarg);
100                         break;
101                 case ARG_HELP:
102                         usage("RegEx test app");
103                         break;
104                 default:
105                         fprintf(stderr, "Invalid option: %s\n", argv[optind]);
106                         usage("RegEx test app");
107                         rte_exit(EXIT_FAILURE, "Invalid option\n");
108                         break;
109                 }
110         }
111
112         if (!perf_mode)
113                 *nb_iterations = 1;
114 }
115
116 static long
117 read_file(char *file, char **buf)
118 {
119         FILE *fp;
120         long buf_len = 0;
121         size_t read_len;
122         int res = 0;
123
124         fp = fopen(file, "r");
125         if (!fp)
126                 return -EIO;
127         if (fseek(fp, 0L, SEEK_END) == 0) {
128                 buf_len = ftell(fp);
129                 if (buf_len == -1) {
130                         res = EIO;
131                         goto error;
132                 }
133                 *buf = rte_malloc(NULL, sizeof(char) * (buf_len + 1), 4096);
134                 if (!*buf) {
135                         res = ENOMEM;
136                         goto error;
137                 }
138                 if (fseek(fp, 0L, SEEK_SET) != 0) {
139                         res = EIO;
140                         goto error;
141                 }
142                 read_len = fread(*buf, sizeof(char), buf_len, fp);
143                 if (read_len != (unsigned long)buf_len) {
144                         res = EIO;
145                         goto error;
146                 }
147         }
148         fclose(fp);
149         return buf_len;
150 error:
151         printf("Error, can't open file %s\n, err = %d", file, res);
152         if (fp)
153                 fclose(fp);
154         if (*buf)
155                 rte_free(*buf);
156         return -res;
157 }
158
159 static int
160 init_port(struct rte_mempool **mbuf_mp, uint32_t nb_jobs,
161           uint16_t *nb_max_payload, char *rules_file, uint8_t *nb_max_matches)
162 {
163         uint16_t id;
164         uint16_t num_devs;
165         char *rules = NULL;
166         long rules_len;
167         struct rte_regexdev_info info;
168         struct rte_regexdev_config dev_conf = {
169                 .nb_queue_pairs = 1,
170                 .nb_groups = 1,
171         };
172         struct rte_regexdev_qp_conf qp_conf = {
173                 .nb_desc = 1024,
174                 .qp_conf_flags = RTE_REGEX_QUEUE_PAIR_CFG_OOS_F,
175         };
176         int res = 0;
177
178         num_devs = rte_regexdev_count();
179         if (num_devs == 0) {
180                 printf("Error, no devices detected.\n");
181                 return -EINVAL;
182         }
183
184         *mbuf_mp = rte_pktmbuf_pool_create("mbuf_pool", nb_jobs, 0,
185                                           0, MBUF_SIZE, rte_socket_id());
186         if (*mbuf_mp == NULL) {
187                 printf("Error, can't create memory pool\n");
188                 res = -ENOMEM;
189                 goto error;
190         }
191
192         rules_len = read_file(rules_file, &rules);
193         if (rules_len < 0) {
194                 printf("Error, can't read rules files.\n");
195                 res = -EIO;
196                 goto error;
197         }
198
199         for (id = 0; id < num_devs; id++) {
200                 res = rte_regexdev_info_get(id, &info);
201                 if (res != 0) {
202                         printf("Error, can't get device info.\n");
203                         goto error;
204                 }
205                 printf(":: initializing dev: %d\n", id);
206                 *nb_max_matches = info.max_matches;
207                 *nb_max_payload = info.max_payload_size;
208                 if (info.regexdev_capa & RTE_REGEXDEV_SUPP_MATCH_AS_END_F)
209                         dev_conf.dev_cfg_flags |= RTE_REGEXDEV_CFG_MATCH_AS_END_F;
210                 dev_conf.nb_max_matches = info.max_matches;
211                 dev_conf.nb_rules_per_group = info.max_rules_per_group;
212                 dev_conf.rule_db_len = rules_len;
213                 dev_conf.rule_db = rules;
214                 res = rte_regexdev_configure(id, &dev_conf);
215                 if (res < 0) {
216                         printf("Error, can't configure device %d.\n", id);
217                         goto error;
218                 }
219                 res = rte_regexdev_queue_pair_setup(id, 0, &qp_conf);
220                 if (res < 0) {
221                         printf("Error, can't setup queue pair for device %d.\n",
222                                id);
223                         goto error;
224                 }
225                 printf(":: initializing device: %d done\n", id);
226         }
227         rte_free(rules);
228         return 0;
229 error:
230         if (rules)
231                 rte_free(rules);
232         if (*mbuf_mp)
233                 rte_mempool_free(*mbuf_mp);
234         return res;
235 }
236
237 static void
238 extbuf_free_cb(void *addr __rte_unused, void *fcb_opaque __rte_unused)
239 {
240 }
241
242 static int
243 run_regex(struct rte_mempool *mbuf_mp, uint32_t nb_jobs,
244           uint16_t nb_max_payload, bool perf_mode, uint32_t nb_iterations,
245           char *data_file, uint8_t nb_max_matches)
246 {
247         char *buf = NULL;
248         long buf_len;
249         long job_len;
250         uint32_t actual_jobs = 0;
251         uint32_t i;
252         struct rte_regex_ops **ops;
253         uint16_t dev_id = 0;
254         uint16_t qp_id = 0;
255         uint8_t nb_matches;
256         struct rte_regexdev_match *match;
257         long pos = 0;
258         unsigned long d_ind = 0;
259         struct rte_mbuf_ext_shared_info shinfo;
260         uint32_t total_enqueue = 0;
261         uint32_t total_dequeue = 0;
262         uint32_t total_matches = 0;
263         int res = 0;
264         time_t start;
265         time_t end;
266         double time;
267
268         shinfo.free_cb = extbuf_free_cb;
269
270         ops = rte_malloc(NULL, sizeof(*ops) * nb_jobs, 0);
271         if (!ops) {
272                 printf("Error, can't allocate memory for ops.\n");
273                 return -ENOMEM;
274         }
275
276         /* Allocate the jobs and assign each job with an mbuf. */
277         for (i = 0; i < nb_jobs; i++) {
278                 ops[i] = rte_malloc(NULL, sizeof(*ops[0]) + nb_max_matches *
279                                     sizeof(struct rte_regexdev_match), 0);
280                 if (!ops[i]) {
281                         printf("Error, can't allocate memory for op.\n");
282                         res = -ENOMEM;
283                         goto end;
284                 }
285                 ops[i]->mbuf = rte_pktmbuf_alloc(mbuf_mp);
286                 if (!ops[i]->mbuf) {
287                         printf("Error, can't attach mbuf.\n");
288                         res = -ENOMEM;
289                         goto end;
290                 }
291         }
292
293         buf_len = read_file(data_file, &buf);
294         if (buf_len <= 0) {
295                 printf("Error, can't read file, or file is empty.\n");
296                 res = -EXIT_FAILURE;
297                 goto end;
298         }
299
300         job_len = buf_len / nb_jobs;
301         if (job_len == 0) {
302                 printf("Error, To many jobs, for the given input.\n");
303                 res = -EXIT_FAILURE;
304                 goto end;
305         }
306
307         if (job_len > nb_max_payload) {
308                 printf("Error, not enough jobs to cover input.\n");
309                 res = -EXIT_FAILURE;
310                 goto end;
311         }
312
313         /* Assign each mbuf with the data to handle. */
314         for (i = 0; (pos < buf_len) && (i < nb_jobs) ; i++) {
315                 long act_job_len = RTE_MIN(job_len, buf_len - pos);
316                 rte_pktmbuf_attach_extbuf(ops[i]->mbuf, &buf[pos], 0,
317                                           act_job_len, &shinfo);
318                 ops[i]->mbuf->data_len = job_len;
319                 ops[i]->mbuf->pkt_len = act_job_len;
320                 ops[i]->user_id = i;
321                 ops[i]->group_id0 = 1;
322                 pos += act_job_len;
323                 actual_jobs++;
324         }
325
326         start = clock();
327         for (i = 0; i < nb_iterations; i++) {
328                 total_enqueue = 0;
329                 total_dequeue = 0;
330                 while (total_dequeue < actual_jobs) {
331                         struct rte_regex_ops **cur_ops_to_enqueue = ops +
332                                 total_enqueue;
333                         struct rte_regex_ops **cur_ops_to_dequeue = ops +
334                                 total_dequeue;
335
336                         if (actual_jobs - total_enqueue)
337                                 total_enqueue += rte_regexdev_enqueue_burst
338                                         (dev_id, qp_id, cur_ops_to_enqueue,
339                                          actual_jobs - total_enqueue);
340
341                         total_dequeue += rte_regexdev_dequeue_burst
342                                 (dev_id, qp_id, cur_ops_to_dequeue,
343                                  total_enqueue - total_dequeue);
344                 }
345         }
346         end = clock();
347         time = ((double)end - start) / CLOCKS_PER_SEC;
348         printf("Job len = %ld Bytes\n",  job_len);
349         printf("Time = %lf sec\n",  time);
350         printf("Perf = %lf Gbps\n",
351                (((double)actual_jobs * job_len * nb_iterations * 8) / time) /
352                 1000000000.0);
353
354         if (!perf_mode) {
355                 /* Log results per job. */
356                 for (d_ind = 0; d_ind < total_dequeue; d_ind++) {
357                         nb_matches = ops[d_ind % actual_jobs]->nb_matches;
358                         printf("Job id %"PRIu64" number of matches = %d\n",
359                                ops[d_ind]->user_id, nb_matches);
360                         total_matches += nb_matches;
361                         match = ops[d_ind % actual_jobs]->matches;
362                         for (i = 0; i < nb_matches; i++) {
363                                 printf("match %d, rule = %d, start = %d,len = %d\n",
364                                        i, match->rule_id, match->start_offset,
365                                        match->len);
366                                 match++;
367                         }
368                 }
369                 printf("Total matches = %d\n", total_matches);
370                 printf("All Matches:\n");
371
372                 /* Log absolute results. */
373                 for (d_ind = 0; d_ind < total_dequeue; d_ind++) {
374                         nb_matches = ops[d_ind % actual_jobs]->nb_matches;
375                         total_matches += nb_matches;
376                         match = ops[d_ind % actual_jobs]->matches;
377                         for (i = 0; i < nb_matches; i++) {
378                                 printf("start = %ld, len = %d, rule = %d\n",
379                                        match->start_offset + d_ind * job_len,
380                                        match->len, match->rule_id);
381                                 match++;
382                         }
383                 }
384         }
385 end:
386         for (i = 0; i < actual_jobs; i++) {
387                 if (ops[i]) {
388                         if (ops[i]->mbuf)
389                                 rte_pktmbuf_free(ops[i]->mbuf);
390                         rte_free(ops[i]);
391                 }
392         }
393         rte_free(ops);
394         if (buf)
395                 rte_free(buf);
396         return res;
397 }
398
399 int
400 main(int argc, char **argv)
401 {
402         char rules_file[MAX_FILE_NAME];
403         char data_file[MAX_FILE_NAME];
404         struct rte_mempool *mbuf_mp = NULL;
405         uint32_t nb_jobs = 0;
406         uint16_t nb_max_payload = 0;
407         bool perf_mode = 0;
408         uint32_t nb_iterations = 0;
409         uint8_t nb_max_matches = 0;
410         int ret;
411
412         ret = rte_eal_init(argc, argv);
413         if (ret < 0)
414                 rte_exit(EXIT_FAILURE, "EAL init failed\n");
415         argc -= ret;
416         argv += ret;
417         if (argc > 1)
418                 args_parse(argc, argv, rules_file, data_file, &nb_jobs,
419                            &perf_mode, &nb_iterations);
420
421         ret = init_port(&mbuf_mp, nb_jobs, &nb_max_payload, rules_file,
422                         &nb_max_matches);
423         if (ret < 0)
424                 rte_exit(EXIT_FAILURE, "init port failed\n");
425         ret = run_regex(mbuf_mp, nb_jobs, nb_max_payload, perf_mode,
426                         nb_iterations, data_file, nb_max_matches);
427         if (ret < 0) {
428                 rte_mempool_free(mbuf_mp);
429                 rte_exit(EXIT_FAILURE, "RegEx function failed\n");
430         }
431         rte_mempool_free(mbuf_mp);
432         return EXIT_SUCCESS;
433 }