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