消息機制
當使用vhost-user時,需要在系統中創建一個unix domain socket server,用來處理qemu發送給host的消息。
如果有新的socket連接,說明guest創建了新的virtio-net設備,vhost驅動會爲之創建一個vhost設備,之後qemu就可以通過socket和vhost進行通信了;當socket關閉,vhost就會銷燬對應的設備。
常用的消息包括:
//driver\net\virtio\virtio_user\vhost_kernel.c
/* vhost kernel ioctls */
#define VHOST_VIRTIO
/*返回vhost支持的virtio-net功能子集*/
#define VHOST_GET_FEATURES
/*檢查功能掩碼,設置vhost和virtio前端共同支持的特性,需要兩者同時支持才能生效*/
#define VHOST_SET_FEATURES
/*將設備設置爲當前進程所有*/
#define VHOST_SET_OWNER
/*當前進程釋放對設備的所有權*/
#define VHOST_RESET_OWNER
/*設置內存空間佈局信息,用於報文收發時的地址轉換*/
#define VHOST_SET_MEM_TABLE
/*下面兩個宏,用於guest在線遷移*/
#define VHOST_SET_LOG_BASE
#define VHOST_SET_LOG_FD
/*vhost記錄每個虛擬隊列的大小*/
#define VHOST_SET_VRING_NUM //設置vring size
/*由qemu發送virtqueue結構的虛擬地址。vhost將該地址轉換成vhost的虛擬地址。*/
#define VHOST_SET_VRING_ADDR
/*傳遞初始索引值,vhost通過該索引值找到初始描述符*/
#define VHOST_SET_VRING_BASE //VirtQueue.last_avail_idx
/*將虛擬隊列的當前可用索引值發送給qemu*/
#define VHOST_GET_VRING_BASE
/*傳遞eventfd文件描述符。當guest有新的數據要發送時,通過該文件描述符通知vhost接收數據併發送到目的地;vhost使用eventfd代理模塊把這個文件描述符從qemu上下文切換到自己的進程上下文*/
#define VHOST_SET_VRING_KICK //設置kick fd (guest -> vhost)
/*也是用來傳遞eventfd文件描述符。使vhost能夠在完成對新的數據包接收時,通過中斷方式通知guest準備接收數據包。使用eventfd代理模塊把這個文件描述符從qemu上下文切換到自己的進程上下文*/
#define VHOST_SET_VRING_CALL //設置call fd (vhost -> guest)
/*代碼中僅有定義,未使用*/
#define VHOST_SET_VRING_ERR
/*用來支持virtio-user*/
#define VHOST_NET_SET_BACKEND
地址轉換和內存映射
virtqueue和vring進行數據交換的核心是使用一種機制將數據緩衝區實現對guest和host同時可見,從而通過避免數據的拷貝來消耗性能。dpdk vhost在這裏使用的是大頁內存、內存映射以及相應的地址轉換來完成這個功能的。
因此,host端必須由足夠的大頁空間,同時需要指定內存預分配。爲了vhost能訪問virtqueue和數據包緩衝區,所有的描述符表、環表地址,其所在頁面必須被映射到vhost的進程空間中。
vhost在收到VHOST_SET_MEM_TABLE消息後,會使用消息中的內存分佈表來完成內存映射工作:
/*下面的兩個數據結構記錄guest的物理地址及偏移量*/
/**
* Information relating to memory regions including offsets to
* addresses in QEMUs memory file.
*/
struct rte_vhost_mem_region {
uint64_t guest_phys_addr;
uint64_t guest_user_addr;
uint64_t host_user_addr;
uint64_t size;
void *mmap_addr;
uint64_t mmap_size;
int fd;
};
/**
* Memory structure includes region and mapping information.
*/
struct rte_vhost_memory {
uint32_t nregions;
struct rte_vhost_mem_region regions[];
};
/*
*將 QEMU virtual address 轉化成 Vhost virtual address. 該函數用來將ring address
* 轉換成host端的virtual address
*/
static uint64_t
qva_to_vva(struct virtio_net *dev, uint64_t qva)
{
struct rte_vhost_mem_region *reg;
uint32_t i;
/* Find the region where the address lives. */
for (i = 0; i < dev->mem->nregions; i++) {
reg = &dev->mem->regions[i];
if (qva >= reg->guest_user_addr &&
qva < reg->guest_user_addr + reg->size) {
return qva - reg->guest_user_addr +
reg->host_user_addr;
}
}
return 0;
}
virtio-net 設備管理
一個virtio-net設備的生命週期包括設備創建、配置、服務啓動和設備銷燬幾個階段。
- 設備創建
vhost-user通過socket連接來創建。當創建一個virtio-net設備是,需要
分配新的virtio-net設備結構,並添加到設備鏈表中
爲該設備分配一個處理處理核並添加設備到數據面的鏈表中
在vhost上分配一個爲virtio-net設備服務的RX\TX隊列
- 配置
利用VHOST_SET_VRING_*消息通知vhost虛擬隊列的大小、基本索引和位置,vhost將虛擬隊列映射到自己的虛擬地址空間
- 服務啓動
vhost利用VHOST_SET_VRING_KICK消息來啓動虛擬隊列服務。之後,vhost便可以輪詢接收隊列,並將數據放到virtio-net設備的接收隊列上。同時,也可以輪詢發送虛擬隊列,查看是否有待發送的數據包,如果有,則將其複製到發送隊列中。
- 設備銷燬
vhost利用VHOST_GET_VRING_BASE消息來通知停止提供對接收隊列和發送虛擬隊列的服務。同時,分配給virtio-net設備的處理和和物理網卡上的RX和TX隊列也將被釋放。
設置使能特性
/*顯式設置支持新特性*/
int rte_vhost_driver_set_features(const char *path, uint64_t features)
/*使能相關特性*/
int rte_vhost_driver_enable_features(const char *path, uint64_t features)
/*去使能相關特性*/
int rte_vhost_driver_disable_features(const char *path, uint64_t features)
以上的操作都是針對socket->features做軟件特性的設置,原理大同小異;這些接口可以用來在driver註冊後,對該driver的特性進行微調。
比如當支持mergeable特性時,可以調用rte_vhost_driver_enable_features(file,1ULL << VIRTIO_NET_F_MRG_RXBUF)來進行設置。 當前支持的特性包括:
/* The feature bitmap for virtio net */
#define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */
#define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */
#define VIRTIO_NET_F_MTU 3 /* Initial MTU advice. */
#define VIRTIO_NET_F_MAC 5 /* Host has given MAC address. */
#define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in. */
#define VIRTIO_NET_F_GUEST_TSO6 8 /* Guest can handle TSOv6 in. */
#define VIRTIO_NET_F_GUEST_ECN 9 /* Guest can handle TSO[6] w/ ECN in. */
#define VIRTIO_NET_F_GUEST_UFO 10 /* Guest can handle UFO in. */
#define VIRTIO_NET_F_HOST_TSO4 11 /* Host can handle TSOv4 in. */
#define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in. */
#define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */
#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */
#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */
#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */
#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */
#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */
#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */
#define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */
#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Guest can announce device on the
* network */
#define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow
* Steering */
#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */
/* Do we get callbacks when the ring is completely used, even if we've
* suppressed them? */
#define VIRTIO_F_NOTIFY_ON_EMPTY 24
/* Can the device handle any descriptor layout? */
#define VIRTIO_F_ANY_LAYOUT 27
/* We support indirect buffer descriptors */
#define VIRTIO_RING_F_INDIRECT_DESC 28
#define VIRTIO_F_VERSION_1 32
#define VIRTIO_F_IOMMU_PLATFORM 33