1b7870e4c2042184645fe2868ea75418a0d791f8
[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 indices1, xmm_t indices2)
176 {
177         xmm_t addr, node_types, range, temp;
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         /* Shuffle low 32 into temp and high 32 into indices2 */
191         temp = (xmm_t)MM_SHUFFLEPS((__m128)indices1, (__m128)indices2, 0x88);
192         range = (xmm_t)MM_SHUFFLEPS((__m128)indices1, (__m128)indices2, 0xdd);
193
194         t = MM_XOR(index_mask, index_mask);
195
196         /* shuffle input byte to all 4 positions of 32 bit value */
197         in = MM_SHUFFLE8(next_input, shuffle_input);
198
199         /* Calc node type and node addr */
200         node_types = MM_ANDNOT(index_mask, temp);
201         addr = MM_AND(index_mask, temp);
202
203         /*
204          * Calc addr for DFAs - addr = dfa_index + input_byte
205          */
206
207         /* mask for DFA type (0) nodes */
208         dfa_msk = MM_CMPEQ32(node_types, t);
209
210         r = _mm_srli_epi32(in, 30);
211         r = _mm_add_epi8(r, range_base);
212
213         t = _mm_srli_epi32(in, 24);
214         r = _mm_shuffle_epi8(range, r);
215
216         dfa_ofs = _mm_sub_epi32(t, r);
217
218         /*
219          * Calculate number of range boundaries that are less than the
220          * input value. Range boundaries for each node are in signed 8 bit,
221          * ordered from -128 to 127 in the indices2 register.
222          * This is effectively a popcnt of bytes that are greater than the
223          * input byte.
224          */
225
226         /* check ranges */
227         temp = MM_CMPGT8(in, range);
228
229         /* convert -1 to 1 (bytes greater than input byte */
230         temp = MM_SIGN8(temp, temp);
231
232         /* horizontal add pairs of bytes into words */
233         temp = MM_MADD8(temp, temp);
234
235         /* horizontal add pairs of words into dwords */
236         quad_ofs = MM_MADD16(temp, ones_16);
237
238         /* mask to range type nodes */
239         temp = _mm_blendv_epi8(quad_ofs, dfa_ofs, dfa_msk);
240
241         /* add index into node position */
242         return MM_ADD32(addr, temp);
243 }
244
245 /*
246  * Process 4 transitions (in 2 SIMD registers) in parallel
247  */
248 static inline __attribute__((always_inline)) xmm_t
249 transition4(xmm_t next_input, const uint64_t *trans,
250         xmm_t *indices1, xmm_t *indices2)
251 {
252         xmm_t addr;
253         uint64_t trans0, trans2;
254
255          /* Calculate the address (array index) for all 4 transitions. */
256
257         addr = calc_addr_sse(xmm_index_mask.x, next_input, xmm_shuffle_input.x,
258                 xmm_ones_16.x, *indices1, *indices2);
259
260          /* Gather 64 bit transitions and pack back into 2 registers. */
261
262         trans0 = trans[MM_CVT32(addr)];
263
264         /* get slot 2 */
265
266         /* {x0, x1, x2, x3} -> {x2, x1, x2, x3} */
267         addr = MM_SHUFFLE32(addr, SHUFFLE32_SLOT2);
268         trans2 = trans[MM_CVT32(addr)];
269
270         /* get slot 1 */
271
272         /* {x2, x1, x2, x3} -> {x1, x1, x2, x3} */
273         addr = MM_SHUFFLE32(addr, SHUFFLE32_SLOT1);
274         *indices1 = MM_SET64(trans[MM_CVT32(addr)], trans0);
275
276         /* get slot 3 */
277
278         /* {x1, x1, x2, x3} -> {x3, x1, x2, x3} */
279         addr = MM_SHUFFLE32(addr, SHUFFLE32_SLOT3);
280         *indices2 = MM_SET64(trans[MM_CVT32(addr)], trans2);
281
282         return MM_SRL32(next_input, CHAR_BIT);
283 }
284
285 /*
286  * Execute trie traversal with 8 traversals in parallel
287  */
288 static inline int
289 search_sse_8(const struct rte_acl_ctx *ctx, const uint8_t **data,
290         uint32_t *results, uint32_t total_packets, uint32_t categories)
291 {
292         int n;
293         struct acl_flow_data flows;
294         uint64_t index_array[MAX_SEARCHES_SSE8];
295         struct completion cmplt[MAX_SEARCHES_SSE8];
296         struct parms parms[MAX_SEARCHES_SSE8];
297         xmm_t input0, input1;
298         xmm_t indices1, indices2, indices3, indices4;
299
300         acl_set_flow(&flows, cmplt, RTE_DIM(cmplt), data, results,
301                 total_packets, categories, ctx->trans_table);
302
303         for (n = 0; n < MAX_SEARCHES_SSE8; n++) {
304                 cmplt[n].count = 0;
305                 index_array[n] = acl_start_next_trie(&flows, parms, n, ctx);
306         }
307
308         /*
309          * indices1 contains index_array[0,1]
310          * indices2 contains index_array[2,3]
311          * indices3 contains index_array[4,5]
312          * indices4 contains index_array[6,7]
313          */
314
315         indices1 = MM_LOADU((xmm_t *) &index_array[0]);
316         indices2 = MM_LOADU((xmm_t *) &index_array[2]);
317
318         indices3 = MM_LOADU((xmm_t *) &index_array[4]);
319         indices4 = MM_LOADU((xmm_t *) &index_array[6]);
320
321          /* Check for any matches. */
322         acl_match_check_x4(0, ctx, parms, &flows,
323                 &indices1, &indices2, xmm_match_mask.x);
324         acl_match_check_x4(4, ctx, parms, &flows,
325                 &indices3, &indices4, xmm_match_mask.x);
326
327         while (flows.started > 0) {
328
329                 /* Gather 4 bytes of input data for each stream. */
330                 input0 = _mm_cvtsi32_si128(GET_NEXT_4BYTES(parms, 0));
331                 input1 = _mm_cvtsi32_si128(GET_NEXT_4BYTES(parms, 4));
332
333                 input0 = MM_INSERT32(input0, GET_NEXT_4BYTES(parms, 1), 1);
334                 input1 = MM_INSERT32(input1, GET_NEXT_4BYTES(parms, 5), 1);
335
336                 input0 = MM_INSERT32(input0, GET_NEXT_4BYTES(parms, 2), 2);
337                 input1 = MM_INSERT32(input1, GET_NEXT_4BYTES(parms, 6), 2);
338
339                 input0 = MM_INSERT32(input0, GET_NEXT_4BYTES(parms, 3), 3);
340                 input1 = MM_INSERT32(input1, GET_NEXT_4BYTES(parms, 7), 3);
341
342                  /* Process the 4 bytes of input on each stream. */
343
344                 input0 = transition4(input0, flows.trans,
345                         &indices1, &indices2);
346                 input1 = transition4(input1, flows.trans,
347                         &indices3, &indices4);
348
349                 input0 = transition4(input0, flows.trans,
350                         &indices1, &indices2);
351                 input1 = transition4(input1, flows.trans,
352                         &indices3, &indices4);
353
354                 input0 = transition4(input0, flows.trans,
355                         &indices1, &indices2);
356                 input1 = transition4(input1, flows.trans,
357                         &indices3, &indices4);
358
359                 input0 = transition4(input0, flows.trans,
360                         &indices1, &indices2);
361                 input1 = transition4(input1, flows.trans,
362                         &indices3, &indices4);
363
364                  /* Check for any matches. */
365                 acl_match_check_x4(0, ctx, parms, &flows,
366                         &indices1, &indices2, xmm_match_mask.x);
367                 acl_match_check_x4(4, ctx, parms, &flows,
368                         &indices3, &indices4, xmm_match_mask.x);
369         }
370
371         return 0;
372 }
373
374 /*
375  * Execute trie traversal with 4 traversals in parallel
376  */
377 static inline int
378 search_sse_4(const struct rte_acl_ctx *ctx, const uint8_t **data,
379          uint32_t *results, int total_packets, uint32_t categories)
380 {
381         int n;
382         struct acl_flow_data flows;
383         uint64_t index_array[MAX_SEARCHES_SSE4];
384         struct completion cmplt[MAX_SEARCHES_SSE4];
385         struct parms parms[MAX_SEARCHES_SSE4];
386         xmm_t input, indices1, indices2;
387
388         acl_set_flow(&flows, cmplt, RTE_DIM(cmplt), data, results,
389                 total_packets, categories, ctx->trans_table);
390
391         for (n = 0; n < MAX_SEARCHES_SSE4; n++) {
392                 cmplt[n].count = 0;
393                 index_array[n] = acl_start_next_trie(&flows, parms, n, ctx);
394         }
395
396         indices1 = MM_LOADU((xmm_t *) &index_array[0]);
397         indices2 = MM_LOADU((xmm_t *) &index_array[2]);
398
399         /* Check for any matches. */
400         acl_match_check_x4(0, ctx, parms, &flows,
401                 &indices1, &indices2, xmm_match_mask.x);
402
403         while (flows.started > 0) {
404
405                 /* Gather 4 bytes of input data for each stream. */
406                 input = _mm_cvtsi32_si128(GET_NEXT_4BYTES(parms, 0));
407                 input = MM_INSERT32(input, GET_NEXT_4BYTES(parms, 1), 1);
408                 input = MM_INSERT32(input, GET_NEXT_4BYTES(parms, 2), 2);
409                 input = MM_INSERT32(input, GET_NEXT_4BYTES(parms, 3), 3);
410
411                 /* Process the 4 bytes of input on each stream. */
412                 input = transition4(input, flows.trans, &indices1, &indices2);
413                 input = transition4(input, flows.trans, &indices1, &indices2);
414                 input = transition4(input, flows.trans, &indices1, &indices2);
415                 input = transition4(input, flows.trans, &indices1, &indices2);
416
417                 /* Check for any matches. */
418                 acl_match_check_x4(0, ctx, parms, &flows,
419                         &indices1, &indices2, xmm_match_mask.x);
420         }
421
422         return 0;
423 }