> From: Kenneth Lee > Sent: Wednesday, August 1, 2018 6:22 PM > > From: Kenneth Lee <liguozhu@xxxxxxxxxxxxx> > > SPIMDEV is "Share Parent IOMMU Mdev". It is a vfio-mdev. But differ from > the general vfio-mdev: > > 1. It shares its parent's IOMMU. > 2. There is no hardware resource attached to the mdev is created. The > hardware resource (A `queue') is allocated only when the mdev is > opened. Alex has concern on doing so, as pointed out in: https://www.spinics.net/lists/kvm/msg172652.html resource allocation should be reserved at creation time. > > Currently only the vfio type-1 driver is updated to make it to be aware > of. > > Signed-off-by: Kenneth Lee <liguozhu@xxxxxxxxxxxxx> > Signed-off-by: Zaibo Xu <xuzaibo@xxxxxxxxxx> > Signed-off-by: Zhou Wang <wangzhou1@xxxxxxxxxxxxx> > --- > drivers/vfio/Kconfig | 1 + > drivers/vfio/Makefile | 1 + > drivers/vfio/spimdev/Kconfig | 10 + > drivers/vfio/spimdev/Makefile | 3 + > drivers/vfio/spimdev/vfio_spimdev.c | 421 > ++++++++++++++++++++++++++++ > drivers/vfio/vfio_iommu_type1.c | 136 ++++++++- > include/linux/vfio_spimdev.h | 95 +++++++ > include/uapi/linux/vfio_spimdev.h | 28 ++ > 8 files changed, 689 insertions(+), 6 deletions(-) > create mode 100644 drivers/vfio/spimdev/Kconfig > create mode 100644 drivers/vfio/spimdev/Makefile > create mode 100644 drivers/vfio/spimdev/vfio_spimdev.c > create mode 100644 include/linux/vfio_spimdev.h > create mode 100644 include/uapi/linux/vfio_spimdev.h > > diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig > index c84333eb5eb5..3719eba72ef1 100644 > --- a/drivers/vfio/Kconfig > +++ b/drivers/vfio/Kconfig > @@ -47,4 +47,5 @@ menuconfig VFIO_NOIOMMU > source "drivers/vfio/pci/Kconfig" > source "drivers/vfio/platform/Kconfig" > source "drivers/vfio/mdev/Kconfig" > +source "drivers/vfio/spimdev/Kconfig" > source "virt/lib/Kconfig" > diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile > index de67c4725cce..28f3ef0cdce1 100644 > --- a/drivers/vfio/Makefile > +++ b/drivers/vfio/Makefile > @@ -9,3 +9,4 @@ obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spapr_eeh.o > obj-$(CONFIG_VFIO_PCI) += pci/ > obj-$(CONFIG_VFIO_PLATFORM) += platform/ > obj-$(CONFIG_VFIO_MDEV) += mdev/ > +obj-$(CONFIG_VFIO_SPIMDEV) += spimdev/ > diff --git a/drivers/vfio/spimdev/Kconfig b/drivers/vfio/spimdev/Kconfig > new file mode 100644 > index 000000000000..1226301f9d0e > --- /dev/null > +++ b/drivers/vfio/spimdev/Kconfig > @@ -0,0 +1,10 @@ > +# SPDX-License-Identifier: GPL-2.0 > +config VFIO_SPIMDEV > + tristate "Support for Share Parent IOMMU MDEV" > + depends on VFIO_MDEV_DEVICE > + help > + Support for VFIO Share Parent IOMMU MDEV, which enable the > kernel to > + support for the light weight hardware accelerator framework, > WrapDrive. > + > + To compile this as a module, choose M here: the module will be > called > + spimdev. > diff --git a/drivers/vfio/spimdev/Makefile b/drivers/vfio/spimdev/Makefile > new file mode 100644 > index 000000000000..d02fb69c37e4 > --- /dev/null > +++ b/drivers/vfio/spimdev/Makefile > @@ -0,0 +1,3 @@ > +# SPDX-License-Identifier: GPL-2.0 > +spimdev-y := spimdev.o > +obj-$(CONFIG_VFIO_SPIMDEV) += vfio_spimdev.o > diff --git a/drivers/vfio/spimdev/vfio_spimdev.c > b/drivers/vfio/spimdev/vfio_spimdev.c > new file mode 100644 > index 000000000000..1b6910c9d27d > --- /dev/null > +++ b/drivers/vfio/spimdev/vfio_spimdev.c > @@ -0,0 +1,421 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +#include <linux/anon_inodes.h> > +#include <linux/idr.h> > +#include <linux/module.h> > +#include <linux/poll.h> > +#include <linux/vfio_spimdev.h> > + > +struct spimdev_mdev_state { > + struct vfio_spimdev *spimdev; > +}; > + > +static struct class *spimdev_class; > +static DEFINE_IDR(spimdev_idr); > + > +static int vfio_spimdev_dev_exist(struct device *dev, void *data) > +{ > + return !strcmp(dev_name(dev), dev_name((struct device *)data)); > +} > + > +#ifdef CONFIG_IOMMU_SVA > +static bool vfio_spimdev_is_valid_pasid(int pasid) > +{ > + struct mm_struct *mm; > + > + mm = iommu_sva_find(pasid); > + if (mm) { > + mmput(mm); > + return mm == current->mm; > + } > + > + return false; > +} > +#endif > + > +/* Check if the device is a mediated device belongs to vfio_spimdev */ > +int vfio_spimdev_is_spimdev(struct device *dev) > +{ > + struct mdev_device *mdev; > + struct device *pdev; > + > + mdev = mdev_from_dev(dev); > + if (!mdev) > + return 0; > + > + pdev = mdev_parent_dev(mdev); > + if (!pdev) > + return 0; > + > + return class_for_each_device(spimdev_class, NULL, pdev, > + vfio_spimdev_dev_exist); > +} > +EXPORT_SYMBOL_GPL(vfio_spimdev_is_spimdev); > + > +struct vfio_spimdev *vfio_spimdev_pdev_spimdev(struct device *dev) > +{ > + struct device *class_dev; > + > + if (!dev) > + return ERR_PTR(-EINVAL); > + > + class_dev = class_find_device(spimdev_class, NULL, dev, > + (int(*)(struct device *, const void > *))vfio_spimdev_dev_exist); > + if (!class_dev) > + return ERR_PTR(-ENODEV); > + > + return container_of(class_dev, struct vfio_spimdev, cls_dev); > +} > +EXPORT_SYMBOL_GPL(vfio_spimdev_pdev_spimdev); > + > +struct vfio_spimdev *mdev_spimdev(struct mdev_device *mdev) > +{ > + struct device *pdev = mdev_parent_dev(mdev); > + > + return vfio_spimdev_pdev_spimdev(pdev); > +} > +EXPORT_SYMBOL_GPL(mdev_spimdev); > + > +static ssize_t iommu_type_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct vfio_spimdev *spimdev = vfio_spimdev_pdev_spimdev(dev); > + > + if (!spimdev) > + return -ENODEV; > + > + return sprintf(buf, "%d\n", spimdev->iommu_type); > +} > + > +static DEVICE_ATTR_RO(iommu_type); > + > +static ssize_t dma_flag_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct vfio_spimdev *spimdev = vfio_spimdev_pdev_spimdev(dev); > + > + if (!spimdev) > + return -ENODEV; > + > + return sprintf(buf, "%d\n", spimdev->dma_flag); > +} > + > +static DEVICE_ATTR_RO(dma_flag); > + > +/* mdev->dev_attr_groups */ > +static struct attribute *vfio_spimdev_attrs[] = { > + &dev_attr_iommu_type.attr, > + &dev_attr_dma_flag.attr, > + NULL, > +}; > +static const struct attribute_group vfio_spimdev_group = { > + .name = VFIO_SPIMDEV_PDEV_ATTRS_GRP_NAME, > + .attrs = vfio_spimdev_attrs, > +}; > +const struct attribute_group *vfio_spimdev_groups[] = { > + &vfio_spimdev_group, > + NULL, > +}; > + > +/* default attributes for mdev->supported_type_groups, used by > registerer*/ > +#define MDEV_TYPE_ATTR_RO_EXPORT(name) \ > + MDEV_TYPE_ATTR_RO(name); \ > + EXPORT_SYMBOL_GPL(mdev_type_attr_##name); > + > +#define DEF_SIMPLE_SPIMDEV_ATTR(_name, spimdev_member, format) > \ > +static ssize_t _name##_show(struct kobject *kobj, struct device *dev, \ > + char *buf) \ > +{ \ > + struct vfio_spimdev *spimdev = vfio_spimdev_pdev_spimdev(dev); > \ > + if (!spimdev) \ > + return -ENODEV; \ > + return sprintf(buf, format, spimdev->spimdev_member); \ > +} \ > +MDEV_TYPE_ATTR_RO_EXPORT(_name) > + > +DEF_SIMPLE_SPIMDEV_ATTR(flags, flags, "%d"); > +DEF_SIMPLE_SPIMDEV_ATTR(name, name, "%s"); /* this should be > algorithm name, */ > + /* but you would not care if you have only one algorithm */ > +DEF_SIMPLE_SPIMDEV_ATTR(device_api, api_ver, "%s"); > + > +/* this return total queue left, not mdev left */ > +static ssize_t > +available_instances_show(struct kobject *kobj, struct device *dev, char > *buf) > +{ > + struct vfio_spimdev *spimdev = vfio_spimdev_pdev_spimdev(dev); > + > + return sprintf(buf, "%d", > + spimdev->ops->get_available_instances(spimdev)); > +} > +MDEV_TYPE_ATTR_RO_EXPORT(available_instances); > + > +static int vfio_spimdev_mdev_create(struct kobject *kobj, > + struct mdev_device *mdev) > +{ > + struct device *dev = mdev_dev(mdev); > + struct device *pdev = mdev_parent_dev(mdev); > + struct spimdev_mdev_state *mdev_state; > + struct vfio_spimdev *spimdev = mdev_spimdev(mdev); > + > + if (!spimdev->ops->get_queue) > + return -ENODEV; > + > + mdev_state = devm_kzalloc(dev, sizeof(struct > spimdev_mdev_state), > + GFP_KERNEL); > + if (!mdev_state) > + return -ENOMEM; > + mdev_set_drvdata(mdev, mdev_state); > + mdev_state->spimdev = spimdev; > + dev->iommu_fwspec = pdev->iommu_fwspec; > + get_device(pdev); > + __module_get(spimdev->owner); > + > + return 0; > +} > + > +static int vfio_spimdev_mdev_remove(struct mdev_device *mdev) > +{ > + struct device *dev = mdev_dev(mdev); > + struct device *pdev = mdev_parent_dev(mdev); > + struct vfio_spimdev *spimdev = mdev_spimdev(mdev); > + > + put_device(pdev); > + module_put(spimdev->owner); > + dev->iommu_fwspec = NULL; > + mdev_set_drvdata(mdev, NULL); > + > + return 0; > +} > + > +/* Wake up the process who is waiting this queue */ > +void vfio_spimdev_wake_up(struct vfio_spimdev_queue *q) > +{ > + wake_up(&q->wait); > +} > +EXPORT_SYMBOL_GPL(vfio_spimdev_wake_up); > + > +static int vfio_spimdev_q_file_open(struct inode *inode, struct file *file) > +{ > + return 0; > +} > + > +static int vfio_spimdev_q_file_release(struct inode *inode, struct file *file) > +{ > + struct vfio_spimdev_queue *q = > + (struct vfio_spimdev_queue *)file->private_data; > + struct vfio_spimdev *spimdev = q->spimdev; > + int ret; > + > + ret = spimdev->ops->put_queue(q); > + if (ret) { > + dev_err(spimdev->dev, "drv put queue fail (%d)!\n", ret); > + return ret; > + } > + > + put_device(mdev_dev(q->mdev)); > + > + return 0; > +} > + > +static long vfio_spimdev_q_file_ioctl(struct file *file, unsigned int cmd, > + unsigned long arg) > +{ > + struct vfio_spimdev_queue *q = > + (struct vfio_spimdev_queue *)file->private_data; > + struct vfio_spimdev *spimdev = q->spimdev; > + > + if (spimdev->ops->ioctl) > + return spimdev->ops->ioctl(q, cmd, arg); > + > + dev_err(spimdev->dev, "ioctl cmd (%d) is not supported!\n", cmd); > + > + return -EINVAL; > +} > + > +static int vfio_spimdev_q_file_mmap(struct file *file, > + struct vm_area_struct *vma) > +{ > + struct vfio_spimdev_queue *q = > + (struct vfio_spimdev_queue *)file->private_data; > + struct vfio_spimdev *spimdev = q->spimdev; > + > + if (spimdev->ops->mmap) > + return spimdev->ops->mmap(q, vma); > + > + dev_err(spimdev->dev, "no driver mmap!\n"); > + return -EINVAL; > +} > + > +static __poll_t vfio_spimdev_q_file_poll(struct file *file, poll_table *wait) > +{ > + struct vfio_spimdev_queue *q = > + (struct vfio_spimdev_queue *)file->private_data; > + struct vfio_spimdev *spimdev = q->spimdev; > + > + poll_wait(file, &q->wait, wait); > + if (spimdev->ops->is_q_updated(q)) > + return EPOLLIN | EPOLLRDNORM; > + > + return 0; > +} > + > +static const struct file_operations spimdev_q_file_ops = { > + .owner = THIS_MODULE, > + .open = vfio_spimdev_q_file_open, > + .unlocked_ioctl = vfio_spimdev_q_file_ioctl, > + .release = vfio_spimdev_q_file_release, > + .poll = vfio_spimdev_q_file_poll, > + .mmap = vfio_spimdev_q_file_mmap, > +}; > + > +static long vfio_spimdev_mdev_get_queue(struct mdev_device *mdev, > + struct vfio_spimdev *spimdev, unsigned long arg) > +{ > + struct vfio_spimdev_queue *q; > + int ret; > + > +#ifdef CONFIG_IOMMU_SVA > + int pasid = arg; > + > + if (!vfio_spimdev_is_valid_pasid(pasid)) > + return -EINVAL; > +#endif > + > + if (!spimdev->ops->get_queue) > + return -EINVAL; > + > + ret = spimdev->ops->get_queue(spimdev, arg, &q); > + if (ret < 0) { > + dev_err(spimdev->dev, "get_queue failed\n"); > + return -ENODEV; > + } > + > + ret = anon_inode_getfd("spimdev_q", &spimdev_q_file_ops, > + q, O_CLOEXEC | O_RDWR); > + if (ret < 0) { > + dev_err(spimdev->dev, "getfd fail %d\n", ret); > + goto err_with_queue; > + } > + > + q->fd = ret; > + q->spimdev = spimdev; > + q->mdev = mdev; > + q->container = arg; > + init_waitqueue_head(&q->wait); > + get_device(mdev_dev(mdev)); > + > + return ret; > + > +err_with_queue: > + spimdev->ops->put_queue(q); > + return ret; > +} > + > +static long vfio_spimdev_mdev_ioctl(struct mdev_device *mdev, unsigned > int cmd, > + unsigned long arg) > +{ > + struct spimdev_mdev_state *mdev_state; > + struct vfio_spimdev *spimdev; > + > + if (!mdev) > + return -ENODEV; > + > + mdev_state = mdev_get_drvdata(mdev); > + if (!mdev_state) > + return -ENODEV; > + > + spimdev = mdev_state->spimdev; > + if (!spimdev) > + return -ENODEV; > + > + if (cmd == VFIO_SPIMDEV_CMD_GET_Q) > + return vfio_spimdev_mdev_get_queue(mdev, spimdev, arg); > + > + dev_err(spimdev->dev, > + "%s, ioctl cmd (0x%x) is not supported!\n", __func__, cmd); > + return -EINVAL; > +} > + > +static void vfio_spimdev_release(struct device *dev) { } > +static void vfio_spimdev_mdev_release(struct mdev_device *mdev) { } > +static int vfio_spimdev_mdev_open(struct mdev_device *mdev) { return > 0; } > + > +/** > + * vfio_spimdev_register - register a spimdev > + * @spimdev: device structure > + */ > +int vfio_spimdev_register(struct vfio_spimdev *spimdev) > +{ > + int ret; > + const char *drv_name; > + > + if (!spimdev->dev) > + return -ENODEV; > + > + drv_name = dev_driver_string(spimdev->dev); > + if (strstr(drv_name, "-")) { > + pr_err("spimdev: parent driver name cannot include '-'!\n"); > + return -EINVAL; > + } > + > + spimdev->dev_id = idr_alloc(&spimdev_idr, spimdev, 0, 0, > GFP_KERNEL); > + if (spimdev->dev_id < 0) > + return spimdev->dev_id; > + > + atomic_set(&spimdev->ref, 0); > + spimdev->cls_dev.parent = spimdev->dev; > + spimdev->cls_dev.class = spimdev_class; > + spimdev->cls_dev.release = vfio_spimdev_release; > + dev_set_name(&spimdev->cls_dev, "%s", dev_name(spimdev- > >dev)); > + ret = device_register(&spimdev->cls_dev); > + if (ret) > + return ret; > + > + spimdev->mdev_fops.owner = spimdev->owner; > + spimdev->mdev_fops.dev_attr_groups = > vfio_spimdev_groups; > + WARN_ON(!spimdev->mdev_fops.supported_type_groups); > + spimdev->mdev_fops.create = > vfio_spimdev_mdev_create; > + spimdev->mdev_fops.remove = > vfio_spimdev_mdev_remove; > + spimdev->mdev_fops.ioctl = vfio_spimdev_mdev_ioctl; > + spimdev->mdev_fops.open = > vfio_spimdev_mdev_open; > + spimdev->mdev_fops.release = > vfio_spimdev_mdev_release; > + > + ret = mdev_register_device(spimdev->dev, &spimdev->mdev_fops); > + if (ret) > + device_unregister(&spimdev->cls_dev); > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(vfio_spimdev_register); > + > +/** > + * vfio_spimdev_unregister - unregisters a spimdev > + * @spimdev: device to unregister > + * > + * Unregister a miscellaneous device that wat previously successully > registered > + * with vfio_spimdev_register(). > + */ > +void vfio_spimdev_unregister(struct vfio_spimdev *spimdev) > +{ > + mdev_unregister_device(spimdev->dev); > + device_unregister(&spimdev->cls_dev); > +} > +EXPORT_SYMBOL_GPL(vfio_spimdev_unregister); > + > +static int __init vfio_spimdev_init(void) > +{ > + spimdev_class = class_create(THIS_MODULE, > VFIO_SPIMDEV_CLASS_NAME); > + return PTR_ERR_OR_ZERO(spimdev_class); > +} > + > +static __exit void vfio_spimdev_exit(void) > +{ > + class_destroy(spimdev_class); > + idr_destroy(&spimdev_idr); > +} > + > +module_init(vfio_spimdev_init); > +module_exit(vfio_spimdev_exit); > + > +MODULE_LICENSE("GPL"); > +MODULE_AUTHOR("Hisilicon Tech. Co., Ltd."); > +MODULE_DESCRIPTION("VFIO Share Parent's IOMMU Mediated Device"); > diff --git a/drivers/vfio/vfio_iommu_type1.c > b/drivers/vfio/vfio_iommu_type1.c > index 3e5b17710a4f..0ec38a17c98c 100644 > --- a/drivers/vfio/vfio_iommu_type1.c > +++ b/drivers/vfio/vfio_iommu_type1.c > @@ -41,6 +41,7 @@ > #include <linux/notifier.h> > #include <linux/dma-iommu.h> > #include <linux/irqdomain.h> > +#include <linux/vfio_spimdev.h> > > #define DRIVER_VERSION "0.2" > #define DRIVER_AUTHOR "Alex Williamson > <alex.williamson@xxxxxxxxxx>" > @@ -89,6 +90,8 @@ struct vfio_dma { > }; > > struct vfio_group { > + /* iommu_group of mdev's parent device */ > + struct iommu_group *parent_group; > struct iommu_group *iommu_group; > struct list_head next; > }; > @@ -1327,6 +1330,109 @@ static bool vfio_iommu_has_sw_msi(struct > iommu_group *group, phys_addr_t *base) > return ret; > } > > +/* return 0 if the device is not spimdev. > + * return 1 if the device is spimdev, the data will be updated with parent > + * device's group. > + * return -errno if other error. > + */ > +static int vfio_spimdev_type(struct device *dev, void *data) > +{ > + struct iommu_group **group = data; > + struct iommu_group *pgroup; > + int (*spimdev_mdev)(struct device *dev); > + struct device *pdev; > + int ret = 1; > + > + /* vfio_spimdev module is not configurated */ > + spimdev_mdev = symbol_get(vfio_spimdev_is_spimdev); > + if (!spimdev_mdev) > + return 0; > + > + /* check if it belongs to vfio_spimdev device */ > + if (!spimdev_mdev(dev)) { > + ret = 0; > + goto get_exit; > + } > + > + pdev = dev->parent; > + pgroup = iommu_group_get(pdev); > + if (!pgroup) { > + ret = -ENODEV; > + goto get_exit; > + } > + > + if (group) { > + /* check if all parent devices is the same */ > + if (*group && *group != pgroup) > + ret = -ENODEV; > + else > + *group = pgroup; > + } > + > + iommu_group_put(pgroup); > + > +get_exit: > + symbol_put(vfio_spimdev_is_spimdev); > + > + return ret; > +} > + > +/* return 0 or -errno */ > +static int vfio_spimdev_bus(struct device *dev, void *data) > +{ > + struct bus_type **bus = data; > + > + if (!dev->bus) > + return -ENODEV; > + > + /* ensure all devices has the same bus_type */ > + if (*bus && *bus != dev->bus) > + return -EINVAL; > + > + *bus = dev->bus; > + return 0; > +} > + > +/* return 0 means it is not spi group, 1 means it is, or -EXXX for error */ > +static int vfio_iommu_type1_attach_spigroup(struct vfio_domain *domain, > + struct vfio_group *group, > + struct iommu_group > *iommu_group) > +{ > + int ret; > + struct bus_type *pbus = NULL; > + struct iommu_group *pgroup = NULL; > + > + ret = iommu_group_for_each_dev(iommu_group, &pgroup, > + vfio_spimdev_type); > + if (ret < 0) > + goto out; > + else if (ret > 0) { > + domain->domain = iommu_group_share_domain(pgroup); > + if (IS_ERR(domain->domain)) > + goto out; > + ret = iommu_group_for_each_dev(pgroup, &pbus, > + vfio_spimdev_bus); > + if (ret < 0) > + goto err_with_share_domain; > + > + if (pbus && iommu_capable(pbus, > IOMMU_CAP_CACHE_COHERENCY)) > + domain->prot |= IOMMU_CACHE; > + > + group->parent_group = pgroup; > + INIT_LIST_HEAD(&domain->group_list); > + list_add(&group->next, &domain->group_list); > + > + return 1; > + } > + > + return 0; > + > +err_with_share_domain: > + iommu_group_unshare_domain(pgroup); > +out: > + return ret; > +} > + > static int vfio_iommu_type1_attach_group(void *iommu_data, > struct iommu_group > *iommu_group) > { > @@ -1335,8 +1441,8 @@ static int vfio_iommu_type1_attach_group(void > *iommu_data, > struct vfio_domain *domain, *d; > struct bus_type *bus = NULL, *mdev_bus; > int ret; > - bool resv_msi, msi_remap; > - phys_addr_t resv_msi_base; > + bool resv_msi = false, msi_remap; > + phys_addr_t resv_msi_base = 0; > > mutex_lock(&iommu->lock); > > @@ -1373,6 +1479,14 @@ static int vfio_iommu_type1_attach_group(void > *iommu_data, > if (mdev_bus) { > if ((bus == mdev_bus) && !iommu_present(bus)) { > symbol_put(mdev_bus_type); > + > + ret = vfio_iommu_type1_attach_spigroup(domain, > group, > + iommu_group); > + if (ret < 0) > + goto out_free; > + else if (ret > 0) > + goto replay_check; > + > if (!iommu->external_domain) { > INIT_LIST_HEAD(&domain->group_list); > iommu->external_domain = domain; > @@ -1451,12 +1565,13 @@ static int > vfio_iommu_type1_attach_group(void *iommu_data, > > vfio_test_domain_fgsp(domain); > > +replay_check: > /* replay mappings on new domains */ > ret = vfio_iommu_replay(iommu, domain); > if (ret) > goto out_detach; > > - if (resv_msi) { > + if (!group->parent_group && resv_msi) { > ret = iommu_get_msi_cookie(domain->domain, > resv_msi_base); > if (ret) > goto out_detach; > @@ -1471,7 +1586,10 @@ static int vfio_iommu_type1_attach_group(void > *iommu_data, > out_detach: > iommu_detach_group(domain->domain, iommu_group); > out_domain: > - iommu_domain_free(domain->domain); > + if (group->parent_group) > + iommu_group_unshare_domain(group->parent_group); > + else > + iommu_domain_free(domain->domain); > out_free: > kfree(domain); > kfree(group); > @@ -1533,6 +1651,7 @@ static void > vfio_iommu_type1_detach_group(void *iommu_data, > struct vfio_iommu *iommu = iommu_data; > struct vfio_domain *domain; > struct vfio_group *group; > + int ret; > > mutex_lock(&iommu->lock); > > @@ -1560,7 +1679,11 @@ static void > vfio_iommu_type1_detach_group(void *iommu_data, > if (!group) > continue; > > - iommu_detach_group(domain->domain, iommu_group); > + if (group->parent_group) > + iommu_group_unshare_domain(group- > >parent_group); > + else > + iommu_detach_group(domain->domain, > iommu_group); > + > list_del(&group->next); > kfree(group); > /* > @@ -1577,7 +1700,8 @@ static void > vfio_iommu_type1_detach_group(void *iommu_data, > else > > vfio_iommu_unmap_unpin_reaccount(iommu); > } > - iommu_domain_free(domain->domain); > + if (!ret) > + iommu_domain_free(domain->domain); > list_del(&domain->next); > kfree(domain); > } > diff --git a/include/linux/vfio_spimdev.h b/include/linux/vfio_spimdev.h > new file mode 100644 > index 000000000000..f7e7d90013e1 > --- /dev/null > +++ b/include/linux/vfio_spimdev.h > @@ -0,0 +1,95 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +#ifndef __VFIO_SPIMDEV_H > +#define __VFIO_SPIMDEV_H > + > +#include <linux/device.h> > +#include <linux/iommu.h> > +#include <linux/mdev.h> > +#include <linux/vfio.h> > +#include <uapi/linux/vfio_spimdev.h> > + > +struct vfio_spimdev_queue; > +struct vfio_spimdev; > + > +/** > + * struct vfio_spimdev_ops - WD device operations > + * @get_queue: get a queue from the device according to algorithm > + * @put_queue: free a queue to the device > + * @is_q_updated: check whether the task is finished > + * @mask_notify: mask the task irq of queue > + * @mmap: mmap addresses of queue to user space > + * @reset: reset the WD device > + * @reset_queue: reset the queue > + * @ioctl: ioctl for user space users of the queue > + * @get_available_instances: get numbers of the queue remained > + */ > +struct vfio_spimdev_ops { > + int (*get_queue)(struct vfio_spimdev *spimdev, unsigned long arg, > + struct vfio_spimdev_queue **q); > + int (*put_queue)(struct vfio_spimdev_queue *q); > + int (*is_q_updated)(struct vfio_spimdev_queue *q); > + void (*mask_notify)(struct vfio_spimdev_queue *q, int > event_mask); > + int (*mmap)(struct vfio_spimdev_queue *q, struct vm_area_struct > *vma); > + int (*reset)(struct vfio_spimdev *spimdev); > + int (*reset_queue)(struct vfio_spimdev_queue *q); > + long (*ioctl)(struct vfio_spimdev_queue *q, unsigned int cmd, > + unsigned long arg); > + int (*get_available_instances)(struct vfio_spimdev *spimdev); > +}; > + > +struct vfio_spimdev_queue { > + struct mutex mutex; > + struct vfio_spimdev *spimdev; > + int qid; > + __u32 flags; > + void *priv; > + wait_queue_head_t wait; > + struct mdev_device *mdev; > + int fd; > + int container; > +#ifdef CONFIG_IOMMU_SVA > + int pasid; > +#endif > +}; > + > +struct vfio_spimdev { > + const char *name; > + int status; > + atomic_t ref; > + struct module *owner; > + const struct vfio_spimdev_ops *ops; > + struct device *dev; > + struct device cls_dev; > + bool is_vf; > + u32 iommu_type; > + u32 dma_flag; > + u32 dev_id; > + void *priv; > + int flags; > + const char *api_ver; > + struct mdev_parent_ops mdev_fops; > +}; > + > +int vfio_spimdev_register(struct vfio_spimdev *spimdev); > +void vfio_spimdev_unregister(struct vfio_spimdev *spimdev); > +void vfio_spimdev_wake_up(struct vfio_spimdev_queue *q); > +int vfio_spimdev_is_spimdev(struct device *dev); > +struct vfio_spimdev *vfio_spimdev_pdev_spimdev(struct device *dev); > +int vfio_spimdev_pasid_pri_check(int pasid); > +int vfio_spimdev_get(struct device *dev); > +int vfio_spimdev_put(struct device *dev); > +struct vfio_spimdev *mdev_spimdev(struct mdev_device *mdev); > + > +extern struct mdev_type_attribute mdev_type_attr_flags; > +extern struct mdev_type_attribute mdev_type_attr_name; > +extern struct mdev_type_attribute mdev_type_attr_device_api; > +extern struct mdev_type_attribute mdev_type_attr_available_instances; > +#define VFIO_SPIMDEV_DEFAULT_MDEV_TYPE_ATTRS \ > + &mdev_type_attr_name.attr, \ > + &mdev_type_attr_device_api.attr, \ > + &mdev_type_attr_available_instances.attr, \ > + &mdev_type_attr_flags.attr > + > +#define _VFIO_SPIMDEV_REGION(vm_pgoff) (vm_pgoff & 0xf) > + > +#endif > diff --git a/include/uapi/linux/vfio_spimdev.h > b/include/uapi/linux/vfio_spimdev.h > new file mode 100644 > index 000000000000..3435e5c345b4 > --- /dev/null > +++ b/include/uapi/linux/vfio_spimdev.h > @@ -0,0 +1,28 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +#ifndef _UAPIVFIO_SPIMDEV_H > +#define _UAPIVFIO_SPIMDEV_H > + > +#include <linux/ioctl.h> > + > +#define VFIO_SPIMDEV_CLASS_NAME "spimdev" > + > +/* Device ATTRs in parent dev SYSFS DIR */ > +#define VFIO_SPIMDEV_PDEV_ATTRS_GRP_NAME "params" > + > +/* Parent device attributes */ > +#define SPIMDEV_IOMMU_TYPE "iommu_type" > +#define SPIMDEV_DMA_FLAG "dma_flag" > + > +/* Maximum length of algorithm name string */ > +#define VFIO_SPIMDEV_ALG_NAME_SIZE 64 > + > +/* the bits used in SPIMDEV_DMA_FLAG attributes */ > +#define VFIO_SPIMDEV_DMA_INVALID 0 > +#define VFIO_SPIMDEV_DMA_SINGLE_PROC_MAP 1 > +#define VFIO_SPIMDEV_DMA_MULTI_PROC_MAP 2 > +#define VFIO_SPIMDEV_DMA_SVM 4 > +#define VFIO_SPIMDEV_DMA_SVM_NO_FAULT 8 > +#define VFIO_SPIMDEV_DMA_PHY 16 > + > +#define VFIO_SPIMDEV_CMD_GET_Q _IO('W', 1) > +#endif > -- > 2.17.1