DPDK ACL链表

DPDK 专栏收录该内容
38 篇文章 2 订阅

 

本文简介DPDK中ACL链表的几个主要函数,以及数据结构。

 

ACL初始化


函数rte_acl_init如下,主要初始化工作是选择使用的分类算法。对于AVX2(Advanced Vector Extensions),仅当编译DPDK代码的编译器支持AVX2指令集,并且运行DPDK程序的处理器支持AVX2指令集时,才启用RTE_ACL_CLASSIFY_AVX2算法。否则,退而求其次,选用RTE_ACL_CLASSIFY_SSE算法。如果处理器不支持SSE4_1(Streaming SIMD Extensions)指令集,选用默认的算法为RTE_ACL_CLASSIFY_DEFAULT,

RTE_INIT(rte_acl_init)              
{  
    enum rte_acl_classify_alg alg = RTE_ACL_CLASSIFY_DEFAULT;
   
#ifdef CC_AVX2_SUPPORT
    if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX2))
        alg = RTE_ACL_CLASSIFY_AVX2;
    else if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_SSE4_1))
#else
    if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_SSE4_1))
#endif
        alg = RTE_ACL_CLASSIFY_SSE;

    rte_acl_set_default_classify(alg);
}

以下指令检查当前的gcc编译器是否支持AVX2指令集。

$ gcc  -march=core-avx2 -dM -E - </dev/null 2>&1

从输出结果可以看到(输出省略不相关部分),__AVX2__关键字,表明支持AVX2指令集。另外,可见gcc同时支持SSE4_1指令集。

 #define __SSE4_1__ 1
 #define __SSE4_2__ 1
 #define __AVX__ 1
 #define __AVX2__ 1

DPDK中使用如下的代码赋值宏CC_AVX2_SUPPORT,如果grep找到AVX2关键字,echo数字1即赋值给宏CC_AVX2_SUPPORT值1。

CC_AVX2_SUPPORT=$(shell $(CC) -march=core-avx2 -dM -E - </dev/null 2>&1 | grep -q AVX2 && echo 1)

由以下的ACL分类函数定义可见,默认的类型RTE_ACL_CLASSIFY_DEFAULT与RTE_ACL_CLASSIFY_SCALAR相同,都是使用rte_acl_classify_scalar函数作为分类处理函数。

 static const rte_acl_classify_t classify_fns[] = {
     [RTE_ACL_CLASSIFY_DEFAULT] = rte_acl_classify_scalar,
     [RTE_ACL_CLASSIFY_SCALAR] = rte_acl_classify_scalar,
     [RTE_ACL_CLASSIFY_SSE] = rte_acl_classify_sse,
     [RTE_ACL_CLASSIFY_AVX2] = rte_acl_classify_avx2,
     [RTE_ACL_CLASSIFY_NEON] = rte_acl_classify_neon,
     [RTE_ACL_CLASSIFY_ALTIVEC] = rte_acl_classify_altivec,
 };

 

全局ACL链表


宏TAILQ_HEAD定义tailq链表结构rte_acl_list,以及指定链表中元素的类型rte_tailq_entry。宏EAL_REGISTER_TAILQ定义了RTE初始化函数,用于注册名称为“RTE_ACL”类型为rte_tailq_elem的rte_acl_tailq变量。

TAILQ_HEAD(rte_acl_list, rte_tailq_entry);

static struct rte_tailq_elem rte_acl_tailq = {
    .name = "RTE_ACL",
};
EAL_REGISTER_TAILQ(rte_acl_tailq)

#define EAL_REGISTER_TAILQ(t) \
RTE_INIT(tailqinitfn_ ##t) \ 
{ \         
    if (rte_eal_tailq_register(&t) < 0) \
        rte_panic("Cannot initialize tailq: %s\n", t.name); \
}  

如上所示,函数rte_eal_tailq_register将名为“RTE_ACL”的变量结构注册到全局rte_tailq_elem_head链表中。然而“RTE_ACL”结构自身亦包含一个tailq链表头成员head,由定义可见其类型与ACL链表rte_acl_list的类型一致,元素成员都为rte_tailq_entry结构,其正为ACL链表的头指针。

TAILQ_HEAD(rte_tailq_entry_head, rte_tailq_entry);

struct rte_tailq_head {
    struct rte_tailq_entry_head tailq_head; /**< NOTE: must be first element */
    char name[RTE_TAILQ_NAMESIZE];
};
struct rte_tailq_elem {
    /* Reference to head in shared mem, updated at init time by rte_eal_tailqs_init() */
    struct rte_tailq_head *head;
    TAILQ_ENTRY(rte_tailq_elem) next;
    const char name[RTE_TAILQ_NAMESIZE];
};

在函数rte_acl_create中可见,正是通过将rte_acl_tailq的head成员的tailq_head子成员转换为rte_acl_list,而得到全局的ACL链表头部指针的。

struct rte_acl_ctx * rte_acl_create(const struct rte_acl_param *param)
{
    struct rte_acl_ctx *ctx;
    struct rte_acl_list *acl_list;
    struct rte_tailq_entry *te;
    char name[sizeof(ctx->name)];

    acl_list = RTE_TAILQ_CAST(rte_acl_tailq.head, rte_acl_list);
}

#define RTE_TAILQ_CAST(tailq_entry, struct_name) \
    (struct struct_name *)&(tailq_entry)->tailq_head


ACL创建


主函数rte_acl_create,根据传入的rte_acl_param结构类型的参数,创建一个新的rte_acl_ctx结构,但是如果已经存在与参数中指定的名称相同的ACL上下文结构,直接返回其地址,不执行新建操作。以DPDK例程中的l3fwd-acl而言,其在调用ACL创建函数rte_acl_create时,传入的参数结构如下,参见文件/examples/l3fwd-acl/main.c中的函数setup_acl:

    int dim = ipv6 ? RTE_DIM(ipv6_defs) : RTE_DIM(ipv4_defs);
    /* Create ACL contexts */
    snprintf(name, sizeof(name), "%s%d", ipv6 ? L3FWD_ACL_IPV6_NAME : L3FWD_ACL_IPV4_NAME, socketid);
    acl_param.name = name;   
	
    acl_param.socket_id = socketid;
    acl_param.rule_size = RTE_ACL_RULE_SZ(dim);
    acl_param.max_rule_num = MAX_ACL_RULE_NUM;

对于IPv4而言,其ACL的名称为l3fwd-acl-ipv4(宏L3FWD_ACL_IPV4_NAME)与socketid拼接的字符串。对于IPv6,其名称为l3fwd-acl-ipv6+socketid的值。之后将会看到在函数rte_acl_create中,其将传入的ACL名称添加上ACL_的前缀。所以,实际的ACL名称为(以IPv4为例,假设socketid等于0):ACL_l3fwd-acl-ipv40。

在l3fwd-acl例程中,ACL单个规则项的大小(rule_size)设定为RTE_ACL_RULE_SZ(dim),其中dim的值为ACL规则的字段数量,对于IPv4来说,定义了5个字段,分别为协议proto、源IP、目的IP、源端口和目的端口。对于IPv6来说,同样是五元组,但是由于IPv6的地址长度为16个字节,所以,多出了6个字段,IPv6规则定义了11个字段。ACL规则大小等于rte_acl_rule结构本身的长度加上字段数量与字段结构体rte_acl_field的乘积。

#define RTE_ACL_RULE_SZ(fld_num)    \
    (sizeof(struct rte_acl_rule) + sizeof(struct rte_acl_field) * (fld_num))

最后一个参数最大规则数量(max_rule_num)设定为10万条(宏MAX_ACL_RULE_NUM)。

函数rte_acl_create实现如下,首先为传入参数结构中的ACL名称增加ACL_前缀。随后,在全局ACL链表中遍历查找,如果已经存在其名称与当前名称相同的ACL上下文结构,返回此结构即可。

struct rte_acl_ctx *rte_acl_create(const struct rte_acl_param *param)
{
    struct rte_acl_list *acl_list;
    struct rte_tailq_entry *te;

    acl_list = RTE_TAILQ_CAST(rte_acl_tailq.head, rte_acl_list);

    snprintf(name, sizeof(name), "ACL_%s", param->name);

    /* if we already have one with that name */
    TAILQ_FOREACH(te, acl_list, next) {
        ctx = (struct rte_acl_ctx *) te->data;
        if (strncmp(param->name, ctx->name, sizeof(ctx->name)) == 0)
            break;
    }

否则,创建新的ACL上下文结构(rte_acl_ctx),其占用空间大小(sz)为rte_acl_ctx上下文结构自身空间大小与所有的规则所占空间大小之和。另外,分配一个rte_tailq_entry链表项结构,以便向全局ACL链表上添加。在ACL上下文结构中规则链表开始的地址(rules成员)需要跳过rte_acl_ctx自身的长度。成员alg初始化为默认的rte_acl_default_classify。

    /* calculate amount of memory required for pattern set. */
    sz = sizeof(*ctx) + param->max_rule_num * param->rule_size;

    /* if ACL with such name doesn't exist, then create a new one. */
    if (te == NULL) {
        ctx = NULL;
        te = rte_zmalloc("ACL_TAILQ_ENTRY", sizeof(*te), 0);

        ctx = rte_zmalloc_socket(name, sz, RTE_CACHE_LINE_SIZE, param->socket_id);

        /* init new allocated context. */
        ctx->rules = ctx + 1;
        ctx->max_rules = param->max_rule_num;
        ctx->rule_sz = param->rule_size;
        ctx->socket_id = param->socket_id;
        ctx->alg = rte_acl_default_classify;
        snprintf(ctx->name, sizeof(ctx->name), "%s", param->name);

        te->data = (void *) ctx;
        TAILQ_INSERT_TAIL(acl_list, te, next);
    }


添加ACL规则


函数acl_add_rules向指定的ACL上下文中一次添加多条规则(包括单条),首先偏移到ACL上下文中已有规则的末尾,之后将要添加的规则拷贝进去,最后增加ACL上下文结构中的规则计数。

static int acl_add_rules(struct rte_acl_ctx *ctx, const void *rules, uint32_t num)
{
    uint8_t *pos;

    if (num + ctx->num_rules > ctx->max_rules)
        return -ENOMEM;

    pos = ctx->rules;
    pos += ctx->rule_sz * ctx->num_rules;
    memcpy(pos, rules, num * ctx->rule_sz);
    ctx->num_rules += num;


ACL规则树


以上创建的ACL上下文结构及其规则,如果要使用,需要对其进行build编译,生成运行时的trie树结构,以便进行高效的查询。由函数rte_acl_build完成,其参数为ACL上下文和rte_acl_config结构的编译控制参数。再次以示例程序l3fwd-acl来说明,初始化了ACL配置参数结构的三个成员,分别是类别数目num_categories,赋值为DEFAULT_MAX_CATEGORIES(1);字段数量num_fields,负责为dim,其值参见以上的说明;以及所有字段的定义成员defs,对于IPv4而言,将ipv4_defs结构的内容拷贝到其中。

    struct rte_acl_config acl_build_param;

    /* Perform builds */
    memset(&acl_build_param, 0, sizeof(acl_build_param));
    
    acl_build_param.num_categories = DEFAULT_MAX_CATEGORIES;
    acl_build_param.num_fields = dim;
    memcpy(&acl_build_param.defs, ipv6 ? ipv6_defs : ipv4_defs, ipv6 ? sizeof(ipv6_defs) : sizeof(ipv4_defs));

    if (rte_acl_build(context, &acl_build_param) != 0)
        rte_exit(EXIT_FAILURE, "Failed to build ACL trie\n");

最后,看一下IPv4的ACL字段定义ipv4_defs,如下列出了三种不同的ACL字段类型,由于目的IP地址、目的端口号分别与源IP地址和源端口号同类型,此处做了省略未列出。类型RTE_ACL_FIELD_TYPE_BITMASK对应于五元组的协议字段,长度为一个字节;类型RTE_ACL_FIELD_TYPE_MASK对应于源地址字段,长度为4个字节;类型RTE_ACL_FIELD_TYPE_RANGE对应于源端口字段,长度为2个字节。

struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
    {
        .type = RTE_ACL_FIELD_TYPE_BITMASK,
        .size = sizeof(uint8_t),
        .field_index = PROTO_FIELD_IPV4,
        .input_index = RTE_ACL_IPV4VLAN_PROTO,
        .offset = 0,
    },
    {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32_t),
        .field_index = SRC_FIELD_IPV4,
        .input_index = RTE_ACL_IPV4VLAN_SRC,
        .offset = offsetof(struct ipv4_hdr, src_addr) -
            offsetof(struct ipv4_hdr, next_proto_id),
    },
    ...
    {
        .type = RTE_ACL_FIELD_TYPE_RANGE,
        .size = sizeof(uint16_t),
        .field_index = SRCP_FIELD_IPV4,
        .input_index = RTE_ACL_IPV4VLAN_PORTS,
        .offset = sizeof(struct ipv4_hdr) -
            offsetof(struct ipv4_hdr, next_proto_id),
    },
    ...
};

 

DPDK-19.02

 

  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值