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 }; 71 72 argvopt = argv; 73 while ((opt = getopt_long(argc, argvopt, "", 74 lgopts, &opt_idx)) != EOF) { 75 switch (opt) { 76 case ARG_RULES_FILE_NAME: 77 len = strnlen(optarg, MAX_FILE_NAME - 1); 78 if (len == MAX_FILE_NAME) 79 rte_exit(EXIT_FAILURE, 80 "Rule file name to long max %d\n", 81 MAX_FILE_NAME - 1); 82 strncpy(rules_file, optarg, MAX_FILE_NAME - 1); 83 break; 84 case ARG_DATA_FILE_NAME: 85 len = strnlen(optarg, MAX_FILE_NAME - 1); 86 if (len == MAX_FILE_NAME) 87 rte_exit(EXIT_FAILURE, 88 "Data file name to long max %d\n", 89 MAX_FILE_NAME - 1); 90 strncpy(data_file, optarg, MAX_FILE_NAME - 1); 91 break; 92 case ARG_NUM_OF_JOBS: 93 *nb_jobs = atoi(optarg); 94 break; 95 case ARG_PERF_MODE: 96 *perf_mode = true; 97 break; 98 case ARG_NUM_OF_ITERATIONS: 99 *nb_iterations = atoi(optarg); 100 break; 101 case ARG_HELP: 102 usage("RegEx test app"); 103 break; 104 default: 105 fprintf(stderr, "Invalid option: %s\n", argv[optind]); 106 usage("RegEx test app"); 107 rte_exit(EXIT_FAILURE, "Invalid option\n"); 108 break; 109 } 110 } 111 112 if (!perf_mode) 113 *nb_iterations = 1; 114 } 115 116 static long 117 read_file(char *file, char **buf) 118 { 119 FILE *fp; 120 long buf_len = 0; 121 size_t read_len; 122 int res = 0; 123 124 fp = fopen(file, "r"); 125 if (!fp) 126 return -EIO; 127 if (fseek(fp, 0L, SEEK_END) == 0) { 128 buf_len = ftell(fp); 129 if (buf_len == -1) { 130 res = EIO; 131 goto error; 132 } 133 *buf = rte_malloc(NULL, sizeof(char) * (buf_len + 1), 4096); 134 if (!*buf) { 135 res = ENOMEM; 136 goto error; 137 } 138 if (fseek(fp, 0L, SEEK_SET) != 0) { 139 res = EIO; 140 goto error; 141 } 142 read_len = fread(*buf, sizeof(char), buf_len, fp); 143 if (read_len != (unsigned long)buf_len) { 144 res = EIO; 145 goto error; 146 } 147 } 148 fclose(fp); 149 return buf_len; 150 error: 151 printf("Error, can't open file %s\n, err = %d", file, res); 152 if (fp) 153 fclose(fp); 154 if (*buf) 155 rte_free(*buf); 156 return -res; 157 } 158 159 static int 160 init_port(struct rte_mempool **mbuf_mp, uint32_t nb_jobs, 161 uint16_t *nb_max_payload, char *rules_file, uint8_t *nb_max_matches) 162 { 163 uint16_t id; 164 uint16_t num_devs; 165 char *rules = NULL; 166 long rules_len; 167 struct rte_regexdev_info info; 168 struct rte_regexdev_config dev_conf = { 169 .nb_queue_pairs = 1, 170 .nb_groups = 1, 171 }; 172 struct rte_regexdev_qp_conf qp_conf = { 173 .nb_desc = 1024, 174 .qp_conf_flags = RTE_REGEX_QUEUE_PAIR_CFG_OOS_F, 175 }; 176 int res = 0; 177 178 num_devs = rte_regexdev_count(); 179 if (num_devs == 0) { 180 printf("Error, no devices detected.\n"); 181 return -EINVAL; 182 } 183 184 *mbuf_mp = rte_pktmbuf_pool_create("mbuf_pool", nb_jobs, 0, 185 0, MBUF_SIZE, rte_socket_id()); 186 if (*mbuf_mp == NULL) { 187 printf("Error, can't create memory pool\n"); 188 res = -ENOMEM; 189 goto error; 190 } 191 192 rules_len = read_file(rules_file, &rules); 193 if (rules_len < 0) { 194 printf("Error, can't read rules files.\n"); 195 res = -EIO; 196 goto error; 197 } 198 199 for (id = 0; id < num_devs; id++) { 200 res = rte_regexdev_info_get(id, &info); 201 if (res != 0) { 202 printf("Error, can't get device info.\n"); 203 goto error; 204 } 205 printf(":: initializing dev: %d\n", id); 206 *nb_max_matches = info.max_matches; 207 *nb_max_payload = info.max_payload_size; 208 if (info.regexdev_capa & RTE_REGEXDEV_SUPP_MATCH_AS_END_F) 209 dev_conf.dev_cfg_flags |= RTE_REGEXDEV_CFG_MATCH_AS_END_F; 210 dev_conf.nb_max_matches = info.max_matches; 211 dev_conf.nb_rules_per_group = info.max_rules_per_group; 212 dev_conf.rule_db_len = rules_len; 213 dev_conf.rule_db = rules; 214 res = rte_regexdev_configure(id, &dev_conf); 215 if (res < 0) { 216 printf("Error, can't configure device %d.\n", id); 217 goto error; 218 } 219 res = rte_regexdev_queue_pair_setup(id, 0, &qp_conf); 220 if (res < 0) { 221 printf("Error, can't setup queue pair for device %d.\n", 222 id); 223 goto error; 224 } 225 printf(":: initializing device: %d done\n", id); 226 } 227 rte_free(rules); 228 return 0; 229 error: 230 if (rules) 231 rte_free(rules); 232 if (*mbuf_mp) 233 rte_mempool_free(*mbuf_mp); 234 return res; 235 } 236 237 static void 238 extbuf_free_cb(void *addr __rte_unused, void *fcb_opaque __rte_unused) 239 { 240 } 241 242 static int 243 run_regex(struct rte_mempool *mbuf_mp, uint32_t nb_jobs, 244 uint16_t nb_max_payload, bool perf_mode, uint32_t nb_iterations, 245 char *data_file, uint8_t nb_max_matches) 246 { 247 char *buf = NULL; 248 long buf_len; 249 long job_len; 250 uint32_t actual_jobs = 0; 251 uint32_t i; 252 struct rte_regex_ops **ops; 253 uint16_t dev_id = 0; 254 uint16_t qp_id = 0; 255 uint8_t nb_matches; 256 struct rte_regexdev_match *match; 257 long pos = 0; 258 unsigned long d_ind = 0; 259 struct rte_mbuf_ext_shared_info shinfo; 260 uint32_t total_enqueue = 0; 261 uint32_t total_dequeue = 0; 262 uint32_t total_matches = 0; 263 int res = 0; 264 time_t start; 265 time_t end; 266 double time; 267 268 shinfo.free_cb = extbuf_free_cb; 269 270 ops = rte_malloc(NULL, sizeof(*ops) * nb_jobs, 0); 271 if (!ops) { 272 printf("Error, can't allocate memory for ops.\n"); 273 return -ENOMEM; 274 } 275 276 /* Allocate the jobs and assign each job with an mbuf. */ 277 for (i = 0; i < nb_jobs; i++) { 278 ops[i] = rte_malloc(NULL, sizeof(*ops[0]) + nb_max_matches * 279 sizeof(struct rte_regexdev_match), 0); 280 if (!ops[i]) { 281 printf("Error, can't allocate memory for op.\n"); 282 res = -ENOMEM; 283 goto end; 284 } 285 ops[i]->mbuf = rte_pktmbuf_alloc(mbuf_mp); 286 if (!ops[i]->mbuf) { 287 printf("Error, can't attach mbuf.\n"); 288 res = -ENOMEM; 289 goto end; 290 } 291 } 292 293 buf_len = read_file(data_file, &buf); 294 if (buf_len <= 0) { 295 printf("Error, can't read file, or file is empty.\n"); 296 res = -EXIT_FAILURE; 297 goto end; 298 } 299 300 job_len = buf_len / nb_jobs; 301 if (job_len == 0) { 302 printf("Error, To many jobs, for the given input.\n"); 303 res = -EXIT_FAILURE; 304 goto end; 305 } 306 307 if (job_len > nb_max_payload) { 308 printf("Error, not enough jobs to cover input.\n"); 309 res = -EXIT_FAILURE; 310 goto end; 311 } 312 313 /* Assign each mbuf with the data to handle. */ 314 for (i = 0; (pos < buf_len) && (i < nb_jobs) ; i++) { 315 long act_job_len = RTE_MIN(job_len, buf_len - pos); 316 rte_pktmbuf_attach_extbuf(ops[i]->mbuf, &buf[pos], 0, 317 act_job_len, &shinfo); 318 ops[i]->mbuf->data_len = job_len; 319 ops[i]->mbuf->pkt_len = act_job_len; 320 ops[i]->user_id = i; 321 ops[i]->group_id0 = 1; 322 pos += act_job_len; 323 actual_jobs++; 324 } 325 326 start = clock(); 327 for (i = 0; i < nb_iterations; i++) { 328 total_enqueue = 0; 329 total_dequeue = 0; 330 while (total_dequeue < actual_jobs) { 331 struct rte_regex_ops **cur_ops_to_enqueue = ops + 332 total_enqueue; 333 struct rte_regex_ops **cur_ops_to_dequeue = ops + 334 total_dequeue; 335 336 if (actual_jobs - total_enqueue) 337 total_enqueue += rte_regexdev_enqueue_burst 338 (dev_id, qp_id, cur_ops_to_enqueue, 339 actual_jobs - total_enqueue); 340 341 total_dequeue += rte_regexdev_dequeue_burst 342 (dev_id, qp_id, cur_ops_to_dequeue, 343 total_enqueue - total_dequeue); 344 } 345 } 346 end = clock(); 347 time = ((double)end - start) / CLOCKS_PER_SEC; 348 printf("Job len = %ld Bytes\n", job_len); 349 printf("Time = %lf sec\n", time); 350 printf("Perf = %lf Gbps\n", 351 (((double)actual_jobs * job_len * nb_iterations * 8) / time) / 352 1000000000.0); 353 354 if (!perf_mode) { 355 /* Log results per job. */ 356 for (d_ind = 0; d_ind < total_dequeue; d_ind++) { 357 nb_matches = ops[d_ind % actual_jobs]->nb_matches; 358 printf("Job id %"PRIu64" number of matches = %d\n", 359 ops[d_ind]->user_id, nb_matches); 360 total_matches += nb_matches; 361 match = ops[d_ind % actual_jobs]->matches; 362 for (i = 0; i < nb_matches; i++) { 363 printf("match %d, rule = %d, start = %d,len = %d\n", 364 i, match->rule_id, match->start_offset, 365 match->len); 366 match++; 367 } 368 } 369 printf("Total matches = %d\n", total_matches); 370 printf("All Matches:\n"); 371 372 /* Log absolute results. */ 373 for (d_ind = 0; d_ind < total_dequeue; d_ind++) { 374 nb_matches = ops[d_ind % actual_jobs]->nb_matches; 375 total_matches += nb_matches; 376 match = ops[d_ind % actual_jobs]->matches; 377 for (i = 0; i < nb_matches; i++) { 378 printf("start = %ld, len = %d, rule = %d\n", 379 match->start_offset + d_ind * job_len, 380 match->len, match->rule_id); 381 match++; 382 } 383 } 384 } 385 end: 386 for (i = 0; i < actual_jobs; i++) { 387 if (ops[i]) { 388 if (ops[i]->mbuf) 389 rte_pktmbuf_free(ops[i]->mbuf); 390 rte_free(ops[i]); 391 } 392 } 393 rte_free(ops); 394 if (buf) 395 rte_free(buf); 396 return res; 397 } 398 399 int 400 main(int argc, char **argv) 401 { 402 char rules_file[MAX_FILE_NAME]; 403 char data_file[MAX_FILE_NAME]; 404 struct rte_mempool *mbuf_mp = NULL; 405 uint32_t nb_jobs = 0; 406 uint16_t nb_max_payload = 0; 407 bool perf_mode = 0; 408 uint32_t nb_iterations = 0; 409 uint8_t nb_max_matches = 0; 410 int ret; 411 412 ret = rte_eal_init(argc, argv); 413 if (ret < 0) 414 rte_exit(EXIT_FAILURE, "EAL init failed\n"); 415 argc -= ret; 416 argv += ret; 417 if (argc > 1) 418 args_parse(argc, argv, rules_file, data_file, &nb_jobs, 419 &perf_mode, &nb_iterations); 420 421 ret = init_port(&mbuf_mp, nb_jobs, &nb_max_payload, rules_file, 422 &nb_max_matches); 423 if (ret < 0) 424 rte_exit(EXIT_FAILURE, "init port failed\n"); 425 ret = run_regex(mbuf_mp, nb_jobs, nb_max_payload, perf_mode, 426 nb_iterations, data_file, nb_max_matches); 427 if (ret < 0) { 428 rte_mempool_free(mbuf_mp); 429 rte_exit(EXIT_FAILURE, "RegEx function failed\n"); 430 } 431 rte_mempool_free(mbuf_mp); 432 return EXIT_SUCCESS; 433 } 434