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 } 450