numam-dpdk/lib/librte_acl/acl_bld.c

1570 lines
36 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2010-2014 Intel Corporation
*/
#include <rte_acl.h>
#include "tb_mem.h"
#include "acl.h"
#define ACL_POOL_ALIGN 8
#define ACL_POOL_ALLOC_MIN 0x800000
/* number of pointers per alloc */
#define ACL_PTR_ALLOC 32
/* macros for dividing rule sets heuristics */
#define NODE_MAX 0x4000
#define NODE_MIN 0x800
/* TALLY are statistics per field */
enum {
TALLY_0 = 0, /* number of rules that are 0% or more wild. */
TALLY_25, /* number of rules that are 25% or more wild. */
TALLY_50,
TALLY_75,
TALLY_100,
TALLY_DEACTIVATED, /* deactivated fields (100% wild in all rules). */
TALLY_DEPTH,
/* number of rules that are 100% wild for this field and higher. */
TALLY_NUM
};
static const uint32_t wild_limits[TALLY_DEACTIVATED] = {0, 25, 50, 75, 100};
enum {
ACL_INTERSECT_NONE = 0,
ACL_INTERSECT_A = 1, /* set A is a superset of A and B intersect */
ACL_INTERSECT_B = 2, /* set B is a superset of A and B intersect */
ACL_INTERSECT = 4, /* sets A and B intersect */
};
enum {
ACL_PRIORITY_EQUAL = 0,
ACL_PRIORITY_NODE_A = 1,
ACL_PRIORITY_NODE_B = 2,
ACL_PRIORITY_MIXED = 3
};
struct acl_mem_block {
uint32_t block_size;
void *mem_ptr;
};
#define MEM_BLOCK_NUM 16
/* Single ACL rule, build representation.*/
struct rte_acl_build_rule {
struct rte_acl_build_rule *next;
struct rte_acl_config *config;
/**< configuration for each field in the rule. */
const struct rte_acl_rule *f;
uint32_t *wildness;
};
/* Context for build phase */
struct acl_build_context {
const struct rte_acl_ctx *acx;
struct rte_acl_build_rule *build_rules;
struct rte_acl_config cfg;
int32_t node_max;
int32_t cur_node_max;
uint32_t node;
uint32_t num_nodes;
uint32_t category_mask;
uint32_t num_rules;
uint32_t node_id;
uint32_t src_mask;
uint32_t num_build_rules;
uint32_t num_tries;
struct tb_mem_pool pool;
struct rte_acl_trie tries[RTE_ACL_MAX_TRIES];
struct rte_acl_bld_trie bld_tries[RTE_ACL_MAX_TRIES];
uint32_t data_indexes[RTE_ACL_MAX_TRIES][RTE_ACL_MAX_FIELDS];
/* memory free lists for nodes and blocks used for node ptrs */
struct acl_mem_block blocks[MEM_BLOCK_NUM];
struct rte_acl_node *node_free_list;
};
static int acl_merge_trie(struct acl_build_context *context,
struct rte_acl_node *node_a, struct rte_acl_node *node_b,
uint32_t level, struct rte_acl_node **node_c);
static void
acl_deref_ptr(struct acl_build_context *context,
struct rte_acl_node *node, int index);
static void *
acl_build_alloc(struct acl_build_context *context, size_t n, size_t s)
{
uint32_t m;
void *p;
size_t alloc_size = n * s;
/*
* look for memory in free lists
*/
for (m = 0; m < RTE_DIM(context->blocks); m++) {
if (context->blocks[m].block_size ==
alloc_size && context->blocks[m].mem_ptr != NULL) {
p = context->blocks[m].mem_ptr;
context->blocks[m].mem_ptr = *((void **)p);
memset(p, 0, alloc_size);
return p;
}
}
/*
* return allocation from memory pool
*/
p = tb_alloc(&context->pool, alloc_size);
return p;
}
/*
* Free memory blocks (kept in context for reuse).
*/
static void
acl_build_free(struct acl_build_context *context, size_t s, void *p)
{
uint32_t n;
for (n = 0; n < RTE_DIM(context->blocks); n++) {
if (context->blocks[n].block_size == s) {
*((void **)p) = context->blocks[n].mem_ptr;
context->blocks[n].mem_ptr = p;
return;
}
}
for (n = 0; n < RTE_DIM(context->blocks); n++) {
if (context->blocks[n].block_size == 0) {
context->blocks[n].block_size = s;
*((void **)p) = NULL;
context->blocks[n].mem_ptr = p;
return;
}
}
}
/*
* Allocate and initialize a new node.
*/
static struct rte_acl_node *
acl_alloc_node(struct acl_build_context *context, int level)
{
struct rte_acl_node *node;
if (context->node_free_list != NULL) {
node = context->node_free_list;
context->node_free_list = node->next;
memset(node, 0, sizeof(struct rte_acl_node));
} else {
node = acl_build_alloc(context, sizeof(struct rte_acl_node), 1);
}
if (node != NULL) {
node->num_ptrs = 0;
node->level = level;
node->node_type = RTE_ACL_NODE_UNDEFINED;
node->node_index = RTE_ACL_NODE_UNDEFINED;
context->num_nodes++;
node->id = context->node_id++;
}
return node;
}
/*
* Dereference all nodes to which this node points
*/
static void
acl_free_node(struct acl_build_context *context,
struct rte_acl_node *node)
{
uint32_t n;
if (node->prev != NULL)
node->prev->next = NULL;
for (n = 0; n < node->num_ptrs; n++)
acl_deref_ptr(context, node, n);
/* free mrt if this is a match node */
if (node->mrt != NULL) {
acl_build_free(context, sizeof(struct rte_acl_match_results),
node->mrt);
node->mrt = NULL;
}
/* free transitions to other nodes */
if (node->ptrs != NULL) {
acl_build_free(context,
node->max_ptrs * sizeof(struct rte_acl_ptr_set),
node->ptrs);
node->ptrs = NULL;
}
/* put it on the free list */
context->num_nodes--;
node->next = context->node_free_list;
context->node_free_list = node;
}
/*
* Include src bitset in dst bitset
*/
static void
acl_include(struct rte_acl_bitset *dst, struct rte_acl_bitset *src, bits_t mask)
{
uint32_t n;
for (n = 0; n < RTE_ACL_BIT_SET_SIZE; n++)
dst->bits[n] = (dst->bits[n] & mask) | src->bits[n];
}
/*
* Set dst to bits of src1 that are not in src2
*/
static int
acl_exclude(struct rte_acl_bitset *dst,
struct rte_acl_bitset *src1,
struct rte_acl_bitset *src2)
{
uint32_t n;
bits_t all_bits = 0;
for (n = 0; n < RTE_ACL_BIT_SET_SIZE; n++) {
dst->bits[n] = src1->bits[n] & ~src2->bits[n];
all_bits |= dst->bits[n];
}
return all_bits != 0;
}
/*
* Add a pointer (ptr) to a node.
*/
static int
acl_add_ptr(struct acl_build_context *context,
struct rte_acl_node *node,
struct rte_acl_node *ptr,
struct rte_acl_bitset *bits)
{
uint32_t n, num_ptrs;
struct rte_acl_ptr_set *ptrs = NULL;
/*
* If there's already a pointer to the same node, just add to the bitset
*/
for (n = 0; n < node->num_ptrs; n++) {
if (node->ptrs[n].ptr != NULL) {
if (node->ptrs[n].ptr == ptr) {
acl_include(&node->ptrs[n].values, bits, -1);
acl_include(&node->values, bits, -1);
return 0;
}
}
}
/* if there's no room for another pointer, make room */
if (node->num_ptrs >= node->max_ptrs) {
/* add room for more pointers */
num_ptrs = node->max_ptrs + ACL_PTR_ALLOC;
ptrs = acl_build_alloc(context, num_ptrs, sizeof(*ptrs));
/* copy current points to new memory allocation */
if (node->ptrs != NULL) {
memcpy(ptrs, node->ptrs,
node->num_ptrs * sizeof(*ptrs));
acl_build_free(context, node->max_ptrs * sizeof(*ptrs),
node->ptrs);
}
node->ptrs = ptrs;
node->max_ptrs = num_ptrs;
}
/* Find available ptr and add a new pointer to this node */
for (n = node->min_add; n < node->max_ptrs; n++) {
if (node->ptrs[n].ptr == NULL) {
node->ptrs[n].ptr = ptr;
acl_include(&node->ptrs[n].values, bits, 0);
acl_include(&node->values, bits, -1);
if (ptr != NULL)
ptr->ref_count++;
if (node->num_ptrs <= n)
node->num_ptrs = n + 1;
return 0;
}
}
return 0;
}
/*
* Add a pointer for a range of values
*/
static int
acl_add_ptr_range(struct acl_build_context *context,
struct rte_acl_node *root,
struct rte_acl_node *node,
uint8_t low,
uint8_t high)
{
uint32_t n;
struct rte_acl_bitset bitset;
/* clear the bitset values */
for (n = 0; n < RTE_ACL_BIT_SET_SIZE; n++)
bitset.bits[n] = 0;
/* for each bit in range, add bit to set */
for (n = 0; n < UINT8_MAX + 1; n++)
if (n >= low && n <= high)
bitset.bits[n / (sizeof(bits_t) * 8)] |=
1U << (n % (sizeof(bits_t) * CHAR_BIT));
return acl_add_ptr(context, root, node, &bitset);
}
/*
* Generate a bitset from a byte value and mask.
*/
static int
acl_gen_mask(struct rte_acl_bitset *bitset, uint32_t value, uint32_t mask)
{
int range = 0;
uint32_t n;
/* clear the bitset values */
for (n = 0; n < RTE_ACL_BIT_SET_SIZE; n++)
bitset->bits[n] = 0;
/* for each bit in value/mask, add bit to set */
for (n = 0; n < UINT8_MAX + 1; n++) {
if ((n & mask) == value) {
range++;
bitset->bits[n / (sizeof(bits_t) * 8)] |=
1U << (n % (sizeof(bits_t) * CHAR_BIT));
}
}
return range;
}
/*
* Determine how A and B intersect.
* Determine if A and/or B are supersets of the intersection.
*/
static int
acl_intersect_type(const struct rte_acl_bitset *a_bits,
const struct rte_acl_bitset *b_bits,
struct rte_acl_bitset *intersect)
{
uint32_t n;
bits_t intersect_bits = 0;
bits_t a_superset = 0;
bits_t b_superset = 0;
/*
* calculate and store intersection and check if A and/or B have
* bits outside the intersection (superset)
*/
for (n = 0; n < RTE_ACL_BIT_SET_SIZE; n++) {
intersect->bits[n] = a_bits->bits[n] & b_bits->bits[n];
a_superset |= a_bits->bits[n] ^ intersect->bits[n];
b_superset |= b_bits->bits[n] ^ intersect->bits[n];
intersect_bits |= intersect->bits[n];
}
n = (intersect_bits == 0 ? ACL_INTERSECT_NONE : ACL_INTERSECT) |
(b_superset == 0 ? 0 : ACL_INTERSECT_B) |
(a_superset == 0 ? 0 : ACL_INTERSECT_A);
return n;
}
/*
* Duplicate a node
*/
static struct rte_acl_node *
acl_dup_node(struct acl_build_context *context, struct rte_acl_node *node)
{
uint32_t n;
struct rte_acl_node *next;
next = acl_alloc_node(context, node->level);
/* allocate the pointers */
if (node->num_ptrs > 0) {
next->ptrs = acl_build_alloc(context,
node->max_ptrs,
sizeof(struct rte_acl_ptr_set));
next->max_ptrs = node->max_ptrs;
}
/* copy over the pointers */
for (n = 0; n < node->num_ptrs; n++) {
if (node->ptrs[n].ptr != NULL) {
next->ptrs[n].ptr = node->ptrs[n].ptr;
next->ptrs[n].ptr->ref_count++;
acl_include(&next->ptrs[n].values,
&node->ptrs[n].values, -1);
}
}
next->num_ptrs = node->num_ptrs;
/* copy over node's match results */
if (node->match_flag == 0)
next->match_flag = 0;
else {
next->match_flag = -1;
next->mrt = acl_build_alloc(context, 1, sizeof(*next->mrt));
memcpy(next->mrt, node->mrt, sizeof(*next->mrt));
}
/* copy over node's bitset */
acl_include(&next->values, &node->values, -1);
node->next = next;
next->prev = node;
return next;
}
/*
* Dereference a pointer from a node
*/
static void
acl_deref_ptr(struct acl_build_context *context,
struct rte_acl_node *node, int index)
{
struct rte_acl_node *ref_node;
/* De-reference the node at the specified pointer */
if (node != NULL && node->ptrs[index].ptr != NULL) {
ref_node = node->ptrs[index].ptr;
ref_node->ref_count--;
if (ref_node->ref_count == 0)
acl_free_node(context, ref_node);
}
}
/*
* acl_exclude rte_acl_bitset from src and copy remaining pointer to dst
*/
static int
acl_copy_ptr(struct acl_build_context *context,
struct rte_acl_node *dst,
struct rte_acl_node *src,
int index,
struct rte_acl_bitset *b_bits)
{
int rc;
struct rte_acl_bitset bits;
if (b_bits != NULL)
if (!acl_exclude(&bits, &src->ptrs[index].values, b_bits))
return 0;
rc = acl_add_ptr(context, dst, src->ptrs[index].ptr, &bits);
if (rc < 0)
return rc;
return 1;
}
/*
* Fill in gaps in ptrs list with the ptr at the end of the list
*/
static void
acl_compact_node_ptrs(struct rte_acl_node *node_a)
{
uint32_t n;
int min_add = node_a->min_add;
while (node_a->num_ptrs > 0 &&
node_a->ptrs[node_a->num_ptrs - 1].ptr == NULL)
node_a->num_ptrs--;
for (n = min_add; n + 1 < node_a->num_ptrs; n++) {
/* if this entry is empty */
if (node_a->ptrs[n].ptr == NULL) {
/* move the last pointer to this entry */
acl_include(&node_a->ptrs[n].values,
&node_a->ptrs[node_a->num_ptrs - 1].values,
0);
node_a->ptrs[n].ptr =
node_a->ptrs[node_a->num_ptrs - 1].ptr;
/*
* mark the end as empty and adjust the number
* of used pointer enum_tries
*/
node_a->ptrs[node_a->num_ptrs - 1].ptr = NULL;
while (node_a->num_ptrs > 0 &&
node_a->ptrs[node_a->num_ptrs - 1].ptr == NULL)
node_a->num_ptrs--;
}
}
}
static int
acl_resolve_leaf(struct acl_build_context *context,
struct rte_acl_node *node_a,
struct rte_acl_node *node_b,
struct rte_acl_node **node_c)
{
uint32_t n;
int combined_priority = ACL_PRIORITY_EQUAL;
for (n = 0; n < context->cfg.num_categories; n++) {
if (node_a->mrt->priority[n] != node_b->mrt->priority[n]) {
combined_priority |= (node_a->mrt->priority[n] >
node_b->mrt->priority[n]) ?
ACL_PRIORITY_NODE_A : ACL_PRIORITY_NODE_B;
}
}
/*
* if node a is higher or equal priority for all categories,
* then return node_a.
*/
if (combined_priority == ACL_PRIORITY_NODE_A ||
combined_priority == ACL_PRIORITY_EQUAL) {
*node_c = node_a;
return 0;
}
/*
* if node b is higher or equal priority for all categories,
* then return node_b.
*/
if (combined_priority == ACL_PRIORITY_NODE_B) {
*node_c = node_b;
return 0;
}
/*
* mixed priorities - create a new node with the highest priority
* for each category.
*/
/* force new duplication. */
node_a->next = NULL;
*node_c = acl_dup_node(context, node_a);
for (n = 0; n < context->cfg.num_categories; n++) {
if ((*node_c)->mrt->priority[n] < node_b->mrt->priority[n]) {
(*node_c)->mrt->priority[n] = node_b->mrt->priority[n];
(*node_c)->mrt->results[n] = node_b->mrt->results[n];
}
}
return 0;
}
/*
* Merge nodes A and B together,
* returns a node that is the path for the intersection
*
* If match node (leaf on trie)
* For each category
* return node = highest priority result
*
* Create C as a duplicate of A to point to child intersections
* If any pointers in C intersect with any in B
* For each intersection
* merge children
* remove intersection from C pointer
* add a pointer from C to child intersection node
* Compact the pointers in A and B
* Copy any B pointers that are outside of the intersection to C
* If C has no references to the B trie
* free C and return A
* Else If C has no references to the A trie
* free C and return B
* Else
* return C
*/
static int
acl_merge_trie(struct acl_build_context *context,
struct rte_acl_node *node_a, struct rte_acl_node *node_b,
uint32_t level, struct rte_acl_node **return_c)
{
uint32_t n, m, ptrs_c, ptrs_b;
uint32_t min_add_c, min_add_b;
int node_intersect_type;
struct rte_acl_bitset node_intersect;
struct rte_acl_node *node_c;
struct rte_acl_node *node_a_next;
int node_b_refs;
int node_a_refs;
node_c = node_a;
node_a_next = node_a->next;
min_add_c = 0;
min_add_b = 0;
node_a_refs = node_a->num_ptrs;
node_b_refs = 0;
node_intersect_type = 0;
/* Resolve leaf nodes (matches) */
if (node_a->match_flag != 0) {
acl_resolve_leaf(context, node_a, node_b, return_c);
return 0;
}
/*
* Create node C as a copy of node A, and do: C = merge(A,B);
* If node A can be used instead (A==C), then later we'll
* destroy C and return A.
*/
if (level > 0)
node_c = acl_dup_node(context, node_a);
/*
* If the two node transitions intersect then merge the transitions.
* Check intersection for entire node (all pointers)
*/
node_intersect_type = acl_intersect_type(&node_c->values,
&node_b->values,
&node_intersect);
if (node_intersect_type & ACL_INTERSECT) {
min_add_b = node_b->min_add;
node_b->min_add = node_b->num_ptrs;
ptrs_b = node_b->num_ptrs;
min_add_c = node_c->min_add;
node_c->min_add = node_c->num_ptrs;
ptrs_c = node_c->num_ptrs;
for (n = 0; n < ptrs_c; n++) {
if (node_c->ptrs[n].ptr == NULL) {
node_a_refs--;
continue;
}
node_c->ptrs[n].ptr->next = NULL;
for (m = 0; m < ptrs_b; m++) {
struct rte_acl_bitset child_intersect;
int child_intersect_type;
struct rte_acl_node *child_node_c = NULL;
if (node_b->ptrs[m].ptr == NULL ||
node_c->ptrs[n].ptr ==
node_b->ptrs[m].ptr)
continue;
child_intersect_type = acl_intersect_type(
&node_c->ptrs[n].values,
&node_b->ptrs[m].values,
&child_intersect);
if ((child_intersect_type & ACL_INTERSECT) !=
0) {
if (acl_merge_trie(context,
node_c->ptrs[n].ptr,
node_b->ptrs[m].ptr,
level + 1,
&child_node_c))
return 1;
if (child_node_c != NULL &&
child_node_c !=
node_c->ptrs[n].ptr) {
node_b_refs++;
/*
* Added link from C to
* child_C for all transitions
* in the intersection.
*/
acl_add_ptr(context, node_c,
child_node_c,
&child_intersect);
/*
* inc refs if pointer is not
* to node b.
*/
node_a_refs += (child_node_c !=
node_b->ptrs[m].ptr);
/*
* Remove intersection from C
* pointer.
*/
if (!acl_exclude(
&node_c->ptrs[n].values,
&node_c->ptrs[n].values,
&child_intersect)) {
acl_deref_ptr(context,
node_c, n);
node_c->ptrs[n].ptr =
NULL;
node_a_refs--;
}
}
}
}
}
/* Compact pointers */
node_c->min_add = min_add_c;
acl_compact_node_ptrs(node_c);
node_b->min_add = min_add_b;
acl_compact_node_ptrs(node_b);
}
/*
* Copy pointers outside of the intersection from B to C
*/
if ((node_intersect_type & ACL_INTERSECT_B) != 0) {
node_b_refs++;
for (m = 0; m < node_b->num_ptrs; m++)
if (node_b->ptrs[m].ptr != NULL)
acl_copy_ptr(context, node_c,
node_b, m, &node_intersect);
}
/*
* Free node C if top of trie is contained in A or B
* if node C is a duplicate of node A &&
* node C was not an existing duplicate
*/
if (node_c != node_a && node_c != node_a_next) {
/*
* if the intersection has no references to the
* B side, then it is contained in A
*/
if (node_b_refs == 0) {
acl_free_node(context, node_c);
node_c = node_a;
} else {
/*
* if the intersection has no references to the
* A side, then it is contained in B.
*/
if (node_a_refs == 0) {
acl_free_node(context, node_c);
node_c = node_b;
}
}
}
if (return_c != NULL)
*return_c = node_c;
if (level == 0)
acl_free_node(context, node_b);
return 0;
}
/*
* Reset current runtime fields before next build:
* - free allocated RT memory.
* - reset all RT related fields to zero.
*/
static void
acl_build_reset(struct rte_acl_ctx *ctx)
{
rte_free(ctx->mem);
memset(&ctx->num_categories, 0,
sizeof(*ctx) - offsetof(struct rte_acl_ctx, num_categories));
}
static void
acl_gen_range(struct acl_build_context *context,
const uint8_t *hi, const uint8_t *lo, int size, int level,
struct rte_acl_node *root, struct rte_acl_node *end)
{
struct rte_acl_node *node, *prev;
uint32_t n;
prev = root;
for (n = size - 1; n > 0; n--) {
node = acl_alloc_node(context, level++);
acl_add_ptr_range(context, prev, node, lo[n], hi[n]);
prev = node;
}
acl_add_ptr_range(context, prev, end, lo[0], hi[0]);
}
static struct rte_acl_node *
acl_gen_range_trie(struct acl_build_context *context,
const void *min, const void *max,
int size, int level, struct rte_acl_node **pend)
{
int32_t n;
struct rte_acl_node *root;
const uint8_t *lo = min;
const uint8_t *hi = max;
*pend = acl_alloc_node(context, level+size);
root = acl_alloc_node(context, level++);
if (lo[size - 1] == hi[size - 1]) {
acl_gen_range(context, hi, lo, size, level, root, *pend);
} else {
uint8_t limit_lo[64];
uint8_t limit_hi[64];
uint8_t hi_ff = UINT8_MAX;
uint8_t lo_00 = 0;
memset(limit_lo, 0, RTE_DIM(limit_lo));
memset(limit_hi, UINT8_MAX, RTE_DIM(limit_hi));
for (n = size - 2; n >= 0; n--) {
hi_ff = (uint8_t)(hi_ff & hi[n]);
lo_00 = (uint8_t)(lo_00 | lo[n]);
}
if (hi_ff != UINT8_MAX) {
limit_lo[size - 1] = hi[size - 1];
acl_gen_range(context, hi, limit_lo, size, level,
root, *pend);
}
if (lo_00 != 0) {
limit_hi[size - 1] = lo[size - 1];
acl_gen_range(context, limit_hi, lo, size, level,
root, *pend);
}
if (hi[size - 1] - lo[size - 1] > 1 ||
lo_00 == 0 ||
hi_ff == UINT8_MAX) {
limit_lo[size-1] = (uint8_t)(lo[size-1] + (lo_00 != 0));
limit_hi[size-1] = (uint8_t)(hi[size-1] -
(hi_ff != UINT8_MAX));
acl_gen_range(context, limit_hi, limit_lo, size,
level, root, *pend);
}
}
return root;
}
static struct rte_acl_node *
acl_gen_mask_trie(struct acl_build_context *context,
const void *value, const void *mask,
int size, int level, struct rte_acl_node **pend)
{
int32_t n;
struct rte_acl_node *root;
struct rte_acl_node *node, *prev;
struct rte_acl_bitset bits;
const uint8_t *val = value;
const uint8_t *msk = mask;
root = acl_alloc_node(context, level++);
prev = root;
for (n = size - 1; n >= 0; n--) {
node = acl_alloc_node(context, level++);
acl_gen_mask(&bits, val[n] & msk[n], msk[n]);
acl_add_ptr(context, prev, node, &bits);
prev = node;
}
*pend = prev;
return root;
}
static struct rte_acl_node *
build_trie(struct acl_build_context *context, struct rte_acl_build_rule *head,
struct rte_acl_build_rule **last, uint32_t *count)
{
uint32_t n, m;
int field_index, node_count;
struct rte_acl_node *trie;
struct rte_acl_build_rule *prev, *rule;
struct rte_acl_node *end, *merge, *root, *end_prev;
const struct rte_acl_field *fld;
prev = head;
rule = head;
*last = prev;
trie = acl_alloc_node(context, 0);
while (rule != NULL) {
root = acl_alloc_node(context, 0);
root->ref_count = 1;
end = root;
for (n = 0; n < rule->config->num_fields; n++) {
field_index = rule->config->defs[n].field_index;
fld = rule->f->field + field_index;
end_prev = end;
/* build a mini-trie for this field */
switch (rule->config->defs[n].type) {
case RTE_ACL_FIELD_TYPE_BITMASK:
merge = acl_gen_mask_trie(context,
&fld->value,
&fld->mask_range,
rule->config->defs[n].size,
end->level + 1,
&end);
break;
case RTE_ACL_FIELD_TYPE_MASK:
{
/*
* set msb for the size of the field and
* all higher bits.
*/
uint64_t mask;
mask = RTE_ACL_MASKLEN_TO_BITMASK(
fld->mask_range.u32,
rule->config->defs[n].size);
/* gen a mini-trie for this field */
merge = acl_gen_mask_trie(context,
&fld->value,
(char *)&mask,
rule->config->defs[n].size,
end->level + 1,
&end);
}
break;
case RTE_ACL_FIELD_TYPE_RANGE:
merge = acl_gen_range_trie(context,
&rule->f->field[field_index].value,
&rule->f->field[field_index].mask_range,
rule->config->defs[n].size,
end->level + 1,
&end);
break;
default:
RTE_LOG(ERR, ACL,
"Error in rule[%u] type - %hhu\n",
rule->f->data.userdata,
rule->config->defs[n].type);
return NULL;
}
/* merge this field on to the end of the rule */
if (acl_merge_trie(context, end_prev, merge, 0,
NULL) != 0) {
return NULL;
}
}
end->match_flag = ++context->num_build_rules;
/*
* Setup the results for this rule.
* The result and priority of each category.
*/
if (end->mrt == NULL)
end->mrt = acl_build_alloc(context, 1,
sizeof(*end->mrt));
for (m = context->cfg.num_categories; 0 != m--; ) {
if (rule->f->data.category_mask & (1U << m)) {
end->mrt->results[m] = rule->f->data.userdata;
end->mrt->priority[m] = rule->f->data.priority;
} else {
end->mrt->results[m] = 0;
end->mrt->priority[m] = 0;
}
}
node_count = context->num_nodes;
(*count)++;
/* merge this rule into the trie */
if (acl_merge_trie(context, trie, root, 0, NULL))
return NULL;
node_count = context->num_nodes - node_count;
if (node_count > context->cur_node_max) {
*last = prev;
return trie;
}
prev = rule;
rule = rule->next;
}
*last = NULL;
return trie;
}
static void
acl_calc_wildness(struct rte_acl_build_rule *head,
const struct rte_acl_config *config)
{
uint32_t n;
struct rte_acl_build_rule *rule;
for (rule = head; rule != NULL; rule = rule->next) {
for (n = 0; n < config->num_fields; n++) {
double wild = 0;
uint32_t bit_len = CHAR_BIT * config->defs[n].size;
uint64_t msk_val = RTE_LEN2MASK(bit_len,
typeof(msk_val));
double size = bit_len;
int field_index = config->defs[n].field_index;
const struct rte_acl_field *fld = rule->f->field +
field_index;
switch (rule->config->defs[n].type) {
case RTE_ACL_FIELD_TYPE_BITMASK:
wild = (size - __builtin_popcountll(
fld->mask_range.u64 & msk_val)) /
size;
break;
case RTE_ACL_FIELD_TYPE_MASK:
wild = (size - fld->mask_range.u32) / size;
break;
case RTE_ACL_FIELD_TYPE_RANGE:
wild = (fld->mask_range.u64 & msk_val) -
(fld->value.u64 & msk_val);
wild = wild / msk_val;
break;
}
rule->wildness[field_index] = (uint32_t)(wild * 100);
}
}
}
static void
acl_rule_stats(struct rte_acl_build_rule *head, struct rte_acl_config *config)
{
struct rte_acl_build_rule *rule;
uint32_t n, m, fields_deactivated = 0;
uint32_t start = 0, deactivate = 0;
int tally[RTE_ACL_MAX_LEVELS][TALLY_NUM];
memset(tally, 0, sizeof(tally));
for (rule = head; rule != NULL; rule = rule->next) {
for (n = 0; n < config->num_fields; n++) {
uint32_t field_index = config->defs[n].field_index;
tally[n][TALLY_0]++;
for (m = 1; m < RTE_DIM(wild_limits); m++) {
if (rule->wildness[field_index] >=
wild_limits[m])
tally[n][m]++;
}
}
for (n = config->num_fields - 1; n > 0; n--) {
uint32_t field_index = config->defs[n].field_index;
if (rule->wildness[field_index] == 100)
tally[n][TALLY_DEPTH]++;
else
break;
}
}
/*
* Look for any field that is always wild and drop it from the config
* Only deactivate if all fields for a given input loop are deactivated.
*/
for (n = 1; n < config->num_fields; n++) {
if (config->defs[n].input_index !=
config->defs[n - 1].input_index) {
for (m = start; m < n; m++)
tally[m][TALLY_DEACTIVATED] = deactivate;
fields_deactivated += deactivate;
start = n;
deactivate = 1;
}
/* if the field is not always completely wild */
if (tally[n][TALLY_100] != tally[n][TALLY_0])
deactivate = 0;
}
for (m = start; m < n; m++)
tally[m][TALLY_DEACTIVATED] = deactivate;
fields_deactivated += deactivate;
/* remove deactivated fields */
if (fields_deactivated) {
uint32_t k, l = 0;
for (k = 0; k < config->num_fields; k++) {
if (tally[k][TALLY_DEACTIVATED] == 0) {
memmove(&tally[l][0], &tally[k][0],
TALLY_NUM * sizeof(tally[0][0]));
memmove(&config->defs[l++],
&config->defs[k],
sizeof(struct rte_acl_field_def));
}
}
config->num_fields = l;
}
}
static int
rule_cmp_wildness(struct rte_acl_build_rule *r1, struct rte_acl_build_rule *r2)
{
uint32_t n;
for (n = 1; n < r1->config->num_fields; n++) {
int field_index = r1->config->defs[n].field_index;
if (r1->wildness[field_index] != r2->wildness[field_index])
return r1->wildness[field_index] -
r2->wildness[field_index];
}
return 0;
}
/*
* Split the rte_acl_build_rule list into two lists.
*/
static void
rule_list_split(struct rte_acl_build_rule *source,
struct rte_acl_build_rule **list_a,
struct rte_acl_build_rule **list_b)
{
struct rte_acl_build_rule *fast;
struct rte_acl_build_rule *slow;
if (source == NULL || source->next == NULL) {
/* length < 2 cases */
*list_a = source;
*list_b = NULL;
} else {
slow = source;
fast = source->next;
/* Advance 'fast' two nodes, and advance 'slow' one node */
while (fast != NULL) {
fast = fast->next;
if (fast != NULL) {
slow = slow->next;
fast = fast->next;
}
}
/* 'slow' is before the midpoint in the list, so split it in two
at that point. */
*list_a = source;
*list_b = slow->next;
slow->next = NULL;
}
}
/*
* Merge two sorted lists.
*/
static struct rte_acl_build_rule *
rule_list_sorted_merge(struct rte_acl_build_rule *a,
struct rte_acl_build_rule *b)
{
struct rte_acl_build_rule *result = NULL;
struct rte_acl_build_rule **last_next = &result;
while (1) {
if (a == NULL) {
*last_next = b;
break;
} else if (b == NULL) {
*last_next = a;
break;
}
if (rule_cmp_wildness(a, b) >= 0) {
*last_next = a;
last_next = &a->next;
a = a->next;
} else {
*last_next = b;
last_next = &b->next;
b = b->next;
}
}
return result;
}
/*
* Sort list of rules based on the rules wildness.
* Use recursive mergesort algorithm.
*/
static struct rte_acl_build_rule *
sort_rules(struct rte_acl_build_rule *head)
{
struct rte_acl_build_rule *a;
struct rte_acl_build_rule *b;
/* Base case -- length 0 or 1 */
if (head == NULL || head->next == NULL)
return head;
/* Split head into 'a' and 'b' sublists */
rule_list_split(head, &a, &b);
/* Recursively sort the sublists */
a = sort_rules(a);
b = sort_rules(b);
/* answer = merge the two sorted lists together */
return rule_list_sorted_merge(a, b);
}
static uint32_t
acl_build_index(const struct rte_acl_config *config, uint32_t *data_index)
{
uint32_t n, m;
int32_t last_header;
m = 0;
last_header = -1;
for (n = 0; n < config->num_fields; n++) {
if (last_header != config->defs[n].input_index) {
last_header = config->defs[n].input_index;
data_index[m++] = config->defs[n].offset;
}
}
return m;
}
static struct rte_acl_build_rule *
build_one_trie(struct acl_build_context *context,
struct rte_acl_build_rule *rule_sets[RTE_ACL_MAX_TRIES],
uint32_t n, int32_t node_max)
{
struct rte_acl_build_rule *last;
struct rte_acl_config *config;
config = rule_sets[n]->config;
acl_rule_stats(rule_sets[n], config);
rule_sets[n] = sort_rules(rule_sets[n]);
context->tries[n].type = RTE_ACL_FULL_TRIE;
context->tries[n].count = 0;
context->tries[n].num_data_indexes = acl_build_index(config,
context->data_indexes[n]);
context->tries[n].data_index = context->data_indexes[n];
context->cur_node_max = node_max;
context->bld_tries[n].trie = build_trie(context, rule_sets[n],
&last, &context->tries[n].count);
return last;
}
static int
acl_build_tries(struct acl_build_context *context,
struct rte_acl_build_rule *head)
{
uint32_t n, num_tries;
struct rte_acl_config *config;
struct rte_acl_build_rule *last;
struct rte_acl_build_rule *rule_sets[RTE_ACL_MAX_TRIES];
config = head->config;
rule_sets[0] = head;
/* initialize tries */
for (n = 0; n < RTE_DIM(context->tries); n++) {
context->tries[n].type = RTE_ACL_UNUSED_TRIE;
context->bld_tries[n].trie = NULL;
context->tries[n].count = 0;
}
context->tries[0].type = RTE_ACL_FULL_TRIE;
/* calc wildness of each field of each rule */
acl_calc_wildness(head, config);
for (n = 0;; n = num_tries) {
num_tries = n + 1;
last = build_one_trie(context, rule_sets, n, context->node_max);
if (context->bld_tries[n].trie == NULL) {
RTE_LOG(ERR, ACL, "Build of %u-th trie failed\n", n);
return -ENOMEM;
}
/* Build of the last trie completed. */
if (last == NULL)
break;
if (num_tries == RTE_DIM(context->tries)) {
RTE_LOG(ERR, ACL,
"Exceeded max number of tries: %u\n",
num_tries);
return -ENOMEM;
}
/* Trie is getting too big, split remaining rule set. */
rule_sets[num_tries] = last->next;
last->next = NULL;
acl_free_node(context, context->bld_tries[n].trie);
/* Create a new copy of config for remaining rules. */
config = acl_build_alloc(context, 1, sizeof(*config));
memcpy(config, rule_sets[n]->config, sizeof(*config));
/* Make remaining rules use new config. */
for (head = rule_sets[num_tries]; head != NULL;
head = head->next)
head->config = config;
/*
* Rebuild the trie for the reduced rule-set.
* Don't try to split it any further.
*/
last = build_one_trie(context, rule_sets, n, INT32_MAX);
if (context->bld_tries[n].trie == NULL || last != NULL) {
RTE_LOG(ERR, ACL, "Build of %u-th trie failed\n", n);
return -ENOMEM;
}
}
context->num_tries = num_tries;
return 0;
}
static void
acl_build_log(const struct acl_build_context *ctx)
{
uint32_t n;
RTE_LOG(DEBUG, ACL, "Build phase for ACL \"%s\":\n"
"node limit for tree split: %u\n"
"nodes created: %u\n"
"memory consumed: %zu\n",
ctx->acx->name,
ctx->node_max,
ctx->num_nodes,
ctx->pool.alloc);
for (n = 0; n < RTE_DIM(ctx->tries); n++) {
if (ctx->tries[n].count != 0)
RTE_LOG(DEBUG, ACL,
"trie %u: number of rules: %u, indexes: %u\n",
n, ctx->tries[n].count,
ctx->tries[n].num_data_indexes);
}
}
static int
acl_build_rules(struct acl_build_context *bcx)
{
struct rte_acl_build_rule *br, *head;
const struct rte_acl_rule *rule;
uint32_t *wp;
uint32_t fn, i, n, num;
size_t ofs, sz;
fn = bcx->cfg.num_fields;
n = bcx->acx->num_rules;
ofs = n * sizeof(*br);
sz = ofs + n * fn * sizeof(*wp);
br = tb_alloc(&bcx->pool, sz);
wp = (uint32_t *)((uintptr_t)br + ofs);
num = 0;
head = NULL;
for (i = 0; i != n; i++) {
rule = (const struct rte_acl_rule *)
((uintptr_t)bcx->acx->rules + bcx->acx->rule_sz * i);
if ((rule->data.category_mask & bcx->category_mask) != 0) {
br[num].next = head;
br[num].config = &bcx->cfg;
br[num].f = rule;
br[num].wildness = wp;
wp += fn;
head = br + num;
num++;
}
}
bcx->num_rules = num;
bcx->build_rules = head;
return 0;
}
/*
* Copy data_indexes for each trie into RT location.
*/
static void
acl_set_data_indexes(struct rte_acl_ctx *ctx)
{
uint32_t i, n, ofs;
ofs = 0;
for (i = 0; i != ctx->num_tries; i++) {
n = ctx->trie[i].num_data_indexes;
memcpy(ctx->data_indexes + ofs, ctx->trie[i].data_index,
n * sizeof(ctx->data_indexes[0]));
ctx->trie[i].data_index = ctx->data_indexes + ofs;
ofs += RTE_ACL_MAX_FIELDS;
}
}
/*
* Internal routine, performs 'build' phase of trie generation:
* - setups build context.
* - analizes given set of rules.
* - builds internal tree(s).
*/
static int
acl_bld(struct acl_build_context *bcx, struct rte_acl_ctx *ctx,
const struct rte_acl_config *cfg, uint32_t node_max)
{
int32_t rc;
/* setup build context. */
memset(bcx, 0, sizeof(*bcx));
bcx->acx = ctx;
bcx->pool.alignment = ACL_POOL_ALIGN;
bcx->pool.min_alloc = ACL_POOL_ALLOC_MIN;
bcx->cfg = *cfg;
bcx->category_mask = RTE_LEN2MASK(bcx->cfg.num_categories,
typeof(bcx->category_mask));
bcx->node_max = node_max;
rc = sigsetjmp(bcx->pool.fail, 0);
/* build phase runs out of memory. */
if (rc != 0) {
RTE_LOG(ERR, ACL,
"ACL context: %s, %s() failed with error code: %d\n",
bcx->acx->name, __func__, rc);
return rc;
}
/* Create a build rules copy. */
rc = acl_build_rules(bcx);
if (rc != 0)
return rc;
/* No rules to build for that context+config */
if (bcx->build_rules == NULL) {
rc = -EINVAL;
} else {
/* build internal trie representation. */
rc = acl_build_tries(bcx, bcx->build_rules);
}
return rc;
}
/*
* Check that parameters for acl_build() are valid.
*/
static int
acl_check_bld_param(struct rte_acl_ctx *ctx, const struct rte_acl_config *cfg)
{
static const size_t field_sizes[] = {
sizeof(uint8_t), sizeof(uint16_t),
sizeof(uint32_t), sizeof(uint64_t),
};
uint32_t i, j;
if (ctx == NULL || cfg == NULL || cfg->num_categories == 0 ||
cfg->num_categories > RTE_ACL_MAX_CATEGORIES ||
cfg->num_fields == 0 ||
cfg->num_fields > RTE_ACL_MAX_FIELDS)
return -EINVAL;
for (i = 0; i != cfg->num_fields; i++) {
if (cfg->defs[i].type > RTE_ACL_FIELD_TYPE_BITMASK) {
RTE_LOG(ERR, ACL,
"ACL context: %s, invalid type: %hhu for %u-th field\n",
ctx->name, cfg->defs[i].type, i);
return -EINVAL;
}
for (j = 0;
j != RTE_DIM(field_sizes) &&
cfg->defs[i].size != field_sizes[j];
j++)
;
if (j == RTE_DIM(field_sizes)) {
RTE_LOG(ERR, ACL,
"ACL context: %s, invalid size: %hhu for %u-th field\n",
ctx->name, cfg->defs[i].size, i);
return -EINVAL;
}
}
return 0;
}
int
rte_acl_build(struct rte_acl_ctx *ctx, const struct rte_acl_config *cfg)
{
int32_t rc;
uint32_t n;
size_t max_size;
struct acl_build_context bcx;
rc = acl_check_bld_param(ctx, cfg);
if (rc != 0)
return rc;
acl_build_reset(ctx);
if (cfg->max_size == 0) {
n = NODE_MIN;
max_size = SIZE_MAX;
} else {
n = NODE_MAX;
max_size = cfg->max_size;
}
for (rc = -ERANGE; n >= NODE_MIN && rc == -ERANGE; n /= 2) {
/* perform build phase. */
rc = acl_bld(&bcx, ctx, cfg, n);
if (rc == 0) {
/* allocate and fill run-time structures. */
rc = rte_acl_gen(ctx, bcx.tries, bcx.bld_tries,
bcx.num_tries, bcx.cfg.num_categories,
RTE_ACL_MAX_FIELDS * RTE_DIM(bcx.tries) *
sizeof(ctx->data_indexes[0]), max_size);
if (rc == 0) {
/* set data indexes. */
acl_set_data_indexes(ctx);
/* copy in build config. */
ctx->config = *cfg;
}
}
acl_build_log(&bcx);
/* cleanup after build. */
tb_free_pool(&bcx.pool);
}
return rc;
}