sriov是设备虚拟化的一种很好的实现方式,但是它也有一些问题:
mdev提供了一种不用硬件设备提供虚拟功能相关实现,由软件去模拟虚拟设备的机制。linux中mdev框架是结合vfio实现的。
注:后述源码mdev框架结合mtty为例讲解
首先,通过uuid工具生成一组序列,假设为:53b2f4fa-80af-632a-3a1e-b6bfe1fa1221。
echo "53b2f4fa-80af-632a-3a1e-b6bfe1fa1221" > /sys/devices/virtual/mtty/mtty/mdev_supported_types/mtty-2/create
<hostdev mode='subsystem' type='mdev' managed='no' model='vfio-pci' display='off'>
<source>
<address uuid='53b2f4fa-80af-632a-3a1e-b6bfe1fa1221'/>
</source>
</hostdev>
最后,就成功将创建的虚拟设备配置给虚拟机
在mtty_dev_init函数中,创建了一个mtty的字符设备mtty_dev.dev;然后再将该设备通过函数mdev_register_device注册到mdev框架中,其回调为struct mdev_parent_ops mdev_fops。该回调实现了将虚拟设备模拟为PCI设备。mdev_fops定义如下:
// dev_attr_groups定义
static DEVICE_ATTR_RO(sample_mtty_dev);
static struct attribute *mtty_dev_attrs[] = {
&dev_attr_sample_mtty_dev.attr,
NULL,
};
static const struct attribute_group mtty_dev_group = {
.name = "mtty_dev",
.attrs = mtty_dev_attrs,
};
static const struct attribute_group *mtty_dev_groups[] = {
&mtty_dev_group,
NULL,
};
// mdev_attr_groups定义
static DEVICE_ATTR_RO(sample_mdev_dev);
static struct attribute *mdev_dev_attrs[] = {
&dev_attr_sample_mdev_dev.attr,
NULL,
};
static const struct attribute_group mdev_dev_group = {
.name = "vendor",
.attrs = mdev_dev_attrs,
};
static const struct attribute_group *mdev_dev_groups[] = {
&mdev_dev_group,
NULL,
};
// supported_type_groups定义
static MDEV_TYPE_ATTR_RO(name);
static MDEV_TYPE_ATTR_RO(available_instances);
static MDEV_TYPE_ATTR_RO(device_api);
static struct attribute_group mdev_type_group1 = {
.name = "1",
.attrs = mdev_types_attrs,
};
static struct attribute_group mdev_type_group2 = {
.name = "2",
.attrs = mdev_types_attrs,
};
static struct attribute_group *mdev_type_groups[] = {
&mdev_type_group1,
&mdev_type_group2,
NULL,
};
// mdev设备回调
static const struct mdev_parent_ops mdev_fops = {
.owner = THIS_MODULE,
.dev_attr_groups = mtty_dev_groups,
.mdev_attr_groups = mdev_dev_groups,
.supported_type_groups = mdev_type_groups,
.create = mtty_create,
.remove = mtty_remove,
.open = mtty_open,
.release = mtty_close,
.read = mtty_read,
.write = mtty_write,
.ioctl = mtty_ioctl,
};
注:mtty_*系列回调实现了pci配置空间、BAR空间的模拟
在vfio_mdev_init函数中,直接通过mdev_register_driver注册了一个设备驱动vfio_mdev_driver;在mdev_register_driver内部,将vfio_mdev_driver.driver.bus设置为mdev_bus_type。
需要明确,mdev_register_device是对host端的真实设备的注册、而非对mdev设备的注册。在mdev_register_device函数内:
当执行echo uuid > /sys/devices/virtual/mtty/mtty/mdev_supported_types/mtty-2/create时,在回调中会调用mdev_device_create实现mdev设备创建:
注:vfio_mdev_dev_ops各个函数集是对父设备注册的函数集合的调用包装。