New upstream version 18.11-rc1
[deb_dpdk.git] / lib / librte_acl / rte_acl.c
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 }