> From: Kenneth Lee
> Sent: Wednesday, August 1, 2018 6:22 PM
> 
> From: Kenneth Lee <liguo...@hisilicon.com>
> 
> 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 <liguo...@hisilicon.com>
> Signed-off-by: Zaibo Xu <xuza...@huawei.com>
> Signed-off-by: Zhou Wang <wangzh...@hisilicon.com>
> ---
>  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.william...@redhat.com>"
> @@ -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

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to