多核网络处理器媒体面软件架构探讨——同步流水线架构

1. 报文接收/发送队列

每个协议模块,在每个核上,拥有两个队列,分别是接收与发送队列。

例如,rcv_que_eth、snd_que_eth、rcv_que_ip、snd_que_ip、rcv_que_ipsec、snd_que_ipsec、rcv_que_udp、snd_que_udp、


2. 每个协议模块,有两个入口函数,一个是接收入口——负责从接收队列接收处理本模块的报文,一个是发送入口——负责将发送队列中的报文发送出去。

例如,rcv_eth、snd_eth、rcv_ip、snd_ip等。


3. 媒体面线程(每核一个)主循环。

int main_loop()

{

    get a packat from packet pool into rcv_que_eth;

    rcv_eth();    /*处理rcv_que_eth中的报文,送到rcv_que_ip中 */

    rcv_ip();      /*处理rcv_que_ip中的报文,送到上层协议模块的接收队列中 */

    rcv_ipsec();  rcv_udp(); rcv_tcp();  /*  各协议模块处理接收队列中的报文,送入相应模块的接收/发送队列中 */

    snd_tcp();  snd_udp(); snd_ipsec();

    snd_eth();  /*将snd_que_ip中的报文全部发送出去 */

    return 0;

}


上述架构,虽然采用的是非常明显的流水线式的处理模式,但却是以同步的方式执行的。

为什么要这样设计,我们可以考虑如下问题。

一、如果将流水线不同阶段的工作分配到不同的核上,会有两个难题:

1.  各阶段的处理工作,需要几个核?弄得不好,可能造成性能损失。而且各种协议的流量是动态变化的,固定的分配难以达到最佳性能。

2. 多个核之间,可能会存在同步/互斥的开销,另外还可能有cache开销。

二、如果在每个核上为流水线不同阶段的工作创建线程,会有如下问题:
1.  如果大家同时运行,可能会带来性能损失。
2.  如果在有报文时唤醒相应线程,可能难以适应流量突发的情况。有时一瞬间流量可能会达到上百万pps。

三、如果不采用流水线式的处理模式,则可能会有如下难题:

某些隧道报文处理完成后,可能需要再次经过ip层的入队接收。这时可能会造成递归调用IP层的接收函数。让软件变得复杂。

而且,实际应用中,可能存在多重隧道(例如gre与ipsec隧道并存),并且隧道的封装顺序在不同的组网环境下是由用户动态配置的。这种情况下,软件就变得更加复杂了。

而采用流水线式的处理模式,就好办了。

构造一张函数指针表,根据用户的隧道配置情况,将相应模块的函数指针填入表中,然后main_loop()中按顺序调用其中的函数即可。

例如,某个环境下,用户配置了ipsec隧道与gre隧道。并且,封装顺序是先进行ipsec封装,再进行gre封装。

我们得到用户的配置后,生成如下的函数指针表proto_chain。

proto_chain:

rcv_eth(); 

rcv_ip(); 

rcv_gre();

rcv_ipsec();

rcv_ip();

rcv_udp(); 

rcv_tcp(); 

snd_tcp();  

snd_udp(); 

snd_ipsec();

snd_gre();

snd_eth();

main_loop()的实现就简单了,直接按顺序调用proto_chain中的函数即可。

而协议模块的实现也很简单,如果解封装后得到ip报文,直接放入rcv_que_ip即可。


你可能感兴趣的:(多核网络处理器媒体面软件架构探讨——同步流水线架构)