virtio系列-规范解读

11 篇文章 12 订阅

virtio

virtio 是一种 I/O 半虚拟化解决方案,是一套通用 I/O 设备虚拟化的程序,是对半虚拟化 Hypervisor 中的一组通用 I/O 设备的抽象。提供了一套上层应用与各 Hypervisor 虚拟化设备(KVM,Xen,VMware等)之间的通信框架和编程接口,减少跨平台所带来的兼容性问题,大大提高驱动程序开发效率。

历史背景

qemu支持多种设备,例如网卡有e1000,virtio等,其中e1000属于全虚拟化设备,它模拟了一个真实的硬件设备,提供的访问接口完全遵循硬件手册,当虚拟机使用全虚拟化设备时根本感知不到自身属于虚拟机。而virtio设备是一个专为虚拟化而设计的,早期并没有对应的硬件设备,需要重新编写驱动来使用这个设备,从而一定程度上根据驱动可以判定自身处于虚拟机环境中。相比于半虚拟化设备,访问完全仿真的硬件设备需要更多的陷入/陷出操作和更多的内存拷贝操作。早期的开发者Rusty Russell设计并实现了virtio,之后成为了virtio规范, 经历了0.95, 1.0, 1.1版本的演进。0.95之前称为传统virtio设备,1.0修改了一些PCI配置空间访问方式和virtqueue的优化和特定设备的约定,查看详情。之后虚拟化技术飞速发展,纯粹的virtio软件性能已经满足不了需要,有部分半导体厂商就开始将virtio固化成硬件来提升性能,发现原来为半虚拟化软件实现设计的virtqueue对硬件cache性能不友好,那么干脆找人修改规范,也就有了1.1版本中增加的packed virtqueue支持.

基于PCI的virtio设备

virtio设备可以是基于MMIO,PCI,Channel I/O,目前基于PCI实现最为广泛,下面也主要是基于PCI的virtio 设备解释规范。规范分为两部分,一部分是virtio设备规范,另外一部分是virtio设备接入系统的方式规范,层级关系为:
virtio pci layout

virtio设备本身的规范

1.1 status,可以判断当前的设备和驱动的状态,主要在驱动初始化时显示状态。
    ACKNOWLEDGE:guest已经识别到设备,准备匹配驱动了
    DRIVER:guest已经找到设备的驱动
    FEATURES_OK:驱动已经和设备协商好feature
    DRIVER_OK:驱动已经ready,可以工作了
    FAILED:驱动匹配过程中出错了
    DEVICE_NEEDS_RESET:设备需要重置
1.2 feature协商:device和driver各自有自己的feature集合,device向driver提供它支持的feature,driver读取device的feature并告诉device它支持的feature子集,这样不同版本的驱动和device可以互相兼容,找一个最大子集进行工作。
    0-23:device和driver自定义使用
    24-37:给queue和feature协商使用
    38+:目前是reserved状态,未使用
1.3 中断和notify
	device通过中断通知driver,driver通过notify通知device,还有一个是device配置空间改变时发送中断给driver
1.4 virtqueue
	设备和驱动数据通信的方式,每个设备包含1个和多个queue, 又称为vring:Descriptor table,available buffer table, used buffer table;

virtio PCI规范

PCI virtio设备的VID和PID:

Vendor ID:0x1AF4
Device ID: 0x1000- 0x107f;
	0x1000- 0x1040是legacy, 0x1040- 0x107f是modern,其中driver识别device ID, id - 0x1040就是传统的virtio device id. 例如网卡可以是0x1000也可以是0x1041

linux kernel中PCI总线通过VID和PID来匹配PCI驱动,通过VID判断是virtio设备,注册到virtio bus;virtio bus根据注册设备的PID来进一步匹配设备驱动

legacy的device id,在此基础上加0x40即是Modern PCI设备的PID,主要是两者规范不同且部分不兼容,在驱动中使用两种方式来操作virtio PCI设备:virtio_pci_modern.c, virtio_pci_legacy.c。

0x1000 network card
0x1001 block device
0x1002 memory ballooning (traditional)
0x1003 console
0x1004 SCSI host
0x1005 entropy source
0x1009 9P transport

virtio ring

split virtqueue

一个vring实际上就包含三个环形的buffer,因为存放的信息都不是数据而是元数据,所以换了个名称:descriptor table,available table,used table。

  1. 这些表只能被device或driver写,不能两个同时写,因为写要加锁,就需要临界区保护
  2. 通过读/写顺序保证,不会有两方同时读/写一块区域的情况,免除同步问题
  3. 允许读写同时操作的区域都是原子操作来保证数据的完整性
  4. 通过buffer的角度看,driver是生产者,device是消费者,无论是提供空数据的buffer让device写,还是提供数据buffer让device处理。buffer是有driver维护的,device只有读/写数据的权限,对buffer本身是没有任何主权的
  • descriptor table
    保存有buffer的信息,driver可写
  • available table
    保存有descriptor table的索引,间接指向buffer,提供buffer给device
  • used table
    保存有descriptor table的索引,间接指向buffer,指示已经消费完成的buffer

split virtqueue layout逻辑上三个表的关系如上图:

  1. 每个表的大小都是nr queue
  2. descriptor table中保存的信息buffer
  3. 如果一次插入多个多个buffer,buffer的数量不能超过queue的queue_size;有两种方式,如果支持INDIRECT特性,则会申请一个indirect descriptor buffer用来存储buffer的信息,descriptor table指向indirect descriptor table区域并且flag反映这种情况,置位INDIRECT位;如果不支持INDIRECT特性则插入多个descriptor的信息,并置位NEXT位flag表示后续还有buffer,最后一个descriptor table项没有NEXT;两者方式目前不会混用,优先使用indirect descriptor table方式。
  4. 如果device可写则置位WRITE flag,表示device生产数据,driver消费数据;否则表示device只读,driver生产数据
通知优化

但是在实际使用时,三张表是放在一起的,还额外加了一些东西来优化通知和中断.首先是feature协商过程中,双方是否支持VIRTIO_RING_F_EVENT_IDX特性,如果不支持则每次操作available/used table时都需要通知对方。
如果支持该特性,则每次VM都会发布以下它期望收到中断的used index,只有当host满足条件时才会投递中断;同样的,host发布它期望收到notify的available index, VM每次通知时检查是否满足条件才决定是否通知host。

packed virtqueue

TODO

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值