网络可编程数据面——DPDK Graph Library
——lvyilong316
Graph library是DPDK 20.05版本引入的新特性,最近抽时间把其中关键代码看了一遍,主要希望看下其实现思路是否有可以借鉴的东西。下面将其大概实现进行了总结。
DPDK中的Graph架构将数据的处理抽象成了node和link构成的graph,这种思想并不是新东西,VPP就采用了类似的思路实现,只不过DPDK这里讲graph思想实现为了一个lib框架,提供了graph的创建,删除,查找,node clone,edge update,adge shrink等操作,可供上层应用基于此框架开发。
这种graph架构有什么优势呢?首先graph架构将报文处理的相似逻辑抽象同样到一个node中,这样减少了报文处理过程中的I cache和D cache miss;其次提高了报文处理逻辑的复用性,同样的处理逻辑抽象为node可以在graph路径中多次出现复用;其次提高了报文处理模块的灵活性,通过不同node的任意顺序组合完成不同的报文处理流程。
node的构成
Graph lib中的node构成如下图所示,由以下几部分构成:
(1)process:这是一个callback function,是node的核心处理流程,包含了数据处理的实现,通过rte_graph_walk()函数遍历每个node,然后调用每个node的process函数实现数据的处理,如果数据处理完成后需要交给下一个node处理,则需要在其中调用rte_node_enqueue*()函数。
(2)init:node的初始化函数,在rte_graph_create()函数创建graph时会调用各个node的init函数;
(3)fini:node的析构函数,在rte_graph_destroy()函数销毁graph时会调用各个node的fini函数;
(4)Context memory:存放当前node所需的私有信息的内存空间,process,init,fini等函数都可能会用到;
(5)nb_edges:和当前node关联的边数;
(6)next_node[]:存放当前node的邻居节点,由于graph是一个有向图,所以这里next_node[]其实存放是其下游的邻居节点;
内置node类型
用户可以根据自身需求实现注册自己的node节点,dpdk中的rte_node库中也实现了一些内置的基础node。下面我们以ethdev_tx node为例分析一下一个node的定义和注册过程。
ethdev_tx
ethdev_tx node定义了网卡port的收包行为。在rte_node中每个node的定义是通过注册rte_node_register信息完成的,rte_node_register包含了node所需要的基本信息,包括其process处理函数,ethdev_tx的rte_node_register如下所示:
点击(此处)折叠或打开
static struct rte_node_register ethdev_tx_node_base = {
.process = ethdev_tx_node_process,
.name = "ethdev_tx",
.init = ethdev_tx_node_init,
.nb_edges = ETHDEV_TX_NEXT_MAX,
.next_nodes = {
[ETHDEV_TX_NEXT_PKT_DROP] = "pkt_drop",
},
};
之后通过RTE_NODE_REGISTER进行注册:
RTE_NODE_REGISTER(ethdev_tx_node_base);
RTE_NODE_REGISTER的实现如下,最终调用__rte_node_register。
点击(此处)折叠或打开
#define RTE_NODE_REGISTER(node) \
RTE_INIT(rte_node_register_##node) \
{ \
node.parent_id = RTE_NODE_ID_INVALID; \
node.id = __rte_node_register(&node); \
}
__rte_node_register的主要工作就是分配struct node结构和对应的nodeid,并将其加入到全局链表node_list中。
点击(此处)折叠或打开
rte_node_t
__rte_node_register(const struct rte_node_register *reg)
{
struct node *node;
rte_edge_t i;
size_t sz;
graph_spinlock_lock();
sz = sizeof(struct node) + (reg->nb_edges * RTE_NODE_NAMESIZE);
node = calloc(1, sz);
if (node == NULL) {
rte_errno = ENOMEM;
goto fail;
}
/* Initialize the node */
if (rte_strscpy(node->name, reg->name, RTE_NODE_NAMESIZE) < 0) {
rte_errno = E2BIG;
goto free;
}
node->flags = reg->flags;
node->process = reg->process;
node->init = reg->init;
node->fini = reg->fini;
node->nb_edges = reg->nb_edges;
node->parent_id = reg->parent_id;
for (i = 0; i < reg->nb_edges; i++) {
if (rte_strscpy(node->next_nodes[i], reg->next_nodes[i],
RTE_NODE_NAMESIZE) < 0) {
rte_errno = E2BIG;
goto free;
}
}
node->id = node_id++;
/* Add the node at tail */
STAILQ_INSERT_TAIL(&node_list, node, next);
graph_spinlock_unlock();
return node->id;
free:
free(