acl: add AVX2 classify method
[dpdk.git] / lib / librte_acl / acl_run_sse.h
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include "acl_run.h"
35 #include "acl_vect.h"
36
37 enum {
38         SHUFFLE32_SLOT1 = 0xe5,
39         SHUFFLE32_SLOT2 = 0xe6,
40         SHUFFLE32_SLOT3 = 0xe7,
41         SHUFFLE32_SWAP64 = 0x4e,
42 };
43
44 static const rte_xmm_t xmm_shuffle_input = {
45         .u32 = {0x00000000, 0x04040404, 0x08080808, 0x0c0c0c0c},
46 };
47
48 static const rte_xmm_t xmm_shuffle_input64 = {
49         .u32 = {0x00000000, 0x04040404, 0x80808080, 0x80808080},
50 };
51
52 static const rte_xmm_t xmm_ones_16 = {
53         .u16 = {1, 1, 1, 1, 1, 1, 1, 1},
54 };
55
56 static const rte_xmm_t xmm_match_mask = {
57         .u32 = {
58                 RTE_ACL_NODE_MATCH,
59                 RTE_ACL_NODE_MATCH,
60                 RTE_ACL_NODE_MATCH,
61                 RTE_ACL_NODE_MATCH,
62         },
63 };
64
65 static const rte_xmm_t xmm_match_mask64 = {
66         .u32 = {
67                 RTE_ACL_NODE_MATCH,
68                 0,
69                 RTE_ACL_NODE_MATCH,
70                 0,
71         },
72 };
73
74 static const rte_xmm_t xmm_index_mask = {
75         .u32 = {
76                 RTE_ACL_NODE_INDEX,
77                 RTE_ACL_NODE_INDEX,
78                 RTE_ACL_NODE_INDEX,
79                 RTE_ACL_NODE_INDEX,
80         },
81 };
82
83 static const rte_xmm_t xmm_index_mask64 = {
84         .u32 = {
85                 RTE_ACL_NODE_INDEX,
86                 RTE_ACL_NODE_INDEX,
87                 0,
88                 0,
89         },
90 };
91
92
93 /*
94  * Resolve priority for multiple results (sse version).
95  * This consists comparing the priority of the current traversal with the
96  * running set of results for the packet.
97  * For each result, keep a running array of the result (rule number) and
98  * its priority for each category.
99  */
100 static inline void
101 resolve_priority_sse(uint64_t transition, int n, const struct rte_acl_ctx *ctx,
102         struct parms *parms, const struct rte_acl_match_results *p,
103         uint32_t categories)
104 {
105         uint32_t x;
106         xmm_t results, priority, results1, priority1, selector;
107         xmm_t *saved_results, *saved_priority;
108
109         for (x = 0; x < categories; x += RTE_ACL_RESULTS_MULTIPLIER) {
110
111                 saved_results = (xmm_t *)(&parms[n].cmplt->results[x]);
112                 saved_priority =
113                         (xmm_t *)(&parms[n].cmplt->priority[x]);
114
115                 /* get results and priorities for completed trie */
116                 results = MM_LOADU((const xmm_t *)&p[transition].results[x]);
117                 priority = MM_LOADU((const xmm_t *)&p[transition].priority[x]);
118
119                 /* if this is not the first completed trie */
120                 if (parms[n].cmplt->count != ctx->num_tries) {
121
122                         /* get running best results and their priorities */
123                         results1 = MM_LOADU(saved_results);
124                         priority1 = MM_LOADU(saved_priority);
125
126                         /* select results that are highest priority */
127                         selector = MM_CMPGT32(priority1, priority);
128                         results = MM_BLENDV8(results, results1, selector);
129                         priority = MM_BLENDV8(priority, priority1, selector);
130                 }
131
132                 /* save running best results and their priorities */
133                 MM_STOREU(saved_results, results);
134                 MM_STOREU(saved_priority, priority);
135         }
136 }
137
138 /*
139  * Extract transitions from an XMM register and check for any matches
140  */
141 static void
142 acl_process_matches(xmm_t *indices, int slot, const struct rte_acl_ctx *ctx,
143         struct parms *parms, struct acl_flow_data *flows)
144 {
145         uint64_t transition1, transition2;
146
147         /* extract transition from low 64 bits. */
148         transition1 = MM_CVT64(*indices);
149
150         /* extract transition from high 64 bits. */
151         *indices = MM_SHUFFLE32(*indices, SHUFFLE32_SWAP64);
152         transition2 = MM_CVT64(*indices);
153
154         transition1 = acl_match_check(transition1, slot, ctx,
155                 parms, flows, resolve_priority_sse);
156         transition2 = acl_match_check(transition2, slot + 1, ctx,
157                 parms, flows, resolve_priority_sse);
158
159         /* update indices with new transitions. */
160         *indices = MM_SET64(transition2, transition1);
161 }
162
163 /*
164  * Check for a match in 2 transitions (contained in SSE register)
165  */
166 static inline __attribute__((always_inline)) void
167 acl_match_check_x2(int slot, const struct rte_acl_ctx *ctx, struct parms *parms,
168         struct acl_flow_data *flows, xmm_t *indices, xmm_t match_mask)
169 {
170         xmm_t temp;
171
172         temp = MM_AND(match_mask, *indices);
173         while (!MM_TESTZ(temp, temp)) {
174                 acl_process_matches(indices, slot, ctx, parms, flows);
175                 temp = MM_AND(match_mask, *indices);
176         }
177 }
178
179 /*
180  * Check for any match in 4 transitions (contained in 2 SSE registers)
181  */
182 static inline __attribute__((always_inline)) void
183 acl_match_check_x4(int slot, const struct rte_acl_ctx *ctx, struct parms *parms,
184         struct acl_flow_data *flows, xmm_t *indices1, xmm_t *indices2,
185         xmm_t match_mask)
186 {
187         xmm_t temp;
188
189         /* put low 32 bits of each transition into one register */
190         temp = (xmm_t)MM_SHUFFLEPS((__m128)*indices1, (__m128)*indices2,
191                 0x88);
192         /* test for match node */
193         temp = MM_AND(match_mask, temp);
194
195         while (!MM_TESTZ(temp, temp)) {
196                 acl_process_matches(indices1, slot, ctx, parms, flows);
197                 acl_process_matches(indices2, slot + 2, ctx, parms, flows);
198
199                 temp = (xmm_t)MM_SHUFFLEPS((__m128)*indices1,
200                                         (__m128)*indices2,
201                                         0x88);
202                 temp = MM_AND(match_mask, temp);
203         }
204 }
205
206 /*
207  * Calculate the address of the next transition for
208  * all types of nodes. Note that only DFA nodes and range
209  * nodes actually transition to another node. Match
210  * nodes don't move.
211  */
212 static inline __attribute__((always_inline)) xmm_t
213 calc_addr_sse(xmm_t index_mask, xmm_t next_input, xmm_t shuffle_input,
214         xmm_t ones_16, xmm_t indices1, xmm_t indices2)
215 {
216         xmm_t addr, node_types, range, temp;
217         xmm_t dfa_msk, dfa_ofs, quad_ofs;
218         xmm_t in, r, t;
219
220         const xmm_t range_base = _mm_set_epi32(0xffffff0c, 0xffffff08,
221                 0xffffff04, 0xffffff00);
222
223         /*
224          * Note that no transition is done for a match
225          * node and therefore a stream freezes when
226          * it reaches a match.
227          */
228
229         /* Shuffle low 32 into temp and high 32 into indices2 */
230         temp = (xmm_t)MM_SHUFFLEPS((__m128)indices1, (__m128)indices2, 0x88);
231         range = (xmm_t)MM_SHUFFLEPS((__m128)indices1, (__m128)indices2, 0xdd);
232
233         t = MM_XOR(index_mask, index_mask);
234
235         /* shuffle input byte to all 4 positions of 32 bit value */
236         in = MM_SHUFFLE8(next_input, shuffle_input);
237
238         /* Calc node type and node addr */
239         node_types = MM_ANDNOT(index_mask, temp);
240         addr = MM_AND(index_mask, temp);
241
242         /*
243          * Calc addr for DFAs - addr = dfa_index + input_byte
244          */
245
246         /* mask for DFA type (0) nodes */
247         dfa_msk = MM_CMPEQ32(node_types, t);
248
249         r = _mm_srli_epi32(in, 30);
250         r = _mm_add_epi8(r, range_base);
251
252         t = _mm_srli_epi32(in, 24);
253         r = _mm_shuffle_epi8(range, r);
254
255         dfa_ofs = _mm_sub_epi32(t, r);
256
257         /*
258          * Calculate number of range boundaries that are less than the
259          * input value. Range boundaries for each node are in signed 8 bit,
260          * ordered from -128 to 127 in the indices2 register.
261          * This is effectively a popcnt of bytes that are greater than the
262          * input byte.
263          */
264
265         /* check ranges */
266         temp = MM_CMPGT8(in, range);
267
268         /* convert -1 to 1 (bytes greater than input byte */
269         temp = MM_SIGN8(temp, temp);
270
271         /* horizontal add pairs of bytes into words */
272         temp = MM_MADD8(temp, temp);
273
274         /* horizontal add pairs of words into dwords */
275         quad_ofs = MM_MADD16(temp, ones_16);
276
277         /* mask to range type nodes */
278         temp = _mm_blendv_epi8(quad_ofs, dfa_ofs, dfa_msk);
279
280         /* add index into node position */
281         return MM_ADD32(addr, temp);
282 }
283
284 /*
285  * Process 4 transitions (in 2 SIMD registers) in parallel
286  */
287 static inline __attribute__((always_inline)) xmm_t
288 transition4(xmm_t next_input, const uint64_t *trans,
289         xmm_t *indices1, xmm_t *indices2)
290 {
291         xmm_t addr;
292         uint64_t trans0, trans2;
293
294          /* Calculate the address (array index) for all 4 transitions. */
295
296         addr = calc_addr_sse(xmm_index_mask.x, next_input, xmm_shuffle_input.x,
297                 xmm_ones_16.x, *indices1, *indices2);
298
299          /* Gather 64 bit transitions and pack back into 2 registers. */
300
301         trans0 = trans[MM_CVT32(addr)];
302
303         /* get slot 2 */
304
305         /* {x0, x1, x2, x3} -> {x2, x1, x2, x3} */
306         addr = MM_SHUFFLE32(addr, SHUFFLE32_SLOT2);
307         trans2 = trans[MM_CVT32(addr)];
308
309         /* get slot 1 */
310
311         /* {x2, x1, x2, x3} -> {x1, x1, x2, x3} */
312         addr = MM_SHUFFLE32(addr, SHUFFLE32_SLOT1);
313         *indices1 = MM_SET64(trans[MM_CVT32(addr)], trans0);
314
315         /* get slot 3 */
316
317         /* {x1, x1, x2, x3} -> {x3, x1, x2, x3} */
318         addr = MM_SHUFFLE32(addr, SHUFFLE32_SLOT3);
319         *indices2 = MM_SET64(trans[MM_CVT32(addr)], trans2);
320
321         return MM_SRL32(next_input, CHAR_BIT);
322 }
323
324 /*
325  * Execute trie traversal with 8 traversals in parallel
326  */
327 static inline int
328 search_sse_8(const struct rte_acl_ctx *ctx, const uint8_t **data,
329         uint32_t *results, uint32_t total_packets, uint32_t categories)
330 {
331         int n;
332         struct acl_flow_data flows;
333         uint64_t index_array[MAX_SEARCHES_SSE8];
334         struct completion cmplt[MAX_SEARCHES_SSE8];
335         struct parms parms[MAX_SEARCHES_SSE8];
336         xmm_t input0, input1;
337         xmm_t indices1, indices2, indices3, indices4;
338
339         acl_set_flow(&flows, cmplt, RTE_DIM(cmplt), data, results,
340                 total_packets, categories, ctx->trans_table);
341
342         for (n = 0; n < MAX_SEARCHES_SSE8; n++) {
343                 cmplt[n].count = 0;
344                 index_array[n] = acl_start_next_trie(&flows, parms, n, ctx);
345         }
346
347         /*
348          * indices1 contains index_array[0,1]
349          * indices2 contains index_array[2,3]
350          * indices3 contains index_array[4,5]
351          * indices4 contains index_array[6,7]
352          */
353
354         indices1 = MM_LOADU((xmm_t *) &index_array[0]);
355         indices2 = MM_LOADU((xmm_t *) &index_array[2]);
356
357         indices3 = MM_LOADU((xmm_t *) &index_array[4]);
358         indices4 = MM_LOADU((xmm_t *) &index_array[6]);
359
360          /* Check for any matches. */
361         acl_match_check_x4(0, ctx, parms, &flows,
362                 &indices1, &indices2, xmm_match_mask.x);
363         acl_match_check_x4(4, ctx, parms, &flows,
364                 &indices3, &indices4, xmm_match_mask.x);
365
366         while (flows.started > 0) {
367
368                 /* Gather 4 bytes of input data for each stream. */
369                 input0 = _mm_cvtsi32_si128(GET_NEXT_4BYTES(parms, 0));
370                 input1 = _mm_cvtsi32_si128(GET_NEXT_4BYTES(parms, 4));
371
372                 input0 = MM_INSERT32(input0, GET_NEXT_4BYTES(parms, 1), 1);
373                 input1 = MM_INSERT32(input1, GET_NEXT_4BYTES(parms, 5), 1);
374
375                 input0 = MM_INSERT32(input0, GET_NEXT_4BYTES(parms, 2), 2);
376                 input1 = MM_INSERT32(input1, GET_NEXT_4BYTES(parms, 6), 2);
377
378                 input0 = MM_INSERT32(input0, GET_NEXT_4BYTES(parms, 3), 3);
379                 input1 = MM_INSERT32(input1, GET_NEXT_4BYTES(parms, 7), 3);
380
381                  /* Process the 4 bytes of input on each stream. */
382
383                 input0 = transition4(input0, flows.trans,
384                         &indices1, &indices2);
385                 input1 = transition4(input1, flows.trans,
386                         &indices3, &indices4);
387
388                 input0 = transition4(input0, flows.trans,
389                         &indices1, &indices2);
390                 input1 = transition4(input1, flows.trans,
391                         &indices3, &indices4);
392
393                 input0 = transition4(input0, flows.trans,
394                         &indices1, &indices2);
395                 input1 = transition4(input1, flows.trans,
396                         &indices3, &indices4);
397
398                 input0 = transition4(input0, flows.trans,
399                         &indices1, &indices2);
400                 input1 = transition4(input1, flows.trans,
401                         &indices3, &indices4);
402
403                  /* Check for any matches. */
404                 acl_match_check_x4(0, ctx, parms, &flows,
405                         &indices1, &indices2, xmm_match_mask.x);
406                 acl_match_check_x4(4, ctx, parms, &flows,
407                         &indices3, &indices4, xmm_match_mask.x);
408         }
409
410         return 0;
411 }
412
413 /*
414  * Execute trie traversal with 4 traversals in parallel
415  */
416 static inline int
417 search_sse_4(const struct rte_acl_ctx *ctx, const uint8_t **data,
418          uint32_t *results, int total_packets, uint32_t categories)
419 {
420         int n;
421         struct acl_flow_data flows;
422         uint64_t index_array[MAX_SEARCHES_SSE4];
423         struct completion cmplt[MAX_SEARCHES_SSE4];
424         struct parms parms[MAX_SEARCHES_SSE4];
425         xmm_t input, indices1, indices2;
426
427         acl_set_flow(&flows, cmplt, RTE_DIM(cmplt), data, results,
428                 total_packets, categories, ctx->trans_table);
429
430         for (n = 0; n < MAX_SEARCHES_SSE4; n++) {
431                 cmplt[n].count = 0;
432                 index_array[n] = acl_start_next_trie(&flows, parms, n, ctx);
433         }
434
435         indices1 = MM_LOADU((xmm_t *) &index_array[0]);
436         indices2 = MM_LOADU((xmm_t *) &index_array[2]);
437
438         /* Check for any matches. */
439         acl_match_check_x4(0, ctx, parms, &flows,
440                 &indices1, &indices2, xmm_match_mask.x);
441
442         while (flows.started > 0) {
443
444                 /* Gather 4 bytes of input data for each stream. */
445                 input = _mm_cvtsi32_si128(GET_NEXT_4BYTES(parms, 0));
446                 input = MM_INSERT32(input, GET_NEXT_4BYTES(parms, 1), 1);
447                 input = MM_INSERT32(input, GET_NEXT_4BYTES(parms, 2), 2);
448                 input = MM_INSERT32(input, GET_NEXT_4BYTES(parms, 3), 3);
449
450                 /* Process the 4 bytes of input on each stream. */
451                 input = transition4(input, flows.trans, &indices1, &indices2);
452                 input = transition4(input, flows.trans, &indices1, &indices2);
453                 input = transition4(input, flows.trans, &indices1, &indices2);
454                 input = transition4(input, flows.trans, &indices1, &indices2);
455
456                 /* Check for any matches. */
457                 acl_match_check_x4(0, ctx, parms, &flows,
458                         &indices1, &indices2, xmm_match_mask.x);
459         }
460
461         return 0;
462 }
463
464 static inline __attribute__((always_inline)) xmm_t
465 transition2(xmm_t next_input, const uint64_t *trans, xmm_t *indices1)
466 {
467         uint64_t t;
468         xmm_t addr, indices2;
469
470         indices2 = _mm_setzero_si128();
471
472         addr = calc_addr_sse(xmm_index_mask.x, next_input, xmm_shuffle_input.x,
473                 xmm_ones_16.x, *indices1, indices2);
474
475         /* Gather 64 bit transitions and pack 2 per register. */
476
477         t = trans[MM_CVT32(addr)];
478
479         /* get slot 1 */
480         addr = MM_SHUFFLE32(addr, SHUFFLE32_SLOT1);
481         *indices1 = MM_SET64(trans[MM_CVT32(addr)], t);
482
483         return MM_SRL32(next_input, CHAR_BIT);
484 }
485
486 /*
487  * Execute trie traversal with 2 traversals in parallel.
488  */
489 static inline int
490 search_sse_2(const struct rte_acl_ctx *ctx, const uint8_t **data,
491         uint32_t *results, uint32_t total_packets, uint32_t categories)
492 {
493         int n;
494         struct acl_flow_data flows;
495         uint64_t index_array[MAX_SEARCHES_SSE2];
496         struct completion cmplt[MAX_SEARCHES_SSE2];
497         struct parms parms[MAX_SEARCHES_SSE2];
498         xmm_t input, indices;
499
500         acl_set_flow(&flows, cmplt, RTE_DIM(cmplt), data, results,
501                 total_packets, categories, ctx->trans_table);
502
503         for (n = 0; n < MAX_SEARCHES_SSE2; n++) {
504                 cmplt[n].count = 0;
505                 index_array[n] = acl_start_next_trie(&flows, parms, n, ctx);
506         }
507
508         indices = MM_LOADU((xmm_t *) &index_array[0]);
509
510         /* Check for any matches. */
511         acl_match_check_x2(0, ctx, parms, &flows, &indices,
512                 xmm_match_mask64.x);
513
514         while (flows.started > 0) {
515
516                 /* Gather 4 bytes of input data for each stream. */
517                 input = _mm_cvtsi32_si128(GET_NEXT_4BYTES(parms, 0));
518                 input = MM_INSERT32(input, GET_NEXT_4BYTES(parms, 1), 1);
519
520                 /* Process the 4 bytes of input on each stream. */
521
522                 input = transition2(input, flows.trans, &indices);
523                 input = transition2(input, flows.trans, &indices);
524                 input = transition2(input, flows.trans, &indices);
525                 input = transition2(input, flows.trans, &indices);
526
527                 /* Check for any matches. */
528                 acl_match_check_x2(0, ctx, parms, &flows, &indices,
529                         xmm_match_mask64.x);
530         }
531
532         return 0;
533 }