1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #include <rte_eal_memconfig.h> 6 #include <rte_string_fns.h> 7 #include <rte_acl.h> 8 #include <rte_tailq.h> 9 10 #include "acl.h" 11 12 TAILQ_HEAD(rte_acl_list, rte_tailq_entry); 13 14 static struct rte_tailq_elem rte_acl_tailq = { 15 .name = "RTE_ACL", 16 }; 17 EAL_REGISTER_TAILQ(rte_acl_tailq) 18 19 #ifndef CC_AVX512_SUPPORT 20 /* 21 * If the compiler doesn't support AVX512 instructions, 22 * then the dummy one would be used instead for AVX512 classify method. 23 */ 24 int 25 rte_acl_classify_avx512x16(__rte_unused const struct rte_acl_ctx *ctx, 26 __rte_unused const uint8_t **data, 27 __rte_unused uint32_t *results, 28 __rte_unused uint32_t num, 29 __rte_unused uint32_t categories) 30 { 31 return -ENOTSUP; 32 } 33 34 int 35 rte_acl_classify_avx512x32(__rte_unused const struct rte_acl_ctx *ctx, 36 __rte_unused const uint8_t **data, 37 __rte_unused uint32_t *results, 38 __rte_unused uint32_t num, 39 __rte_unused uint32_t categories) 40 { 41 return -ENOTSUP; 42 } 43 #endif 44 45 #ifndef CC_AVX2_SUPPORT 46 /* 47 * If the compiler doesn't support AVX2 instructions, 48 * then the dummy one would be used instead for AVX2 classify method. 49 */ 50 int 51 rte_acl_classify_avx2(__rte_unused const struct rte_acl_ctx *ctx, 52 __rte_unused const uint8_t **data, 53 __rte_unused uint32_t *results, 54 __rte_unused uint32_t num, 55 __rte_unused uint32_t categories) 56 { 57 return -ENOTSUP; 58 } 59 #endif 60 61 #ifndef RTE_ARCH_X86 62 int 63 rte_acl_classify_sse(__rte_unused const struct rte_acl_ctx *ctx, 64 __rte_unused const uint8_t **data, 65 __rte_unused uint32_t *results, 66 __rte_unused uint32_t num, 67 __rte_unused uint32_t categories) 68 { 69 return -ENOTSUP; 70 } 71 #endif 72 73 #ifndef RTE_ARCH_ARM 74 int 75 rte_acl_classify_neon(__rte_unused const struct rte_acl_ctx *ctx, 76 __rte_unused const uint8_t **data, 77 __rte_unused uint32_t *results, 78 __rte_unused uint32_t num, 79 __rte_unused uint32_t categories) 80 { 81 return -ENOTSUP; 82 } 83 #endif 84 85 #ifndef RTE_ARCH_PPC_64 86 int 87 rte_acl_classify_altivec(__rte_unused const struct rte_acl_ctx *ctx, 88 __rte_unused const uint8_t **data, 89 __rte_unused uint32_t *results, 90 __rte_unused uint32_t num, 91 __rte_unused uint32_t categories) 92 { 93 return -ENOTSUP; 94 } 95 #endif 96 97 static const rte_acl_classify_t classify_fns[] = { 98 [RTE_ACL_CLASSIFY_DEFAULT] = rte_acl_classify_scalar, 99 [RTE_ACL_CLASSIFY_SCALAR] = rte_acl_classify_scalar, 100 [RTE_ACL_CLASSIFY_SSE] = rte_acl_classify_sse, 101 [RTE_ACL_CLASSIFY_AVX2] = rte_acl_classify_avx2, 102 [RTE_ACL_CLASSIFY_NEON] = rte_acl_classify_neon, 103 [RTE_ACL_CLASSIFY_ALTIVEC] = rte_acl_classify_altivec, 104 [RTE_ACL_CLASSIFY_AVX512X16] = rte_acl_classify_avx512x16, 105 [RTE_ACL_CLASSIFY_AVX512X32] = rte_acl_classify_avx512x32, 106 }; 107 108 /* 109 * Helper function for acl_check_alg. 110 * Check support for ARM specific classify methods. 111 */ 112 static int 113 acl_check_alg_arm(enum rte_acl_classify_alg alg) 114 { 115 if (alg == RTE_ACL_CLASSIFY_NEON) { 116 #if defined(RTE_ARCH_ARM64) 117 if (rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128) 118 return 0; 119 #elif defined(RTE_ARCH_ARM) 120 if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_NEON) && 121 rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128) 122 return 0; 123 #endif 124 return -ENOTSUP; 125 } 126 127 return -EINVAL; 128 } 129 130 /* 131 * Helper function for acl_check_alg. 132 * Check support for PPC specific classify methods. 133 */ 134 static int 135 acl_check_alg_ppc(enum rte_acl_classify_alg alg) 136 { 137 if (alg == RTE_ACL_CLASSIFY_ALTIVEC) { 138 #if defined(RTE_ARCH_PPC_64) 139 if (rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128) 140 return 0; 141 #endif 142 return -ENOTSUP; 143 } 144 145 return -EINVAL; 146 } 147 148 #ifdef CC_AVX512_SUPPORT 149 static int 150 acl_check_avx512_cpu_flags(void) 151 { 152 return (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX512F) && 153 rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX512VL) && 154 rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX512CD) && 155 rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX512BW)); 156 } 157 #endif 158 159 /* 160 * Helper function for acl_check_alg. 161 * Check support for x86 specific classify methods. 162 */ 163 static int 164 acl_check_alg_x86(enum rte_acl_classify_alg alg) 165 { 166 if (alg == RTE_ACL_CLASSIFY_AVX512X32) { 167 #ifdef CC_AVX512_SUPPORT 168 if (acl_check_avx512_cpu_flags() != 0 && 169 rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_512) 170 return 0; 171 #endif 172 return -ENOTSUP; 173 } 174 175 if (alg == RTE_ACL_CLASSIFY_AVX512X16) { 176 #ifdef CC_AVX512_SUPPORT 177 if (acl_check_avx512_cpu_flags() != 0 && 178 rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_256) 179 return 0; 180 #endif 181 return -ENOTSUP; 182 } 183 184 if (alg == RTE_ACL_CLASSIFY_AVX2) { 185 #ifdef CC_AVX2_SUPPORT 186 if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX2) && 187 rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_256) 188 return 0; 189 #endif 190 return -ENOTSUP; 191 } 192 193 if (alg == RTE_ACL_CLASSIFY_SSE) { 194 #ifdef RTE_ARCH_X86 195 if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_SSE4_1) && 196 rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128) 197 return 0; 198 #endif 199 return -ENOTSUP; 200 } 201 202 return -EINVAL; 203 } 204 205 /* 206 * Check if input alg is supported by given platform/binary. 207 * Note that both conditions should be met: 208 * - at build time compiler supports ISA used by given methods 209 * - at run time target cpu supports necessary ISA. 210 */ 211 static int 212 acl_check_alg(enum rte_acl_classify_alg alg) 213 { 214 switch (alg) { 215 case RTE_ACL_CLASSIFY_NEON: 216 return acl_check_alg_arm(alg); 217 case RTE_ACL_CLASSIFY_ALTIVEC: 218 return acl_check_alg_ppc(alg); 219 case RTE_ACL_CLASSIFY_AVX512X32: 220 case RTE_ACL_CLASSIFY_AVX512X16: 221 case RTE_ACL_CLASSIFY_AVX2: 222 case RTE_ACL_CLASSIFY_SSE: 223 return acl_check_alg_x86(alg); 224 /* scalar method is supported on all platforms */ 225 case RTE_ACL_CLASSIFY_SCALAR: 226 return 0; 227 default: 228 return -EINVAL; 229 } 230 } 231 232 /* 233 * Get preferred alg for given platform. 234 */ 235 static enum rte_acl_classify_alg 236 acl_get_best_alg(void) 237 { 238 /* 239 * array of supported methods for each platform. 240 * Note that order is important - from most to less preferable. 241 */ 242 static const enum rte_acl_classify_alg alg[] = { 243 #if defined(RTE_ARCH_ARM) 244 RTE_ACL_CLASSIFY_NEON, 245 #elif defined(RTE_ARCH_PPC_64) 246 RTE_ACL_CLASSIFY_ALTIVEC, 247 #elif defined(RTE_ARCH_X86) 248 RTE_ACL_CLASSIFY_AVX512X32, 249 RTE_ACL_CLASSIFY_AVX512X16, 250 RTE_ACL_CLASSIFY_AVX2, 251 RTE_ACL_CLASSIFY_SSE, 252 #endif 253 RTE_ACL_CLASSIFY_SCALAR, 254 }; 255 256 uint32_t i; 257 258 /* find best possible alg */ 259 for (i = 0; i != RTE_DIM(alg) && acl_check_alg(alg[i]) != 0; i++) 260 ; 261 262 /* we always have to find something suitable */ 263 RTE_VERIFY(i != RTE_DIM(alg)); 264 return alg[i]; 265 } 266 267 extern int 268 rte_acl_set_ctx_classify(struct rte_acl_ctx *ctx, enum rte_acl_classify_alg alg) 269 { 270 int32_t rc; 271 272 /* formal parameters check */ 273 if (ctx == NULL || (uint32_t)alg >= RTE_DIM(classify_fns)) 274 return -EINVAL; 275 276 /* user asked us to select the *best* one */ 277 if (alg == RTE_ACL_CLASSIFY_DEFAULT) 278 alg = acl_get_best_alg(); 279 280 /* check that given alg is supported */ 281 rc = acl_check_alg(alg); 282 if (rc != 0) 283 return rc; 284 285 ctx->alg = alg; 286 return 0; 287 } 288 289 int 290 rte_acl_classify_alg(const struct rte_acl_ctx *ctx, const uint8_t **data, 291 uint32_t *results, uint32_t num, uint32_t categories, 292 enum rte_acl_classify_alg alg) 293 { 294 if (categories != 1 && 295 ((RTE_ACL_RESULTS_MULTIPLIER - 1) & categories) != 0) 296 return -EINVAL; 297 298 return classify_fns[alg](ctx, data, results, num, categories); 299 } 300 301 int 302 rte_acl_classify(const struct rte_acl_ctx *ctx, const uint8_t **data, 303 uint32_t *results, uint32_t num, uint32_t categories) 304 { 305 return rte_acl_classify_alg(ctx, data, results, num, categories, 306 ctx->alg); 307 } 308 309 struct rte_acl_ctx * 310 rte_acl_find_existing(const char *name) 311 { 312 struct rte_acl_ctx *ctx = NULL; 313 struct rte_acl_list *acl_list; 314 struct rte_tailq_entry *te; 315 316 acl_list = RTE_TAILQ_CAST(rte_acl_tailq.head, rte_acl_list); 317 318 rte_mcfg_tailq_read_lock(); 319 TAILQ_FOREACH(te, acl_list, next) { 320 ctx = (struct rte_acl_ctx *) te->data; 321 if (strncmp(name, ctx->name, sizeof(ctx->name)) == 0) 322 break; 323 } 324 rte_mcfg_tailq_read_unlock(); 325 326 if (te == NULL) { 327 rte_errno = ENOENT; 328 return NULL; 329 } 330 return ctx; 331 } 332 333 void 334 rte_acl_free(struct rte_acl_ctx *ctx) 335 { 336 struct rte_acl_list *acl_list; 337 struct rte_tailq_entry *te; 338 339 if (ctx == NULL) 340 return; 341 342 acl_list = RTE_TAILQ_CAST(rte_acl_tailq.head, rte_acl_list); 343 344 rte_mcfg_tailq_write_lock(); 345 346 /* find our tailq entry */ 347 TAILQ_FOREACH(te, acl_list, next) { 348 if (te->data == (void *) ctx) 349 break; 350 } 351 if (te == NULL) { 352 rte_mcfg_tailq_write_unlock(); 353 return; 354 } 355 356 TAILQ_REMOVE(acl_list, te, next); 357 358 rte_mcfg_tailq_write_unlock(); 359 360 rte_free(ctx->mem); 361 rte_free(ctx); 362 rte_free(te); 363 } 364 365 struct rte_acl_ctx * 366 rte_acl_create(const struct rte_acl_param *param) 367 { 368 size_t sz; 369 struct rte_acl_ctx *ctx; 370 struct rte_acl_list *acl_list; 371 struct rte_tailq_entry *te; 372 char name[sizeof(ctx->name)]; 373 374 acl_list = RTE_TAILQ_CAST(rte_acl_tailq.head, rte_acl_list); 375 376 /* check that input parameters are valid. */ 377 if (param == NULL || param->name == NULL) { 378 rte_errno = EINVAL; 379 return NULL; 380 } 381 382 snprintf(name, sizeof(name), "ACL_%s", param->name); 383 384 /* calculate amount of memory required for pattern set. */ 385 sz = sizeof(*ctx) + param->max_rule_num * param->rule_size; 386 387 /* get EAL TAILQ lock. */ 388 rte_mcfg_tailq_write_lock(); 389 390 /* if we already have one with that name */ 391 TAILQ_FOREACH(te, acl_list, next) { 392 ctx = (struct rte_acl_ctx *) te->data; 393 if (strncmp(param->name, ctx->name, sizeof(ctx->name)) == 0) 394 break; 395 } 396 397 /* if ACL with such name doesn't exist, then create a new one. */ 398 if (te == NULL) { 399 ctx = NULL; 400 te = rte_zmalloc("ACL_TAILQ_ENTRY", sizeof(*te), 0); 401 402 if (te == NULL) { 403 RTE_LOG(ERR, ACL, "Cannot allocate tailq entry!\n"); 404 goto exit; 405 } 406 407 ctx = rte_zmalloc_socket(name, sz, RTE_CACHE_LINE_SIZE, param->socket_id); 408 409 if (ctx == NULL) { 410 RTE_LOG(ERR, ACL, 411 "allocation of %zu bytes on socket %d for %s failed\n", 412 sz, param->socket_id, name); 413 rte_free(te); 414 goto exit; 415 } 416 /* init new allocated context. */ 417 ctx->rules = ctx + 1; 418 ctx->max_rules = param->max_rule_num; 419 ctx->rule_sz = param->rule_size; 420 ctx->socket_id = param->socket_id; 421 ctx->alg = acl_get_best_alg(); 422 strlcpy(ctx->name, param->name, sizeof(ctx->name)); 423 424 te->data = (void *) ctx; 425 426 TAILQ_INSERT_TAIL(acl_list, te, next); 427 } 428 429 exit: 430 rte_mcfg_tailq_write_unlock(); 431 return ctx; 432 } 433 434 static int 435 acl_add_rules(struct rte_acl_ctx *ctx, const void *rules, uint32_t num) 436 { 437 uint8_t *pos; 438 439 if (num + ctx->num_rules > ctx->max_rules) 440 return -ENOMEM; 441 442 pos = ctx->rules; 443 pos += ctx->rule_sz * ctx->num_rules; 444 memcpy(pos, rules, num * ctx->rule_sz); 445 ctx->num_rules += num; 446 447 return 0; 448 } 449 450 static int 451 acl_check_rule(const struct rte_acl_rule_data *rd) 452 { 453 if ((RTE_LEN2MASK(RTE_ACL_MAX_CATEGORIES, typeof(rd->category_mask)) & 454 rd->category_mask) == 0 || 455 rd->priority > RTE_ACL_MAX_PRIORITY || 456 rd->priority < RTE_ACL_MIN_PRIORITY) 457 return -EINVAL; 458 return 0; 459 } 460 461 int 462 rte_acl_add_rules(struct rte_acl_ctx *ctx, const struct rte_acl_rule *rules, 463 uint32_t num) 464 { 465 const struct rte_acl_rule *rv; 466 uint32_t i; 467 int32_t rc; 468 469 if (ctx == NULL || rules == NULL || 0 == ctx->rule_sz) 470 return -EINVAL; 471 472 for (i = 0; i != num; i++) { 473 rv = (const struct rte_acl_rule *) 474 ((uintptr_t)rules + i * ctx->rule_sz); 475 rc = acl_check_rule(&rv->data); 476 if (rc != 0) { 477 RTE_LOG(ERR, ACL, "%s(%s): rule #%u is invalid\n", 478 __func__, ctx->name, i + 1); 479 return rc; 480 } 481 } 482 483 return acl_add_rules(ctx, rules, num); 484 } 485 486 /* 487 * Reset all rules. 488 * Note that RT structures are not affected. 489 */ 490 void 491 rte_acl_reset_rules(struct rte_acl_ctx *ctx) 492 { 493 if (ctx != NULL) 494 ctx->num_rules = 0; 495 } 496 497 /* 498 * Reset all rules and destroys RT structures. 499 */ 500 void 501 rte_acl_reset(struct rte_acl_ctx *ctx) 502 { 503 if (ctx != NULL) { 504 rte_acl_reset_rules(ctx); 505 rte_acl_build(ctx, &ctx->config); 506 } 507 } 508 509 /* 510 * Dump ACL context to the stdout. 511 */ 512 void 513 rte_acl_dump(const struct rte_acl_ctx *ctx) 514 { 515 if (!ctx) 516 return; 517 printf("acl context <%s>@%p\n", ctx->name, ctx); 518 printf(" socket_id=%"PRId32"\n", ctx->socket_id); 519 printf(" alg=%"PRId32"\n", ctx->alg); 520 printf(" first_load_sz=%"PRIu32"\n", ctx->first_load_sz); 521 printf(" max_rules=%"PRIu32"\n", ctx->max_rules); 522 printf(" rule_size=%"PRIu32"\n", ctx->rule_sz); 523 printf(" num_rules=%"PRIu32"\n", ctx->num_rules); 524 printf(" num_categories=%"PRIu32"\n", ctx->num_categories); 525 printf(" num_tries=%"PRIu32"\n", ctx->num_tries); 526 } 527 528 /* 529 * Dump all ACL contexts to the stdout. 530 */ 531 void 532 rte_acl_list_dump(void) 533 { 534 struct rte_acl_ctx *ctx; 535 struct rte_acl_list *acl_list; 536 struct rte_tailq_entry *te; 537 538 acl_list = RTE_TAILQ_CAST(rte_acl_tailq.head, rte_acl_list); 539 540 rte_mcfg_tailq_read_lock(); 541 TAILQ_FOREACH(te, acl_list, next) { 542 ctx = (struct rte_acl_ctx *) te->data; 543 rte_acl_dump(ctx); 544 } 545 rte_mcfg_tailq_read_unlock(); 546 } 547