当在qemu中调用ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU)的时候会用到vfio_fops->vfio_fops_compat_ioctl->vfio_fops_unl_ioctl
static long vfio_fops_unl_ioctl(struct file *filep,
unsigned int cmd, unsigned long arg)
{
case VFIO_SET_IOMMU:
ret = vfio_ioctl_set_iommu(container, arg);
break;
}
继续调用vfio_ioctl_set_iommu->__vfio_container_attach_groups
static int __vfio_container_attach_groups(struct vfio_container *container,
struct vfio_iommu_driver *driver,
void *data)
{
struct vfio_group *group;
int ret = -ENODEV;
list_for_each_entry(group, &container->group_list, container_next) {
ret = driver->ops->attach_group(data, group->iommu_group);
if (ret)
goto unwind;
}
return ret;
}
针对vfio_iommu_type1.c 中的初始化函数
static int __init vfio_iommu_type1_init(void)
{
return vfio_register_iommu_driver(&vfio_iommu_driver_ops_type1);
}
主要是注册一个vfio_iommu_driver_ops_type1。
static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
.name = "vfio-iommu-type1",
.owner = THIS_MODULE,
.open = vfio_iommu_type1_open,
.release = vfio_iommu_type1_release,
.ioctl = vfio_iommu_type1_ioctl,
.attach_group = vfio_iommu_type1_attach_group,
.detach_group = vfio_iommu_type1_detach_group,
};
因此在__vfio_container_attach_groups中的attach函数就对应vfio_iommu_type1_attach_group
static int vfio_iommu_type1_attach_group(void *iommu_data,
struct iommu_group *iommu_group)
{
struct vfio_iommu *iommu = iommu_data;
struct vfio_group *group, *g;
struct vfio_domain *domain, *d;
struct bus_type *bus = NULL;
int ret;
mutex_lock(&iommu->lock);
//遍历group_list看是否可以找到group第一次肯定找不到
list_for_each_entry(d, &iommu->domain_list, next) {
list_for_each_entry(g, &d->group_list, next) {
if (g->iommu_group != iommu_group)
continue;
mutex_unlock(&iommu->lock);
return -EINVAL;
}
}
//申请group和domian 对象
group = kzalloc(sizeof(*group), GFP_KERNEL);
domain = kzalloc(sizeof(*domain), GFP_KERNEL);
if (!group || !domain) {
ret = -ENOMEM;
goto out_free;
}
//让group->iommu_group指向iommu_group,这样下次在group_list 中就可以找到了
group->iommu_group = iommu_group;
/* Determine bus_type in order to allocate a domain */
ret = iommu_group_for_each_dev(iommu_group, &bus, vfio_bus_type);
if (ret)
goto out_free;
domain->domain = iommu_domain_alloc(bus);
if (!domain->domain) {
ret = -EIO;
goto out_free;
}
if (iommu->nesting) {
int attr = 1;
ret = iommu_domain_set_attr(domain->domain, DOMAIN_ATTR_NESTING,
&attr);
if (ret)
goto out_domain;
}
ret = iommu_attach_group(domain->domain, iommu_group);
if (ret)
goto out_domain;
//将group 添加到dimain的group_list 中
INIT_LIST_HEAD(&domain->group_list);
list_add(&group->next, &domain->group_list);
if (iommu_capable(bus, IOMMU_CAP_CACHE_COHERENCY))
domain->prot |= IOMMU_CACHE;
list_for_each_entry(d, &iommu->domain_list, next) {
if (d->domain->ops == domain->domain->ops &&
d->prot == domain->prot) {
iommu_detach_group(domain->domain, iommu_group);
if (!iommu_attach_group(d->domain, iommu_group)) {
list_add(&group->next, &d->group_list);
iommu_domain_free(domain->domain);
kfree(domain);
mutex_unlock(&iommu->lock);
return 0;
}
ret = iommu_attach_group(domain->domain, iommu_group);
if (ret)
goto out_domain;
}
}
vfio_test_domain_fgsp(domain);
/* replay mappings on new domains */
ret = vfio_iommu_replay(iommu, domain);
if (ret)
goto out_detach;
//将domian添加到vfio_iommu的domain_list 中
list_add(&domain->next, &iommu->domain_list);
mutex_unlock(&iommu->lock);
return 0;
}
总结一个通过在qemu中调用VFIO_SET_IOMMU,让vfio-iommu-type1类型的iommu和vfio_fops 产生关联
static long vfio_fops_unl_ioctl(struct file *filep,
unsigned int cmd, unsigned long arg)
{
case VFIO_SET_IOMMU:
ret = vfio_ioctl_set_iommu(container, arg);
break;
}
继续调用vfio_ioctl_set_iommu->__vfio_container_attach_groups
static int __vfio_container_attach_groups(struct vfio_container *container,
struct vfio_iommu_driver *driver,
void *data)
{
struct vfio_group *group;
int ret = -ENODEV;
list_for_each_entry(group, &container->group_list, container_next) {
ret = driver->ops->attach_group(data, group->iommu_group);
if (ret)
goto unwind;
}
return ret;
}
针对vfio_iommu_type1.c 中的初始化函数
static int __init vfio_iommu_type1_init(void)
{
return vfio_register_iommu_driver(&vfio_iommu_driver_ops_type1);
}
主要是注册一个vfio_iommu_driver_ops_type1。
static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
.name = "vfio-iommu-type1",
.owner = THIS_MODULE,
.open = vfio_iommu_type1_open,
.release = vfio_iommu_type1_release,
.ioctl = vfio_iommu_type1_ioctl,
.attach_group = vfio_iommu_type1_attach_group,
.detach_group = vfio_iommu_type1_detach_group,
};
因此在__vfio_container_attach_groups中的attach函数就对应vfio_iommu_type1_attach_group
static int vfio_iommu_type1_attach_group(void *iommu_data,
struct iommu_group *iommu_group)
{
struct vfio_iommu *iommu = iommu_data;
struct vfio_group *group, *g;
struct vfio_domain *domain, *d;
struct bus_type *bus = NULL;
int ret;
mutex_lock(&iommu->lock);
//遍历group_list看是否可以找到group第一次肯定找不到
list_for_each_entry(d, &iommu->domain_list, next) {
list_for_each_entry(g, &d->group_list, next) {
if (g->iommu_group != iommu_group)
continue;
mutex_unlock(&iommu->lock);
return -EINVAL;
}
}
//申请group和domian 对象
group = kzalloc(sizeof(*group), GFP_KERNEL);
domain = kzalloc(sizeof(*domain), GFP_KERNEL);
if (!group || !domain) {
ret = -ENOMEM;
goto out_free;
}
//让group->iommu_group指向iommu_group,这样下次在group_list 中就可以找到了
group->iommu_group = iommu_group;
/* Determine bus_type in order to allocate a domain */
ret = iommu_group_for_each_dev(iommu_group, &bus, vfio_bus_type);
if (ret)
goto out_free;
domain->domain = iommu_domain_alloc(bus);
if (!domain->domain) {
ret = -EIO;
goto out_free;
}
if (iommu->nesting) {
int attr = 1;
ret = iommu_domain_set_attr(domain->domain, DOMAIN_ATTR_NESTING,
&attr);
if (ret)
goto out_domain;
}
ret = iommu_attach_group(domain->domain, iommu_group);
if (ret)
goto out_domain;
//将group 添加到dimain的group_list 中
INIT_LIST_HEAD(&domain->group_list);
list_add(&group->next, &domain->group_list);
if (iommu_capable(bus, IOMMU_CAP_CACHE_COHERENCY))
domain->prot |= IOMMU_CACHE;
list_for_each_entry(d, &iommu->domain_list, next) {
if (d->domain->ops == domain->domain->ops &&
d->prot == domain->prot) {
iommu_detach_group(domain->domain, iommu_group);
if (!iommu_attach_group(d->domain, iommu_group)) {
list_add(&group->next, &d->group_list);
iommu_domain_free(domain->domain);
kfree(domain);
mutex_unlock(&iommu->lock);
return 0;
}
ret = iommu_attach_group(domain->domain, iommu_group);
if (ret)
goto out_domain;
}
}
vfio_test_domain_fgsp(domain);
/* replay mappings on new domains */
ret = vfio_iommu_replay(iommu, domain);
if (ret)
goto out_detach;
//将domian添加到vfio_iommu的domain_list 中
list_add(&domain->next, &iommu->domain_list);
mutex_unlock(&iommu->lock);
return 0;
}
总结一个通过在qemu中调用VFIO_SET_IOMMU,让vfio-iommu-type1类型的iommu和vfio_fops 产生关联