Mempool Labrary
一个内存池(memory pool)就是固定大小对象的分配器。在dpdk中,它是通过名字来标示唯一性的,且使用环形队列来保存没有使用的空闲对象。它提供了一些可选项服务例如针对每个核的对象缓存以及数据对齐,有助于将对象使用的内存空间均等的展开在DARM或者是DDR3通道上。
这个库被Mbuf Library和EAL使用。
5.1 cookies
在调试模式下(CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG打开),cookies会被加到分配的内存块的头尾。分配的对象就包含了越界检查区域以帮助调试缓冲区溢出。
5.2 统计
在调试模式下(CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG打开),对于从内存池中获取和释放的统计会保存在内存池结构体中。统计是针对每个核的,避免并发访问统计计数器。
5.3 内存对齐限制
取决于硬件内存配置,通过在对象之间做填充,性能可以得到巨大的提升。确保每个对象的起始地址是在内存中的不同通道和rank,以便所有的通道都是均等的加载。
在做数据包三层转发或者是流量分级时,这个是绝对实用的。由于只有前64个字节的访问,所以将对象的开始地址展开到不同的通道,性能可以提升。
每个DIMM(双列直插式内存)rank数目就是是DIMM可访问的全数据宽度的相互独立内存集合的数目。不同rank上的内存是不能同时访问的,即使是共享同样的数据总线。DIMM自身的内存芯片的物理分布和rank的数目没有必然的联系。
当启动程序时,EAL命令行参数提供了设置内存通道数和rank数的选项。
注意:必须在命令行参数中指定处理器的内存通道数。
下面是不同的DIMM架构中内存对齐的示例:
本例中,包的大小被设置成16个64字节内存块。
intel 5520芯片有3个内存通道,所以在大部分情况下,不需要在对象之间做填充扩展(除非对象的大小为N*3*64字节)。
当创建一个mempool时,用户可以配置这一特性也可不配。
5.4 本地缓存
以cpu的尿性,多核访问mempool的空闲缓冲队列的开销是很高的,即使是每个核都只是需要一个CAS原语操作。为了避免对mempool队列的太多,mempool的分配程序会保持对每个核的缓存以及对mempool队列的批量请求,通过缓存就可以减少对实际mempool结构体的锁开销。用这种方式,每个核都可以完全访问它自己的空闲对象缓存(有锁)且仅当缓存满的时候,这个核会将一些空闲对象写回mempool队列,而在缓存空的时候会获取更多的对象到缓存。
这就意味着在核上的缓存中有一定量的对象是占着茅坑不拉屎的,在一个核无锁访问mempool缓存到高速缓存中的数据时,会获得性能提升。
高速缓存时由一个小巧的,针对每个核的指针表组成,它的长度(用起来像栈一样)。
是否缓存mempool的内的对象数据可以在创建mempool的时候打开或者关闭。
最大缓存数据的大小是静态设置的,定义的是编译时变量(CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE)。
5.5 用例
所有内存的分配如果需要高性能的处理就需要使用基于内存池的分配方式。下面是一些例子:
l Mbuf Library
l EAL,日志服务
l 任何程序在数据面需要申请固定大小的对象,这些对象应该并系统重复循环利用。
Mbuf Library
mbuf库提供了分配和释放报文消息缓冲(mbufs)的能力,后者用于dpdk程序存放报文数据。mbufs是保存在mempool中,使用的是mempool Library。
rte_mbuf结构体可以传输网络报文数据或者是普通的控制数据流(用CTRL_MBUF_FLAG标示)。它也可以扩成其它类型。rte_mbuf头结构保持尽可能的小,当前是使用了两个缓冲行的大小,最频繁使用的在这两个缓冲行上。
设计报文缓冲
对于报文数据(包括协议头)的存储,可以从两个方面考虑:
- 在mbuf结构体中嵌入metadata,结构体之后带上用于保存报文数据的固定大小的区域。
- 分别给metadata结构体和报文数据使用分开的mbuf。
前者的好处是分配和释放报文所使用的内存只需要一个操作。在另一方面,第二种方法更加灵活,且允许将metadata结构体的分配和报文数据buffer的分配完全分离。
DPDK选用了第一种方法。metadata包含控制信息例如消息类型,长度,报文数据偏移,一个指向mbuf链表中其它mbuf结构体的指针。
mbuf用于传递网络报文,当多个mbuf用于装载一个完整的报文时,它可以使用报文缓冲链表。对于巨大的数据帧,多个mbufs通过成员next连接到一起来保存报文。
对于刚分配的mbuf,报文段开始的位置在整个消息缓冲开头偏移RTE_PKTMBUF_HEADROOM字节,报文段是缓存对齐的。消息缓冲也用于在系统内不同的对象间传递控制信息,报文,事件等等。消息缓冲结构体中也使用缓冲指针指向其它的消息缓冲数据区或者是其它的数据结构。
buffer存于mempool
buffer管理使用mempool来分配buffers。因此,它能够确保报文头位于不同的通道和rank上以便于L3缓存交叉存取。一个mbuf中有一个变量表明这个pool的来源。当使用rte_ctrlmbuf_free(m)或者是rte_pktmbuf_free(m)时,将mbuf归还给所属的pool。
构造函数
报文和控制mbuf构造函数由API提供。rte_pktmbuf_init()和rte_ctrlmbuf_init()函数初始化mbuf结构体的一些一旦用户创建就不会被修改的成员(mbuf类型,所属的pool,buffer开始地址等等)。这个函数式作为回调函数在创建一个mempool的时候传递给函数rte_mempool_create()。
分配和释放mbuf
分配一个新的mbuf需要用户指定从哪个mempool中分配。对任意新分配的mbuf,它包括一个长度为0的分段。至于数据位置的偏移,在初始化时就设置成了headroom大小(RTE_PKTMBUF_HEADROOM)。
释放一个nbuf意味着将其放回所属的mempool中。mbuf中的内容保存到mempool中时不用修改(作为一个空闲mbuf)。成员在构造mempool时就已经初始化了,没必要在分配mbuf时再重新初始化。
当释放一个包含几个分片的报文mbuf时,他们会一起释放并放回mempool中。
对mbuf的操作
本库提供了一个函数接口操作报文mbuf中的报文数据,例如:
获取报文数据长度
获取报文数据的开始地址指针
在报文数据前添加数据
在报文数据后添加数据
从buffer中的开始位置移除数据(rte_pktmbuf_adj())
从buffer的尾部移除数据(rte_pktmbuf_trim())。
查阅dpdk的API文档获取细节信息
meta信息
一些被网卡驱动使用和存储在mbuf中的信息可以使处理更加简单。例如,VLAN标志,RSS哈希的结果(请看PollMode Driver)和硬件做校验计算的标志。
mbuf也包含了来源网卡端口标志,本mbuf在分片报文存储链表中的编号。
对于分片储存报文链表,只有链表中第一个mbuf保存meta信息。
例如,以RX收包端为例,有IEEE1558报文时间戳机制,VLAN标记和IP层校验。
在TX发包端,也可以将应用程序的一些处理交给硬件来实现(如果硬件支持)。例如,PKT_TX_IP_CKSUM标志允许不做IPv4校验计算。
下面的例子解释了怎么在封装了虚拟可扩展局域网的TCP报文中配置不同的TX标记:out_eth,out_ip,out_udp,vxlan,in_eth,in_up,in_tcp,payload.
l 计算out_ip的校验和:
mb->l2_len=len(out_eth)
mb->l3_len=len(out_ip)
mb->ol_flags |= PKT_TX_IPV4|PKT_TX_IP_CSUM
设置发送报文的ip层校验为0
这是有标志DEV_TX_OFFLOAD_IPV4_CKSUM的硬件支持的。
l 计算out_ip和out_udp校验和
mb->l2_len=len(out_eth)
mb->l3_len=len(out_ip)
mb->ol_flags |= PKT_TX_IPV4|PKT_TX_IP_CSUM|PKT_TX_UDP_CKSUM
设置out_ip检验为0
设置out_udp在虚拟头中的校验使用rte_ipv4_phdr_cksum()
这是有DEV_TX_OFFLOAD_IPV$_CKSUM和DEV_TX_OFFLOAD_UDP_CKSUM标记硬件支持的。
l 计算in_ip校验和
mb->l2_len=len(out_eth+out_ip+out_udp+vxlan+in_eth)
mb->l3_len=len(in_ip)
mb->ol_flags|=PKT_TX_IPV4|PKT_TX_IP_CSUM
设置in_ip的校验为0
这个和例子1相似的,但是l2_len不一样。硬件有DEV_TX_OFFLOAD_IPV4_CKSUM标记的支持。注意,它仅在outer L4校验为0时起效。
l 计算in_ip和in_tcp校验
mb->l2_len=len(out_eth+out_ip+out_udp+vxlan+in_eth)
mb->l3_len=len(in_ip)
mb->ol_flags|=PKT_TX_IPV4|PKT_TX_IP_CSUM|PKT_TX_TCP_CKSUM
这和例2相似,但是l2_len不一样。同二,硬件要有DEV_TX_OFFLOAD_IPV4_CKSUM and DEV_TX_OFFLOAD_TCP_CKSUM.注意仅在outer L4校验位0时才工作。
l TCP分片报文
mb->l2_len = len(out_eth + out_ip + out_udp + vxlan + in_eth)
mb->l3_len = len(in_ip)
mb->l4_len = len(in_tcp)
mb->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM|PKT_TX_TCP_SEG;
硬件支持DEV_TX_OFFLOAD_TCP_TSO。注意只在outer L4检验为0时工作。
这些标志的定义和精确的含义在mbuf的API文档中有讲到(rte_mbuf.h)。也可以查阅testpmd程序源码(特别是csumonly.c文件)获取更多的细节。
直接和间接buffer
一个直接buffer就是完全独立的且只包含自己的。而一个间接buffer的行为看起来和直接buffer一样,但是实际上这个buffer的指针和数据偏移都是指向另外一个直接buffer。当需要拷贝报文或者是分片的时候,这个很有用。因此间接buffer提供了多个buffer直接复用同样的报文数据。
当使用rte_pktmbuf_attach()将一个buffer附加到一个直接buffer上,则buffer变成间接的。每个buffer中有一个引用计数器成员,不论是什么时候一个间接的buffer附加到该直接buffer上,应用计数器都会增加。同样的,无论什么时候间接buffer分离,直接buffer上的计数器都会减少。当应用计数器的结果等于0时,直接buffer会被释放掉不再使用。
在处理间接buffer时有一些事要记住。首先,不能将一个间接buffer附加到一个间接buffer上。其次,一个buffer变成间接buffer,它的引用计数器必须等于1,准确的说,它在之前必须不被其它间接buffer引用。最后,不能将一个间接buffer重新附加到一个直接buffer上(除非先detach)。
当做attach/detach操作时,可以直接使用推荐的函数rte_pktmbuf_attach()和rte_pktmbuf_detach()。建议使用更高级的函数rte_pktmbuf_clone(),它会更小心的初始化一个间接buffer且能拷贝多个分段buffer。
由于间接buffer设计成不存储任何实际数据,所以间接buffer的mempool应该配置成减少这部分内存开销。间接buffer的mempool初始化的例子(和直接buffer的例子一样)可以在几个范例应用程序中找到,比如,IPV4广播范例程序。
调试
在调试模式下(CONFIG_RTE_MBUF_DEBUG打开),mbuf库的接口在执行任何操作之前都做安全检查(例如,buffer错误,错误类型等等)
用例
所有的网络应用程序应该用mbufs来传输网络报文。
Poll Mode Driver
DPDK包括千兆,万兆,4万兆网卡和虚拟IO网卡设备的轮询驱动。
PMD由运行在用户态的BSD驱动提供的API组成,配置设备和收发包队列。此外,PMD不通过任何中断来直接访问RX和TX描述符(除了网卡连接状态改变的中断之外)实现在应用程序中快速收包,处理和转发。本章讲述了PMD的必要条件,系统设计原则和上层架构以及为以太网PMD的通用扩展API。
必要条件和设定
对于包处理程序DPDK允许两种模式,run-to-completion和pipe-line:
l 在run-to-completion模式下,使用API来轮询指定的网卡收包RX描述符队列。然后报文也就在这个核上处理,然后通过发送API将报文放入网卡TX描述符队列中。
l 在pipe-line模式下,一个core通过API轮询一个或者是多个端口的RX描述符队列。报文收下来之后通过ring传递给其它core。其它的core处理报文,处理完后通过发送API将报文放到端口的TX描述符ring中。
在同步的run-to-completion模式下,每个逻辑core被分配一个DPDK执行报文处理循环,这个循环包括以下步骤:
l 通过PMD收包API重新获取报文
l 逐一处理收到的报文直到发送
l 通过PMD发送API发送报文
反之,在异步的pipe-line模式下,一些逻辑core专注于收取报文,其它逻辑core来处理之前收到的报文。重新收到的报文在逻辑core之间的交换通过ring来完成。收包循环包括以下步骤:
l 通过PMD收包API收取报文。
l 通过报文队列将收到的报文给处理的逻辑core
报文处理的循环包括以下步骤:
l 重新从报文队列获取收到的报文。
l 处理收到的报文,如果是发送则直到转发为止。
设计原则
以太网PMD的API和架构是以下面的知道原则来考虑设计的。
PMD必须适应全局策略决定了必须运行在上层的用户态。反之,网卡的PMD功能不应该是我们通过上层全局策略获取的好处的阻碍,或者是不会让应用这些策略变得更糟。
例如,PMD的收包和发包功能有一个轮询报文/描述符的最大值。这就运行一个run-to-completion处理程序使用静态固定值或者是匹配不同全局循环策略的动态值,例如:
l 以零碎的方式一次一个报文的接收,立即处理且发送
l 接收尽可能多的报文,然后处理所有收到的报文,接着立即放松它们
l 接收给定最大数目的报文,处理收到的报文,堆积处理的报文,最后再将其一起发送
要达到最优的性能,软件的总体设计选择和纯粹的软件优化技术必须考虑到,且需要在底层的硬件优化特性(CPU缓存特性,总线加速,网卡PCI带宽等等)。在报文的发送中,优化网络报文burst方式的处理性能,就是软件和硬件之间的折中的例子。在初始的版本中,PMD仅支持rte_eth_tx_one功能来想一个队列中一次发送一个报文。在其之上,很轻松就可以实现rte_eth_tx_burst功能,循环调用rte_eth_tx_one函数来逐一发送多个报文。然而,rte_eth_tx_burst功能被PMD有效的执行并最小化了在驱动层的发送开销,通过以下的优化来实现:
l 在多个报文之间调用rte_eth_tx_one函数非应摊成本的开销的共享
l 使用rte_eth_tx_burst函数来一次提交多个报文,可以充分利用硬件特性(预取数据到缓存中,使用网卡的头尾寄存器)来最小化每个报文的处理cpu时钟周期。例如,避免不必要的访问发送描述符环形队列的内存读取,或者是系统使用指针数组来准确的匹配缓存line的边界和大小。
l 应用大量提交报文的软件优化技术来减少一些无法避免的操作开销,比如环形队列的回环管理。
以大量报文处理的功能在通过API的引进在PMD中大量的使用。特别是在nic的buffer ring中分配和释放多个报文buffer。例如,mbuf_multiple_alloc函数返回一个rte_mbuf的指针数组,当补充多个描述符到收包的ring中时,这会加速PMD的收包轮询函数。
逻辑core,内存和网卡队列的关系
dpdk支持NUMA以获取更好的性能,处理器的逻辑核和接口利用本地的内存。因此,PCIe接口的mbuf的内存池应该在本地分配。buffers如果可能应该保持在本地处理器来获取更好的处理性能,RX和TX buffer描述符装载的mbuf应该是从本地内存创建的mempool中分配。
run-to-completion模式在报文和数据操作都是在本地内存而不是remote处理器内存性能会更好。这对于pipe-line模式也是一样的,所有的逻辑核都是在一个处理器上。
多个逻辑核最好是绝不共享网卡收包和发包队列,因为这会要有全局的锁和降低性能。
设备标示和配置
每个NIC端口的配置包括以下操作:
l 分配PCI资源
l 重置硬件的默认状态
l 建立物理链路层和连接
l 初始化统计计数器
PMD API必须提供接口开启和关闭端口的组播特性的功能以及设置和清除端口混杂模式的功能。
一些硬件加速特性必须在端口初始化时通过特定的配置参数分别配置。比如RSS和DCB。
on-eth-fly配置
所有的设备特性就是on-the-fly下启动的和停止(准确的说就是没有停止设备)不需要PMD API为这个目的提供专门的功能。
这些都需要将PCI设备寄存器映射地址来在驱动外部使用指定功能配置这些特性。
为了达到这个目的,PMD API导出一个功能,提供了在设备的驱动之外启动设备的特性的所有的关联信息。这包括PCI vendor ID,PCI device ID,pci设备寄存器映射的地址,驱动的名字。
这方面做的主要好处是给用户完全*的选择API来配置,开启,关闭这些特性。
举个例子,参考intel的82576千兆网卡控制器和82599万兆网卡控制器在testpmd程序中IEEE1588特性的配置。
其它特性,例如端口的L3/L4 五元组包过滤特性也可以用同样的方式配置。以太网留控(帧暂停)可以配置在个别的端口上。参考testpmd源码获取细节。还有,L4(UDP/TCP/SCTP)校验网卡加速可以针对特殊的报文打开,只要之歌报文的mbuf是正确的。看hardwareoffload(硬件加速)获取细节。
配置收发包队列
每个发包队列都是单独配置下列信息的:
l 发送环形队列的描述符个数
l 插槽标识用于在numa架构下从合适的节点分配DMA内存区域
l 发送队列的预取值,主机和回写阀值
l 最小报文发送释放阀值(tx_free_thresh).当发送的报文描述符个数超过这个值,网络适配器应该检查是否已经写回描述符.当值是0是会采用默认值32.这就保证了PMD网卡的发包队列处理了至少32个报文时才去检索完成的报文描述符.
l 最小的RS位阀值.在发送描述符中最小数目的发送描述符用于设置状态报告位RS.注意这个参数只对万兆网卡有效.RS位
l
该部分暂略.
硬件负载均衡Hardware Offload
依赖于驱动的内力,可通过rte_eth_dev_info_get()获取,PMD可能支持硬件负载均衡,像校验和,TCP分片,或者是VLAN插入。
对上述负载特性的支持意味着在rte_mbuf结构体中添加额外的状态位。这些标志的精确描述参看API文档,在Mbuf Library章的Meta Information小节。
轮询驱动API
一般性
默认所有PMD接口都是无锁的,我们假定它不会在不同的逻辑核之间并行访问同样的对象。例如,PMD收包函数不会被不同的逻辑核调用轮询同样的网卡的相同队列。当然,这个函数可以被不同的逻辑核并行调用从不同的rx收包队列中取包。上层应用比如遵守这个规则。
通用的报文处理
可以用一个rte_mbuf结构体代表一个报文,这是通用的metadata结构体,包含所有必须的内部信息。包括fields和硬件负载均衡状态位,例如IP头检验和或者是VLAN标记。
Rte_mbuf结构体包含特定的fields,略。
以太网设备API
参见API手册。
DPDK IVSHMEM Library
DPDK IVSHMEM库在虚拟机之间快速零拷贝数据(host-to-guest 或者是guest-to-guest),通过QEMU的IVSHMEM机制实现。
这个库通过命令行调用QEMU映射几个大页成单独的ivshmem设备。对于guest需要知道每个给定的ivshmem设备(识别dpdk和非dpdk ivshmem设备)的内部信息,一个metadata成员也被映射到ivshmem段上。Guest应用程序没必要将ivshmem设备映射到内存,这个是dpdk抽象层自动识别完成的。
一个典型的dpdk ivshmem用例如下:
在几个虚拟机之间也是同样的工作,提供了host-to-VM或者是VM-to-VM通讯。最大数量的metadta成员数为32(默认)且每个metadata成员可包含不同的大页(甚至是相同的大页)。唯一的限制就是每个VM必须和其它对象访问共享的内存(可能是host或者是其它的vm)。如果用户想在两个VM之间共享同一个内存区域,每个VM必须在其metadta成员包含这个内存区域。
8.1 IVSHMEM API 概览
下面是对使用IVSHMEM库API的简单引导:
l 调用rte_ivshmem_metadata_create()创建一个新的metadata文件。这个metadata名字用于多个metadata之间做区别。
l 在metadata结构中加入DPDK数据结构体。可通过一下API调用来实现:
n Rte_ivshmem_metadata_add_memzone()来将rte_memzone添加到metadata中。
n Rte_ivshmem_metadata_add_ring()添加rte_ring结构。
n Rte_ivshmem_metadata_add_mempool()添加rte_mempool。
最后,调用rte_ivshmem_metadata_cmdline_generate()来生成用于QEMU的命令行。多个metadata文件(对应多个命令行)可以设置到一个VM上。
注意:只有数据结构体位于DPDK大页内存区域时才能正常工作。如果支持的结构体是通过malloc,mmap,或者是其它非dpdk内存创建的,会导致无法预料的错误,甚至是栈错误。
8.2 IVSHMEM环境配置
要成功运行IVSHMEM应用程序,下面的步骤是必需的:
l 编译QEMU的特定版本
源码可以从QEMU网站获取(当前版本1.4.x是支持的,但是1.5.x也可以工作),需要打补丁才能支持啊。补丁不再DPDK包中,可以到官网上去下。
l 在DPDK创建配置时打开IVSHMEM库开关
在默认配置下,IVSHMEM库是不编译的。要编译IVSHMEM库,要么使用有IVSHMEM目标的编译(例如,x86_64-ivshmem-linuxapp-gcc),或者是设置配置文件中CONFIG_RTE_LIBRTE_IVSHMEM为y。
l 在虚拟机中创建大页内存
Guest应用程序作为DPDK(主)程序运行,那就需要在VM中创建大页内存。这个过程在DPDK入门手册中讲到的一样。
8.3 开发IVSHMEM应用程序最佳实践
当考虑使用IVSHMEM来共享内存,就需要评估安全问题。对于不信任的guest,IVSHMEM并不合适,因为IVSHMEM本质上是一个访问host处理程序内存的窗口。这对多个VM情况也是一样适用的。当IVSHMEM库共享尽可能小的内存区域,很有可能的,对于这个VM设计的数据也会存在于一个IVSHMEM设备下的其它VM中。所以,任何共享内存出错会影响到host和其它所有共享了该内存的VM。
IVSHMEM应用程序本质上运行就相当于多进程应用程序,所以就需要考虑数据访问的串行行和线程安全。DPDK ring结构是线程安全的,但是,任何客户数据结构的使用者都必须是线程安全的。
和PDDK多进程程序类似,在不同的进程中访问的数据内存地址是不同的。
做好是避免在分配的机器之外的机器释放rte_mbuf结构体,换句话说,host分配的就应该host来释放。所以,报文的发送和接收也应该在同样的机器上发生。(要么虚拟机要么物理机)。如果不是这样做可能会导致mempool缓存数据错误。
尽管IVSHMEM机制是零拷贝且有很好的性能。在批量处理上也遵循性能优化中的步骤。
8.4 运行IVSHMEM程序的最佳实践
处于性能上的考虑,最好将host进程和QEMU进程分配到不同的核上以便其互不干扰。如果系统支持NUMA,也需要保证host进程的大页内存和QEMU进程的在同一个NUMA节点上。
对于跨NUMA节点的最佳性能,每个QEMU核应该和host 核在同样的NUMA节点上。QEMU的虚拟NUMA节点也设置对应到物理NUMA节点。更多关于如何创建DPDK和QEMU numa支持可以看DPDK入门手册和QEMU文档。Dpdk源码包中脚本cpu_lauout.py(在tools目录下)可以用来识别cpu核属于那个numa节点。
QEMU IVSHMEM命令行在虚拟机中启动前需要考虑最后一步。当前,QEMU IVSHMEM设备不支持热插拔,所以一旦创建了就不能添加额外的内存到IVSHMEM设备中。因此,运行IVSHMEM程序的正确顺序是先运行host程序,获得每个IVSHMEM设备的命令行,然后再以guest应用程序运行每个QEMU实例。
有一点很重要的需要注意,QEMU一旦启动,它就会获取用于IVSHMEM设备的大页内存。结果就是,如果用户想要关闭或者是重启IVSHMEM host进程,就不是简单的关闭程序了。虚拟机也必须关闭(如果没有,它会一直占有host数据)。
LINK BONDING POLL MODE DRIVER
LIBRARY
除了物理和虚拟硬件的轮询驱动,DPDK也包括一套软件库允许物理硬件的PMD 聚合到一起创建一个逻辑的PMD。
链路聚合PMD库(librte_pmd_bond)支持聚合一组相同速度和双工的rte_eth_dev端口,提供和linux上bond驱动的相同功能,允许主机和交换机之间多个(slave)网卡聚合成一个逻辑接口。新的聚合PMD将按照指定的操作模式处理底层网卡,例如链路主备,故障容错,负载均衡。
Librte_pmd_bond库提供API来创建bond设备,包括配置和管理bond设备和它的slave设备。
注意:librte_pmd_bond库默认是开启的,关闭可以将CONFIG_RTE_LIBRTE_PMD_BOND设置为n再重新编译。
9.1
链路聚合模式概览
当前的链路聚合PMD库支持4中操作模式:
l Round-Robin(Mode 0):
这个模式提供了报文传送的负载均衡和容错处理,按顺序尝试第一个可以被动设备直到最后。在round-robind模式下运行报文会大量的从设备的队列中移除,这种模式不保证顺序的接收报文,下行的流应该能处理乱序的报文
l Active Backup(Mode 1):
在这个模式下,bond中只有一个slave设备在任何时间是活动的,当且仅当主活动的slave错误时,另外一个slave才会变成活动状态。因此必须提供容错机制来处理slave错误。整个逻辑bond接口的mac地址在外部可见的知识一个端口以避免混淆网络转换。
l Balance XOR(Mode 2):
注意:报文的颜色不同用于标示不同的流类别,选择的传输策略计算
这种模式提供了传输负载均和(居于选择的传输策略)和容错。默认策略(2层)使用一个简单的计算,基于报文流源和目的mac地址计以及Bond设备中可用的活动slave设备数目的结果标记报文使用指定的slave传输。2层和3层支持轮询传输策略,使用ip源地址和目的地址来计算出使用的传输slave端口,最后支持的3+4层使用源端口和目的端口,就和源ip、目的ip方式一样。
l Broadcast(Mode 3):
这种模式提供了报文传输过程中的容错,就是每个端口都发一遍。
l Link Aggregation 802.3AD(Mode 4):
这个模式支持802.3ad标准的动态链路聚合。它聚合管理一组网卡设备共享同样的速度和双工设置,使用选择的输出平衡策略。
DPDK要支持这种模式需要应用程序满足一些其它要求:
- 需要调用rte_eth_tx_burst和rte_eth_rx_burst的间隔时间小于100ms
- 调用rte_eth_tx_burst的大小必须为2乘以N,N为slave的个数。这是LACP协议帧需要的大小。此外,LACP报文也包含在统计计数中,但是它们并不传递给应用程序。
l Transmit Load Balancing(Mode 5):
这种模式提供了自适应的传输负载均衡。它动态的改变传输slave,依据据算的负载。每100ms收集一次统计值并每10ms计算调度一次。
9.2
细节
Librte_pmd_bond 聚合的设备和DPDK的以太网PMD接口兼容。Link
Bonding库支持在程序启动时创建bond设备,在EAL初始化时使用—vdev选项来实现,等同于程序自动调用rte_eth_bond_create函数。
绑定的设备支持动态的添加和移除slave设备,接口为rte_eth_bond_slave_add和rte_eth_bond_slave_remove。
在一个slave设备添加到绑定的设备之后,可以用rte_eth_dev_stop来stop这个slave设备且使用rte_eth_dev_configure来重新配置,rx和tx队列也可以通过rte_eth_tx_queue_setup和rte_eth_rx_queue_setup来重新配置绑定设备。
9.2.1
链路状态改变中断/轮询
链路绑定设备支持连接状态改变的回调函数注册,使用API rte_eth_dev_callback_register。当绑定的设备状态发生改变时,这个函数会被调用。例如,在一个绑定的设备有3个slave,当一个slave状态变为活动时链路的状态就变为up,或者是所有的slave变为非活动状态时,链路的状态就变为down。当单个的slave状态发生改变时不会触发回调机制,先决条件不满足。如果用户要监控单独某一个slave就必须注册这个slave对应的回调函数。
链路绑定库也支持那些连接状态改变无法触发中断的设备,通过固定时间间隔轮询连接状态来实现,设置接口为rte_eth_bond_link_monitoring_set。默认的轮询间隔为10ms。当一个设备作为slave添加到bond设备中,它需要通过使用RTE_PCI_DRV_INTR_LSC标记来确定设备是支持中断还是轮询获取连接状态。
9.2.2
要求和限制
当前只支持添加到bond设备的slave设备均是说同样的速度和双工。Bond设备继承第一个轰动的slave设备属性,其它之后添加的slave设备必须支持那些参数。
在启动bond设备前,bond设备下至少有最小数量即1个slave设备。
和PMD一样,所有的函数都是无锁的,我们假定这些接口不会被不同的逻辑核并行调用访问同一个对象。
也要注意在slave设备ijaru到bond设备中之后,PMD接口收包函数不能被slave设备直接调用,因为从slave设备直接读取的报文时不能再从bond设备读取的。
9.2.3
配置
链路bond设备通过函数rte_eth_bond_create创建,这个函数需要指定一个唯一的设备名称,bond模式以及numa节点id以便分配bond设备的资源所在。其它的配置参数是slave设备,主slave,balance XOR模式下用到的用户定义的mac地址和传输策略。
Slave Devices
Bond设备支持最大量的slave设备RTE_MAX_ETHPORTS。网卡设备最为slave添加到bond设备的最大数。Slave设备在添加到bond设备时会根据bond设备的配置重新配置。
Bond会确保当一个slave从bond设备移除时能够返回其原始的mac地址值。
Primary Slave
当bond设备是活动主备模式时,那个活动的slave即为默认的主slave。当且仅当主slave down掉时,其它的端口被用到。如果没有指定主slave则默认是将第一个添加到bond设备的端口设为主端口。
MAC Address
Bond设备可以配置为用户指定的mac地址,这个地址会被所有的或者是部分slave继承,具体依赖于操作模式。当设备处于活动主备模式时,只有主设备有用户指定的mac,所有其它的slave保留原有的mac地址。在模式0,2,3,4中,所有的slave设备配置成bond设备的mac地址。
如果用户没有定义bond设备的mac地址则默认使用主slave的mac地址。
Balance XOR Transmit Policies
有3种支持bond设备在Balance XOR 模式下运行的传输策略:
n Layer 2:基于以太网mac地址的均衡策略是Balance XOR模式的默认传输策略。它经过一个简单的基于报文中源mac地址和目的mac的异或运算,再与slave个数取模得到具体传输用的slave设备的值。
n Layer 2+3:基于源和目的mac地址、ip地址联合计算来决定使用哪个slave端口来传输报文。
n Layer3+4:IP地址和udp端口号为基础来计算出用哪个port传输。
所有这些策略支持802.1Q VLAN以太网报文,包括IPv4,IPv6和UDP协议的负载均衡。
9.3
使用链路聚合设备
Librte_pmd_bond库支持两种方式的设备创建,要么C语言的API接口,要么在程序启动时传入EAL命令行参数来静态的配置bond设备。使用后者不需要对API库有一定的了解就可以创建bond设备。
9.3.1
应用程序使用轮询模式
使用librte_pmd_bond库的API可以在程序内存动态的创建和管理bond设备。这一段实际上上面都讲过了,日,估计这个文档是多个人写的。
可以通过以下api来配置或者是查询配置rte_eth_bond_mode_set/get,rte_eth_bond_primary_set/get,rte_eth_bond_mac_set/reset,rte_eth_bond_xmit_policy_set/get.
9.3.2
通过EAL命令行来使用链路聚合设备
链路聚合设备的可以在程序启动时使用命令行—vdev
选项创建。设备名称必须以eth_bond开头,后面接数字或者是字母。名字且必须是唯一的。每个设备的多个选项数组以逗号分隔的列表。多个设备选项可以多次调用—vdev。
例如:
$RTE_TARGET/app/testpmd
–c f –n 4 –vdev ‘eth_bond0,bond_opt0=..,bond_opt1=..’ –vdev ‘eth_bond1,……….’
链路聚合EAL选项
略