1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #include <rte_acl.h> 6 #include "acl.h" 7 8 TAILQ_HEAD(rte_acl_list, rte_tailq_entry); 9 10 static struct rte_tailq_elem rte_acl_tailq = { 11 .name = "RTE_ACL", 12 }; 13 EAL_REGISTER_TAILQ(rte_acl_tailq) 14 15 /* 16 * If the compiler doesn't support AVX2 instructions, 17 * then the dummy one would be used instead for AVX2 classify method. 18 */ 19 __rte_weak int 20 rte_acl_classify_avx2(__rte_unused const struct rte_acl_ctx *ctx, 21 __rte_unused const uint8_t **data, 22 __rte_unused uint32_t *results, 23 __rte_unused uint32_t num, 24 __rte_unused uint32_t categories) 25 { 26 return -ENOTSUP; 27 } 28 29 __rte_weak int 30 rte_acl_classify_sse(__rte_unused const struct rte_acl_ctx *ctx, 31 __rte_unused const uint8_t **data, 32 __rte_unused uint32_t *results, 33 __rte_unused uint32_t num, 34 __rte_unused uint32_t categories) 35 { 36 return -ENOTSUP; 37 } 38 39 __rte_weak int 40 rte_acl_classify_neon(__rte_unused const struct rte_acl_ctx *ctx, 41 __rte_unused const uint8_t **data, 42 __rte_unused uint32_t *results, 43 __rte_unused uint32_t num, 44 __rte_unused uint32_t categories) 45 { 46 return -ENOTSUP; 47 } 48 49 __rte_weak int 50 rte_acl_classify_altivec(__rte_unused const struct rte_acl_ctx *ctx, 51 __rte_unused const uint8_t **data, 52 __rte_unused uint32_t *results, 53 __rte_unused uint32_t num, 54 __rte_unused uint32_t categories) 55 { 56 return -ENOTSUP; 57 } 58 59 static const rte_acl_classify_t classify_fns[] = { 60 [RTE_ACL_CLASSIFY_DEFAULT] = rte_acl_classify_scalar, 61 [RTE_ACL_CLASSIFY_SCALAR] = rte_acl_classify_scalar, 62 [RTE_ACL_CLASSIFY_SSE] = rte_acl_classify_sse, 63 [RTE_ACL_CLASSIFY_AVX2] = rte_acl_classify_avx2, 64 [RTE_ACL_CLASSIFY_NEON] = rte_acl_classify_neon, 65 [RTE_ACL_CLASSIFY_ALTIVEC] = rte_acl_classify_altivec, 66 }; 67 68 /* by default, use always available scalar code path. */ 69 static enum rte_acl_classify_alg rte_acl_default_classify = 70 RTE_ACL_CLASSIFY_SCALAR; 71 72 static void 73 rte_acl_set_default_classify(enum rte_acl_classify_alg alg) 74 { 75 rte_acl_default_classify = alg; 76 } 77 78 extern int 79 rte_acl_set_ctx_classify(struct rte_acl_ctx *ctx, enum rte_acl_classify_alg alg) 80 { 81 if (ctx == NULL || (uint32_t)alg >= RTE_DIM(classify_fns)) 82 return -EINVAL; 83 84 ctx->alg = alg; 85 return 0; 86 } 87 88 /* 89 * Select highest available classify method as default one. 90 * Note that CLASSIFY_AVX2 should be set as a default only 91 * if both conditions are met: 92 * at build time compiler supports AVX2 and target cpu supports AVX2. 93 */ 94 RTE_INIT(rte_acl_init) 95 { 96 enum rte_acl_classify_alg alg = RTE_ACL_CLASSIFY_DEFAULT; 97 98 #if defined(RTE_ARCH_ARM64) 99 alg = RTE_ACL_CLASSIFY_NEON; 100 #elif defined(RTE_ARCH_ARM) 101 if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_NEON)) 102 alg = RTE_ACL_CLASSIFY_NEON; 103 #elif defined(RTE_ARCH_PPC_64) 104 alg = RTE_ACL_CLASSIFY_ALTIVEC; 105 #else 106 #ifdef CC_AVX2_SUPPORT 107 if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX2)) 108 alg = RTE_ACL_CLASSIFY_AVX2; 109 else if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_SSE4_1)) 110 #else 111 if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_SSE4_1)) 112 #endif 113 alg = RTE_ACL_CLASSIFY_SSE; 114 115 #endif 116 rte_acl_set_default_classify(alg); 117 } 118 119 int 120 rte_acl_classify_alg(const struct rte_acl_ctx *ctx, const uint8_t **data, 121 uint32_t *results, uint32_t num, uint32_t categories, 122 enum rte_acl_classify_alg alg) 123 { 124 if (categories != 1 && 125 ((RTE_ACL_RESULTS_MULTIPLIER - 1) & categories) != 0) 126 return -EINVAL; 127 128 return classify_fns[alg](ctx, data, results, num, categories); 129 } 130 131 int 132 rte_acl_classify(const struct rte_acl_ctx *ctx, const uint8_t **data, 133 uint32_t *results, uint32_t num, uint32_t categories) 134 { 135 return rte_acl_classify_alg(ctx, data, results, num, categories, 136 ctx->alg); 137 } 138 139 struct rte_acl_ctx * 140 rte_acl_find_existing(const char *name) 141 { 142 struct rte_acl_ctx *ctx = NULL; 143 struct rte_acl_list *acl_list; 144 struct rte_tailq_entry *te; 145 146 acl_list = RTE_TAILQ_CAST(rte_acl_tailq.head, rte_acl_list); 147 148 rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK); 149 TAILQ_FOREACH(te, acl_list, next) { 150 ctx = (struct rte_acl_ctx *) te->data; 151 if (strncmp(name, ctx->name, sizeof(ctx->name)) == 0) 152 break; 153 } 154 rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK); 155 156 if (te == NULL) { 157 rte_errno = ENOENT; 158 return NULL; 159 } 160 return ctx; 161 } 162 163 void 164 rte_acl_free(struct rte_acl_ctx *ctx) 165 { 166 struct rte_acl_list *acl_list; 167 struct rte_tailq_entry *te; 168 169 if (ctx == NULL) 170 return; 171 172 acl_list = RTE_TAILQ_CAST(rte_acl_tailq.head, rte_acl_list); 173 174 rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); 175 176 /* find our tailq entry */ 177 TAILQ_FOREACH(te, acl_list, next) { 178 if (te->data == (void *) ctx) 179 break; 180 } 181 if (te == NULL) { 182 rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); 183 return; 184 } 185 186 TAILQ_REMOVE(acl_list, te, next); 187 188 rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); 189 190 rte_free(ctx->mem); 191 rte_free(ctx); 192 rte_free(te); 193 } 194 195 struct rte_acl_ctx * 196 rte_acl_create(const struct rte_acl_param *param) 197 { 198 size_t sz; 199 struct rte_acl_ctx *ctx; 200 struct rte_acl_list *acl_list; 201 struct rte_tailq_entry *te; 202 char name[sizeof(ctx->name)]; 203 204 acl_list = RTE_TAILQ_CAST(rte_acl_tailq.head, rte_acl_list); 205 206 /* check that input parameters are valid. */ 207 if (param == NULL || param->name == NULL) { 208 rte_errno = EINVAL; 209 return NULL; 210 } 211 212 snprintf(name, sizeof(name), "ACL_%s", param->name); 213 214 /* calculate amount of memory required for pattern set. */ 215 sz = sizeof(*ctx) + param->max_rule_num * param->rule_size; 216 217 /* get EAL TAILQ lock. */ 218 rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); 219 220 /* if we already have one with that name */ 221 TAILQ_FOREACH(te, acl_list, next) { 222 ctx = (struct rte_acl_ctx *) te->data; 223 if (strncmp(param->name, ctx->name, sizeof(ctx->name)) == 0) 224 break; 225 } 226 227 /* if ACL with such name doesn't exist, then create a new one. */ 228 if (te == NULL) { 229 ctx = NULL; 230 te = rte_zmalloc("ACL_TAILQ_ENTRY", sizeof(*te), 0); 231 232 if (te == NULL) { 233 RTE_LOG(ERR, ACL, "Cannot allocate tailq entry!\n"); 234 goto exit; 235 } 236 237 ctx = rte_zmalloc_socket(name, sz, RTE_CACHE_LINE_SIZE, param->socket_id); 238 239 if (ctx == NULL) { 240 RTE_LOG(ERR, ACL, 241 "allocation of %zu bytes on socket %d for %s failed\n", 242 sz, param->socket_id, name); 243 rte_free(te); 244 goto exit; 245 } 246 /* init new allocated context. */ 247 ctx->rules = ctx + 1; 248 ctx->max_rules = param->max_rule_num; 249 ctx->rule_sz = param->rule_size; 250 ctx->socket_id = param->socket_id; 251 ctx->alg = rte_acl_default_classify; 252 snprintf(ctx->name, sizeof(ctx->name), "%s", param->name); 253 254 te->data = (void *) ctx; 255 256 TAILQ_INSERT_TAIL(acl_list, te, next); 257 } 258 259 exit: 260 rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); 261 return ctx; 262 } 263 264 static int 265 acl_add_rules(struct rte_acl_ctx *ctx, const void *rules, uint32_t num) 266 { 267 uint8_t *pos; 268 269 if (num + ctx->num_rules > ctx->max_rules) 270 return -ENOMEM; 271 272 pos = ctx->rules; 273 pos += ctx->rule_sz * ctx->num_rules; 274 memcpy(pos, rules, num * ctx->rule_sz); 275 ctx->num_rules += num; 276 277 return 0; 278 } 279 280 static int 281 acl_check_rule(const struct rte_acl_rule_data *rd) 282 { 283 if ((RTE_LEN2MASK(RTE_ACL_MAX_CATEGORIES, typeof(rd->category_mask)) & 284 rd->category_mask) == 0 || 285 rd->priority > RTE_ACL_MAX_PRIORITY || 286 rd->priority < RTE_ACL_MIN_PRIORITY) 287 return -EINVAL; 288 return 0; 289 } 290 291 int 292 rte_acl_add_rules(struct rte_acl_ctx *ctx, const struct rte_acl_rule *rules, 293 uint32_t num) 294 { 295 const struct rte_acl_rule *rv; 296 uint32_t i; 297 int32_t rc; 298 299 if (ctx == NULL || rules == NULL || 0 == ctx->rule_sz) 300 return -EINVAL; 301 302 for (i = 0; i != num; i++) { 303 rv = (const struct rte_acl_rule *) 304 ((uintptr_t)rules + i * ctx->rule_sz); 305 rc = acl_check_rule(&rv->data); 306 if (rc != 0) { 307 RTE_LOG(ERR, ACL, "%s(%s): rule #%u is invalid\n", 308 __func__, ctx->name, i + 1); 309 return rc; 310 } 311 } 312 313 return acl_add_rules(ctx, rules, num); 314 } 315 316 /* 317 * Reset all rules. 318 * Note that RT structures are not affected. 319 */ 320 void 321 rte_acl_reset_rules(struct rte_acl_ctx *ctx) 322 { 323 if (ctx != NULL) 324 ctx->num_rules = 0; 325 } 326 327 /* 328 * Reset all rules and destroys RT structures. 329 */ 330 void 331 rte_acl_reset(struct rte_acl_ctx *ctx) 332 { 333 if (ctx != NULL) { 334 rte_acl_reset_rules(ctx); 335 rte_acl_build(ctx, &ctx->config); 336 } 337 } 338 339 /* 340 * Dump ACL context to the stdout. 341 */ 342 void 343 rte_acl_dump(const struct rte_acl_ctx *ctx) 344 { 345 if (!ctx) 346 return; 347 printf("acl context <%s>@%p\n", ctx->name, ctx); 348 printf(" socket_id=%"PRId32"\n", ctx->socket_id); 349 printf(" alg=%"PRId32"\n", ctx->alg); 350 printf(" max_rules=%"PRIu32"\n", ctx->max_rules); 351 printf(" rule_size=%"PRIu32"\n", ctx->rule_sz); 352 printf(" num_rules=%"PRIu32"\n", ctx->num_rules); 353 printf(" num_categories=%"PRIu32"\n", ctx->num_categories); 354 printf(" num_tries=%"PRIu32"\n", ctx->num_tries); 355 } 356 357 /* 358 * Dump all ACL contexts to the stdout. 359 */ 360 void 361 rte_acl_list_dump(void) 362 { 363 struct rte_acl_ctx *ctx; 364 struct rte_acl_list *acl_list; 365 struct rte_tailq_entry *te; 366 367 acl_list = RTE_TAILQ_CAST(rte_acl_tailq.head, rte_acl_list); 368 369 rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK); 370 TAILQ_FOREACH(te, acl_list, next) { 371 ctx = (struct rte_acl_ctx *) te->data; 372 rte_acl_dump(ctx); 373 } 374 rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK); 375 } 376