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