通过MMIO的方式实现VIRTIO-BLK设备(一)
背景知识
什么是VIRTIO
使用完全虚拟化,Guest不加任何修改就可以运行在任何VMM上,VMM对于Guest是完全透明的。但每次I/O都将导致CPU在Guest模式与Host模式间切换,在I/O操作密集时,这个切换是影响虚拟机性能的一个重要因素。对于通过软件方式模拟的虚拟化而言,完全可以制定一个更加高效简洁地适用于软件模拟环境下的驱动和模拟设备交互的标准,于是Virtio诞生了。与完全虚拟化相比,使用Virtio标准的驱动和模拟设备的交互不再使用寄存器等传统的I/O方式,而是采用了Virtqueue的方式传输数据。这种设计降低了设备模拟器实现的复杂度,I/O不再受数据总线宽度、寄存器宽度等因素的影响,一次I/O传递的数据量不受限制,减少了CPU在Guest模式与Host模式之间的切换,提高了虚拟化的性能。
作为一个统一的标准,越来越多的操作系统(例如Linux与Windows)已经提供了对Virtio的支持。
本文将根据Virtio1.0文档讲述VIRTIO的实现。
Dirver 与 Device
在VIRTIO中,Driver实现在虚拟机,是VIRTIO的前端;Device实现在虚拟机监控器,是VIRTIO的后端。
描述符表
Virtqueue是VIRTIO中数据传输的载体,是VIRTIO的核心部分。Virtqueue主要包括三个部分,分别是描述符表(Descriptor Table),可用描述符区域(Available Ring),已用描述符区域(Used Ring)。
VIRTIO要求描述符表,可用描述符表,已用描述符表分别在GPA上连续。
这三个表可以通过下图表示:
从左到右依次是,可用描述符表,描述符表,已用描述符表。
描述符表
描述符表是Virqqueue的核心,它包括Queue Size个描述符(Queue Size由driver决定,必须是2的指数,它的值保存在QueueSize寄存器上)。
每个描述符会指向一块共享内存,如果这块内存是驱动写给设备的数据,则称这个描述符为out类型的,如果这块内存是设备写给驱动的数据,则称这个描述符为in类型。
描述符并不是单独存在的,它们可以通过指针组成描述符链,一个描述符表中会有多条描述符链,一条描述符链记录一次I/O事件。
描述符有4个字段,如上图所示。描述符通过addr指向一块保存有I/O数据的共享内存,需要注意的是addr保存的是GPA,当后端需要根据通过addr读写该块共享内存时,需要视虚拟机监控器的实现将GPA转换成HVA或者HPA。len表示该块共享内存的长度。flags标识了描述符的属性,当flags * F_NEXT成立,则描述符可以通过next指向下一个描述符;当flags * F_WRITE成立,则这个描述符属于in类型,否则则是out类型;当flags * F_INDIRECT成立时,共享内存上将不是直接保存数据,而是保存一连串描述符。next指针则指向描述符链中的下一个描述符。
可用描述符表
driver将数据写入描述符记录的共享内存后,需要让device知道哪些描述符可以消费(可用)。可用描述符负责完成这个任务。
可用描述符中的ring是一个数组,因virtqueue中最多可能有Queue Size可用描述符链,ring的大小是Queue Size。ring中每个元素都记录了对应的描述符链的第一个描述符的ID,因此ring中一个元素对应一条描述符链,也即对应一次I/O事件。可用描述符中idx变量记录的是driver下一个填充的可用描述符,与之对应的是device将在变量last_avail_idx中记录上一个处理完的可用描述符,因此在last_avail_idx到idx之间是等待device处理的可用描述符。
已用描述符表
device将已经处理好的IO请求对应的描述符记录在已用描述符中,从这里可以看出,可用,已用这两个概念都是对device而言的。一个需要注意的点是,可用描述符和已用描述符都是指向描述符链,它们只是说明该条描述符链的状态,并不是代表描述符链的in/out类型。
与可用描述符不同的是,已用描述符的数组中每个元素的大小是8byte,它不仅记录了描述符链第一个描述符的ID,还记录了device向描述符链中写入的byte数。已用描述符通过idx和last_used_idx记录了等待driver回收的描述符链。idx由设备维护,表示设备下一个处理完的描述符链将记录在已用描述符表中的位置,last_used_idx由驱动维护,记录的是驱动上一个回收的描述符链在已用描述符表中的位置。
VIRTIO MMIO寄存器(部分
在MMIO实现VIRTIO的情况下,每个VIRTIO设备都有一个MMIO REGION。这个REGION在设备树中的声明如图:
上图表示GPA 0x1e000 到 0x1e200 的地址段是这个virtio_block设备的MMIO REGION。这个REGION中分布着VIRTIO MMIO寄存器。
42是这个virtio设备对应的中断号,注意42需要加上SPI中断的基础值:32,因此这个virtio driver实际上能够识别的中断号是74。
一些重要的MMIO 寄存器如下:
DeviceFeatures & DeviceFeaturesSel
设备通过DeviceFeatures寄存器告诉驱动设备支持的一些机制,比如VIRTIO_RING_F_INDIRECT_DESC这个bit就是告诉driver:device支持virtqueue通过indirection扩大共享内存区域。driver只能够在device提供的机制上工作,不能够在device没有提供该机制的情况下运行对应的代码。由于DeviceFeatures的区域要大于4bytes,driver需要通过DeviceFeaturesSel寄存器用查看DeviceFeatures的部分bits。
DriverFeatures & DriverFeaturesSel
驱动通过DriverFeatures寄存器告诉设备,驱动支持了设备的哪些机制,DriverFeaturesSel则用于设备查看DriverFeatures。
QueueSel
对于某些virtio设备,比如virtio-net,virtio-console会包括多个virtqueue。为了让设备知道该对在哪条virtqueue上进行处理,driver会通过QueueSel寄存器告诉驱动后续的操作是在哪条virtqueue进行的。
QueueReady
driver会通过写QueueReady寄存器通知设备,当前virtqueue已经初始化好了,设备可以通过读描述符寄存器来获得virtqueue的地址。
QueueNotify
当driver准备了新的可用描述符时,会通过写QueueNotify寄存器通知device进行处理。
InterruptStatus
Virtio设备可以通过发送中断通知虚拟机,每个virtio设备有一个对应的中断号(这个中断号在设备树中声明),虚拟机在收到中断后,会根据中断号找到对应的driver,driver则需要通过InterruptStatus寄存器搞清楚产生这次中断的事件是什么,比如bit 0表示已用描述符更新,bit 1表示设备配置空间更新。
QueueDescLow & QueueDescHigh
driver通过写这两个寄存器告诉device描述符表的GPA。由于每个MMIO寄存器只有32个bit,因此需要两个寄存器。
QueueAvailLow & QueueAvailHigh
driver通过写这两个寄存器告诉device可用描述符表的GPA。
QueueUsedLow & QueueUsedHigh
driver通过写这两个寄存器告诉device已用描述符表的GPA。
Config
Config不是一个寄存器,而是一个区域,这个区域由device进行配置,每种device会有不一样的配置区域。
下图展示的就是block设备配置空间的数据结构。
参考资料
《深度探索Linux系统虚拟化:原理与实现》
《Virtual I/O Device Version 1.0》
《Linux虚拟化KVM-Qemu分析(十一)之virtqueue》
https://github.com/minosproject/minos/
下一期将介绍实现VIRTIO-BLK设备时,虚拟机image,rootfs,dtb文件的制作
通过MMIO的方式实现VIRTIO-BLK设备(一)的更多相关文章
- Virtio SCSI设备介绍
Qemu的存储栈 在KVM虚拟化环境中,当客户机的内核存储系统像在物理机上一样通过页缓存.文件系统.通用块设备层运行到实际设备驱动时,这时驱动对设备寄存器的访问会触发CPU从客户机代码切换到物理机内的 ...
- 每日技术总结:vue router传参方式,js获取设备高度
今天貌似没什么问题,23333…… 1.vue router 路由传参的方式 应用情景:从分类页(category.vue)进入商品列表页(list.vue),需要传递商品分类id(catId),商品 ...
- Tcp方式采集CNC兄弟设备数据
先说下为了采集CNC兄弟设备的数据可谓是一波三折. 因为首次接触brother设备(CNC)是直接在设备上设置IP.用户名.密码,然后直连PC,用Ftp可以查看和下载CNC brother设备里的数据 ...
- 三种方式设置特定设备UWP XAML view
开发者可以设置UWP特定设备xaml view,在桌面,手机,Iot,这个对于设置对不同设备的不同屏幕有用.我们可以使用RelativePanel,VisualStateTriggers,但是这样我们 ...
- 基于Linux的USB 主/从设备之间通讯的三种方式
转载:http://archive.eet-china.com/www.eet-china.com/ART_8800323770_617693_TA_eda530e7.HTM 随着简单易用的USB接口 ...
- 计算机组成原理——I/O接口以及I/O设备数据传送控制方式
接口可以看作是两个部件之间交接的部分.硬件与硬件之间有接口,硬件与软件之间有接口,软件与软件之间也有接口. 这里我们所说的I/O接口,一边连接着主机,一边连接着外设. I/O接口的功能 I/O接口的基 ...
- 5-(微信小程序篇)关于WiFi模块配网以后利用小程序绑定设备,绑定方式说明
https://www.cnblogs.com/yangfengwu/p/11625189.html 众所周知:使用微信Airkiss 只能给设备配网,并不能够获取设备的MAC地址信息,但是我在 ht ...
- 2018-8-10-三种方式设置特定设备UWP-XAML-view
title author date CreateTime categories 三种方式设置特定设备UWP XAML view lindexi 2018-08-10 19:16:52 +0800 20 ...
- iNeuOS工业互联网操作系统,发布实时存储方式:实时存储、变化存储、定时存储,增加设备振动状态和电能状态监测驱动,v3.6.2
目 录 1. 概述... 1 2. 平台演示... 2 3. 存储方式... 2 4. 设备状态和用电状态监控驱动... 3 1. 概述 本次升 ...
随机推荐
- 大对象数据LOB的应用(Day_10)
当你有永不放弃的精神,全力以赴的态度,你会惊叹自己也能创造奇迹! LOB数据类型概述 由于于无结构的数据往往都是大型的,存储量非常大,而LOB(large object)类型主要用来支持无结构的大型数 ...
- 技术干货 | 关于 WKWebview 网络拦截,你想知道的都在这里
原生 WKWebView 在独立于 app 进程之外的进程中执行网络请求,请求数据不经过主进程,因此在 WKWebView 上直接使用 NSURLProtocol 是无法拦截请求的. 但是由于 mPa ...
- 西门子 S7200 以太网模块连接组态王方法
北京华科远创科技有限研发的远创智控ETH-YC模块,以太网通讯模块型号有MPI-ETH-YC01和PPI-ETH-YC01,适用于西门子S7-200/S7-300/S7-400.SMART S7-20 ...
- 企业微信三种token
http://www.upwqy.com/doc/28.html 基本配置介绍 区分三种类型access_token 服务商的token 说明:以corpid(服务商CorpID).provider_ ...
- ADAS测试
ADAS测试 1. ADAS和自动驾驶测试 AD和高级驾驶辅助系统(ADAS)正在不断增加新的雷达.摄像头.激光雷达和GNSS传感器,甚至也在改变 ...
- YOLOvi(i=1,2,3,4)系列
YOLOvi(i=1,2,3,4)系列 YOLOv4论文链接:https://arxiv.org/pdf/2004.10934.pdf YOLOv4源码链接:https://github.com/Al ...
- 自动调试用于移动GPU的卷积网络
自动调试用于移动GPU的卷积网络 对特定设备进行自动调试对于获得最佳性能至关重要.这是有关如何调试整个卷积网络的说明文档. TVM中Mobile GPU的算子实现以模板形式编写.模板具有许多可调旋钮( ...
- 24GHz和77GHz毫米波雷达技术细节
24GHz和77GHz毫米波雷达技术细节 FMCW Radar Sensitivity Measurement Tech Field Test and Raw Data Analysis Capabi ...
- Spring Cloud系列(四):断路器Hystrix
上一篇介绍了负载均衡的配置方法,做负载均衡是为了保证高可用性,但是有时候服务提供者挂掉了,比如服务A调用了服务B,服务B又调用了服务C,然后有一天服务C的所有节点都挂掉了,这时服务B就会因为C异常而在 ...
- MySQL:count()用法
语法 COUNT() 函数返回匹配指定条件的行数. COUNT(column_name) 语法 COUNT(column_name) 函数返回指定列的值的数目(NULL 不计入): SELECT CO ...