[RFC PATCH 3/7] vfio: add spimdev support

Tian, Kevin kevin.tian at intel.com
Thu Aug 2 13:21:25 AEST 2018


> From: Kenneth Lee
> Sent: Wednesday, August 1, 2018 6:22 PM
> 
> From: Kenneth Lee <liguozhu at 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 <liguozhu at hisilicon.com>
> Signed-off-by: Zaibo Xu <xuzaibo at huawei.com>
> Signed-off-by: Zhou Wang <wangzhou1 at 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.williamson at 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



More information about the Linux-accelerators mailing list