4a174e93102f8fb2ca258f7539ad2b169efc515c
[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_ones_16 = {
49         .u16 = {1, 1, 1, 1, 1, 1, 1, 1},
50 };
51
52 static const rte_xmm_t xmm_match_mask = {
53         .u32 = {
54                 RTE_ACL_NODE_MATCH,
55                 RTE_ACL_NODE_MATCH,
56                 RTE_ACL_NODE_MATCH,
57                 RTE_ACL_NODE_MATCH,
58         },
59 };
60
61 static const rte_xmm_t xmm_index_mask = {
62         .u32 = {
63                 RTE_ACL_NODE_INDEX,
64                 RTE_ACL_NODE_INDEX,
65                 RTE_ACL_NODE_INDEX,
66                 RTE_ACL_NODE_INDEX,
67         },
68 };
69
70 /*
71  * Resolve priority for multiple results (sse version).
72  * This consists comparing the priority of the current traversal with the
73  * running set of results for the packet.
74  * For each result, keep a running array of the result (rule number) and
75  * its priority for each category.
76  */
77 static inline void
78 resolve_priority_sse(uint64_t transition, int n, const struct rte_acl_ctx *ctx,
79         struct parms *parms, const struct rte_acl_match_results *p,
80         uint32_t categories)
81 {
82         uint32_t x;
83         xmm_t results, priority, results1, priority1, selector;
84         xmm_t *saved_results, *saved_priority;
85
86         for (x = 0; x < categories; x += RTE_ACL_RESULTS_MULTIPLIER) {
87
88                 saved_results = (xmm_t *)(&parms[n].cmplt->results[x]);
89                 saved_priority =
90                         (xmm_t *)(&parms[n].cmplt->priority[x]);
91
92                 /* get results and priorities for completed trie */
93                 results = MM_LOADU((const xmm_t *)&p[transition].results[x]);
94                 priority = MM_LOADU((const xmm_t *)&p[transition].priority[x]);
95
96                 /* if this is not the first completed trie */
97                 if (parms[n].cmplt->count != ctx->num_tries) {
98
99                         /* get running best results and their priorities */
100                         results1 = MM_LOADU(saved_results);
101                         priority1 = MM_LOADU(saved_priority);
102
103                         /* select results that are highest priority */
104                         selector = MM_CMPGT32(priority1, priority);
105                         results = MM_BLENDV8(results, results1, selector);
106                         priority = MM_BLENDV8(priority, priority1, selector);
107                 }
108
109                 /* save running best results and their priorities */
110                 MM_STOREU(saved_results, results);
111                 MM_STOREU(saved_priority, priority);
112         }
113 }
114
115 /*
116  * Extract transitions from an XMM register and check for any matches
117  */
118 static void
119 acl_process_matches(xmm_t *indices, int slot, const struct rte_acl_ctx *ctx,
120         struct parms *parms, struct acl_flow_data *flows)
121 {
122         uint64_t transition1, transition2;
123
124         /* extract transition from low 64 bits. */
125         transition1 = MM_CVT64(*indices);
126
127         /* extract transition from high 64 bits. */
128         *indices = MM_SHUFFLE32(*indices, SHUFFLE32_SWAP64);
129         transition2 = MM_CVT64(*indices);
130
131         transition1 = acl_match_check(transition1, slot, ctx,
132                 parms, flows, resolve_priority_sse);
133         transition2 = acl_match_check(transition2, slot + 1, ctx,
134                 parms, flows, resolve_priority_sse);
135
136         /* update indices with new transitions. */
137         *indices = MM_SET64(transition2, transition1);
138 }
139
140 /*
141  * Check for any match in 4 transitions (contained in 2 SSE registers)
142  */
143 static inline __attribute__((always_inline)) void
144 acl_match_check_x4(int slot, const struct rte_acl_ctx *ctx, struct parms *parms,
145         struct acl_flow_data *flows, xmm_t *indices1, xmm_t *indices2,
146         xmm_t match_mask)
147 {
148         xmm_t temp;
149
150         /* put low 32 bits of each transition into one register */
151         temp = (xmm_t)MM_SHUFFLEPS((__m128)*indices1, (__m128)*indices2,
152                 0x88);
153         /* test for match node */
154         temp = MM_AND(match_mask, temp);
155
156         while (!MM_TESTZ(temp, temp)) {
157                 acl_process_matches(indices1, slot, ctx, parms, flows);
158                 acl_process_matches(indices2, slot + 2, ctx, parms, flows);
159
160                 temp = (xmm_t)MM_SHUFFLEPS((__m128)*indices1,
161                                         (__m128)*indices2,
162                                         0x88);
163                 temp = MM_AND(match_mask, temp);
164         }
165 }
166
167 /*
168  * Calculate the address of the next transition for
169  * all types of nodes. Note that only DFA nodes and range
170  * nodes actually transition to another node. Match
171  * nodes don't move.
172  */
173 static inline __attribute__((always_inline)) xmm_t
174 calc_addr_sse(xmm_t index_mask, xmm_t next_input, xmm_t shuffle_input,
175         xmm_t ones_16, xmm_t tr_lo, xmm_t tr_hi)
176 {
177         xmm_t addr, node_types;
178         xmm_t dfa_msk, dfa_ofs, quad_ofs;
179         xmm_t in, r, t;
180
181         const xmm_t range_base = _mm_set_epi32(0xffffff0c, 0xffffff08,
182                 0xffffff04, 0xffffff00);
183
184         /*
185          * Note that no transition is done for a match
186          * node and therefore a stream freezes when
187          * it reaches a match.
188          */
189
190         t = MM_XOR(index_mask, index_mask);
191
192         /* shuffle input byte to all 4 positions of 32 bit value */
193         in = MM_SHUFFLE8(next_input, shuffle_input);
194
195         /* Calc node type and node addr */
196         node_types = MM_ANDNOT(index_mask, tr_lo);
197         addr = MM_AND(index_mask, tr_lo);
198
199         /*
200          * Calc addr for DFAs - addr = dfa_index + input_byte
201          */
202
203         /* mask for DFA type (0) nodes */
204         dfa_msk = MM_CMPEQ32(node_types, t);
205
206         r = _mm_srli_epi32(in, 30);
207         r = _mm_add_epi8(r, range_base);
208
209         t = _mm_srli_epi32(in, 24);
210         r = _mm_shuffle_epi8(tr_hi, r);
211
212         dfa_ofs = _mm_sub_epi32(t, r);
213
214         /*
215          * Calculate number of range boundaries that are less than the
216          * input value. Range boundaries for each node are in signed 8 bit,
217          * ordered from -128 to 127 in the indices2 register.
218          * This is effectively a popcnt of bytes that are greater than the
219          * input byte.
220          */
221
222         /* check ranges */
223         t = MM_CMPGT8(in, tr_hi);
224
225         /* convert -1 to 1 (bytes greater than input byte */
226         t = MM_SIGN8(t, t);
227
228         /* horizontal add pairs of bytes into words */
229         t = MM_MADD8(t, t);
230
231         /* horizontal add pairs of words into dwords */
232         quad_ofs = MM_MADD16(t, ones_16);
233
234         /* blend DFA and QUAD/SINGLE. */
235         t = _mm_blendv_epi8(quad_ofs, dfa_ofs, dfa_msk);
236
237         /* add index into node position */
238         return MM_ADD32(addr, t);
239 }
240
241 /*
242  * Process 4 transitions (in 2 SIMD registers) in parallel
243  */
244 static inline __attribute__((always_inline)) xmm_t
245 transition4(xmm_t next_input, const uint64_t *trans,
246         xmm_t *indices1, xmm_t *indices2)
247 {
248         xmm_t addr, tr_lo, tr_hi;
249         uint64_t trans0, trans2;
250
251         /* Shuffle low 32 into tr_lo and high 32 into tr_hi */
252         tr_lo = (xmm_t)_mm_shuffle_ps((__m128)*indices1, (__m128)*indices2,
253                 0x88);
254         tr_hi = (xmm_t)_mm_shuffle_ps((__m128)*indices1, (__m128)*indices2,
255                 0xdd);
256
257          /* Calculate the address (array index) for all 4 transitions. */
258
259         addr = calc_addr_sse(xmm_index_mask.x, next_input, xmm_shuffle_input.x,
260                 xmm_ones_16.x, tr_lo, tr_hi);
261
262          /* Gather 64 bit transitions and pack back into 2 registers. */
263
264         trans0 = trans[MM_CVT32(addr)];
265
266         /* get slot 2 */
267
268         /* {x0, x1, x2, x3} -> {x2, x1, x2, x3} */
269         addr = MM_SHUFFLE32(addr, SHUFFLE32_SLOT2);
270         trans2 = trans[MM_CVT32(addr)];
271
272         /* get slot 1 */
273
274         /* {x2, x1, x2, x3} -> {x1, x1, x2, x3} */
275         addr = MM_SHUFFLE32(addr, SHUFFLE32_SLOT1);
276         *indices1 = MM_SET64(trans[MM_CVT32(addr)], trans0);
277
278         /* get slot 3 */
279
280         /* {x1, x1, x2, x3} -> {x3, x1, x2, x3} */
281         addr = MM_SHUFFLE32(addr, SHUFFLE32_SLOT3);
282         *indices2 = MM_SET64(trans[MM_CVT32(addr)], trans2);
283
284         return MM_SRL32(next_input, CHAR_BIT);
285 }
286
287 /*
288  * Execute trie traversal with 8 traversals in parallel
289  */
290 static inline int
291 search_sse_8(const struct rte_acl_ctx *ctx, const uint8_t **data,
292         uint32_t *results, uint32_t total_packets, uint32_t categories)
293 {
294         int n;
295         struct acl_flow_data flows;
296         uint64_t index_array[MAX_SEARCHES_SSE8];
297         struct completion cmplt[MAX_SEARCHES_SSE8];
298         struct parms parms[MAX_SEARCHES_SSE8];
299         xmm_t input0, input1;
300         xmm_t indices1, indices2, indices3, indices4;
301
302         acl_set_flow(&flows, cmplt, RTE_DIM(cmplt), data, results,
303                 total_packets, categories, ctx->trans_table);
304
305         for (n = 0; n < MAX_SEARCHES_SSE8; n++) {
306                 cmplt[n].count = 0;
307                 index_array[n] = acl_start_next_trie(&flows, parms, n, ctx);
308         }
309
310         /*
311          * indices1 contains index_array[0,1]
312          * indices2 contains index_array[2,3]
313          * indices3 contains index_array[4,5]
314          * indices4 contains index_array[6,7]
315          */
316
317         indices1 = MM_LOADU((xmm_t *) &index_array[0]);
318         indices2 = MM_LOADU((xmm_t *) &index_array[2]);
319
320         indices3 = MM_LOADU((xmm_t *) &index_array[4]);
321         indices4 = MM_LOADU((xmm_t *) &index_array[6]);
322
323          /* Check for any matches. */
324         acl_match_check_x4(0, ctx, parms, &flows,
325                 &indices1, &indices2, xmm_match_mask.x);
326         acl_match_check_x4(4, ctx, parms, &flows,
327                 &indices3, &indices4, xmm_match_mask.x);
328
329         while (flows.started > 0) {
330
331                 /* Gather 4 bytes of input data for each stream. */
332                 input0 = _mm_cvtsi32_si128(GET_NEXT_4BYTES(parms, 0));
333                 input1 = _mm_cvtsi32_si128(GET_NEXT_4BYTES(parms, 4));
334
335                 input0 = MM_INSERT32(input0, GET_NEXT_4BYTES(parms, 1), 1);
336                 input1 = MM_INSERT32(input1, GET_NEXT_4BYTES(parms, 5), 1);
337
338                 input0 = MM_INSERT32(input0, GET_NEXT_4BYTES(parms, 2), 2);
339                 input1 = MM_INSERT32(input1, GET_NEXT_4BYTES(parms, 6), 2);
340
341                 input0 = MM_INSERT32(input0, GET_NEXT_4BYTES(parms, 3), 3);
342                 input1 = MM_INSERT32(input1, GET_NEXT_4BYTES(parms, 7), 3);
343
344                  /* Process the 4 bytes of input on each stream. */
345
346                 input0 = transition4(input0, flows.trans,
347                         &indices1, &indices2);
348                 input1 = transition4(input1, flows.trans,
349                         &indices3, &indices4);
350
351                 input0 = transition4(input0, flows.trans,
352                         &indices1, &indices2);
353                 input1 = transition4(input1, flows.trans,
354                         &indices3, &indices4);
355
356                 input0 = transition4(input0, flows.trans,
357                         &indices1, &indices2);
358                 input1 = transition4(input1, flows.trans,
359                         &indices3, &indices4);
360
361                 input0 = transition4(input0, flows.trans,
362                         &indices1, &indices2);
363                 input1 = transition4(input1, flows.trans,
364                         &indices3, &indices4);
365
366                  /* Check for any matches. */
367                 acl_match_check_x4(0, ctx, parms, &flows,
368                         &indices1, &indices2, xmm_match_mask.x);
369                 acl_match_check_x4(4, ctx, parms, &flows,
370                         &indices3, &indices4, xmm_match_mask.x);
371         }
372
373         return 0;
374 }
375
376 /*
377  * Execute trie traversal with 4 traversals in parallel
378  */
379 static inline int
380 search_sse_4(const struct rte_acl_ctx *ctx, const uint8_t **data,
381          uint32_t *results, int total_packets, uint32_t categories)
382 {
383         int n;
384         struct acl_flow_data flows;
385         uint64_t index_array[MAX_SEARCHES_SSE4];
386         struct completion cmplt[MAX_SEARCHES_SSE4];
387         struct parms parms[MAX_SEARCHES_SSE4];
388         xmm_t input, indices1, indices2;
389
390         acl_set_flow(&flows, cmplt, RTE_DIM(cmplt), data, results,
391                 total_packets, categories, ctx->trans_table);
392
393         for (n = 0; n < MAX_SEARCHES_SSE4; n++) {
394                 cmplt[n].count = 0;
395                 index_array[n] = acl_start_next_trie(&flows, parms, n, ctx);
396         }
397
398         indices1 = MM_LOADU((xmm_t *) &index_array[0]);
399         indices2 = MM_LOADU((xmm_t *) &index_array[2]);
400
401         /* Check for any matches. */
402         acl_match_check_x4(0, ctx, parms, &flows,
403                 &indices1, &indices2, xmm_match_mask.x);
404
405         while (flows.started > 0) {
406
407                 /* Gather 4 bytes of input data for each stream. */
408                 input = _mm_cvtsi32_si128(GET_NEXT_4BYTES(parms, 0));
409                 input = MM_INSERT32(input, GET_NEXT_4BYTES(parms, 1), 1);
410                 input = MM_INSERT32(input, GET_NEXT_4BYTES(parms, 2), 2);
411                 input = MM_INSERT32(input, GET_NEXT_4BYTES(parms, 3), 3);
412
413                 /* Process the 4 bytes of input on each stream. */
414                 input = transition4(input, flows.trans, &indices1, &indices2);
415                 input = transition4(input, flows.trans, &indices1, &indices2);
416                 input = transition4(input, flows.trans, &indices1, &indices2);
417                 input = transition4(input, flows.trans, &indices1, &indices2);
418
419                 /* Check for any matches. */
420                 acl_match_check_x4(0, ctx, parms, &flows,
421                         &indices1, &indices2, xmm_match_mask.x);
422         }
423
424         return 0;
425 }