Table of Contents

加速包处理的vhost优化方案

142.vhost的演进和原理

143.Qemu与virtio-net

144.Linux内核态vhost-net

145.用户态vhost

146.基于DPDK的用户态vhost设计

147.消息机制

148.地址转换和映射虚拟机内存

149.vhost特性协商

150.virtio-net设备管理

(1)设备创建

(2)设置

(3)服务启动

(4)设备销毁

151.vhost中的Checksum和TSO功能卸载

DPDK vhost编程实例

152.DPDK vhost编程实例

153.报文收发接口介绍

154.使用DPDK vhost lib进行编程

155.使用DPDK vhost PMD进行编程

156.小结

系列文章

相关阅读


加速包处理的vhost优化方案


第11章主要介绍了virtio-net网络设备的前端驱动设计,本章将介绍其对应的后端驱动vhost设计。


142.vhost的演进和原理

virtio-net的后端驱动经历过从virtio-net后端,到内核态vhost-net,再到用户态vhost-user的演进过程。其演进的过程是对性能的追求,导致其架构的变化。


143.Qemu与virtio-net

《用QEMU构建嵌入式LINUX系统》

《Linux虚拟化KVM-Qemu分析(一)》

《Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化》

《Linux虚拟化KVM-Qemu分析(三)之KVM源码(1)》

《Linux虚拟化KVM-Qemu分析(四)之CPU虚拟化(2)》

《在CentOS上进行虚拟化:QEMU、Xen、KVM、LibVirt、oVirt》

《ARM SMMU原理与IOMMU技术(“VT-d” DMA、I/O虚拟化、内存虚拟化)》

《OpenVZ,Xen,KVM等:虚拟化解决方案》

《KVM Virtio: An I/O virtualization framework for Linux(Linux虚拟IO框架)》

virtio-net后端驱动的最基本要素是虚拟队列机制、消息通知机制和中断机制。虚拟队列机制连接着客户机和宿主机的数据交互。消息通知机制主要用于从客户机到宿主机的消息通知。中断机制主要用于从宿主机到客户机的中断请求和处理。

图12-1是virtio-net后端模块进行报文处理的系统架构图。其中,KVM是负责为程序提供虚拟化硬件的内核模块,而Qemu利用KVM来模拟整个系统的运行环境,包括处理器和外设等;Tap则是内核中的虚拟以太网设备。

图12-1 vhost实现之前的Qemu virtio-net

当客户机发送报文时,它会利用消息通知机制(图12-1中通路2)),通知KVM,并退出到用户空间Qemu进程,然后由Qemu开始对Tap设备进行读写(图12-1中通路1))。

在这个模型中,由于宿主机、客户机和Qemu之间的上下文频繁切换带来的多次数据拷贝和CPU特权级切换,导致virtio-net性能不如人意。可以看出,性能瓶颈主要存在于数据通道和消息通知路径这两块:

  • 1)数据通道是从Tap设备到Qemu的报文拷贝和Qemu到客户机的报文拷贝,两次报文拷贝导致报文接收和发送上的性能瓶颈。
  • 2)消息通知路径是当报文到达Tap设备时内核发出并送到Qemu的通知消息,然后Qemu利用IOCTL向KVM请求中断,KVM发送中断到客户机。这样的路径带来了不必要的性能开销。

144.Linux内核态vhost-net

为了对上述报文收发性能瓶颈进行优化,Linux内核设计了vhost-net模块,目的是通过卸载virtio-net在报文收发处理上的工作,使Qemu从virtio-net的虚拟队列工作中解放出来,减少上下文切换和数据包拷贝,进而提高报文收发的性能。除此以外,宿主机上的vhost-net模块还需要承担报文到达和发送消息通知及中断的工作。

图12-2展现了加入Linux内核vhost-net模块后virtio-net模块进行报文处理的系统架构图。报文接收仍然包括数据通路和消息通知路径两个方面:

  • 1)数据通路是从Tap设备接收数据报文,通过vhost-net模块把该报文拷贝到虚拟队列中的数据区,从而使客户机接收报文。
  • 2)消息通路是当报文从Tap设备到达vhost-net时,通过KVM模块向客户机发送中断,通知客户机接收报文。

报文发送过程与之类似,此处不再赘述。

Linux内核态vhost-net的设计是建立在Qemu能共享如下信息的基础之上:

图12-2 virtio-net与Linux内核vhost-net

  • ❑Qemu共享在客户机上的内存空间的布局:vhost-net能够得到相应的地址转换的信息,主要是指客户机物理地址(GPA)到宿主机物理地址(HPA)的转换。
  • ❑Qemu共享虚拟队列的地址:vhost-net能直接对这些虚拟队列进行读写操作,从而进行报文的收发处理。由于虚拟队列的地址是Qemu进程上虚拟空间中的地址,实际使用时需要转换成vhost-net所在进程的虚拟地址。
  • ❑Qemu共享KVM中配置的用于向客户机上的virtio-net设备发送中断的事件文件描述符(eventfd):通过这种方式,vhost-net收到报文后可以通知客户机取走接收队列中的报文。
  • ❑Qemu共享KVM中配置的用于virtio-net PCI配置空间写操作触发的事件文件描述符:该描述符在virtio-net端口的PCI配置空间有写入操作时被触发,客户机可以在有报文需要发送时利用这种方式通知vhost-net。

145.用户态vhost

Linux内核态的vhost-net模块需要在内核态完成报文拷贝和消息处理,这会给报文处理带来一定的性能损失,因此用户态的vhost应运而生。用户态vhost采用了共享内存技术,通过共享的虚拟队列来完成报文传输和控制,大大降低了vhost和virtio-net之间的数据传输成本。

DPDK vhost是用户态vhost的一种实现,其实现原理与Linux内核态vhost-net类似,它实现了用户态API,卸载了Qemu在Virtio-net上所承担的虚拟队列功能,同样基于Qemu共享内存空间布局、虚拟化队列的访问地址和事件文件描述符给用户态的vhost,使得vhost能进行报文处理以及跟客户机通信。同时,由于报文拷贝在用户态进行,因此Linux内核的负担得到减轻。

DPDK vhost同时支持Linux virtio-net驱动和DPDK virtio PMD驱动的前端,其包含简易且轻量的2层交换功能以及如下基本功能:

  • ❑virtio-net网络设备的管理,包括virtio-net网络设备的创建和virtio-net网络设备的销毁。
  • ❑虚拟队列中描述符列表、可用环表和已用环表在vhost所在进程的虚拟地址空间的映射和解除映射,以及实际报文数据缓冲区在vhost所在进程的虚拟地址空间的映射和解除映射。
  • ❑当收到报文时,触发发送到客户机的消息通知;当发送报文时,接收来自客户机的消息通知。
  • ❑virtio-net设备间(虚拟队列)以及其与物理设备间(网卡硬件队列)的报文交换。可用VMDQ机制来对数据包进行分类和排序,避免软件方式的报文交换,从而减少报文交换的成本。
  • ❑virtio-net网络后端的实现以及部分新特性的实现,如合并缓冲区实现巨帧的接收,虚拟端口上多队列机制等。

146.基于DPDK的用户态vhost设计

DPDK vhost支持vhost-cuse(用户态字符设备)和vhost-user(用户态socket服务)两种消息机制,它负责为客户机中的virtio-net创建、管理和销毁vhost设备。前者是一个过渡性技术,这里着重介绍目前通用的vhost-user方式。


147.消息机制

当使用vhost-user时,首先需要在系统中创建一个Unix domain socket server,用于处理Qemu发送给vhost的消息,其消息机制如图12-3所示。

图12-3 vhost后端和Qemu消息机制

如果有新的socket连接,说明客户机创建了新的virtio-net设备,因此vhost驱动会为之创建一个vhost设备,如果Qemu发给vhost的消息中已经包含有socket文件描述符,说明该Unix domain socket已创建,因此该描述符可以直接被vhost进程使用。

最后,当socket连接关闭时,vhost会销毁相应的设备。

常用消息如下:

  • VHOST_GET_FEATURES:返回vhost所能支持的virtio-net功能子集。
  • VHOST_SET_FEATURES:检查功能掩码,设置vhost和前端virtio-net所共同支持的特性,任何特性只有二者同时支持的情况下才真正有效。
  • VHOST_SET_OWNER:将设备设置为当前进程所有。
  • VHOST_RESET_OWNER:当前进程释放对该设备的所有权。
  • VHOST_SET_MEM_TABLE:设置内存空间布局信息,用于在报文收发时进行数据缓冲区地址转换。
  • VHOST_SET_LOG_BASE/VHOST_SET_LOG_FD:该消息可用于客户机在线迁移。
  • VHOST_SET_VRING_NUM:vhost记录每个虚拟队列(包括接收队列和发送队列)的大小信息。
  • VHOST_SET_VRING_ADDR:这个消息在Qemu地址空间里发送Virtqueue结构的虚拟地址。vhost将该地址转换成vhost的虚拟地址空间。它使用VHOST_SET_VRING_NUM的消息确定描述符队列、AVAIL队列、USED队列的大小(通常每个队列分配一页的大小)。
  • VHOST_SET_BASE:这个消息传递初始索引值,vhost根据该索引值找到可用的描述符。vhost同时记录该索引值并设置成当前位置。
  • VHOST_GET_BASE:这个消息将返回vhost当前的索引值,即vhost目前期望找到可用的描述符的地方。
  • VHOST_SET_VRING_KICK:这个消息传递eventfd文件描述符。当客户端有新的数据包需要发送时,通过该文件描述符通知vhost接收新的数据包并发送到目的地。vhost使用eventfd代理模块把这个eventfd文件描述符从Qemu上下文映射到它自己的进程上下文中。
  • VHOST_SET_VRING_CALL:这个消息同样传递eventfd文件描述符,使vhost能够在完成对新的数据包接收时,通过中断的方式来通知客户机,准备接收新的数据包。vhost使用eventfd代理模块把这个eventfd文件描述符从Qemu上下文映射到它自己的进程上下文中。
  • VHOST_USER_GET_VRING_BASE:这个消息将虚拟队列的当前可用索引值发送给Qemu。

148.地址转换和映射虚拟机内存

Qemu支持一个参数选项(mem-path),用于传送目录/文件系统,Qemu在该文件系统中分配所需的内存空间。因此,必须保证宿主机上有足够的大页空间,同时总是需要指定内存预分配(mem-prealloc)。

为了vhost能访问虚拟队列和数据包缓冲区,所有的虚拟队列中的描述符表、可用环表和已用环表的地址,其所在的的页面必须被映射到vhost的进程空间中。

vhost收到Qemu发送的VHOST_SET_MEM_TABLE消息后,使用消息中的内存分布表(文件描述符、地址偏移、块大小等信息),将Qemu的物理内存映射到自己的虚拟内存空间。

这里有如下几个概念需要描述。

Guest的物理地址(GPA):客户机的物理地址,如虚拟队列中的报文缓冲区的地址,可以被认为是一个基于上述系统函数MMAP返回起始地址的偏移量。

Qemu地址空间虚拟地址(QVA):当Qemu发送VHOST_SET_VRING_ADDR消息时,它传递虚拟队列在Qemu虚拟地址空间中的位置。

vhost地址空间虚拟地址(VVA):要查找虚拟队列和存储报文的缓存在vhost进程的虚拟地址空间地址,必须将Qemu虚拟地址和Guest物理地址转换成vhost地址空间的虚拟地址。

在DPDK的实现中,使用virtio_memory数据结构存储Qemu内存文件的区域信息和映射关系。其中,区域信息使用virtio_memory_regions数据结构进行存储。

/**
* Information relating to memory regions including offsets
* to addresses in QEMUs memory file.
*/
struct virtio_memory_regions { /**< Base guest physical address of region. */ uint64_t     guest_phys_address; /**< End guest physical address of region. */ uint64_t     guest_phys_address_end; /**< Size of region. */ uint64_t     memory_size; /**< Base userspace address of region. */ uint64_t     userspace_address; /**< Offset of region for address translation. */ uint64_t     address_offset;
};
/**
* Memory structure includes region and mapping information.
*/
struct virtio_memory { /**< Base QEMU userspace address of the memory file. */ uint64_t     base_address; /**< Mapped address of memory file base in our applications memory space. */ uint64_t     mapped_address; /**< Total size of memory file. */ uint64_t     mapped_size; /**< Number of memory regions. */ uint32_t     nregions; /**< Memory region information. */ struct virtio_memory_regions        regions[0];
}; 

通过这两个数据结构,DPDK就可以通过地址偏移计算出客户机物理地址或Qemu虚拟地址在vhost地址空间的虚拟地址。

struct virtio_memory_regions *region;
vhost_va = region->address_offset + guest_pa;
vhost_va = qemu_va + region->guest_phys_address + region->address_offset - region->userspace_address;

149.vhost特性协商

在设备初始化时,客户机virtio-net前端驱动询问vhost后端所支持的特性。当其收到回复后,将代表vhost特性的字段与自身所支持特性的字段进行与运算,确定二者共同支持的特性,并将最终可用的特性集合发送给vhost。

如下是DPDK vhost支持的特性集合:

  • VIRTIO_NET_F_HOST_TSO4:宿主机支持TSO V4。
  • VIRTIO_NET_F_HOST_TSO6:宿主机支持TSO V6。
  • VIRTIO_NET_F_CSUM:宿主机支持校验和。
  • VIRTIO_NET_F_MRG_RXBUF:宿主机可合并收包缓冲区。
  • VHOST_SUPPORTS_MQ:支持虚拟多队列。
  • VIRTIO_NET_F_CTRL_VQ:支持控制通道。
  • VIRTIO_NET_F_CTRL_RX:支持接收模式控制通道。
  • VHOST_USER_F_PROTOCOL_FEATURES:支持特性协商。
  • VHOST_F_LOG_ALL:用于vhost动态迁移。

150.virtio-net设备管理

一个virtio-net设备的生命周期包含设备创建、配置、服务启动和设备销毁四个阶段。

(1)设备创建

vhost-user通过建立socket连接来创建。

当创建一个virtio-net设备时,需要:

  • ❑分配一个新的virtio-net设备结构,并添加到virtio-net设备链表中。
  • ❑分配一个为virtio-net设备服务的处理核并添加virtio-net设备到数据面的链表中。
  • ❑在vhost上分配一个为virtio-net设备服务的RX / TX队列。

(2)设置

利用VHOST_SET_VRING_*消息通知vhost虚拟队列的大小、基本索引和位置,vhost将虚拟队列映射到它自己的虚拟地址空间。

(3)服务启动

vhost-user利用VHOST_USER_SET_VRING_KICK消息来启动虚拟队列服务。之后,vhost便可以轮询其接收队列,并将数据放在virtio-net设备接收队列上。同时,也可轮询发送虚拟队列,查看是否有待发送的数据包,若有,则将其复制到发送队列中。

(4)设备销毁

vhost-user利用VHOST_USER_GET_VRING_BASE消息来通知停止提供对接收和发送虚拟队列的服务。收到消息后,vhost会立即停止轮询传输虚拟队列,还将停止轮询网卡接收队列。同时,分配给virtio-net设备的处理核和物理网卡上的RX / TX队列也将被释放。


151.vhost中的Checksum和TSO功能卸载

为了降低高速网络系统对CPU的消耗,现代网卡大多都支持多种功能卸载技术,如第9章所述。其中,较为重要的两种功能是Checksum(校验和)的计算和TSO(TCP分片卸载)。

Checksum(校验和)被广泛应用于网络协议中,用于检验消息在传递过程中是否发生错误。如果网卡支持Checksum功能的卸载,则Checksum的计算可以在网卡中完成,而不需要消耗CPU资源。

TSO(TCP Segmentation Off load, TCP分片卸载)技术利用网卡的处理能力,将上层传来的TCP大数据包分解成若干个小的TCP数据包,完成添加IP包头、复制TCP协议头并针对每一个小包计算校验和等工作。因此,如果网卡不支持TSO,则TCP软件协议层在向IP层发送数据包时会考虑MSS(Maximum Segment Size,最大分片大小),将较大的数据分成多个包进行发送,从而带来更多CPU负载。

在DPDK vhost的实现中,为了避免给虚拟机带来额外的CPU负载,同样可以对Checksum卸载和TSO进行支持。

由于数据包通过virtio从客户机到宿主机是用内存拷贝的方式完成的,期间并没有通过物理网络,因此不存在产生传输错误的风险,也不需要考虑MSS如何对大包进行分片。因此,vhost中的Checksum卸载和TSO的实现只需要在特性协商时告诉虚拟机这些特性已经被支持。之后,在虚拟机virtio-net发送数据包时,在包头中标注该数据包的Checksum和TCP分片的工作需要在vhost端完成。最后,当vhost收到该数据包时,修改包头,标注这些工作已经完成。


DPDK vhost编程实例

152.DPDK vhost编程实例

DPDK的vhost有两种封装形式:vhost lib和vhost PMD。vhost lib实现了用户态的vhost驱动供vhost应用程序调用,而vhost PMD则对vhost lib进行了封装,将其抽象成一个虚拟端口,可以使用标准端口的接口来进行管理和报文收发。

vhost lib和vhost PMD在性能上并无本质区别,不过vhost lib可以提供更多的函数功能来供使用,而vhost PMD受制于抽象层次,不能直接对非标准端口功能的函数进行封装。为了使用vhost lib的所有功能,保证其使用灵活性和功能完备性,vhost PMD提供了以下两种方式。

  • 1)添加了回调函数:如果使用老版本vhost lib的程序需要在新建或销毁设备时进行额外的操作,可使用新增的回调函数来完成。
  • 2)添加了帮助函数:帮助函数可以将端口号转换成virtio-net设备指针,这样便可以通过这个指针来调用vhost lib中的其他函数。

153.报文收发接口介绍

在使用vhost lib进行编程时,使用如下函数进行报文收发:

/*  This  function  get  guest  buffers  from  the  virtio  device  TX  virtqueue  for
processing. */
uint16_t  rte_vhost_dequeue_burst(struct  virtio_net  *dev,  uint16_t  queue_id, struct rte_mempool *mbuf_pool, struct rte_mbuf **pkts, uint16_t count);
/* This function adds buffers to the virtio devices RX virtqueue. */ uint16_t  rte_vhost_enqueue_burst(struct  virtio_net  *dev,  uint16_t  queue_id, struct rte_mbuf **pkts, uint16_t count);

而vhost PMD可以使用如下接口函数:

static  inline  uint16_t  rte_eth_rx_burst(uint8_t  port_id,  uint16_t  queue_id, struct rte_mbuf **rx_pkts, const uint16_t nb_pkts);
static  inline  uint16_t  rte_eth_tx_burst(uint8_t  port_id,  uint16_t  queue_id, struct rte_mbuf **tx_pkts, uint16_t nb_pkts); 

该接口会通过端口号查找设备指针,并最终调用设备所提供的收发函数:

struct rte_eth_dev *dev = &rte_eth_devices[port_id];
(*dev->rx_pkt_burst)(dev->data->rx_queues[queue_id], rx_pkts, nb_pkts);
(*dev->tx_pkt_burst)(dev->data->tx_queues[queue_id], tx_pkts, nb_pkts);

vhost PMD设备所注册的收发函数如下:

static uint16_t eth_vhost_rx(void *q, struct rte_mbuf **bufs, uint16_t nb_bufs);
static uint16_t eth_vhost_tx(void *q, struct rte_mbuf **bufs, uint16_t nb_bufs);

它们分别对rte_vhost_dequeue_burst和rte_vhost_enqueue_burst进行了封装。

本章将介绍两个编程实例,它们分别使用vhost lib和vhost PMD进行报文转发。


154.使用DPDK vhost lib进行编程

在DPDK所包含的示例程序中,vhost-switch是基于vhost lib的一个用户态以太网交换机的实现,可以完成在virtio-net设备和物理网卡之间的报文交换。

实例中还使用了虚拟设备队列(VMDQ)技术来减少交换过程中的软件开销,该技术在网卡上实现了报文处理分类的任务,大大减轻了处理器的负担。

该实例包含配置平面和数据平面。在运行时,vhost-switch需要至少两个处理器核心:一个用于配置平面,另一个用于数据平面。为了提高性能,可以为数据平面配置多个处理核。

配置平面主要包含下面这些服务。

  • ❑virtio-net设备管理:virtio-net设备创建和销毁以及处理核的关联。
  • ❑vhost API实现:虚拟主机API的实现。
  • ❑物理网卡的配置:为一个virtio-net设备配置MAC/ VLAN(VMDQ)滤波器到绑定的物理网卡上。

数据平面的每个处理核对绑定在其上的所有vhost设备进行轮询操作,轮询该设备所对应的VMDQ接收队列。如有任何数据包,则接收并将其放到该vhost设备的接收虚拟队列上。同时,处理核也将轮询相应virtio-net设备的虚拟发送队列,如有数据需要发送,则把待发送数据包放到物理网卡的VMDQ传输队列中。

在完成vhost驱动的注册后,即可通过调用vhost lib中的rte_vhost_dequeue_burst和rte_vhost_enqueue_burst进行报文的接收和发送。

其核心交换代码如下(基于DPDK 2.1.0):

while (dev_ll ! = NULL) { /* 查找得到Virtio-net设备 */ vdev = dev_ll->vdev; dev = vdev->dev; /* 检查设备有效性 */ if (unlikely(vdev->remove)) { dev_ll = dev_ll->next; unlink_vmdq(vdev); vdev->ready = DEVICE_SAFE_REMOVE; continue; } if (likely(vdev->ready == DEVICE_RX)) { /* 从接收端口接收数据包 */ rx_count = rte_eth_rx_burst(ports[0], vdev->vmdq_rx_q, pkts_burst, MAX_PKT_BURST); if (rx_count) { /* 若虚拟队列条目不够,为避免丢包,等待后尝试重发 */ if (enable_retry && unlikely(rx_count > rte_vring_available_entries(dev, VIRTIO_RXQ))) { for (retry = 0; retry < burst_rx_retry_num; retry++) { rte_delay_us(burst_rx_delay_time); if (rx_count <= rte_vring_available_entries(dev, VIRTIO_RXQ)) break; } } /* 调用vhost lib中的enqueue函数,将报文发送到客户机 */ ret_count = rte_vhost_enqueue_burst(dev, VIRTIO_RXQ, pkts_burst, rx_count); if (enable_stats) { rte_atomic64_add( &dev_statistics[dev_ll->vdev->dev->device_fh].rx_total_atomic, rx_count); rte_atomic64_add( &dev_statistics[dev_ll->vdev->dev->device_fh].rx_atomic, ret_count); } /* 释放缓存区 */ while (likely(rx_count)) { rx_count--; rte_pktmbuf_free(pkts_burst[rx_count]); } } } if (likely(! vdev->remove)) { /* 调用vhost lib中的dequeue函数,从客户机接收报文 */ tx_count = rte_vhost_dequeue_burst(dev, VIRTIO_TXQ, mbuf_pool, pkts_burst,  MAX_PKT_BURST); /* 如果首次收到该MAC则进行MAC学习,并设置VMDQ */ if (unlikely(vdev->ready == DEVICE_MAC_LEARNING) && tx_count) { if (vdev->remove —— (link_vmdq(vdev, pkts_burst[0]) == -1)) { while (tx_count) rte_pktmbuf_free(pkts_burst[--tx_count]); } } /* 将报文转发到对应的端口 */ while (tx_count) virtio_tx_route(vdev, pkts_burst[--tx_count], (uint16_t)dev->device_fh); } /* 开始处理下一个设备 */ dev_ll = dev_ll->next;
} 

155.使用DPDK vhost PMD进行编程

如果使用vhost PMD进行报文收发,由于使用了标准端口的接口,因此函数的调用过程相对简单。

首先,需要注册vhost PMD驱动,其数据结构如下:

static struct rte_driver pmd_vhost_drv = { .name = "eth_vhost", .type = PMD_VDEV, .init = rte_pmd_vhost_devinit, .uninit = rte_pmd_vhost_devuninit,
}; 

rte_pmd_vhost_devinit()调用eth_dev_vhost_create()来注册网络设备并完成所需数据结构的分配。其中,网络设备的数据结构rte_eth_dev定义如下:

struct rte_eth_dev { eth_rx_burst_t rx_pkt_burst; /**< Pointer to PMD receive function. */ eth_tx_burst_t tx_pkt_burst; /**< Pointer to PMD transmit function. */ struct rte_eth_dev_data *data;   /**< Pointer to device data */ const struct eth_driver *driver; /**< Driver for this device */ const struct eth_dev_ops *dev_ops; /**< Functions exported by PMD */ struct rte_pci_device *pci_dev; /**< PCI info. supplied by probing */ /** User application callbacks for NIC interrupts */ struct rte_eth_dev_cb_list link_intr_cbs; /** * User-supplied functions called from rx_burst to post-process * received packets before passing them to the user */ struct rte_eth_rxtx_callback *post_rx_burst_cbs[RTE_MAX_QUEUES_PER_PORT]; /** * User-supplied functions called from tx_burst to pre-process * received packets before passing them to the driver for transmission. */ struct rte_eth_rxtx_callback *pre_tx_burst_cbs[RTE_MAX_QUEUES_PER_PORT]; uint8_t attached; /**< Flag indicating the port is attached */ enum rte_eth_dev_type dev_type; /**< Flag indicating the device type */
};

rx_pkt_burst和tx_pkt_burst即指向该设备用于接收和发送报文的函数,在vhost PMD设备中注册如下:

eth_dev->rx_pkt_burst = eth_vhost_rx;
eth_dev->tx_pkt_burst = eth_vhost_tx;

完成设备的注册后,操作vhost PMD的端口与操作任何物理端口并无区别。如下代码即可完成一个简单的转发过程。

struct fwd_stream { portid_t    rx_port;    /* 接收报文的端口 */ queueid_t  rx_queue;  /* 接收报文的队列 */ portid_t    tx_port;    /* 发送报文的端口 */ queueid_t  tx_queue;  /* 发送报文的队列 */
};
struct fwd_stream *fs;
/* 从接收端口接收报文 */
nb_rx = rte_eth_rx_burst(fs->rx_port, fs->rx_queue, pkts_burst, nb_pkt_per_burst);
if (unlikely(nb_rx == 0)) return;
/* 从发送端口发送报文 */
nb_tx = rte_eth_tx_burst(fs->tx_port, fs->tx_queue, pkts_burst, nb_rx);
/* 若发送失败,则释放缓存区 */
if (unlikely(nb_tx < nb_rx)) { do { rte_pktmbuf_free(pkts_burst[nb_tx]); } while (++nb_tx < nb_rx);
} 

最终rte_eth_rx_burst和rte_eth_tx_burst通过设备的指针调用设备的rx_pkt_burst和tx_pkt_burst。


156.小结

virtio半虚拟化的性能优化不能仅仅只优化前端virtio或后端vhost,还需要两者同时优化,才能更好地提升性能。本章先介绍后端vhost演进之路,分析了各自架构的优缺点。然后重点介绍了DPDK在用户态vhost的设计思路以及优化点。最后,对如何使用DPDK进行vhost编程给出了简要示例。


系列文章

《《深入浅出DPDK》读书笔记(一):基础部分知识点》

《《深入浅出DPDK》读书笔记(二):网卡的读写数据操作》

《《深入浅出DPDK》读书笔记(三):NUMA - Non Uniform Memory Architecture 非统一内存架构》

《《深入浅出DPDK》读书笔记(四):并行计算-SIMD是Single-Instruction Multiple-Data(单指令多数据)》

《《深入浅出DPDK》读书笔记(五):同步互斥机制》

《《深入浅出DPDK》读书笔记(六):报文转发(run to completion、pipeline、精确匹配算法、最长前缀匹配LPM)》

《《深入浅出DPDK》读书笔记(七):PCIe与包处理I/O》

《《深入浅出DPDK》读书笔记(八):网卡性能优化(异步中断模式、轮询模式、混和中断轮询模式)》

《《深入浅出DPDK》读书笔记(九):流分类与多队列》

《《深入浅出DPDK》读书笔记(十):硬件加速与功能卸载(VLAN、IEEE1588、IP TCP/UDP/SCTP checksum、Tunnel)》

《《深入浅出DPDK》读书笔记(十一):DPDK虚拟化技术篇(I/O虚拟化、CPU虚拟化、内存虚拟化、VT-d、I/O透传)》

《《深入浅出DPDK》读书笔记(十二):DPDK虚拟化技术篇(半虚拟化Virtio)》

《《深入浅出DPDK》读书笔记(十三):DPDK虚拟化技术篇(加速包处理的vhost优化方案)》


相关阅读

DPDK PMD( Poll Mode Driver)轮询模式驱动程序

DPDK笔记 RSS(receive side scaling)网卡分流机制

《网卡多队列:RPS、RFS、RSS、Flow Director(DPDK支持)》

《Linux环境中的网络分段卸载技术 GSO/TSO/UFO/LRO/GRO》

《ARM SMMU原理与IOMMU技术(“VT-d” DMA、I/O虚拟化、内存虚拟化)》

《KVM Virtio: An I/O virtualization framework for Linux(Linux虚拟IO框架)》

《用QEMU构建嵌入式LINUX系统》

《Linux虚拟化KVM-Qemu分析(一)》

《Linux虚拟化KVM-Qemu分析(二)之ARMv8虚拟化》

《Linux虚拟化KVM-Qemu分析(三)之KVM源码(1)》

《Linux虚拟化KVM-Qemu分析(四)之CPU虚拟化(2)》

《在CentOS上进行虚拟化:QEMU、Xen、KVM、LibVirt、oVirt》

《ARM SMMU原理与IOMMU技术(“VT-d” DMA、I/O虚拟化、内存虚拟化)》

《OpenVZ,Xen,KVM等:虚拟化解决方案》

《KVM Virtio: An I/O virtualization framework for Linux(Linux虚拟IO框架)》

《深入浅出DPDK》读书笔记(十三):DPDK虚拟化技术篇(加速包处理的vhost优化方案)相关推荐

  1. 深入浅出数据分析读书笔记

    深入浅出数据分析读书笔记 一.数据分析引言 1.数据分析的基本流程:确定--分解--评估--决策.一个完整的分析项目可能经过多轮流程 确定:客户的论点和数据 分解:将手头的资料汇总为有用的格式 评估: ...

  2. Node.js: 深入浅出Nodejs读书笔记

    今天终于把朴灵老师写的<深入浅出Node.js>给学习 完了, 这本书不是一本简单的Node入门书籍,它没有停留在Node介绍或者框架.库的使用层面上,而是从不同的视角来揭示Node自己内 ...

  3. 深入浅出DPDK学习笔记——认识DPDK

    什么是DPDK? 对于用户来说, 它可能是一个性能出色的包数据处理加速软件库: 对于开发者来说, 它可能是一个实践包处理新想法的创新工场: 对于性能调优者来说, 它可能又是一个绝佳的成果分享平台.当下 ...

  4. 计算机图形学 读书笔记(八) 光线跟踪加速Ray Tracing Acceleration

    写个读书笔记,一来作为字典以后可以查,二来记录自己的理解. 并没有对每个知识点的详细解释,大部分只有主观的定性的解释. 光线跟踪受到的限制: 1.时间复杂度和空间复杂度都很高. 2.主要时间用在了可见 ...

  5. 《信息存储与管理》读书笔记7 存储虚拟化

    1.内存虚拟化 2.网络虚拟化 3.服务器虚拟化 4.存储虚拟化 1)块级存储虚拟化 2)文件级虚拟化 PS:详细内容请看虚拟化一板块 转载于:https://blog.51cto.com/nppst ...

  6. 深入浅出统计学读书笔记

    一.信息图形化 1.垂直条形图更常用.不过,如果类名称太长,水平条形图就有用了–你将有大量空白位置标识每个类的名称. 2.堆积条形图:针对每种游戏,用一条长方形代表这类游戏的满意玩家频数,用另一条长方 ...

  7. 《Programming in Lua 3》读书笔记(十三)

    日期:2014.7.16 PartⅡ 17 Weak Tables and Finalizers Lua实现的是自动的内存管理.程序可以创建对象,可是没有现成的函数来实现删除对象.Lua使用 garb ...

  8. 深入浅出MFC 读书笔记1

    第六章 MFC程序的生死因果 1.不同类的PreCreateWindow()成员函数都是在窗口产生之前一刻被调用,准备用来注册窗口类.如果我们指定的窗口类是NULL,那么就使用系统默认类. 第九章 消 ...

  9. 深入浅出Docker 读书笔记(九)

    第16章:企业版工具 Docker 企业版(Enterprise Edition,EE):企业需要 Docker 能实现私有化部署.这通常意味着 Docker 需要一个本地化部署方案,并且由企业自己掌 ...

最新文章

  1. 无法在指定计算机上定位,Win10电脑无法打开定位功能时启动GeolocationService服务提示找不到文件怎么办...
  2. centos 7下独立的python 2.7环境安装
  3. python telnetlib详解 执行循环命令_Python的Telnetlib read_until'#'或'&GT;',多发串的决心?...
  4. php如何新建xml文件,PHP新建XML打开XML读取XML怎么写
  5. ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER
  6. 函数重载与运算符重载
  7. 幼儿学数数的c语言程序,【资源学习】c语言程序代码,登录幼儿园200个小朋友的数据...
  8. 时间序列趋势判断(三)——Mann-Kendall趋势检验
  9. MySQL下载安装教程及Navicat安装教程
  10. 二项式展开 matlab,二项式造句_造句大全
  11. 团队协作神器,告别信息孤岛
  12. IJCAI-18 阿里妈妈搜索广告转化预测 Top2%思路
  13. python高级数据筛选的方法_使用python对多个txt文件中的数据进行筛选的方法
  14. 无法启动服务,原因可能是已被禁用或与其相关联的设备没有启动
  15. 西瓜书-2.5偏差与方差
  16. 遗传算法:交叉操作 Inver-over 算子
  17. 大菠萝 Pinia 持久化方案 Pinia Persist Own
  18. 在几何画板中如何制作圆柱的侧面展开动画_几何画板如何制作圆柱体形成的过程动画...
  19. 玩转全球最大同性交友网站--- 开源社区GitHub
  20. AndroidUtilCode 最强大的工具类

热门文章

  1. oracle11g db files,oracle11g整个DB迁移
  2. php写的注册登录系统吗,php注册登录系统简化版_php技巧
  3. 清除Docker中所有为<none>的镜像(虚悬镜像)
  4. Spring的消息 Java Message Service (JMS)
  5. [转]SQL中的case when then else end用法
  6. [转]Linux下显示硬件信息--lshw
  7. weiphp 简介--笔记
  8. postgreSQL源码分析——索引的建立与使用——GIN索引(2)
  9. devexpress 创建窗口句柄时出错_MATLAB函数句柄
  10. 4地形编辑插件_SketchUp高效建模插件