xref: /dpdk/app/test-regex/main.c (revision cb056611a8ed9ab9024f3b91bf26e97255194514)
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 = RTE_REGEX_QUEUE_PAIR_CFG_OOS_F,
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 		res = rte_regexdev_queue_pair_setup(id, 0, &qp_conf);
222 		if (res < 0) {
223 			printf("Error, can't setup queue pair for device %d.\n",
224 			       id);
225 			goto error;
226 		}
227 		printf(":: initializing device: %d done\n", id);
228 	}
229 	rte_free(rules);
230 	return 0;
231 error:
232 	if (rules)
233 		rte_free(rules);
234 	if (*mbuf_mp)
235 		rte_mempool_free(*mbuf_mp);
236 	return res;
237 }
238 
239 static void
240 extbuf_free_cb(void *addr __rte_unused, void *fcb_opaque __rte_unused)
241 {
242 }
243 
244 static int
245 run_regex(struct rte_mempool *mbuf_mp, uint32_t nb_jobs,
246 	  uint16_t nb_max_payload, bool perf_mode, uint32_t nb_iterations,
247 	  char *data_file, uint8_t nb_max_matches)
248 {
249 	char *buf = NULL;
250 	long buf_len;
251 	long job_len;
252 	uint32_t actual_jobs = 0;
253 	uint32_t i;
254 	struct rte_regex_ops **ops;
255 	uint16_t dev_id = 0;
256 	uint16_t qp_id = 0;
257 	uint8_t nb_matches;
258 	struct rte_regexdev_match *match;
259 	long pos = 0;
260 	unsigned long d_ind = 0;
261 	struct rte_mbuf_ext_shared_info shinfo;
262 	uint32_t total_enqueue = 0;
263 	uint32_t total_dequeue = 0;
264 	uint32_t total_matches = 0;
265 	int res = 0;
266 	time_t start;
267 	time_t end;
268 	double time;
269 
270 	shinfo.free_cb = extbuf_free_cb;
271 
272 	ops = rte_malloc(NULL, sizeof(*ops) * nb_jobs, 0);
273 	if (!ops) {
274 		printf("Error, can't allocate memory for ops.\n");
275 		return -ENOMEM;
276 	}
277 
278 	/* Allocate the jobs and assign each job with an mbuf. */
279 	for (i = 0; i < nb_jobs; i++) {
280 		ops[i] = rte_malloc(NULL, sizeof(*ops[0]) + nb_max_matches *
281 				    sizeof(struct rte_regexdev_match), 0);
282 		if (!ops[i]) {
283 			printf("Error, can't allocate memory for op.\n");
284 			res = -ENOMEM;
285 			goto end;
286 		}
287 		ops[i]->mbuf = rte_pktmbuf_alloc(mbuf_mp);
288 		if (!ops[i]->mbuf) {
289 			printf("Error, can't attach mbuf.\n");
290 			res = -ENOMEM;
291 			goto end;
292 		}
293 	}
294 
295 	buf_len = read_file(data_file, &buf);
296 	if (buf_len <= 0) {
297 		printf("Error, can't read file, or file is empty.\n");
298 		res = -EXIT_FAILURE;
299 		goto end;
300 	}
301 
302 	job_len = buf_len / nb_jobs;
303 	if (job_len == 0) {
304 		printf("Error, To many jobs, for the given input.\n");
305 		res = -EXIT_FAILURE;
306 		goto end;
307 	}
308 
309 	if (job_len > nb_max_payload) {
310 		printf("Error, not enough jobs to cover input.\n");
311 		res = -EXIT_FAILURE;
312 		goto end;
313 	}
314 
315 	/* Assign each mbuf with the data to handle. */
316 	for (i = 0; (pos < buf_len) && (i < nb_jobs) ; i++) {
317 		long act_job_len = RTE_MIN(job_len, buf_len - pos);
318 		rte_pktmbuf_attach_extbuf(ops[i]->mbuf, &buf[pos], 0,
319 					  act_job_len, &shinfo);
320 		ops[i]->mbuf->data_len = job_len;
321 		ops[i]->mbuf->pkt_len = act_job_len;
322 		ops[i]->user_id = i;
323 		ops[i]->group_id0 = 1;
324 		pos += act_job_len;
325 		actual_jobs++;
326 	}
327 
328 	start = clock();
329 	for (i = 0; i < nb_iterations; i++) {
330 		total_enqueue = 0;
331 		total_dequeue = 0;
332 		while (total_dequeue < actual_jobs) {
333 			struct rte_regex_ops **cur_ops_to_enqueue = ops +
334 				total_enqueue;
335 			struct rte_regex_ops **cur_ops_to_dequeue = ops +
336 				total_dequeue;
337 
338 			if (actual_jobs - total_enqueue)
339 				total_enqueue += rte_regexdev_enqueue_burst
340 					(dev_id, qp_id, cur_ops_to_enqueue,
341 					 actual_jobs - total_enqueue);
342 
343 			total_dequeue += rte_regexdev_dequeue_burst
344 				(dev_id, qp_id, cur_ops_to_dequeue,
345 				 total_enqueue - total_dequeue);
346 		}
347 	}
348 	end = clock();
349 	time = ((double)end - start) / CLOCKS_PER_SEC;
350 	printf("Job len = %ld Bytes\n",  job_len);
351 	printf("Time = %lf sec\n",  time);
352 	printf("Perf = %lf Gbps\n",
353 	       (((double)actual_jobs * job_len * nb_iterations * 8) / time) /
354 		1000000000.0);
355 
356 	if (!perf_mode) {
357 		/* Log results per job. */
358 		for (d_ind = 0; d_ind < total_dequeue; d_ind++) {
359 			nb_matches = ops[d_ind % actual_jobs]->nb_matches;
360 			printf("Job id %"PRIu64" number of matches = %d\n",
361 			       ops[d_ind]->user_id, nb_matches);
362 			total_matches += nb_matches;
363 			match = ops[d_ind % actual_jobs]->matches;
364 			for (i = 0; i < nb_matches; i++) {
365 				printf("match %d, rule = %d, start = %d,len = %d\n",
366 				       i, match->rule_id, match->start_offset,
367 				       match->len);
368 				match++;
369 			}
370 		}
371 		printf("Total matches = %d\n", total_matches);
372 		printf("All Matches:\n");
373 
374 		/* Log absolute results. */
375 		for (d_ind = 0; d_ind < total_dequeue; d_ind++) {
376 			nb_matches = ops[d_ind % actual_jobs]->nb_matches;
377 			total_matches += nb_matches;
378 			match = ops[d_ind % actual_jobs]->matches;
379 			for (i = 0; i < nb_matches; i++) {
380 				printf("start = %ld, len = %d, rule = %d\n",
381 				       match->start_offset + d_ind * job_len,
382 				       match->len, match->rule_id);
383 				match++;
384 			}
385 		}
386 	}
387 end:
388 	for (i = 0; i < actual_jobs; i++) {
389 		if (ops[i]) {
390 			if (ops[i]->mbuf)
391 				rte_pktmbuf_free(ops[i]->mbuf);
392 			rte_free(ops[i]);
393 		}
394 	}
395 	rte_free(ops);
396 	if (buf)
397 		rte_free(buf);
398 	return res;
399 }
400 
401 int
402 main(int argc, char **argv)
403 {
404 	char rules_file[MAX_FILE_NAME];
405 	char data_file[MAX_FILE_NAME];
406 	struct rte_mempool *mbuf_mp = NULL;
407 	uint32_t nb_jobs = 0;
408 	uint16_t nb_max_payload = 0;
409 	bool perf_mode = 0;
410 	uint32_t nb_iterations = 0;
411 	uint8_t nb_max_matches = 0;
412 	int ret;
413 
414 	ret = rte_eal_init(argc, argv);
415 	if (ret < 0)
416 		rte_exit(EXIT_FAILURE, "EAL init failed\n");
417 	argc -= ret;
418 	argv += ret;
419 	if (argc > 1)
420 		args_parse(argc, argv, rules_file, data_file, &nb_jobs,
421 			   &perf_mode, &nb_iterations);
422 
423 	ret = init_port(&mbuf_mp, nb_jobs, &nb_max_payload, rules_file,
424 			&nb_max_matches);
425 	if (ret < 0)
426 		rte_exit(EXIT_FAILURE, "init port failed\n");
427 	ret = run_regex(mbuf_mp, nb_jobs, nb_max_payload, perf_mode,
428 			nb_iterations, data_file, nb_max_matches);
429 	if (ret < 0) {
430 		rte_mempool_free(mbuf_mp);
431 		rte_exit(EXIT_FAILURE, "RegEx function failed\n");
432 	}
433 	rte_mempool_free(mbuf_mp);
434 	return EXIT_SUCCESS;
435 }
436