主要查看一下virtio-user的工作思路,个人觉得他主要是用来替换KNI或者OVS的TAP设备,更好的用法应该是给container来用,主要是通过操作/dev/vhost-net创建kernel的tap设备用,然后kernel和virtio-user进行内存映射,便于收发报文。

| 更正一下:目前来说container用virtio-user还不太合适,因为如果TAP设备加入到namespace的话,重启virtio-user就因为在当前的宿主机找不到TAP设备,会生成一个新的TAP设备。这块目前没有解决。

官方图

请输入图片描述

以上就是virtio-user的结构图,我们可以看到virtio-user驱动会在内核生成一个tap设备,然后另一端连接DPDK,报文通过内核发送到用户态。

virtio-user创建tap端口流程

接下来详细看一下,函数路径如下,就是初始化的时候,根据参数设置初始化虚拟设备。

rte_eal_init-->rte_bus_probe-->vdev_probe-->vdev_probe_all_drivers-->virtio_user_pmd_probe主要是两个操作

  • 首先会解析一些参数,用来配置tap口,支持的参数为queues,cq,mac,path,queue_size,iface,这里面path是必备的,但是mac和iface我们可能也常用。
  • 如果进程是primary的话,调用virtio_user_dev_init初始化设备
  • 如果进程是secondary的话,调用rte_eth_dev_attach_secondary绑定设备,这里所做的就是简单的获取端口,默认就是由primary进行端口的配置了。
  • eth_virtio_dev_init初始化vhost-user或者vhost-net设备

virtio_user_dev_init-->virtio_user_dev_setup主要是根据支持对端为vhost-user和vhost-net,并且配置通知和中断,另外我们接下来主要关心一下vhost-net。

virtio_user_dev_setup

  • 当是vhost-user时,得到vhost-user相关的ops,vhost-net时,得到kernel相关的vhost的ops,接下来就看kernel的vhost-net,不管vhost-user。
  • 首先申请vhostfds和tapfds的内存相关空间。
  • 调用vhost_kernel_setup打开相关的设备,比如我们打开的是dev/vhost-net
  • 调用virtio_user_dev_init_notify去初始化通知。
  • 调用virtio_user_fill_intr_handle创建中断。

eth_virtio_dev_init

  • virtio_init_device初始化设备
  • rte_intr_callback_register注册中断回调

virtio_init_device

  • vtpci_reset重置设备
  • vtpci_set_status告诉host,注意到设备,并且知道怎么去驱动它了
  • virtio_alloc_queues为设备创建队列
  • virtio_configure_intr配置中断
  • vtpci_reinit_complete实际调用vtpci_set_status告诉host,驱动完成。

vtpci_set_status-->virtio_user_set_status

  • 如果驱动完成了,调用virtio_user_start_device驱动设备
  • 如果是重置标志,调用virtio_user_reset重置设备
  • 如果其他状态,则将状态记录在设备中

virtio_user_start_device

  • virtio_user_queue_setup调用virtio_user_create_queue,最终调用ioctl告知kernel的vhost,创建队列。
  • dev->ops->send_request调用vhost_kernel_ioctl配置共享内存
  • virtio_user_queue_setup调用virtio_user_kick_queue配置队列的vring
  • dev->ops->enable_qp调用vhost_kernel_enable_queue_pair创建并且配置tap设备

vhost_kernel_enable_queue_pair

  • vhost_kernel_open_tap创建tap设备,并且进行配置。
  • vhost_kernel_set_backend配置tap设备

数据传递流程

Kernel部分

接下来我们看一下数据的传递路径,主要看一个流向,就是pktgen通过tap口进行流量发送,然后最终会传递给virtio-user的接收。pktgen发送直接调用的是tap的ndo_start_xmit,即tun_net_xmit

tun_net_xmit-->vhost_poll_wakeup-->vhost_poll_queue-->vhost_work_queue-->wake_up_process会唤醒vhost_worker

vhost_worker-->handle_rx_kick-->handle_rx-->tun_recvmsg-->tun_do_read会进行报文的收取,然后发送到用户态,而virtio-user处会轮询收取报文,所以我们略过了消息通知机制,其实是我没有找到。

DPDK部分

rte_eth_rx_burst-->virtio_recv_pkts_vec会从相应的队列中读取数据。

目前问题

  • virtio-user创建的tap设备是down的状态,需要手动up,up之前,virtio-user是不知道设备是down的,如果有别的类型的端口的报文,可能就会向virtio-user端口转发。最后就是tap设备up或者down的状态目前还不能通知virtio-user。
  • mvs重启后tap设备丢失,tap设备重新生成后,是down状态,并且Mac会变化,创建时并不能指定,tap设备丢失的问题会在DPDK-18.05中加入,但是这个功能稳定性和可用性还需要再验证。
  • virtio-user创建的tap设备的mac地址不能指定。
最后修改:2021 年 08 月 20 日
如果觉得我的文章对你有用,请随意赞赏