Virtio驱动研究

发布时间:2023-05-20

一、virtio概述

virtio是一种轻量级的虚拟化I/O框架,它的核心思想是将设备的复杂功能转移到虚拟机监控程序(VMM)上。virtio将外部设备的访问逻辑封装在虚拟机监控程序内部,只向虚拟机交付一组简单而统一的接口。这些接口可以看作是一种通用的虚拟IO设备规范。 virtio规范主要包括以下几种类型的设备:

  • 块设备(blk):提供块级别的读写操作。
  • 网卡设备(net):提供网络通信服务。
  • 内存设备(mem):提供直接内存访问等服务。
  • 控制台设备(console):提供与虚拟机控制台的通信服务。 virtio驱动负责在虚拟机中对这些设备的操作进行处理,以及和宿主机的交互。

二、virtio驱动实现

1. virtio设备的表示

在Linux系统中,virtio设备表示为一组及其描述符。描述符中包含了virtio设备的各种信息,通过它可以实现virtio设备数据的读写等操作。驱动通过描述符寻找到相应的设备,并进行控制和管理。

2. 设备的初始化

虚拟机在运行时,virtio驱动会被加载到内核中,并和virtio设备进行初始化连接的过程。主要过程包括以下几个环节:

  1. 读取VM的配置信息。
  2. 填充设备描述符信息。
  3. 将设备描述符传递到虚拟机中,随后虚拟机完成设备初始化的过程。 虚拟机完成设备初始化后,virtio驱动可以开始与设备进行交互。

3. 数据处理

在进行数据处理时,virtio驱动主要负责两个过程:读取数据和写入数据。具体过程如下:

  1. 读取数据:首先,驱动程序发送对应的读取请求给virtio设备。虚拟机完成数据的读取后,驱动会将数据从虚拟机中读出并发送到应用程序中。
  2. 倌入数据:在写入数据的时候,应用程序会将数据发送给virtio驱动,随后驱动会将数据发送给虚拟机并请求写入。

三、代码示例

static struct virtio_driver virtio_blk_driver = {
	.name   = "virtio_blk",
	.id_table = id_table,
	.features = features,
	.feature_table_size = ARRAY_SIZE(features),
	.probe  = virtblk_probe,
	.remove = virtblk_remove,
};
module_virtio_driver(virtio_blk_driver);
struct virtio_device *vdev;
struct virtqueue *vq;
int rc;
char buf[512];
/* 首先初始化device */
vdev = virtio_find_device(&virtio_blk_driver, PCI_VENDOR_ID_REDHAT_QUMRANET, VIRTIO_BLK_DEVICE_ID, NULL);
/* 如果没有找到可用的virtio设备就退出 */
if (!vdev){
	printk(KERN_ERR "virtio_blk: no devices found!\n");
	return -ENODEV;
}
/* 使用模块提供的方法进行请求队列的初始化 */
rc = virtqueue_initialize(vdev, 0, virtblk_request);
/* 如果返回值小于0,则表示初始化失败 */
if (rc < 0){
	printk(KERN_ERR "virtio_blk: can not initialize virtqueue\n");
	ret = -ENODEV;
	goto error;
}
/* 初始化成功,则取出请求队列 */
vq = vdev->vqs ? vdev->vqs[0] : NULL;
if (!vq) {
	printk(KERN_ERR "virtio_blk: NO vq\n");
        goto error;
}
/* 将写入的数据添加到队列中去 */
struct scatterlist sg_list;
sg_init_one(&sg_list, buf, sizeof(buf));
struct virtio_blk_req *req = kzalloc(sizeof(struct virtio_blk_req), GFP_KERNEL);
virtblk_setup_req(req, VIRTIO_BLK_T_IN, 0, &sg_list, 0);
virtqueue_push(vq, req, 0, GFP_KERNEL);