dpdk介绍及应用
DPDK介绍
DPDK(Data Plane Development Kit)是一组快速处理数据包的开发平台及接口。有intel主导开发,主要基于Linux系统,用于快速数据包处理的函 数库与驱动集合,可以极大提高数据处理性能和吞吐量,提高数据平面应用程序的工作效率。
DPDK的作用
在数据平面应用中为快速处理数据包提供一个简单而完善的架构。在理解此工具集之后,开发人员可以以此为基础进行新的原型设计处理大并发网络数据请求。
原始数据包的处理流程:
数据包到达网卡,网卡发送中断通知CPU,CPU将数据包拷贝到内核空间中,应用程序从内核空间中拷贝数据到用户态空间,数据处理。
在这个过程中数据包处理耗时的操作有:
- 网卡每次收到数据都发送中断,打断cpu的工作。 切换和恢复过程都耗时
- 网络数据包经过TCP/IP协议栈,达到真正的应用处理程序时走过很多的流程
- 应用程序拿到网络数据时需要经过内核空间到用户态空间的一次copy,增加耗时
dpdk解决问题办法 - DPDK技术是重载网卡驱动,直接将数据传递给用户态的应用程序,避免了中间环节的经过TCP/IP协议栈,内核空间到用户空间的copy。
- 同时针对第一点网卡频繁的中断,应用程序可以使用轮询的方式获取网卡上的数据,避免中断造成的场景切换和恢复浪费的时间。
DPDK 库
- 核心库Core Libs:提供系统抽象、大页内存、缓存池、定时器及无锁环等基础组件。
- PMD库:提供全用户态的驱动,以便通过轮询和线程绑定得到极高的网络吞吐,支持各种本地和虚拟的 网卡。
- Classify库:支持精确匹配(Exact Match)、最长匹配(LPM)和通配符匹配(ACL),提供常用包处 理的查表操作。
- QoS库:提供网络服务质量相关组件,如限速(Meter)和调度(Sched)。
dpdk 工作在哪里
内核态工作流
- nic:linux 内核为了兼容适配适配市面上绝大多数网卡,抽象出来的一层。ifconfig 看到的eth0_0
等信息并不是网卡,是一个逻辑概念,一个网卡nic 会给分配一个对象,eth0_0 就是这么产生的。 - 协议栈:nic 处理完会数据会放到协议栈解析,如UDP\TCP\ICP协议都是协议栈解析,我们是不能直接操作协议栈的。
- socket:套接字,表示一一对应的一个关系;程序调用socket时,会分配一个fd,同时根据传入的网络协议、通信方式创建对应的TCB(TCP control block)\UDPCB(udp control block),创建的控制块包含TCP\UDP协议的所有状态信息和控制信息,如源IP地址、源端口、目的IP地址、目的端口、传输层协议、序列号、确认号、窗口大小、拥塞控制等。这些信息将用于实现TCP协议的行为和进行网络通信的控制。
- 应用程序:开发的代码
那么dpdk 是如何工作的?
数据从左到右流转,dpdk 直接接管网卡,整个数据不经过nic、协议栈,socket,也不经过应用程序。
- dpdk 用来接管网卡,dpdk 内部有对应的driver去接管驱动,是一种旁路技术。
- 从nic 放到协议栈,有一个结构体是sk_buff, sk_buff就是每一个网络数据包的存储方式。dpdk从网卡接收数据有一个自己的结构,是mbuf。
- dpdk 接收完之后,接收网卡的原始数据,原始数据是包含了以太网,处理完之后再到协议栈,dpdk 协议栈与内核协议栈不是同一协议栈。
dpdk 的优势
- 网卡到内核中,通过协议栈解析完。应用程序 通过recv 读取数据,这里会有两次copy 。
- copy_from_user:用于将用户空间的数据传送到内核空间
- copy_to_user:是一种数据程序,作用是从内核区中读取数据到用户区
dpdk 是怎么做的?
dpdk 用来接管网卡从网卡内抓取取原始数据,用以我们做协议处理。 为什么要这么做?
性能高
- zero-copy,零copy
可以把网卡的数据映射到内存,由内核的多次copy,到现在的零copy(copy 是由cpu参与的,而dpdk网络模式无须cpu 参与) - huagepage,大页内存
大页的作用是减少页表数量,减少TLB缓存的失效,提高内存访问效率,从而提升性能,巨页有两种,2M和1G,如果传输的数据量越大就选1G的,如果传输数据量小就用2M。 - 多队列网卡
dpdk充分的利用了多队列网卡的优势。它的粒度比内核更小,可以指定网卡的每一个队列接收。在软件上着重对多队列网卡兼容支持。 - 轮询接收
大数据传输轮询传输更优,大范围提升了网卡的接收性能,对与小量数据中断的性能要更优 - cpu 亲缘性
dpdk应用
dpdk 是一个开发框架,也是一个网络处理方案,dpdk是对网络数据进行截获抓取的。 在dpdk 之上有应用框架 vpp,ovs 。
dpdk 应用场景
- 安全产品
- 云原生
- nginx 等应用网关
- 虚拟化
dpdk 提供俩种方式接管网卡
- uio
uio 是对io 的操作 - vfio
vfio 是虚拟功能的io
dpdk 对服务器并发有没有帮助
并发是一个服务能够承载的客户端数量。dpdk 是用来提升网卡的吞吐量,使网卡的处理能力性能更高,提升整个网络的吞吐量。dpdk对IO的并发数量帮助不大,但是对单一个IO每秒接收数据能力和处理数据能力的提升,每秒向IO灌的数据变多。
网卡绑定
modprobe vfio-pci #加载vfio模块
ip link set eth0 down
ip link set eth1 down
dpdk-devbind.py -b vfio-pci eth0
dpdk-devbind.py -b vfio-pci eth1
大页内存
大页内存(Large Page Memory)是操作系统提供的一种内存管理技术,它将传统的页(通常大小为 4KB)扩展为更大的页(例如2MB或1GB)。这些大页能够显著减少页表的大小和内存管理的开销,同时提高内存访问的效率和性能。
内存分页查找虚拟内存到物理内存过程
TLB
TLB中保存着逻辑地址前20位[31:12]和页框号的对应关系,如果匹配到逻辑地址就可以迅速找到页框号 (页框号可以理解为页表项),通过页框号与逻辑地址后12位的偏移组合得到最终的物理地址。
大页内存的主要特点和优势
- 减少页表数量
使用大页内存可以减少操作系统维护的页表数量,因为每个大页只需要一个页表项来映射整个大页的地址范围,而不是许多小页所需的多个页表
项。 - 减少TLB(Translation Lookaside Buffer)缓存的失效
TLB是用于加速虚拟地址到物理地址转换的缓存。使用大页内存可以减少TLB缓存的失效率,因为每个TLB条目可以覆盖更多的内存页。 - 提高内存访问效率
由于减少了页表的大小和TLB缓存的失效,大页内存可以显著提高内存访问的效率和响应速度,特别是对于大量数据的连续访问。
Open vSwitch
ovs 介绍
Open vSwitch 简称 OVS,是一个开源的多层虚拟交换机。Open vSwitch 支持标准管理接口、应用程序扩展和控制转发功能,非常适合在 VM 环境中用作虚拟交换机,目前 Open vSwitch 支持多种基于 Linux 的虚拟化技术。
Open vSwitch 具有以下特点和功能:
- 支持 802.1Q VLAN 标准的 trunk 和 access 端口
- 支持物理网卡绑定和 LACP(链路汇聚控制协议,802.3ad)
- 支持 NetFlow、sFlow® 和端口镜像增强通信过程的可视性
- 支持 QoS(服务质量)配置、监控和管理
- 支持多种隧道技术,如 Geneve、GRE、VXLAN、STT、ERSPAN、GTP-U、SRv6、Bareudp 和 LISP
- 支持 802.1ag 标准的连接故障管理
- 支持 OpenFlow 1.0 协议和后续的诸多扩展
- 具有支持事务的轻量级数据库和基于 C / Python 的远程配置协议
- 使用 Linux 内核模块的高性能转发(这里指 Datapath)
Open vSwitch 提供了一种灵活、可扩展的虚拟交换机解决方案,适用于数据中心、云计算和网络虚拟化等应用场景,提供了高级网络功能和网络集中管理的能力。
Open vSwitch 在网络中扮演的角色如下图所示(虚拟交换机)。
可以简单理解:Open vSwitch 是 Linux Bridge 的加强版,加强的点主要体现在:
- Open vSwitch 的设计基于 SDN 的思想,支持多种标准管理接口和协议,如 OpenFlow 协议
- Linux Bridge 只支持Vxlan,而 Open vSwitch 支持 Vxlan、GRE、IPSec 等许多种隧道技术
- Open vSwitch 适用于多种虚拟化架构,如 Xen、KVM、VMware 等
Bridge 桥
Bridge代表一个以太网交换机(Switch),一个主机中可以创建一个或者多个Bridge。Bridge的功能是根据一定规则,把从端口收到的数据包转发到另一个或多个端口。
Port
端口Port与物理交换机的端口概念类似,Port是OVS Bridge上创建的一个虚拟端口,每个Port都隶属于一个Bridge。Port有以下几种类型:
- Normal
可以把操作系统中已有的网卡(物理网卡em1/eth0,或虚拟机的虚拟网卡tapxxx)挂载到ovs上,ovs会生成一个同名Port处理这块网卡进出的数据包。 - Internal
Internal类型是OVS内部创建的虚拟网卡接口,每创建一个Port,OVS会自动创建一个同名接口(Interface)挂载到新创建的Port上。 - Patch
当主机中有多个ovs网桥时,可以使用Patch Port把两个网桥连起来。Patch Port总是成对出现,分别连接在两个网桥上,从一个Patch Port收到的数据包会被转发到另一个Patch Port。 - Tunnel
OVS中支持添加隧道(Tunnel)端口,常见隧道技术有两种gre或vxlan。
Interface
Interface是连接到Port的网络接口设备,是OVS与外部交换数据包的组件,在通常情况下,Port和Interface是一对一的关系,只有在配置Port为 bond模式后,Port和Interface是一对多的关系。这个网络接口设备可能是创建Internal类型Port时OVS自动生成的虚拟网卡,也可能是系统的物理网卡或虚拟网卡(TUN/TAP)挂载在ovs上。 OVS中只有”Internal”类型的网卡接口才支持配置IP地址。
Interface是一块网络接口设备,负责接收或发送数据包,Port是OVS网桥上建立的一个虚拟端口,Interface挂载在Port上。
OpenFlow技术的驱动
OpenVSwitch的另一大特点就是基于OpenFlow。OpenFlow可以定义网络包在交换机中的处理流程(pipeline),因此支持OpenFlow的交换机,其功能不再是固定的,通过OpenFlow可以软件定义OpenVSwitch所具备的功能。
OpenFlow以多个Table串行工作的方式来处理网络数据包,如下图所示。
OpenFlow的灵活性是实现SDN必不可少的一部分,但是在一些实际场景中,因为涉及的功能多且复杂,相应的OpenFlow pipeline会变得很长。直观上来看,pipeline越长,处理一个网络包需要的时间也越长。这是OpenFlow从理论到实际的一个问题,Open vSwitch为此尝试过很多优化。
对于一个Linux系统来说,可以分为用户空间(user space)和内核空间(kernel space),网络设备接入到内核空间。如果需要将数据传输到用户程序则需要通过内核空间将数据上送到用户空间,如果需要在网络设备之间转发数据,直接在内核空间就可以完成。
ovs 架构及工作原理
总体架构
以下两张图都是 Open vSwitch 的总体架构图,但是它们的模块内容都不够全面。
用户空间ovs-vswitchd和内核模块datapath决定了数据包的转发,首先,datapath内核模块收到进入数据包(物理网卡或虚拟网卡),然后查找其缓存(datapath flows),当有一个匹配的flow时它执行对应的操作,否则datapath会把该数据包送入用户空间由ovs-vswitchd负责在其OpenFlow flows中查询,ovs-vswitchd查询后把匹配的actions返回给datapath并设置一条datapath flows到datapath中,这样后续进入的同类型的数据包因为缓存匹配会被datapath直接处理,不用再次进入用户空间。
datapath专注于数据交换,它不需要知道OpenFlow的存在。与OpenFlow打交道的是ovs-vswitchd,ovs-vswitchd存储所有Flow规则供datapath查询或缓存。
工作原理
Bridge 处理数据帧遵循以下几条规则:
- 在一个 Port 上接收到的帧不会再往此 Port 发送此帧。
- 接收到的帧都要学习其 Source MAC 地址。
- 如果数据帧是多播或者广播包(通过 2 层 MAC 地址确定)则要向接收 Port 以外的所有 Port转发,如果上层协议感兴趣,则还会递交上层处理。
- 如果数据帧的地址不能在 CAM(MAC-Port Mapping)表中找到,则向接收 Port 以外的所有 Port 转发。
- 如果 CAM 表中能找到,则转发给相应 Port,如果发送和接收都是同一个 Port,则不发送。
- 网桥是以混杂模式工作的,所有 MAC 地址的数据帧都能够通过。
用户空间 ovs-vswitchd 和内核模块 Datapath 决定了数据包的转发:
- 内核态的 Datapath 监听接口设备流入的数据包。
- 如果 Datapath 在内核态流表缓存没有找到相应的匹配流表项则将数据包传入(upcall)到用户态的 ovs-vswitchd 守护进程处理。
- (可选)用户态的 ovs-vswitchd 拥有完整的流表项,通过 OpenFlow 协议与 OpenFlow 控制器或者 ovs-ofctl 命令行工具进行通信,主要是接收 OpenFlow 控制器南向接口的流表项下发。或者根据流表项设置,ovs-vswitchd 可能会将网络包以 Packet-In 消息发送给 OpenFlow 控制器处理。
- ovs-vswitchd 接收到来自 OpenFlow 控制器或 ovs-ofctl 命令行工具的消息后会对内核态的 Flow Table 进行更新。或者根据局部性原理,用户态的 ovs-vswitchd 会将刚刚执行过的 Datapath 没有缓存的流表项注入到 Flow Table 中。
- ovs-vswitchd 匹配完流表项之后将数据包重新注入(reinject)到 Datapath。
- Datapath 再次访问 Flow Table 获取流表项进行匹配。
- 最后,网络包被 Datapath 根据流表项 Actions 转发或丢弃。
OVS 的交换机角色
在SDN的架构下,ovs作为 SDN交换机,向上连接控制器,向下连接主机。并且Open vSwitch交换机是能够与真是物理交换机通信,相互交流数据。
ovs的交换机组成
- ovs-vswitchd:实现交换机核心功能(如寻址、转发和端口绑定等)的守护进程
- Datapath:支持数据流转发的 Linux 内核模块(唯一工作在内核态的模块)
- ovsdb-server:轻量级数据库服务器(存储配置、日志和状态等信息)
- ovs-dpctl:用于配置交换机内核模块(Datapath)的工具
- ovs-vsctl:用于查询和更新 ovs-vswitchd 配置的工具
- ovs-appctl:用于向正在运行中的 ovs-vswitchd 发送命令的工具
- NetFlow / sFlow:网络监测模块
- ovsdb-tool:数据库
- ovsdb 的工具库模块
- ovs-ofctl:用于查询和控制 OpenFlow 交换机和控制器的工具(仅 OpenFlow 协议可用)
- ovs-controller:OpenFlow 控制器(现在还有 ovs-testcontroller 功能类似)
- ovs-pki:用于创建和管理 OpenFlow 交换机的公钥基础设施的模块
- ovs-tcpdmp:抓包
可以通过命令ovsdb-client dump将数据库结构打印出来。OVSDB中包含一系列记录网桥、端口、QoS等网络配置信息的表,这些表均以JSON格式保存。
每一个ovs交换机中,数据库中存在的表如下:
由于 Open vSwitch 是参照软件定义网络 SDN 的思想设计的,所以 SDN 的三层架构(应用层、控制层、转发层)在 OVS 的架构中也有很好的体现: - 应用层:OVS 提供的各种工具,如:ovs-dpctl,ovs-vsctl,ovs-appctl 等工具模块
- 控制层:主要由 ovs-ofctl 模块负责(因为它支持 OpenFlow 协议)
- 转发层:以 ovs-vswitchd 模块 + Datapath 模块为主,并使用 ovsdb 模块存储信息
数据包处理流程
- ovs的datapath接收到从ovs连接的某个网络设备发来的数据包,从数据包中提取源/目的IP、源/目的MAC、端口等信息。
- ovs在内核状态下查看流表结构(通过Hash),观察是否有缓存的信息可用于转发这个数据包。
- 假设数据包是这个网络设备发来的第一个数据包,在OVS内核中,将不会有相应的流表缓存信息存在,那么内核将不会知道如何处置这个数据包。所以内核将发送upcall给用户态。
- ovs-vswitchd进程接收到upcall后,将检查数据库以查询数据包的目的端口是哪里,然后告诉内核应该将数据包转发到哪个端口,例如eth0。
- 内核执行用户此前设置的动作。即内核将数据包转发给端口eth0,进而数据被发送出去。
流表
流表的基本概念
OVS(Open vSwitch)流表是Open vSwitch中用于数据包转发的规则集合。每个流表包含一系列的流表项,每个流表项定义了数据包的匹配条件和对应的动作。流表的主要作用是根据数据包的特性(如源MAC地址、目的MAC地址、输入端口等)来决定数据包的处理方式(如转发、丢弃等)。流表的生成和维护由外部控制器管理,实现了网络流量的灵活控制。
流表由基础字段、匹配字段、动作字段三部分组成。
流表的匹配和动作
流表的阅读可以分为两个部分:匹配和动作。
- 匹配是指在一堆流表中找到包所走的那条流表。
匹配涉及到的字段包括priority、in_port、metadata、dl_dst、reg6等。 - 动作是指这个包接下来的操作
动作涉及到的字段包括write_metadata、goto_table、drop、output、load等。
在匹配操作中进行精确匹配,在动作操作中进行写更新。
流表的查询过程
流表的查询过程涉及到多个函数和结构体的操作。例如,miniflow_expand函数用于将miniflow结构体的数据与flow结构体进行位运算,以实现流表的查询和更新。具体的实现细节包括遍历所有的map,并对每个非0bit进行位运算,最终得到结果。
ovs-dpdk
在OVS DPDK中,可以建立两种方式的DPDK port,type分别是dpdk和dpdkvhostuser/dpdkvhostuserclient
-
OVS port type=dpdk
这种方式的DPDK port可以用如下命令建立:ovs-vsctl add-port BR_LAN phy_lan -- set Interface phy_lan type=dpdk options:dpdk-devargs=0000:01:00.0
这个port需要对应某个PCI设备,这个PCI设备就是使用DPDK工具脚本所绑定的网卡
-
OVS port type=dpdkvhostuser/dpdkvhostuserclient
dpdkvhostuser和dpdkvhostuserclient是同一种方式的两个模式,通过如下命令可以实现:ovs-vsctl add-port br0 vhost-user-1 -- set Interface vhost-user-1 type=dpdkvhostuser ovs-vsctl add-port BR_LAN nsa_lan -- set Interface nsa_lan type=dpdkvhostuserclient options:vhost-serverpath=/tmp/nsa_lan
这两种方式的区别在于在进行控制面消息沟通的时候,是QEMU和OVS谁作为server谁作为client
普通OVS
ovs的架构图:
ovs处理流表的过程是:
- ovs的datapath接收到从ovs连接的某个网络设备发来的数据包,从数据包中提取源/目的IP、源/目的MAC、端口等信息。
- ovs在内核状态下查看流表结构(通过Hash),观察是否有缓存的信息可用于转发这个数据包。
- 内核不知道如何处置这个数据包会将其发送给用户态的ovs-vswitchd。
- ovs-vswitchd进程接收到upcall后,将检查数据库以查询数据包的目的端口是哪里,然后告诉内核应该将数据包转发到哪个端口,例如eth0。
- 内核执行用户此前设置的动作。即内核将数据包转发给端口eth0,进而数据被发送出去。
ovs+dpdk
DPDK加速的OVS与原始OVS的区别在于,从OVS连接的某个网络端口接收到的报文不需要openvswitch.ko内核态的处理,报文通过DPDK PMD驱动直接到达用户态ovs-vswitchd里。
DPDK加速的OVS数据流转发的大致流程如下:
- OVS的ovs-vswitchd接收到从OVS连接的某个网络端口发来的数据包,从数据包中提取源/目的IP、源/目的MAC、端口等信息。
- OVS在用户态查看精确流表和模糊流表,如果命中,则直接转发。
- 如果还不命中,在SDN控制器接入的情况下,经过OpenFlow协议,通告给控制器,由控制器处理。
- 控制器下发新的流表,该数据包重新发起选路,匹配;报文转发,结束。 总结 主要区别在于流表的处理。
主要区别在于流表的处理。普通ovs流表转发在内核态,而ovs-dpdk流表转发在用户态
OVS DPDK与QEMU之间通过vhost user协议通信
qemu 与kvm的关系
- kvm:kvm是Linux内核的虚拟机
- qemu:qemu 在linux上运行的时候,是它的一种模式。qemu 可以借助kvm 然后实现虚拟化的技术。
virtio
virtio对准虚拟化 hypervisor 中的一组通用模拟设备IO的抽象。Virtio是一种前后端架构,包括前端驱动(Guest内部)、后端设备(QEMU设备)、传输协议(vring)。框架如下图所示:
- 前端驱动:
虚拟机内部的 virtio模拟设备对应的驱动。作用为接收用户态的请求,然后按照传输协议对请求进行封装,再写I/O操作,发送通知到QEMU后端设备。 - 后端设备:
在QEMU中创建,用来接收前端驱动发送的I/O请求,然后按照传输协议进行解析,在对物理设备进行操作,之后通过终端机制通知前端设备。 - 传输协议:
使用virtio队列(virtio queue,virtqueue)完成。设备有若干个队列,每个队列处理不同的数据传输(如virtio-balloon包含ivq、dvq、svq三个)。
virtqueue通过vring实现。Vring是虚拟机和QEMU之间共享的一段环形缓冲区,QEMU和前端设备都可以从vring中读取数据和放入数据。
virtio的代码主要分两个部分:QEMU和内核驱动程序。Virtio设备的模拟就是通过QEMU完成的,QEMU代码在虚拟机启动之前,创建虚拟设备。虚拟机启动后检测到设备,调用内核的virtio设备驱动程序来加载这个virtio设备。
对于KVM虚拟机,都是通过QEMU这个用户空间程序创建的,每个KVM虚拟机都是一个QEMU进程,虚拟机的virtio设备是QEMU进程模拟的,虚拟机的内存也是从QEMU进程的地址空间内分配的。
VRING是由虚拟机virtio设备驱动创建的用于数据传输的共享内存,QEMU进程通过这块共享内存获取前端设备递交的IO请求。
vhost-user 协议及其在OVS-DPDK、qemu和virtio-net中的实现
vhost-user 的实现简述
vhost-user 和 vhost-net 的实现原理是一样,都是采用 vring 完成共享内存,eventfd 机制完成事件通知。不同在于 vhost-net 实现在内核中,而 vhostuser 实现在用户空间中,用于用户空间中两个进程之间的通信,其采用共享内存的通信方式。
vhost-user 基于 C/S 的模式,采用 UNIX 域套接字(UNIX domain socket)来完成进程间的事件通知和数据交互,相比 vhost_net 中采用 ioctl 的方式,vhost-user 采用 socket 的方式大大简化了操作。
vhost-user 基于 vring 这套通用的共享内存通信方案,只要 client 和 server 按照 vring 提供的接口实现所需功能即可,常见的实现方案是 client 实现在 guest OS 中,一般是集成在 virtio 驱动上,server 端实现在 qemu 中,也可以实现在各种数据面中,如 OVS,Snabbswitch 等虚拟交换机。
vhost-user使用DPDK加速
基于vhost协议,DPDK设计了一套新的用户态协议,名为vhost-user协议,这套协议允许qemu将virtio设备的网络包处理offload到任何DPDK应用中(例如OVS-DPDK)。vhost-user协议和vhost协议最大的区别其实就是通信信道的区别。Vhost协议通过对vhost-net字符设备进行ioctl实现,而vhost-user协议则通过unix socket进行实现。通过这个unix socket,vhost-user协议允许QEMU通过以下重要的操作来配置数据平面的offload:
- 特性协商:virtio的特性与vhost-user新定义的特性都可以通过类似的方式协商,而所谓协商的具体实现就是QEMU接收vhost-user的特性,与自己支持的特性取交集。
- 内存区域配置:QEMU配置好内存映射区域,vhost-user使用mmap接口来映射它们。
- Vring配置:QEMU将Virtqueue的个数与地址发送给vhost-user,以便vhost-user访问。
- 通知配置:vhost-user仍然使用eventfd来实现前后端通知。
基于DPDK的Open vSwitch(OVS-DPDK)一直以来就对vhost-user提供了支持,可以通过在OVS-DPDK上创建vhost-user端口来使用这种高效的用户态后端。
所有的控制信息通过UNIX套接口(控制通道)交互。包括为进行直接内存访问而交换的内存映射信息,以及当数据填入virtio队列后需要出发的kick事件和中断信息。
数据通道事实上由内存直接访问实现。客户机中的virtio-net驱动分配一部分内存用于virtio的队列。virtio标准定义了此队列的结构。QEMU通过控制通道将此部分内存的地址共享给OVS DPDK。DPDK自身映射一个相同标准的virtio队列结构到此内存上,藉此来读写客户机巨页内存中的virtio队列。直接内存访问的实现需要在OVS DPDK和QEMU之间使用巨页内存。如果QEMU设置正确,但是没有配置巨页内存,OVS DPDK将不能访问QEMU的内存,二者也就不能交换数据报文。
当OVS DPDK向虚机发送数据包时,这些数据包在OVS DPDK的统计里面显示为接口的发送Tx流量。在虚机中,显示为接收Rx流量。
当虚机向OVS DPDK发送数据包时,这些数据包在虚机中显示为发送Tx流量,而在OVS DPDK中显示为接口的接收Rx流量。
虽然数据包可通过共享内存传输,但是还需要一种方法告知对端数据包已经拷贝到virtio队列中。通过vhost user套接口实现的控制通道可用来完成通知(kicking)对方的功能。通知必然有代价。首先,需要一个写套接口的系统调用;之后对端需要处理一个中断操作。所以,接收双方都会在控制通道上消耗时间。
为避免控制通道的通知消耗,OpenvSwitch和QEMU都可以设置特殊标志以告知对方其不愿接收中断。尽管如此,只有在采用临时或者固定查询virtio队列方式时才能使用不接收中断的功能。
为虚机的性能考虑其本身可采用DPDK处理数据包。尽管Linux内核采用轮询处理和中断相结合的NAPI机制,但是产生的中断数量仍然很多。OVS DPDK以非常高的速率发送数据包到客户机。同时,QEMU的virtio队列的收发缓存数被限制在了默认的256与最大1024之间。结果,客户机必须以非常快的速度处理数据包。理想的实现就是使用DPDK的PMD驱动不停的轮询客户机端口进行数据包处理。
vhost-user协议标准
此协议旨在补充实现在Linux内核中的vhost的ioctl 接口。实现了与同一宿主机中的用户进程交互建立virtqueue队列的控制平面。通过UNIX套接口消息中的附加数据字段来共享文件描述符。
协议定义了通信的两端:主和从。主时要共享其virtqueue队列的进程,即QEMU。从为virtqueues队列的消费者。
主和从在通信时都可以作为客户端(主动连接)或者服务端(监听)。
vhost user协议由两方组成:
- 主方 - QEMU
- 从方 - Open vSwitch或者其它软件交换机
vhost user各方都可运行在2中模式下:
- vhostuser——client - QEMU作为服务端,软件交换机作为客户端。
vhostuser client模式下,QEMU创建vhu套接口,OVS进行连接。
当某个虚机停掉后,不会影响ovs,也不会影响其他虚机。当停掉的虚机启动后,ovs 会自己连接此server。 - vhostuser - 软件交换机作为服务端,QEMU作为客户端。
此模式下,OVS创建vhu套接口,QEMU主动进行连接。
vhost user实现基于内核的vhost架构,将所有特性实现在用户空间。
当QEMU虚机启动时,它将所有的虚机内存分配为共享的巨页内存。其操作系统的半虚拟化驱动virtio将保留这些巨页内存的一部分用作virtio环形缓存。这样OVS DPDK将可以直接读写客户机的virtio环形缓存。OVS DPDK和QEMU可通过此保留的内存空间交换网络数据包。
用户空间进程接收到客户机预先分配的共享内存文件描述符后,可直接存取与之关联的客户机内存空间中的vrings环结构。 (http://www.
virtualopensystems.com/en/solutions/guides/snabbswitch-qemu/)
vhost user协议使用一个UNIX套接口处理OVS和QEMU之间的通信,包括在初始化过程中,和数据包拷贝到共享内存的virtio环中需要通知对方时。所以两者的交互包括基于控制通道(vhu)的创建操作和通知机制,与拷贝数据包的数据通道(直接内存访问)。
DPDK UNIX套接口的注册和消息交互
通过ovs-vsctl add-port 创建一个名称为vhuxxxxxxxx-xx的接口。在OVS内部,此名称保存在netdev结构体的成员name中(netdev->name)。
当创建vhost user接口时,Open vSwitch控制DPDK注册一个新的vhost-user UNIX套接口。套接口的路径为vhost_sock_dir加netdev->name加设备的dev->vhost_id。
通过设置RTE_VHOST_USER_CLIENT标志,OVS可请求创建vhost user套接口的客户端模式。
OVS函数netdev_dpdk_vhost_construct调用DPDK的rte_vhost_driver_register函数,其又调用vhost_user_create_server或者vhost_user_create_client函数创建套接口。默认使用前者创建服务端模式的套接口,如果设置了RTE_VHOST_USER_CLIENT标志,创建客户端模式套接口。
相关的函数调用关系如下:
- netdev_dpdk_vhost_construct 定义在openvswitch/lib/netdev-dpdk.c
- rte_vhost_driver_register\create_unix_socket\rte_vhost_driver_start定义在dpdk-stable-23.11.1/lib/vhost/socket.c
dpdk ovs 数据怎么到虚机内
ovs 是用来管理网口的,dpdk是接收物理网卡的数据。
ovs 从dpdk上接收数据,然后通过vhostuser协议转发数据到对应的虚机内
数据流转
那么数据是如何解析的?
ovs 是通过dpdk 的PMD驱动直接获取数据,拿到数据后通过vhostuser协议转发到虚机,此时数据都是转发的原始数据到虚机,那数据是如何解析的?
数据是在虚机内解析的,vhostuser在虚拟机里就是virtio网卡,虚机本身也是一个操作系统,收到数据后通过网卡内核态工作处理来数据