New upstream version 18.02
[deb_dpdk.git] / lib / librte_acl / acl_run_neon.h
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2015 Cavium, Inc
3  */
4
5 #include "acl_run.h"
6 #include "acl_vect.h"
7
8 struct _neon_acl_const {
9         rte_xmm_t xmm_shuffle_input;
10         rte_xmm_t xmm_index_mask;
11         rte_xmm_t range_base;
12 } neon_acl_const  __attribute__((aligned(RTE_CACHE_LINE_SIZE))) = {
13         {
14                 .u32 = {0x00000000, 0x04040404, 0x08080808, 0x0c0c0c0c}
15         },
16         {
17                 .u32 = {RTE_ACL_NODE_INDEX, RTE_ACL_NODE_INDEX,
18                 RTE_ACL_NODE_INDEX, RTE_ACL_NODE_INDEX}
19         },
20         {
21                 .u32 = {0xffffff00, 0xffffff04, 0xffffff08, 0xffffff0c}
22         },
23 };
24
25 /*
26  * Resolve priority for multiple results (neon version).
27  * This consists comparing the priority of the current traversal with the
28  * running set of results for the packet.
29  * For each result, keep a running array of the result (rule number) and
30  * its priority for each category.
31  */
32 static inline void
33 resolve_priority_neon(uint64_t transition, int n, const struct rte_acl_ctx *ctx,
34                       struct parms *parms,
35                       const struct rte_acl_match_results *p,
36                       uint32_t categories)
37 {
38         uint32_t x;
39         int32x4_t results, priority, results1, priority1;
40         uint32x4_t selector;
41         int32_t *saved_results, *saved_priority;
42
43         for (x = 0; x < categories; x += RTE_ACL_RESULTS_MULTIPLIER) {
44                 saved_results = (int32_t *)(&parms[n].cmplt->results[x]);
45                 saved_priority = (int32_t *)(&parms[n].cmplt->priority[x]);
46
47                 /* get results and priorities for completed trie */
48                 results = vld1q_s32(
49                         (const int32_t *)&p[transition].results[x]);
50                 priority = vld1q_s32(
51                         (const int32_t *)&p[transition].priority[x]);
52
53                 /* if this is not the first completed trie */
54                 if (parms[n].cmplt->count != ctx->num_tries) {
55                         /* get running best results and their priorities */
56                         results1 = vld1q_s32(saved_results);
57                         priority1 = vld1q_s32(saved_priority);
58
59                         /* select results that are highest priority */
60                         selector = vcgtq_s32(priority1, priority);
61                         results = vbslq_s32(selector, results1, results);
62                         priority = vbslq_s32(selector, priority1, priority);
63                 }
64
65                 /* save running best results and their priorities */
66                 vst1q_s32(saved_results, results);
67                 vst1q_s32(saved_priority, priority);
68         }
69 }
70
71 /*
72  * Check for any match in 4 transitions
73  */
74 static __rte_always_inline uint32_t
75 check_any_match_x4(uint64_t val[])
76 {
77         return (val[0] | val[1] | val[2] | val[3]) & RTE_ACL_NODE_MATCH;
78 }
79
80 static __rte_always_inline void
81 acl_match_check_x4(int slot, const struct rte_acl_ctx *ctx, struct parms *parms,
82                    struct acl_flow_data *flows, uint64_t transitions[])
83 {
84         while (check_any_match_x4(transitions)) {
85                 transitions[0] = acl_match_check(transitions[0], slot, ctx,
86                         parms, flows, resolve_priority_neon);
87                 transitions[1] = acl_match_check(transitions[1], slot + 1, ctx,
88                         parms, flows, resolve_priority_neon);
89                 transitions[2] = acl_match_check(transitions[2], slot + 2, ctx,
90                         parms, flows, resolve_priority_neon);
91                 transitions[3] = acl_match_check(transitions[3], slot + 3, ctx,
92                         parms, flows, resolve_priority_neon);
93         }
94 }
95
96 /*
97  * Process 4 transitions (in 2 NEON Q registers) in parallel
98  */
99 static __rte_always_inline int32x4_t
100 transition4(int32x4_t next_input, const uint64_t *trans, uint64_t transitions[])
101 {
102         int32x4x2_t tr_hi_lo;
103         int32x4_t t, in, r;
104         uint32x4_t index_msk, node_type, addr;
105         uint32x4_t dfa_msk, mask, quad_ofs, dfa_ofs;
106
107         /* Move low 32 into tr_hi_lo.val[0] and high 32 into tr_hi_lo.val[1] */
108         tr_hi_lo = vld2q_s32((const int32_t *)transitions);
109
110         /* Calculate the address (array index) for all 4 transitions. */
111
112         index_msk = vld1q_u32((const uint32_t *)&neon_acl_const.xmm_index_mask);
113
114         /* Calc node type and node addr */
115         node_type = vbicq_s32(tr_hi_lo.val[0], index_msk);
116         addr = vandq_s32(tr_hi_lo.val[0], index_msk);
117
118         /* t = 0 */
119         t = veorq_s32(node_type, node_type);
120
121         /* mask for DFA type(0) nodes */
122         dfa_msk = vceqq_u32(node_type, t);
123
124         mask = vld1q_s32((const int32_t *)&neon_acl_const.xmm_shuffle_input);
125         in = vqtbl1q_u8((uint8x16_t)next_input, (uint8x16_t)mask);
126
127         /* DFA calculations. */
128         r = vshrq_n_u32(in, 30); /* div by 64 */
129         mask = vld1q_s32((const int32_t *)&neon_acl_const.range_base);
130         r = vaddq_u8(r, mask);
131         t = vshrq_n_u32(in, 24);
132         r = vqtbl1q_u8((uint8x16_t)tr_hi_lo.val[1], (uint8x16_t)r);
133         dfa_ofs = vsubq_s32(t, r);
134
135         /* QUAD/SINGLE calculations. */
136         t = vcgtq_s8(in, tr_hi_lo.val[1]);
137         t = vabsq_s8(t);
138         t = vpaddlq_u8(t);
139         quad_ofs = vpaddlq_u16(t);
140
141         /* blend DFA and QUAD/SINGLE. */
142         t = vbslq_u8(dfa_msk, dfa_ofs, quad_ofs);
143
144         /* calculate address for next transitions */
145         addr = vaddq_u32(addr, t);
146
147         /* Fill next transitions */
148         transitions[0] = trans[vgetq_lane_u32(addr, 0)];
149         transitions[1] = trans[vgetq_lane_u32(addr, 1)];
150         transitions[2] = trans[vgetq_lane_u32(addr, 2)];
151         transitions[3] = trans[vgetq_lane_u32(addr, 3)];
152
153         return vshrq_n_u32(next_input, CHAR_BIT);
154 }
155
156 /*
157  * Execute trie traversal with 8 traversals in parallel
158  */
159 static inline int
160 search_neon_8(const struct rte_acl_ctx *ctx, const uint8_t **data,
161               uint32_t *results, uint32_t total_packets, uint32_t categories)
162 {
163         int n;
164         struct acl_flow_data flows;
165         uint64_t index_array[8];
166         struct completion cmplt[8];
167         struct parms parms[8];
168         int32x4_t input0, input1;
169
170         acl_set_flow(&flows, cmplt, RTE_DIM(cmplt), data, results,
171                      total_packets, categories, ctx->trans_table);
172
173         for (n = 0; n < 8; n++) {
174                 cmplt[n].count = 0;
175                 index_array[n] = acl_start_next_trie(&flows, parms, n, ctx);
176         }
177
178          /* Check for any matches. */
179         acl_match_check_x4(0, ctx, parms, &flows, &index_array[0]);
180         acl_match_check_x4(4, ctx, parms, &flows, &index_array[4]);
181
182         while (flows.started > 0) {
183                 /* Gather 4 bytes of input data for each stream. */
184                 input0 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 0), input0, 0);
185                 input1 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 4), input1, 0);
186
187                 input0 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 1), input0, 1);
188                 input1 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 5), input1, 1);
189
190                 input0 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 2), input0, 2);
191                 input1 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 6), input1, 2);
192
193                 input0 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 3), input0, 3);
194                 input1 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 7), input1, 3);
195
196                 /* Process the 4 bytes of input on each stream. */
197
198                 input0 = transition4(input0, flows.trans, &index_array[0]);
199                 input1 = transition4(input1, flows.trans, &index_array[4]);
200
201                 input0 = transition4(input0, flows.trans, &index_array[0]);
202                 input1 = transition4(input1, flows.trans, &index_array[4]);
203
204                 input0 = transition4(input0, flows.trans, &index_array[0]);
205                 input1 = transition4(input1, flows.trans, &index_array[4]);
206
207                 input0 = transition4(input0, flows.trans, &index_array[0]);
208                 input1 = transition4(input1, flows.trans, &index_array[4]);
209
210                  /* Check for any matches. */
211                 acl_match_check_x4(0, ctx, parms, &flows, &index_array[0]);
212                 acl_match_check_x4(4, ctx, parms, &flows, &index_array[4]);
213         }
214
215         return 0;
216 }
217
218 /*
219  * Execute trie traversal with 4 traversals in parallel
220  */
221 static inline int
222 search_neon_4(const struct rte_acl_ctx *ctx, const uint8_t **data,
223               uint32_t *results, int total_packets, uint32_t categories)
224 {
225         int n;
226         struct acl_flow_data flows;
227         uint64_t index_array[4];
228         struct completion cmplt[4];
229         struct parms parms[4];
230         int32x4_t input;
231
232         acl_set_flow(&flows, cmplt, RTE_DIM(cmplt), data, results,
233                      total_packets, categories, ctx->trans_table);
234
235         for (n = 0; n < 4; n++) {
236                 cmplt[n].count = 0;
237                 index_array[n] = acl_start_next_trie(&flows, parms, n, ctx);
238         }
239
240         /* Check for any matches. */
241         acl_match_check_x4(0, ctx, parms, &flows, index_array);
242
243         while (flows.started > 0) {
244                 /* Gather 4 bytes of input data for each stream. */
245                 input = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 0), input, 0);
246                 input = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 1), input, 1);
247                 input = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 2), input, 2);
248                 input = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 3), input, 3);
249
250                 /* Process the 4 bytes of input on each stream. */
251                 input = transition4(input, flows.trans, index_array);
252                 input = transition4(input, flows.trans, index_array);
253                 input = transition4(input, flows.trans, index_array);
254                 input = transition4(input, flows.trans, index_array);
255
256                 /* Check for any matches. */
257                 acl_match_check_x4(0, ctx, parms, &flows, index_array);
258         }
259
260         return 0;
261 }