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