天天看点

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

<code>Read the fucking source code!</code> --By 鲁迅

<code>A picture is worth a thousand words.</code> --By 高尔基

说明:

KVM版本:5.9.1

QEMU版本:5.0.0

工具:Source Insight 3.5, Visio

文章同步在博客园:<code>https://www.cnblogs.com/LoyenWang/</code>

新的一年, 大家牛起来!

祝小姐姐们:

落雁沉鱼 兰质蕙心 明眸皓齿 螓首蛾眉 天生丽质 天香国色 杏脸桃腮 煦色韶光 涎玉沫珠 宜嗔宜喜 远山芙蓉 艳色绝世 余霞成绮 阿娇金屋 逞娇呈美 国色天香 花颜月貌 绝色佳人 暗香盈袖 闭月羞花 倾国倾城 温婉娴淑 千娇百媚 仪态万千...

祝男的:

新年好。

先来张图:

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

图中罗列了四个关键模块:<code>Virtio Device</code>、<code>Virtio Driver</code>、<code>Virtqueue</code>、<code>Notification(eventfd/irqfd)</code>;

<code>Virtio Driver</code>:前端部分,处理用户请求,并将I/O请求转移到后端;

<code>Virtio Device</code>:后端部分,由Qemu来实现,接收前端的I/O请求,并通过物理设备进行I/O操作;

<code>Virtqueue</code>:中间层部分,用于数据的传输;

<code>Notification</code>:交互方式,用于异步事件的通知;

想在一篇文章中写完这四个模块,有点<code>too yong too simple</code>,所以,看起来又是一个系列文章了。

本文先从Qemu侧的virtio device入手,我会选择从一个实际的设备来阐述,没错,还是上篇文章中提到的网络设备。

在Qemu的网卡虚拟化时,通常会创建一个虚拟网卡前端和虚拟网卡后端,如下图:

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

在虚拟机创建的时候指定参数:<code>-netdev tap, id = tap0, -device virtio-net-pci, netdev=tap0</code>;

创建一个<code>Tap</code>网卡后端设备;

创建一个<code>Virtio-Net</code>网卡前端设备;

网卡前端设备和后端设备进行交互,最终与Host的驱动完成数据的收发;

全文围绕着<code>Tap</code>设备的创建和<code>Virtio-Net</code>设备的创建展开。

入口流程如下:

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

Qemu的代码阅读起来还是比较费劲的,各种盘根错节,里边充斥着面向对象的思想,先给自己挖个坑,后续会专题研究的,<code>this is for you, you have my words.</code>;

图中与本文相关的有三个模块:1)模块初始化;2)网络设备初始化;3)设备初始化;

Qemu中设备模拟通过<code>type_init</code>先编译进系统,在<code>module_call_init</code>时进行回调,比如图中的<code>xxx_register_types</code>,在这些函数中都是根据<code>TypeInfo</code>类型信息来创建具体的实现信息;

<code>net_init_client</code>用来创建网络设备,比如<code>Tap</code>设备;

<code>device_init_func</code>根据Qemu命令的传入参数创建虚拟设备,比如<code>Virtio-Net</code>;

下边进入细节,<code>the devil is in the details</code>。

从上文中,我们知道,<code>Tap</code>与<code>Virtio-Net</code>属于前后端的关系,最终是通过结构体分别指向对方,如下图:

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

<code>NetClientState</code>是网卡模拟的核心结构,表示网络设备中的几个端点,两个端点通过<code>peer</code>指向对方;

创建Tap设备的主要工作就是创建一个<code>NetClientState</code>结构,并添加到<code>net_clients</code>链表中:

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

函数的调用细节如下图:

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

处理流程只关注了核心的处理流程,整个过程有很多关于传入参数的处理,选择性忽略了;

<code>net_tap_init</code>:与Host的<code>tun</code>驱动进行交互,其实质就是打开该设备文件,并进行相应的配置等;

<code>net_tap_fd_init</code>:根据<code>net_tap_info</code>结构,创建<code>NetClientState</code>,并进行相关设置,这里边<code>net_tap_info</code>结构体中的接收函数指针用于实际的数据传输处理;

<code>tap_read_poll</code>用于将fd添加到Qemu的AioContext中,用于异步响应,当有数据来临时,捕获事件并进行处理;

以上就是Tap后端的创建过程,下文将针对前端创建了。

这是一个复杂的流程。

Qemu中用C语言实现了面向对象的模型,用于对设备进行抽象,精妙!

针对Virtio-Net设备,结构体及拓扑组织关系如下图:

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

<code>DeviceState</code>作为所有设备的父类,其中派生了<code>VirtIODevice</code>和<code>PCIDevice</code>,而本文研究的<code>Virtio-Net</code>派生自<code>VirtIODevice</code>;

Qemu中会虚拟一个PCI总线,同时创建<code>virtio-net-pci</code>,<code>virtio-balloon-pci</code>,<code>virtio-scsi-pci</code>等PCI代理设备,这些代理设备挂载在PCI总线上,同时会创建Virtio总线,用于挂载最终的设备,比如<code>VirtIONet</code>;

PCI代理设备就是一个纽带;

与设备创建相关的三个函数,可以从<code>device_init_func</code>入口跟踪得知:

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

当Qemu命令通过<code>-device</code>传入参数时,<code>device_init_func</code>会根据参数去查找设备,并最终调用到该设备对应的类初始化函数、对象初始化函数、以及realize函数;

所以,我们的分析就是这三个入口;

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

在网卡虚拟化过程中,参数只需要指定PCI代理设备即可,也就是<code>-device virtio-net-pci, netdev=tap0</code>,从而会调用到<code>virtio_net_pci_class_init</code>函数;

由于实现了类的继承关系,在子类初始化之前,需要先调用父类的实现,图中也表明了继承关系以及调用函数顺序;

C语言实现继承,也就是将父对象放置在自己结构体的开始位置,图中的颜色能看出来;

类初始化结束后,开始对象的创建:

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

针对<code>Virtio-Net-PCI</code>的实例化比较简单,作为代理,负责将它的后继对象初始化,也就是本文的前端设备<code>Virtio-Net</code>;

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

<code>realize</code>的调用,比较绕,简单来说,它的类继承关系中存在多个<code>realize</code>的函数指针,最终会从父类开始执行,一直调用到子类,而这些函数指针的初始化在什么时候做的呢?没错,就是在class_init类初始化的时候,进行了赋值,细节不表,结论可靠;

最终的调用关系就如图了;

到目前为止,我们似乎都还没有看到<code>Virtio-Net</code>设备的相关操作,不用着急,已经很接近真相了:

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

<code>virtio_net_pci_realize</code>函数,会触发<code>virtio_device_realize</code>的调用,该函数是一个通用的virtio设备实现函数,所有的virtio设备都会调用,而我们的前端设备<code>Virtio-Net</code>也是virtio设备;

<code>virtio_net_device_realize</code>就到了我们的主角了,它进行了virtio通用的设置(后续在数据通信中再分析),还创建了一个<code>NetClientState</code>端点,与<code>Tap</code>设备对应,分别指向了对方,惺惺相惜,各自安好;

<code>virtio_bus_device_plugged</code>表示设备插入总线时的处理,完成的工作就是按照PCI总线规划,配置各类信息,以便与Guest OS中的virtio驱动交互,后续的文章再分析了;

本文基本捋清了虚拟网卡前端设备和后端设备的创建过程,完成的工作只是绑定了彼此,数据交互以及通知机制,留给后续吧。

<code>《 Virtual I/O Device (VIRTIO) Version 1.1》</code>

<code>https://www.redhat.com/en/blog/virtio-devices-and-drivers-overview-headjack-and-phone</code>

欢迎关注个人公众号,不定期更新技术文章。

【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备

作者:LoyenWang

出处:https://www.cnblogs.com/LoyenWang/

公众号:<b>LoyenWang</b>

版权:本文版权归作者和博客园共有

转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任

继续阅读