app/regex: move mempool creation to worker routine
[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(uint16_t *nb_max_payload, char *rules_file, uint8_t *nb_max_matches)
167 {
168         uint16_t id;
169         uint16_t num_devs;
170         char *rules = NULL;
171         long rules_len;
172         struct rte_regexdev_info info;
173         struct rte_regexdev_config dev_conf = {
174                 .nb_queue_pairs = 1,
175                 .nb_groups = 1,
176         };
177         struct rte_regexdev_qp_conf qp_conf = {
178                 .nb_desc = 1024,
179                 .qp_conf_flags = 0,
180         };
181         int res = 0;
182
183         num_devs = rte_regexdev_count();
184         if (num_devs == 0) {
185                 printf("Error, no devices detected.\n");
186                 return -EINVAL;
187         }
188
189         rules_len = read_file(rules_file, &rules);
190         if (rules_len < 0) {
191                 printf("Error, can't read rules files.\n");
192                 res = -EIO;
193                 goto error;
194         }
195
196         for (id = 0; id < num_devs; id++) {
197                 res = rte_regexdev_info_get(id, &info);
198                 if (res != 0) {
199                         printf("Error, can't get device info.\n");
200                         goto error;
201                 }
202                 printf(":: initializing dev: %d\n", id);
203                 *nb_max_matches = info.max_matches;
204                 *nb_max_payload = info.max_payload_size;
205                 if (info.regexdev_capa & RTE_REGEXDEV_SUPP_MATCH_AS_END_F)
206                         dev_conf.dev_cfg_flags |= RTE_REGEXDEV_CFG_MATCH_AS_END_F;
207                 dev_conf.nb_max_matches = info.max_matches;
208                 dev_conf.nb_rules_per_group = info.max_rules_per_group;
209                 dev_conf.rule_db_len = rules_len;
210                 dev_conf.rule_db = rules;
211                 res = rte_regexdev_configure(id, &dev_conf);
212                 if (res < 0) {
213                         printf("Error, can't configure device %d.\n", id);
214                         goto error;
215                 }
216                 if (info.regexdev_capa & RTE_REGEXDEV_CAPA_QUEUE_PAIR_OOS_F)
217                         qp_conf.qp_conf_flags |= RTE_REGEX_QUEUE_PAIR_CFG_OOS_F;
218                 res = rte_regexdev_queue_pair_setup(id, 0, &qp_conf);
219                 if (res < 0) {
220                         printf("Error, can't setup queue pair for device %d.\n",
221                                id);
222                         goto error;
223                 }
224                 printf(":: initializing device: %d done\n", id);
225         }
226         rte_free(rules);
227         return 0;
228 error:
229         if (rules)
230                 rte_free(rules);
231         return res;
232 }
233
234 static void
235 extbuf_free_cb(void *addr __rte_unused, void *fcb_opaque __rte_unused)
236 {
237 }
238
239 static int
240 run_regex(uint32_t nb_jobs,
241           uint16_t nb_max_payload, bool perf_mode, uint32_t nb_iterations,
242           char *data_file, uint8_t nb_max_matches)
243 {
244         char *buf = NULL;
245         long buf_len;
246         long job_len;
247         uint32_t actual_jobs = 0;
248         uint32_t i;
249         struct rte_regex_ops **ops;
250         uint16_t dev_id = 0;
251         uint16_t qp_id = 0;
252         uint8_t nb_matches;
253         struct rte_regexdev_match *match;
254         long pos = 0;
255         unsigned long d_ind = 0;
256         struct rte_mbuf_ext_shared_info shinfo;
257         uint32_t total_enqueue = 0;
258         uint32_t total_dequeue = 0;
259         uint32_t total_matches = 0;
260         int res = 0;
261         time_t start;
262         time_t end;
263         double time;
264         struct job_ctx *jobs_ctx;
265         struct rte_mempool *mbuf_mp;
266
267         shinfo.free_cb = extbuf_free_cb;
268
269         mbuf_mp = rte_pktmbuf_pool_create("mbuf_pool", nb_jobs, 0,
270                         0, MBUF_SIZE, rte_socket_id());
271         if (mbuf_mp == NULL) {
272                 printf("Error, can't create memory pool\n");
273                 return -ENOMEM;
274         }
275
276         ops = rte_malloc(NULL, sizeof(*ops) * nb_jobs, 0);
277         if (!ops) {
278                 printf("Error, can't allocate memory for ops.\n");
279                 return -ENOMEM;
280         }
281
282         jobs_ctx = rte_malloc(NULL, sizeof(struct job_ctx)*nb_jobs, 0);
283         if (!jobs_ctx) {
284                 printf("Error, can't allocate memory for jobs_ctx.\n");
285                 return -ENOMEM;
286         }
287
288         /* Allocate the jobs and assign each job with an mbuf. */
289         for (i = 0; i < nb_jobs; i++) {
290                 ops[i] = rte_malloc(NULL, sizeof(*ops[0]) + nb_max_matches *
291                                     sizeof(struct rte_regexdev_match), 0);
292                 if (!ops[i]) {
293                         printf("Error, can't allocate memory for op.\n");
294                         res = -ENOMEM;
295                         goto end;
296                 }
297                 ops[i]->mbuf = rte_pktmbuf_alloc(mbuf_mp);
298                 if (!ops[i]->mbuf) {
299                         printf("Error, can't attach mbuf.\n");
300                         res = -ENOMEM;
301                         goto end;
302                 }
303         }
304
305         buf_len = read_file(data_file, &buf);
306         if (buf_len <= 0) {
307                 printf("Error, can't read file, or file is empty.\n");
308                 res = -EXIT_FAILURE;
309                 goto end;
310         }
311
312         job_len = buf_len / nb_jobs;
313         if (job_len == 0) {
314                 printf("Error, To many jobs, for the given input.\n");
315                 res = -EXIT_FAILURE;
316                 goto end;
317         }
318
319         if (job_len > nb_max_payload) {
320                 printf("Error, not enough jobs to cover input.\n");
321                 res = -EXIT_FAILURE;
322                 goto end;
323         }
324
325         /* Assign each mbuf with the data to handle. */
326         for (i = 0; (pos < buf_len) && (i < nb_jobs) ; i++) {
327                 long act_job_len = RTE_MIN(job_len, buf_len - pos);
328                 rte_pktmbuf_attach_extbuf(ops[i]->mbuf, &buf[pos], 0,
329                                           act_job_len, &shinfo);
330                 jobs_ctx[i].mbuf = ops[i]->mbuf;
331                 ops[i]->mbuf->data_len = job_len;
332                 ops[i]->mbuf->pkt_len = act_job_len;
333                 ops[i]->user_id = i;
334                 ops[i]->group_id0 = 1;
335                 pos += act_job_len;
336                 actual_jobs++;
337         }
338
339         start = clock();
340         for (i = 0; i < nb_iterations; i++) {
341                 total_enqueue = 0;
342                 total_dequeue = 0;
343                 while (total_dequeue < actual_jobs) {
344                         struct rte_regex_ops **cur_ops_to_enqueue = ops +
345                                 total_enqueue;
346                         struct rte_regex_ops **cur_ops_to_dequeue = ops +
347                                 total_dequeue;
348
349                         if (actual_jobs - total_enqueue)
350                                 total_enqueue += rte_regexdev_enqueue_burst
351                                         (dev_id, qp_id, cur_ops_to_enqueue,
352                                          actual_jobs - total_enqueue);
353
354                         total_dequeue += rte_regexdev_dequeue_burst
355                                 (dev_id, qp_id, cur_ops_to_dequeue,
356                                  total_enqueue - total_dequeue);
357                 }
358         }
359         end = clock();
360         time = ((double)end - start) / CLOCKS_PER_SEC;
361         printf("Job len = %ld Bytes\n",  job_len);
362         printf("Time = %lf sec\n",  time);
363         printf("Perf = %lf Gbps\n",
364                (((double)actual_jobs * job_len * nb_iterations * 8) / time) /
365                 1000000000.0);
366
367         if (!perf_mode) {
368                 /* Log results per job. */
369                 for (d_ind = 0; d_ind < total_dequeue; d_ind++) {
370                         nb_matches = ops[d_ind % actual_jobs]->nb_matches;
371                         printf("Job id %"PRIu64" number of matches = %d\n",
372                                ops[d_ind]->user_id, nb_matches);
373                         total_matches += nb_matches;
374                         match = ops[d_ind % actual_jobs]->matches;
375                         for (i = 0; i < nb_matches; i++) {
376                                 printf("match %d, rule = %d, start = %d,len = %d\n",
377                                        i, match->rule_id, match->start_offset,
378                                        match->len);
379                                 match++;
380                         }
381                 }
382                 printf("Total matches = %d\n", total_matches);
383                 printf("All Matches:\n");
384
385                 /* Log absolute results. */
386                 for (d_ind = 0; d_ind < total_dequeue; d_ind++) {
387                         nb_matches = ops[d_ind % actual_jobs]->nb_matches;
388                         total_matches += nb_matches;
389                         match = ops[d_ind % actual_jobs]->matches;
390                         for (i = 0; i < nb_matches; i++) {
391                                 printf("start = %ld, len = %d, rule = %d\n",
392                                        match->start_offset + d_ind * job_len,
393                                        match->len, match->rule_id);
394                                 match++;
395                         }
396                 }
397         }
398 end:
399         for (i = 0; i < actual_jobs; i++) {
400                 if (ops[i])
401                         rte_free(ops[i]);
402                 if (jobs_ctx[i].mbuf)
403                         rte_pktmbuf_free(jobs_ctx[i].mbuf);
404         }
405         rte_free(ops);
406         rte_free(jobs_ctx);
407         if (buf)
408                 rte_free(buf);
409         if (mbuf_mp)
410                 rte_mempool_free(mbuf_mp);
411
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         uint32_t nb_jobs = 0;
421         uint16_t nb_max_payload = 0;
422         bool perf_mode = 0;
423         uint32_t nb_iterations = 0;
424         uint8_t nb_max_matches = 0;
425         int ret;
426
427         ret = rte_eal_init(argc, argv);
428         if (ret < 0)
429                 rte_exit(EXIT_FAILURE, "EAL init failed\n");
430         argc -= ret;
431         argv += ret;
432         if (argc > 1)
433                 args_parse(argc, argv, rules_file, data_file, &nb_jobs,
434                            &perf_mode, &nb_iterations);
435
436         ret = init_port(&nb_max_payload, rules_file, &nb_max_matches);
437         if (ret < 0)
438                 rte_exit(EXIT_FAILURE, "init port failed\n");
439         ret = run_regex(nb_jobs, nb_max_payload, perf_mode,
440                         nb_iterations, data_file, nb_max_matches);
441         if (ret < 0) {
442                 rte_exit(EXIT_FAILURE, "RegEx function failed\n");
443         }
444         return EXIT_SUCCESS;
445 }