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. 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