199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson * Copyright(c) 2015 Cavium, Inc
399a2dd95SBruce Richardson */
499a2dd95SBruce Richardson
5*e9fd1ebfSTyler Retzlaff #include <stdalign.h>
6*e9fd1ebfSTyler Retzlaff
799a2dd95SBruce Richardson #include "acl_run.h"
899a2dd95SBruce Richardson #include "acl_vect.h"
999a2dd95SBruce Richardson
10*e9fd1ebfSTyler Retzlaff alignas(RTE_CACHE_LINE_SIZE) struct _neon_acl_const {
1199a2dd95SBruce Richardson rte_xmm_t xmm_shuffle_input;
1299a2dd95SBruce Richardson rte_xmm_t xmm_index_mask;
1399a2dd95SBruce Richardson rte_xmm_t range_base;
14*e9fd1ebfSTyler Retzlaff } neon_acl_const = {
1599a2dd95SBruce Richardson {
1699a2dd95SBruce Richardson .u32 = {0x00000000, 0x04040404, 0x08080808, 0x0c0c0c0c}
1799a2dd95SBruce Richardson },
1899a2dd95SBruce Richardson {
1999a2dd95SBruce Richardson .u32 = {RTE_ACL_NODE_INDEX, RTE_ACL_NODE_INDEX,
2099a2dd95SBruce Richardson RTE_ACL_NODE_INDEX, RTE_ACL_NODE_INDEX}
2199a2dd95SBruce Richardson },
2299a2dd95SBruce Richardson {
2399a2dd95SBruce Richardson .u32 = {0xffffff00, 0xffffff04, 0xffffff08, 0xffffff0c}
2499a2dd95SBruce Richardson },
2599a2dd95SBruce Richardson };
2699a2dd95SBruce Richardson
2799a2dd95SBruce Richardson /*
2899a2dd95SBruce Richardson * Resolve priority for multiple results (neon version).
2999a2dd95SBruce Richardson * This consists comparing the priority of the current traversal with the
3099a2dd95SBruce Richardson * running set of results for the packet.
3199a2dd95SBruce Richardson * For each result, keep a running array of the result (rule number) and
3299a2dd95SBruce Richardson * its priority for each category.
3399a2dd95SBruce Richardson */
3499a2dd95SBruce Richardson static inline void
resolve_priority_neon(uint64_t transition,int n,const struct rte_acl_ctx * ctx,struct parms * parms,const struct rte_acl_match_results * p,uint32_t categories)3599a2dd95SBruce Richardson resolve_priority_neon(uint64_t transition, int n, const struct rte_acl_ctx *ctx,
3699a2dd95SBruce Richardson struct parms *parms,
3799a2dd95SBruce Richardson const struct rte_acl_match_results *p,
3899a2dd95SBruce Richardson uint32_t categories)
3999a2dd95SBruce Richardson {
4099a2dd95SBruce Richardson uint32_t x;
4199a2dd95SBruce Richardson int32x4_t results, priority, results1, priority1;
4299a2dd95SBruce Richardson uint32x4_t selector;
4399a2dd95SBruce Richardson int32_t *saved_results, *saved_priority;
4499a2dd95SBruce Richardson
4599a2dd95SBruce Richardson for (x = 0; x < categories; x += RTE_ACL_RESULTS_MULTIPLIER) {
4699a2dd95SBruce Richardson saved_results = (int32_t *)(&parms[n].cmplt->results[x]);
4799a2dd95SBruce Richardson saved_priority = (int32_t *)(&parms[n].cmplt->priority[x]);
4899a2dd95SBruce Richardson
4999a2dd95SBruce Richardson /* get results and priorities for completed trie */
5099a2dd95SBruce Richardson results = vld1q_s32(
5199a2dd95SBruce Richardson (const int32_t *)&p[transition].results[x]);
5299a2dd95SBruce Richardson priority = vld1q_s32(
5399a2dd95SBruce Richardson (const int32_t *)&p[transition].priority[x]);
5499a2dd95SBruce Richardson
5599a2dd95SBruce Richardson /* if this is not the first completed trie */
5699a2dd95SBruce Richardson if (parms[n].cmplt->count != ctx->num_tries) {
5799a2dd95SBruce Richardson /* get running best results and their priorities */
5899a2dd95SBruce Richardson results1 = vld1q_s32(saved_results);
5999a2dd95SBruce Richardson priority1 = vld1q_s32(saved_priority);
6099a2dd95SBruce Richardson
6199a2dd95SBruce Richardson /* select results that are highest priority */
6299a2dd95SBruce Richardson selector = vcgtq_s32(priority1, priority);
6399a2dd95SBruce Richardson results = vbslq_s32(selector, results1, results);
6499a2dd95SBruce Richardson priority = vbslq_s32(selector, priority1, priority);
6599a2dd95SBruce Richardson }
6699a2dd95SBruce Richardson
6799a2dd95SBruce Richardson /* save running best results and their priorities */
6899a2dd95SBruce Richardson vst1q_s32(saved_results, results);
6999a2dd95SBruce Richardson vst1q_s32(saved_priority, priority);
7099a2dd95SBruce Richardson }
7199a2dd95SBruce Richardson }
7299a2dd95SBruce Richardson
7399a2dd95SBruce Richardson /*
7499a2dd95SBruce Richardson * Check for any match in 4 transitions
7599a2dd95SBruce Richardson */
7699a2dd95SBruce Richardson static __rte_always_inline uint32_t
check_any_match_x4(uint64_t val[])7799a2dd95SBruce Richardson check_any_match_x4(uint64_t val[])
7899a2dd95SBruce Richardson {
7999a2dd95SBruce Richardson return (val[0] | val[1] | val[2] | val[3]) & RTE_ACL_NODE_MATCH;
8099a2dd95SBruce Richardson }
8199a2dd95SBruce Richardson
8299a2dd95SBruce Richardson static __rte_always_inline void
acl_match_check_x4(int slot,const struct rte_acl_ctx * ctx,struct parms * parms,struct acl_flow_data * flows,uint64_t transitions[])8399a2dd95SBruce Richardson acl_match_check_x4(int slot, const struct rte_acl_ctx *ctx, struct parms *parms,
8499a2dd95SBruce Richardson struct acl_flow_data *flows, uint64_t transitions[])
8599a2dd95SBruce Richardson {
8699a2dd95SBruce Richardson while (check_any_match_x4(transitions)) {
8799a2dd95SBruce Richardson transitions[0] = acl_match_check(transitions[0], slot, ctx,
8899a2dd95SBruce Richardson parms, flows, resolve_priority_neon);
8999a2dd95SBruce Richardson transitions[1] = acl_match_check(transitions[1], slot + 1, ctx,
9099a2dd95SBruce Richardson parms, flows, resolve_priority_neon);
9199a2dd95SBruce Richardson transitions[2] = acl_match_check(transitions[2], slot + 2, ctx,
9299a2dd95SBruce Richardson parms, flows, resolve_priority_neon);
9399a2dd95SBruce Richardson transitions[3] = acl_match_check(transitions[3], slot + 3, ctx,
9499a2dd95SBruce Richardson parms, flows, resolve_priority_neon);
9599a2dd95SBruce Richardson }
9699a2dd95SBruce Richardson }
9799a2dd95SBruce Richardson
9899a2dd95SBruce Richardson /*
9999a2dd95SBruce Richardson * Process 4 transitions (in 2 NEON Q registers) in parallel
10099a2dd95SBruce Richardson */
10199a2dd95SBruce Richardson static __rte_always_inline int32x4_t
transition4(int32x4_t next_input,const uint64_t * trans,uint64_t transitions[])10299a2dd95SBruce Richardson transition4(int32x4_t next_input, const uint64_t *trans, uint64_t transitions[])
10399a2dd95SBruce Richardson {
10499a2dd95SBruce Richardson int32x4x2_t tr_hi_lo;
10599a2dd95SBruce Richardson int32x4_t t, in, r;
10699a2dd95SBruce Richardson uint32x4_t index_msk, node_type, addr;
10799a2dd95SBruce Richardson uint32x4_t dfa_msk, mask, quad_ofs, dfa_ofs;
10899a2dd95SBruce Richardson
10999a2dd95SBruce Richardson /* Move low 32 into tr_hi_lo.val[0] and high 32 into tr_hi_lo.val[1] */
11099a2dd95SBruce Richardson tr_hi_lo = vld2q_s32((const int32_t *)transitions);
11199a2dd95SBruce Richardson
11299a2dd95SBruce Richardson /* Calculate the address (array index) for all 4 transitions. */
11399a2dd95SBruce Richardson
11499a2dd95SBruce Richardson index_msk = vld1q_u32((const uint32_t *)&neon_acl_const.xmm_index_mask);
11599a2dd95SBruce Richardson
11699a2dd95SBruce Richardson /* Calc node type and node addr */
11799a2dd95SBruce Richardson node_type = vbicq_s32(tr_hi_lo.val[0], index_msk);
11899a2dd95SBruce Richardson addr = vandq_s32(tr_hi_lo.val[0], index_msk);
11999a2dd95SBruce Richardson
12099a2dd95SBruce Richardson /* t = 0 */
12199a2dd95SBruce Richardson t = veorq_s32(node_type, node_type);
12299a2dd95SBruce Richardson
12399a2dd95SBruce Richardson /* mask for DFA type(0) nodes */
12499a2dd95SBruce Richardson dfa_msk = vceqq_u32(node_type, t);
12599a2dd95SBruce Richardson
12699a2dd95SBruce Richardson mask = vld1q_s32((const int32_t *)&neon_acl_const.xmm_shuffle_input);
12799a2dd95SBruce Richardson in = vqtbl1q_u8((uint8x16_t)next_input, (uint8x16_t)mask);
12899a2dd95SBruce Richardson
12999a2dd95SBruce Richardson /* DFA calculations. */
13099a2dd95SBruce Richardson r = vshrq_n_u32(in, 30); /* div by 64 */
13199a2dd95SBruce Richardson mask = vld1q_s32((const int32_t *)&neon_acl_const.range_base);
13299a2dd95SBruce Richardson r = vaddq_u8(r, mask);
13399a2dd95SBruce Richardson t = vshrq_n_u32(in, 24);
13499a2dd95SBruce Richardson r = vqtbl1q_u8((uint8x16_t)tr_hi_lo.val[1], (uint8x16_t)r);
13599a2dd95SBruce Richardson dfa_ofs = vsubq_s32(t, r);
13699a2dd95SBruce Richardson
13799a2dd95SBruce Richardson /* QUAD/SINGLE calculations. */
13899a2dd95SBruce Richardson t = vcgtq_s8(in, tr_hi_lo.val[1]);
13999a2dd95SBruce Richardson t = vabsq_s8(t);
14099a2dd95SBruce Richardson t = vpaddlq_u8(t);
14199a2dd95SBruce Richardson quad_ofs = vpaddlq_u16(t);
14299a2dd95SBruce Richardson
14399a2dd95SBruce Richardson /* blend DFA and QUAD/SINGLE. */
14499a2dd95SBruce Richardson t = vbslq_u8(dfa_msk, dfa_ofs, quad_ofs);
14599a2dd95SBruce Richardson
14699a2dd95SBruce Richardson /* calculate address for next transitions */
14799a2dd95SBruce Richardson addr = vaddq_u32(addr, t);
14899a2dd95SBruce Richardson
14999a2dd95SBruce Richardson /* Fill next transitions */
15099a2dd95SBruce Richardson transitions[0] = trans[vgetq_lane_u32(addr, 0)];
15199a2dd95SBruce Richardson transitions[1] = trans[vgetq_lane_u32(addr, 1)];
15299a2dd95SBruce Richardson transitions[2] = trans[vgetq_lane_u32(addr, 2)];
15399a2dd95SBruce Richardson transitions[3] = trans[vgetq_lane_u32(addr, 3)];
15499a2dd95SBruce Richardson
15599a2dd95SBruce Richardson return vshrq_n_u32(next_input, CHAR_BIT);
15699a2dd95SBruce Richardson }
15799a2dd95SBruce Richardson
15899a2dd95SBruce Richardson /*
15999a2dd95SBruce Richardson * Execute trie traversal with 8 traversals in parallel
16099a2dd95SBruce Richardson */
16199a2dd95SBruce Richardson static inline int
search_neon_8(const struct rte_acl_ctx * ctx,const uint8_t ** data,uint32_t * results,uint32_t total_packets,uint32_t categories)16299a2dd95SBruce Richardson search_neon_8(const struct rte_acl_ctx *ctx, const uint8_t **data,
16399a2dd95SBruce Richardson uint32_t *results, uint32_t total_packets, uint32_t categories)
16499a2dd95SBruce Richardson {
16599a2dd95SBruce Richardson int n;
16699a2dd95SBruce Richardson struct acl_flow_data flows;
16799a2dd95SBruce Richardson uint64_t index_array[8];
16899a2dd95SBruce Richardson struct completion cmplt[8];
16999a2dd95SBruce Richardson struct parms parms[8];
17099a2dd95SBruce Richardson int32x4_t input0, input1;
17199a2dd95SBruce Richardson
17299a2dd95SBruce Richardson acl_set_flow(&flows, cmplt, RTE_DIM(cmplt), data, results,
17399a2dd95SBruce Richardson total_packets, categories, ctx->trans_table);
17499a2dd95SBruce Richardson
17599a2dd95SBruce Richardson for (n = 0; n < 8; n++) {
17699a2dd95SBruce Richardson cmplt[n].count = 0;
17799a2dd95SBruce Richardson index_array[n] = acl_start_next_trie(&flows, parms, n, ctx);
17899a2dd95SBruce Richardson }
17999a2dd95SBruce Richardson
18099a2dd95SBruce Richardson /* Check for any matches. */
18199a2dd95SBruce Richardson acl_match_check_x4(0, ctx, parms, &flows, &index_array[0]);
18299a2dd95SBruce Richardson acl_match_check_x4(4, ctx, parms, &flows, &index_array[4]);
18399a2dd95SBruce Richardson
18499a2dd95SBruce Richardson while (flows.started > 0) {
18599a2dd95SBruce Richardson /* Gather 4 bytes of input data for each stream. */
18699a2dd95SBruce Richardson input0 = vdupq_n_s32(GET_NEXT_4BYTES(parms, 0));
18799a2dd95SBruce Richardson input1 = vdupq_n_s32(GET_NEXT_4BYTES(parms, 4));
18899a2dd95SBruce Richardson
18999a2dd95SBruce Richardson input0 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 1), input0, 1);
19099a2dd95SBruce Richardson input1 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 5), input1, 1);
19199a2dd95SBruce Richardson
19299a2dd95SBruce Richardson input0 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 2), input0, 2);
19399a2dd95SBruce Richardson input1 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 6), input1, 2);
19499a2dd95SBruce Richardson
19599a2dd95SBruce Richardson input0 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 3), input0, 3);
19699a2dd95SBruce Richardson input1 = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 7), input1, 3);
19799a2dd95SBruce Richardson
19899a2dd95SBruce Richardson /* Process the 4 bytes of input on each stream. */
19999a2dd95SBruce Richardson
20099a2dd95SBruce Richardson input0 = transition4(input0, flows.trans, &index_array[0]);
20199a2dd95SBruce Richardson input1 = transition4(input1, flows.trans, &index_array[4]);
20299a2dd95SBruce Richardson
20399a2dd95SBruce Richardson input0 = transition4(input0, flows.trans, &index_array[0]);
20499a2dd95SBruce Richardson input1 = transition4(input1, flows.trans, &index_array[4]);
20599a2dd95SBruce Richardson
20699a2dd95SBruce Richardson input0 = transition4(input0, flows.trans, &index_array[0]);
20799a2dd95SBruce Richardson input1 = transition4(input1, flows.trans, &index_array[4]);
20899a2dd95SBruce Richardson
20999a2dd95SBruce Richardson input0 = transition4(input0, flows.trans, &index_array[0]);
21099a2dd95SBruce Richardson input1 = transition4(input1, flows.trans, &index_array[4]);
21199a2dd95SBruce Richardson
21299a2dd95SBruce Richardson /* Check for any matches. */
21399a2dd95SBruce Richardson acl_match_check_x4(0, ctx, parms, &flows, &index_array[0]);
21499a2dd95SBruce Richardson acl_match_check_x4(4, ctx, parms, &flows, &index_array[4]);
21599a2dd95SBruce Richardson }
21699a2dd95SBruce Richardson
21799a2dd95SBruce Richardson return 0;
21899a2dd95SBruce Richardson }
21999a2dd95SBruce Richardson
22099a2dd95SBruce Richardson /*
22199a2dd95SBruce Richardson * Execute trie traversal with 4 traversals in parallel
22299a2dd95SBruce Richardson */
22399a2dd95SBruce Richardson static inline int
search_neon_4(const struct rte_acl_ctx * ctx,const uint8_t ** data,uint32_t * results,int total_packets,uint32_t categories)22499a2dd95SBruce Richardson search_neon_4(const struct rte_acl_ctx *ctx, const uint8_t **data,
22599a2dd95SBruce Richardson uint32_t *results, int total_packets, uint32_t categories)
22699a2dd95SBruce Richardson {
22799a2dd95SBruce Richardson int n;
22899a2dd95SBruce Richardson struct acl_flow_data flows;
22999a2dd95SBruce Richardson uint64_t index_array[4];
23099a2dd95SBruce Richardson struct completion cmplt[4];
23199a2dd95SBruce Richardson struct parms parms[4];
23299a2dd95SBruce Richardson int32x4_t input;
23399a2dd95SBruce Richardson
23499a2dd95SBruce Richardson acl_set_flow(&flows, cmplt, RTE_DIM(cmplt), data, results,
23599a2dd95SBruce Richardson total_packets, categories, ctx->trans_table);
23699a2dd95SBruce Richardson
23799a2dd95SBruce Richardson for (n = 0; n < 4; n++) {
23899a2dd95SBruce Richardson cmplt[n].count = 0;
23999a2dd95SBruce Richardson index_array[n] = acl_start_next_trie(&flows, parms, n, ctx);
24099a2dd95SBruce Richardson }
24199a2dd95SBruce Richardson
24299a2dd95SBruce Richardson /* Check for any matches. */
24399a2dd95SBruce Richardson acl_match_check_x4(0, ctx, parms, &flows, index_array);
24499a2dd95SBruce Richardson
24599a2dd95SBruce Richardson while (flows.started > 0) {
24699a2dd95SBruce Richardson /* Gather 4 bytes of input data for each stream. */
24799a2dd95SBruce Richardson input = vdupq_n_s32(GET_NEXT_4BYTES(parms, 0));
24899a2dd95SBruce Richardson input = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 1), input, 1);
24999a2dd95SBruce Richardson input = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 2), input, 2);
25099a2dd95SBruce Richardson input = vsetq_lane_s32(GET_NEXT_4BYTES(parms, 3), input, 3);
25199a2dd95SBruce Richardson
25299a2dd95SBruce Richardson /* Process the 4 bytes of input on each stream. */
25399a2dd95SBruce Richardson input = transition4(input, flows.trans, index_array);
25499a2dd95SBruce Richardson input = transition4(input, flows.trans, index_array);
25599a2dd95SBruce Richardson input = transition4(input, flows.trans, index_array);
25699a2dd95SBruce Richardson input = transition4(input, flows.trans, index_array);
25799a2dd95SBruce Richardson
25899a2dd95SBruce Richardson /* Check for any matches. */
25999a2dd95SBruce Richardson acl_match_check_x4(0, ctx, parms, &flows, index_array);
26099a2dd95SBruce Richardson }
26199a2dd95SBruce Richardson
26299a2dd95SBruce Richardson return 0;
26399a2dd95SBruce Richardson }
264