vfio是一套用户态驱动框架,主要提供两种基本服务:
1):向用户态提供访问硬件设备的接口。
2):向用户态提供提供IOmmu的接口.
在用户态将vfio分为container/group/device。通过打开dev/vfio 可以得到container,通过打来/dev/vfio/X 可以得到group。通过ioctl可以得到device.
关于vfio的时候在qemu中有完整的例子,可以在hw/vfio中找到.
例如在hw/vfio/common.c 中
static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
Error **errp)
{
int ret, fd;
fd = qemu_open("/dev/vfio/vfio", O_RDWR);
if (fd < 0) {
error_setg_errno(errp, errno, "failed to open /dev/vfio/vfio");
ret = -errno;
goto put_space_exit;
}
if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU) ||
ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU)) {
bool v2 = !!ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU);
struct vfio_iommu_type1_info info;
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
if (ret) {
error_setg_errno(errp, errno, "failed to set group container");
ret = -errno;
goto free_container_exit;
}
container->iommu_type = v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU;
ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type);
if (ret) {
error_setg_errno(errp, errno, "failed to set iommu for container");
ret = -errno;
goto free_container_exit;
}
}
从这个函数中可以看到,通过
fd = qemu_open("/dev/vfio/vfio", O_RDWR);得到container。文件标识符fd在kernel中关联的是
static const struct file_operations vfio_fops = {
.owner = THIS_MODULE,
.open = vfio_fops_open,
.release = vfio_fops_release,
.read = vfio_fops_read,
.write = vfio_fops_write,
.unlocked_ioctl = vfio_fops_unl_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = vfio_fops_compat_ioctl,
#endif
.mmap = vfio_fops_mmap,
};
这个从kernel中vfio.c 中的入口函数可以看到
static int __init vfio_init(void)
{
ret = misc_register(&vfio_dev);
if (ret) {
pr_err("vfio: misc device register failed\n");
return ret;
}
/* /dev/vfio/$GROUP */
vfio.class = class_create(THIS_MODULE, "vfio");
if (IS_ERR(vfio.class)) {
ret = PTR_ERR(vfio.class);
goto err_class;
}
vfio.class->devnode = vfio_devnode;
ret = alloc_chrdev_region(&vfio.group_devt, 0, MINORMASK, "vfio");
if (ret)
goto err_alloc_chrdev;
cdev_init(&vfio.group_cdev, &vfio_group_fops);
ret = cdev_add(&vfio.group_cdev, vfio.group_devt, MINORMASK);
if (ret)
goto err_cdev_add;
pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
request_module_nowait("vfio_iommu_type1");
request_module_nowait("vfio_iommu_spapr_tce");
return 0;
}
从vfio_init 中可以看出是通过misc_register 注册了一个misc设备,且关联的file_operations是vfio_dev这样才用户空间通过qemu_open("/dev/vfio/vfio", O_RDWR) 打开这个字符设备时,就会关联到vfio_dev.
在hw/vfio/common.c 中的vfio_get_group 可以看到是通过vfio_get_group
VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp)
{
VFIOGroup *group;
char path[32];
snprintf(path, sizeof(path), "/dev/vfio/%d", groupid);
group->fd = qemu_open(path, O_RDWR);
if (group->fd < 0) {
error_setg_errno(errp, errno, "failed to open %s", path);
goto free_group_exit;
}
}
可以看到通过打开/dev/vfio/%d 可以得到group,这在kernel中通过对应vfio_init 中的 cdev_init(&vfio.group_cdev, &vfio_group_fops);将其关联。也就是说用户态对group->fd的操作在kernel中对应的是vfio_group_fops。
同样从vfio_connect_container 可以看到iommu 设备分成两类container->iommu_type = v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU;
对应kernel中的vfio_init 中的vfio_iommu_type1.ko和vfio_iommu_spapr_tce.ko 在kernel space通过request_module_nowait 来让用户空间的modprobe来insmod这两个ko.
request_module_nowait("vfio_iommu_type1");
request_module_nowait("vfio_iommu_spapr_tce");
1):向用户态提供访问硬件设备的接口。
2):向用户态提供提供IOmmu的接口.
在用户态将vfio分为container/group/device。通过打开dev/vfio 可以得到container,通过打来/dev/vfio/X 可以得到group。通过ioctl可以得到device.
关于vfio的时候在qemu中有完整的例子,可以在hw/vfio中找到.
例如在hw/vfio/common.c 中
static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
Error **errp)
{
int ret, fd;
fd = qemu_open("/dev/vfio/vfio", O_RDWR);
if (fd < 0) {
error_setg_errno(errp, errno, "failed to open /dev/vfio/vfio");
ret = -errno;
goto put_space_exit;
}
if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU) ||
ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU)) {
bool v2 = !!ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU);
struct vfio_iommu_type1_info info;
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
if (ret) {
error_setg_errno(errp, errno, "failed to set group container");
ret = -errno;
goto free_container_exit;
}
container->iommu_type = v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU;
ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type);
if (ret) {
error_setg_errno(errp, errno, "failed to set iommu for container");
ret = -errno;
goto free_container_exit;
}
}
从这个函数中可以看到,通过
fd = qemu_open("/dev/vfio/vfio", O_RDWR);得到container。文件标识符fd在kernel中关联的是
static const struct file_operations vfio_fops = {
.owner = THIS_MODULE,
.open = vfio_fops_open,
.release = vfio_fops_release,
.read = vfio_fops_read,
.write = vfio_fops_write,
.unlocked_ioctl = vfio_fops_unl_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = vfio_fops_compat_ioctl,
#endif
.mmap = vfio_fops_mmap,
};
这个从kernel中vfio.c 中的入口函数可以看到
static int __init vfio_init(void)
{
ret = misc_register(&vfio_dev);
if (ret) {
pr_err("vfio: misc device register failed\n");
return ret;
}
/* /dev/vfio/$GROUP */
vfio.class = class_create(THIS_MODULE, "vfio");
if (IS_ERR(vfio.class)) {
ret = PTR_ERR(vfio.class);
goto err_class;
}
vfio.class->devnode = vfio_devnode;
ret = alloc_chrdev_region(&vfio.group_devt, 0, MINORMASK, "vfio");
if (ret)
goto err_alloc_chrdev;
cdev_init(&vfio.group_cdev, &vfio_group_fops);
ret = cdev_add(&vfio.group_cdev, vfio.group_devt, MINORMASK);
if (ret)
goto err_cdev_add;
pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
request_module_nowait("vfio_iommu_type1");
request_module_nowait("vfio_iommu_spapr_tce");
return 0;
}
从vfio_init 中可以看出是通过misc_register 注册了一个misc设备,且关联的file_operations是vfio_dev这样才用户空间通过qemu_open("/dev/vfio/vfio", O_RDWR) 打开这个字符设备时,就会关联到vfio_dev.
在hw/vfio/common.c 中的vfio_get_group 可以看到是通过vfio_get_group
VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp)
{
VFIOGroup *group;
char path[32];
snprintf(path, sizeof(path), "/dev/vfio/%d", groupid);
group->fd = qemu_open(path, O_RDWR);
if (group->fd < 0) {
error_setg_errno(errp, errno, "failed to open %s", path);
goto free_group_exit;
}
}
可以看到通过打开/dev/vfio/%d 可以得到group,这在kernel中通过对应vfio_init 中的 cdev_init(&vfio.group_cdev, &vfio_group_fops);将其关联。也就是说用户态对group->fd的操作在kernel中对应的是vfio_group_fops。
同样从vfio_connect_container 可以看到iommu 设备分成两类container->iommu_type = v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU;
对应kernel中的vfio_init 中的vfio_iommu_type1.ko和vfio_iommu_spapr_tce.ko 在kernel space通过request_module_nowait 来让用户空间的modprobe来insmod这两个ko.
request_module_nowait("vfio_iommu_type1");
request_module_nowait("vfio_iommu_spapr_tce");