Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >virtio详细介绍和1.1新功能

virtio详细介绍和1.1新功能

原创
作者头像
惠伟
修改于 2021-04-28 02:18:59
修改于 2021-04-28 02:18:59
4.4K01
代码可运行
举报
文章被收录于专栏:虚拟化笔记虚拟化笔记
运行总次数:1
代码可运行

virtio是一种实践出来的技术,并且最终标准化,virtio是一种通用的虚拟化设备模拟标准,得到了大部分guest操作系统和hypervisor的支持,方便guest操作系统和hypervisor之间任意互相匹配。virtio出现之前hypervisor各有各的IO设备模拟方案,并在guest操作系统中大量合入驱动代码,导致一片混乱,后来xen中出来了部分virtio思想,在kvm中实现并且发扬光大,发表了论文《virtio: Towards a De-Facto Standard For Virtual I/O Devices》,论文促使virtio形成了正式标准。virtio标准最早是0.9.5版本(Virtio PCI Card Specification Version 0.9.5),于2012年形成了draft,并没有正式发布,继续发展,2016年发布了1.0版本(Virtual I/O Device (VIRTIO) Version 1.0),2019年发布了1.1版本(Virtual I/O Device (VIRTIO) Version 1.1)。

virtio详细介绍

virtio分为driver和device,driver部分运行于guest操作系统中,device部分运行于hypervisor中,driver和device是生产者和消费者模式动作,driver生产内存,device消费内存。不同virtio版本之间是互相兼容的,driver和device版本不同也可以互相运转。

基本要素

  • device status field

driver发现了device,driver可以正常驱动device,driver或者device出错了,driver或者device要进行reset。

  • device feature bit

driver和device协商feature以便于不同virtio版本之间兼容。

  • notification

driver和device互通通知对方,driver生产好的内存要通知device去消费,device消费完了要通知driver回收内存。

driver通知deivce用doorbell机制,在kvm中是写寄存器,kvm进行拦截再通知vhost。

device通知driver用中断机制,在kvm中是中断注入。

  • config space

典型的如virtio-net-device的MAC地址/MTU/最大支持队列数等。

  • virtqueue

每个virtqueue分成这三部分,descriptor/available/used,descriptor/available/used就是三个大数组,descriptor数组内容存放真正东西,available和used数组内容存放descriptor数组的下标。driver生产内存,把生产的内存地址和长度写在descriptor,然后把descriptor数据下标写到available数组中,通知device,device消费内存,消费完再把descriptor的数据下标定到used数组中,通知driver进行内存回收。

chained descriptor,几个desciptor项连在一起,适用于scater-gather。

indirect descriptor,主descriptor中一项指向另一个descriptor数组。

一般设备的virtqueue基本可以分三类rx virtqueue/tx virtqueue/ctrl virtqueue,rx virtqueue和tx virtqueue用于进行IO,driver通过ctrl virtqueue控制device。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* Virtio ring descriptors: 16 bytes.  These can chain together via "next". */
struct vring_desc {
    /* Address (guest-physical). */
    __virtio64 addr;
    /* Length. */
    __virtio32 len;
    /* The flags as indicated above. */
    __virtio16 flags;
    /* We chain unused descriptors via this, too */
    __virtio16 next;
};
 
struct vring_avail {
    __virtio16 flags;
    __virtio16 idx;
    __virtio16 ring[];
};
 
/* uint32_t is used here for ids for padding reasons. */
struct vring_used_elem {
    /* Index of start of used descriptor chain. */
    __virtio32 id;
    /* Total length of the descriptor chain which was used (written to) */
    __virtio32 len;
};
 
typedef struct vring_used_elem __attribute__((aligned(VRING_USED_ALIGN_SIZE)))
    vring_used_elem_t;
 
struct vring_used {
    __virtio16 flags;
    __virtio16 idx;
    vring_used_elem_t ring[];
};

used和avaible不一样是因为rx时,device给driver写数据,device写多少长度数据要给driver反回去。

初始化

device准备,driver发现device,状态更新和feature协商,driver分配virtqueue,把virtqueue地址告诉device。

承载

首先virtio设备是IO设备,IO设备得以某种方式和CPU内存联结在一起,IO设备还得以某种方式和内存交互数据,IO设备还得提供一种机制让CPU控制IO设备。

virtio标准中有三种承载机制,分别是pci,mmio和channel i/o,pci是最通用的计算机bus,qemu和kvm能很好的模拟pci bus,mmio主要用于嵌入式设备,这些设备没有pci bus,channel i/o用于一些IBM机器,很少见。这里以最常见的pci来说,它的作用就是让driver正常发现device,让driver有方法控制device,如写pci配置空间,写pci bar空间。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef struct VirtIOPCIRegion {
    MemoryRegion mr;
    uint32_t offset;
    uint32_t size;
    uint32_t type;
} VirtIOPCIRegion;
 
typedef struct VirtIOPCIQueue {
  uint16_t num;
  bool enabled;
  uint32_t desc[2];
  uint32_t avail[2];
  uint32_t used[2];
} VirtIOPCIQueue;
 
struct VirtIOPCIProxy {
    PCIDevice pci_dev;
    MemoryRegion bar;
    union {
        struct {
            VirtIOPCIRegion common;
            VirtIOPCIRegion isr;
            VirtIOPCIRegion device;
            VirtIOPCIRegion notify;
            VirtIOPCIRegion notify_pio;
        };
        VirtIOPCIRegion regs[5];
    };
    MemoryRegion modern_bar;
    MemoryRegion io_bar;
    uint32_t legacy_io_bar_idx;
    uint32_t msix_bar_idx;
    uint32_t modern_io_bar_idx;
    uint32_t modern_mem_bar_idx;
    int config_cap;
    uint32_t flags;
    bool disable_modern;
    bool ignore_backend_features;
    OnOffAuto disable_legacy;
    uint32_t class_code;
    uint32_t nvectors;
    uint32_t dfselect;
    uint32_t gfselect;
    uint32_t guest_features[2];
    VirtIOPCIQueue vqs[VIRTIO_QUEUE_MAX];
 
    VirtIOIRQFD *vector_irqfd;
    int nvqs_with_notifiers;
    VirtioBusState bus;
};

VirtIOPCIProxy存储virtio信息,kvm给guest注册了很多memory region,driver写这些memory region,kvm拦截,把写的值放在VirtIOPCIProxy中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy,
                                           const char *vdev_name)
{
    static const MemoryRegionOps common_ops = {
        .read = virtio_pci_common_read,
        .write = virtio_pci_common_write,
        .impl = {
            .min_access_size = 1,
            .max_access_size = 4,
        },
        .endianness = DEVICE_LITTLE_ENDIAN,
    };
    g_string_printf(name, "virtio-pci-common-%s", vdev_name);
    memory_region_init_io(&proxy->common.mr, OBJECT(proxy),
                          &common_ops,
                          proxy,
                          name->str,
                          proxy->common.size);
}
static void virtio_pci_common_write(void *opaque, hwaddr addr,
                                    uint64_t val, unsigned size)
{
    VirtIOPCIProxy *proxy = opaque;
    VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
 
    switch (addr) {
    case VIRTIO_PCI_COMMON_DFSELECT:
        proxy->dfselect = val;
        break;
    case VIRTIO_PCI_COMMON_GFSELECT:
        proxy->gfselect = val;
        break;
    
    default:
        break;
    }
}

设备分类

virtio分为很多设备类型virtio-net/virtio-blk/virtio-scsi等等,virtqueue实现通用部分,每种设备再实现具体功能部分,可以扩展feature部分,在virtqueue传输的数据中定义自己功能相关标准等。

举例分析

以qemu中实现的virtio-net-pci举例来说

首先它是一个virtio-net类型设备,其次它承载在pci上,所以VirtIONetPCI就把两者结合起来了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct VirtIONetPCI {
    VirtIOPCIProxy parent_obj;
    VirtIONet vdev;
};

virtqueue实现了数据共享,它并不关心到底是网络还是存储数据,所以要在它的buf最前面加上设备类型自己的元数据头,virtio-net-pci用了virtio_net_hdr。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* This header comes first in the scatter-gather list.
 * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must
 * be the first element of the scatter-gather list.  If you don't
 * specify GSO or CSUM features, you can simply ignore the header. */
struct virtio_net_hdr {
    /* See VIRTIO_NET_HDR_F_* */
    uint8_t flags;
    /* See VIRTIO_NET_HDR_GSO_* */
    uint8_t gso_type;
    __virtio16 hdr_len;     /* Ethernet + IP + tcp/udp hdrs */
    __virtio16 gso_size;        /* Bytes to append to hdr_len per frame */
    __virtio16 csum_start;  /* Position to start checksumming from */
    __virtio16 csum_offset; /* Offset after that to place checksum */
};

再看virtio-net-pci ctrl virtqueue传输的数据内容,基本就是打开网卡混杂模式/修改MAC/virtqueue个数/配置rss/配置offload等。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
 * Control virtqueue data structures
 *
 * The control virtqueue expects a header in the first sg entry
 * and an ack/status response in the last entry.  Data for the
 * command goes in between.
 */
struct virtio_net_ctrl_hdr {
    uint8_t class;
    uint8_t cmd;
} QEMU_PACKED;

virtio1.1新功能

virtio 1.0存在的问题第一是性能不高,第二是硬件不太好实现。

driver和device运行在不同的cpu,driver和device共享内存,存在不同cpu之间互相通知进行cache刷新的问题,virtio1.0 virtqueue分成三个数组,三个数组分布在不同的cacheline上需要多次cache刷新,所以virtio 1.1引入了packed ring,把virtio 1.0中的三个数组合并成一个,这样大大减少了cache刷新的次数。具体做法就是packed virtqueue把available和used当成descriptor中flag字段两个bit,driver本地存放一个driver_local_bit,把available_bit=driver_local_bit和used_bit=!driver_local_bit,device本地存放一个device_local_bit,消费完内存后used_bit=device_local_bit。

通知是有开销的,virtio1.1 batch和in-order减少driver和device互相通知对方的次数,batch就是driver一次多生产几块内存,再通知device,in-order就是device按driver生产内存的顺序消费内存,消费完后只通知driver最后一块内存可以回收了,因为严格按顺序消费的,driver由此可知前面的内存也已经消费完了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct vring_packed_desc {
    /* Buffer Address. */
    uint64_t addr;
    /* Buffer Length. */
    uint32_t len;
    /* Buffer ID. */
    uint16_t id;
    /* The flags depending on descriptor type. */
    uint16_t flags;
};

硬件实现也一样,driver写descriptor发现一次pci传输,写available数组又要发现一次pci传输,如果把descriptor和available数组合并只要一次pci传输即可。

实现情况

linux 4.18 virtio-net driver已经能支持virtio 1.1了,但vhost-net不支持virtio 1.1。

qemu master实现了virtio 1.1。

dpdk virtio pmd和vhost-user都支持virtio 1.1。

总结

virtio标准还会继续发展,功能会越来越多,设备类型会越来越多,如virtio GPU和virtio vIOMMU,GPU最难虚拟化,目前用的是mdev,没有IOMMU,virtio设备可以修改任意guest内存,有vIOMMU更安全,vIOMMU也可以用vt-d实现,virtio device emulation可以在qemu/kernel/dpdk中实现,virtio技术百花齐放,创新不断,是做虚拟化必须研究的技术。总结virtio的目标就是统一IO设备,虚拟机看到的所有的外设都是virtio类型,只需要安装virtio类型的驱动即可,如果硬件也能实现virtio,那么裸金属也一样了,虚拟机和裸金属互相热迁移,一个镜像走天下。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
virtio 虚拟化系列之一:从 virtio 论文开始(文末有福利~)
SmartX是中国领先的超融合产品与企业云解决方案提供商,拥有国内最顶尖的分布式存储和超融合架构研发团队,在分布式存储、虚拟化计算、微服务、容器、前端开发、自动化测试等领域都做着行业最前沿的实践。现正在招兵买马,看完请点击左下角阅读原文查看福利哦~
Linux阅码场
2019/07/08
2.4K0
virtio代码分析(一)-qemu部分
virtio内容众多,代码分布于qemu,linux,dpdk等中,而且分为frontend和backend,可以运行于userspace也可以运行于kernelspace,极其难以理解,不看代码只看原理性文档往往流于表面,只有真正看懂了代码才能理解virtio。
惠伟
2021/02/24
3K0
virtIO前后端notify机制详解
本来这是在前端驱动后期分析的,但是这部分内容比较多,且分析了后端notify前端的机制,所以还是单独拿出一节分析比较好!
Linux阅码场
2019/10/08
3.6K0
virtIO前后端notify机制详解
virtio 简介
我的微信公众号 aCloudDeveloper 专注于云计算技术,互联网技术,生活感悟,打造干货分享平台,每周至少一更,欢迎小伙伴们多多关注! 什么是 virtio virtio 是一种 I/O 半虚
Linux云计算网络
2018/03/19
7.7K1
virtio 简介
Intel FPGA 100G VF(IFCVF) DPDK用户态VDPA设备probe探测流程
callfd: host侧IO处理完成后, 如果是split vring, 则将结果写入vring used字段, 然后写callfd通知qemu/guest
晓兵
2024/08/04
3400
Intel FPGA 100G VF(IFCVF) DPDK用户态VDPA设备probe探测流程
【重识云原生】第四章云网络4.7.4节vhost-user方案——virtio的DPDK卸载方案
        在 vhost_net 的方案中,由于 vhost_net 实现在内核中,guest 与 vhost_net 的通信,相较于原生的 virtio 方式性能上有了一定程度的提升,从 guest 到 kvm.ko 的交互只有一次用户态的切换以及数据拷贝。这个方案对于不同 host 之间的通信,或者 guest 到 host nic 之间的通信是比较好的,但是对于某些用户态进程间的通信,比如数据面的通信方案,openvswitch 和与之类似的 SDN 的解决方案,guest 需要和 host 用户态的 vswitch 进行数据交换,如果采用 vhost_net 的方案,guest 和 host 之间又存在多次的上下文切换和数据拷贝,为了避免这种情况,业界就想出将 vhost_net从内核态移到用户态。这就是 vhost-user 的实现。
江中散人_Jun
2022/06/27
2.4K0
【重识云原生】第四章云网络4.7.4节vhost-user方案——virtio的DPDK卸载方案
【分享】OpenAMP的RPMSG_ADDR_ANY含义
在OpenAMP的应用程序中,经常看到地址被设置成RPMSG_ADDR_ANY。在通信过程中,为什么可以把源地址、目的地址设置成任意值?
hankfu
2020/07/16
1.3K0
virtio 与 vhost-net 架构
I/O 虚拟化经历了从 I/O 全虚拟化、I/O 半虚拟化、硬件直通再到 vDPA 加速 Vhost-user 技术的演进。
Flowlet
2023/08/11
2.9K0
virtio 与 vhost-net 架构
Virtio网络的演化之路
作为一个开放的标准接口,virtio一直在云计算与虚拟化中扮演着重要的角色。而virtio网络接口,作为virtio标准支持下最复杂的接口之一,在虚拟机/容器网络加速、混合云加速中一直扮演着重要角色。本文将在读者对virtio标准与虚拟化有一定了解的前提下,介绍virtio网络架构从创造之初到如今的演化之路。
虚拟化云计算
2019/11/18
8.4K0
【重识云原生】第四章云网络4.7.6节——virtio-blk存储虚拟化方案
        基于virtio的virtio-blk是KVM-Qemu虚拟化生态中的虚拟化块存储的一种实现方式,利用了virtio共享内存的机制,提供了一种高效的块存储挂载的方法。Guest OS内核通过加载virtio-blk驱动,实现块存储的读写,无需额外的厂家专用驱动。Virtio-blk设备在虚拟机以一个磁盘的方式呈现,是目前应用最广泛的虚拟存储控制器。如下是qemu所模拟的PC(基于intel i440fx主板架构)的组成结构图。
江中散人_Jun
2022/06/28
2.2K0
【重识云原生】第四章云网络4.7.6节——virtio-blk存储虚拟化方案
virtio —— 一种 Linux I/O 半虚拟化框架 [译]
简言之,virtio 是对于半虚拟化管理程序(para-virtualized hypervisor)中设备的一个抽象层。virtio 是 Rusty Russell 为了支持他自己的虚拟化方案 lguest 而开发的。
Flowlet
2023/08/11
1.4K0
virtio —— 一种 Linux I/O 半虚拟化框架 [译]
virtio代码分析(二)-kernel vhost-net部分
惠伟:virtio代码分析(一)-qemu部分​zhuanlan.zhihu.com
惠伟
2021/02/24
2K0
【重识云原生】第四章云网络4.7.2节——virtio网络半虚拟化简介
        在第二章的计算章节,我们在KVM一节有介绍过QEMU,因相隔较远,这里再将其基本架构做一下简要回顾
江中散人_Jun
2022/06/27
1.7K0
【重识云原生】第四章云网络4.7.2节——virtio网络半虚拟化简介
vDPA:支持 Linux 和 QEMU 中的块设备及内核VDPA块仿真设备vdpa-sim-blk源码分析
vDPA 设备是一种遵循virtio 数据路径规范但具有特定于供应商的控制路径的设备。
晓兵
2024/07/15
7403
vDPA:支持 Linux 和 QEMU 中的块设备及内核VDPA块仿真设备vdpa-sim-blk源码分析
好技能 | 网络虚拟化场景下网络包的发送过程
这篇文章以诙谐的口气对当前的大数据产品Hadoop、HDFS、MapReduce、Hive、Spark、Flink、Yarn各个适合的场景。
穿过生命散发芬芳
2024/11/21
1750
好技能 | 网络虚拟化场景下网络包的发送过程
【重识云原生】第四章云网络4.7.3节——Vhost-net方案
        virtio是一种I/O半虚拟化解决方案,是一套通用I/O设备虚拟化的程序,是对半虚拟化Hypervisior中的一组通用I/O设备的抽象。virtio分为前端和后端,一个backend组件和一个frontend组件。backend组件是virtio接口的host端,frontend组件是virtio的guest端。
江中散人_Jun
2022/06/27
2.6K0
【重识云原生】第四章云网络4.7.3节——Vhost-net方案
KVM详解,学习kvm系列文章
其中,KVM 全称是 基于内核的虚拟机(Kernel-based Virtual Machine),它是一个 Linux 的一个内核模块,该内核模块使得 Linux 变成了一个 Hypervisor:
菲宇
2019/06/12
9.6K1
KVM详解,学习kvm系列文章
vhost-user 简介
什么是 vhost-user 在 vhost 的方案中,由于 vhost 实现在内核中,guest 与 vhost 的通信,相较于原生的 virtio 方式性能上有了一定程度的提升,从 guest 到 kvm.ko 的交互只有一次用户态的切换以及数据拷贝。这个方案对于不同 host 之间的通信,或者 guest 到 host nic 之间的通信是比较好的,但是对于某些用户态进程间的通信,比如数据面的通信方案,openvswitch 和与之类似的 SDN 的解决方案,guest 需要和 host 用户态的 v
Linux云计算网络
2018/03/27
7.5K0
vhost-user 简介
【重识云原生】第三章云存储3.2节——SPDK方案综述
SSD正在迅速扩展它在数据中心中的份额,同旋转介质(HHD)相比,当前的闪存在性能、功耗和机架密度上具有明显优势,随着下一代媒介进入市场,这些优势将持续扩大。
江中散人_Jun
2022/04/11
5.1K0
【重识云原生】第三章云存储3.2节——SPDK方案综述
深入理解VFIO驱动框架
Jack,目前就职于通信行业某上市公司,主要从事Linux相关系统软件开发工作,负责基带芯片Soc芯片建模仿真以及虚拟化系统软件开发,基带芯片soc芯片BringUp及驱动开发,喜欢阅读内核源代码,在不断的学习和工作中深入理解外设虚拟化,网络虚拟化,用户态驱动等内核子系统。
Linux阅码场
2022/02/11
6.7K0
深入理解VFIO驱动框架
推荐阅读
相关推荐
virtio 虚拟化系列之一:从 virtio 论文开始(文末有福利~)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验