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