Message ID | 20180903005204.26041-4-nek.in.cn@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | A General Accelerator Framework, WarpDrive | expand |
On 09/02/2018 05:52 PM, Kenneth Lee wrote: > diff --git a/drivers/vfio/sdmdev/Kconfig b/drivers/vfio/sdmdev/Kconfig > new file mode 100644 > index 000000000000..51474272870d > --- /dev/null > +++ b/drivers/vfio/sdmdev/Kconfig > @@ -0,0 +1,10 @@ > +# SPDX-License-Identifier: GPL-2.0 > +config VFIO_SDMDEV > + tristate "Support for Share Domain MDEV" > + depends on VFIO_MDEV_DEVICE > + help > + Support for VFIO Share Domain MDEV, which enables the kernel to > + support light weight hardware accelerator framework, WarpDrive. lightweight > + > + To compile this as a module, choose M here: the module will be called > + sdmdev. thanks,
Hi, On 09/03/2018 08:52 AM, Kenneth Lee wrote: > From: Kenneth Lee <liguozhu@hisilicon.com> > > SDMDEV is "Share Domain Mdev". It is a vfio-mdev. But differ from > the general vfio-mdev, it shares its parent's IOMMU. If Multi-PASID > support is enabled in the IOMMU (not yet in the current kernel HEAD), > multiple process can share the IOMMU by different PASID. If it is not > support, only one process can share the IOMMU with the kernel driver. > If only for share domain purpose, I don't think it's necessary to create a new device type. > Currently only the vfio type-1 driver is updated to make it to be aware > of. > > Signed-off-by: Kenneth Lee <liguozhu@hisilicon.com> > Signed-off-by: Zaibo Xu <xuzaibo@huawei.com> > Signed-off-by: Zhou Wang <wangzhou1@hisilicon.com> > --- > drivers/vfio/Kconfig | 1 + > drivers/vfio/Makefile | 1 + > drivers/vfio/sdmdev/Kconfig | 10 + > drivers/vfio/sdmdev/Makefile | 3 + > drivers/vfio/sdmdev/vfio_sdmdev.c | 363 ++++++++++++++++++++++++++++++ > drivers/vfio/vfio_iommu_type1.c | 151 ++++++++++++- > include/linux/vfio_sdmdev.h | 96 ++++++++ > include/uapi/linux/vfio_sdmdev.h | 29 +++ > 8 files changed, 648 insertions(+), 6 deletions(-) > create mode 100644 drivers/vfio/sdmdev/Kconfig > create mode 100644 drivers/vfio/sdmdev/Makefile > create mode 100644 drivers/vfio/sdmdev/vfio_sdmdev.c > create mode 100644 include/linux/vfio_sdmdev.h > create mode 100644 include/uapi/linux/vfio_sdmdev.h > [--cut for short --] > diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c > index d9fd3188615d..ba73231d8692 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_sdmdev.h> > > #define DRIVER_VERSION "0.2" > #define DRIVER_AUTHOR "Alex Williamson <alex.williamson@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 sdmdev. > + * return 1 if the device is sdmdev, the data will be updated with parent > + * device's group. > + * return -errno if other error. > + */ > +static int vfio_sdmdev_type(struct device *dev, void *data) > +{ > + struct iommu_group **group = data; > + struct iommu_group *pgroup; > + int (*_is_sdmdev)(struct device *dev); > + struct device *pdev; > + int ret = 1; > + > + /* vfio_sdmdev module is not configurated */ > + _is_sdmdev = symbol_get(vfio_sdmdev_is_sdmdev); > + if (!_is_sdmdev) > + return 0; > + > + /* check if it belongs to vfio_sdmdev device */ > + if (!_is_sdmdev(dev)) { > + ret = 0; > + goto out; > + } > + > + pdev = dev->parent; > + pgroup = iommu_group_get(pdev); > + if (!pgroup) { > + ret = -ENODEV; > + goto out; > + } > + > + if (group) { > + /* check if all parent devices is the same */ > + if (*group && *group != pgroup) > + ret = -ENODEV; > + else > + *group = pgroup; > + } > + > + iommu_group_put(pgroup); > + > +out: > + symbol_put(vfio_sdmdev_is_sdmdev); > + > + return ret; > +} > + > +/* return 0 or -errno */ > +static int vfio_sdmdev_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 sd group, 1 means it is, or -EXXX for error */ > +static int vfio_iommu_type1_attach_sdgroup(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_sdmdev_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_sdmdev_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; > + } This doesn't match the function name. It only gets the domain from the parent device. It hasn't been really attached. > + > + 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_sdgroup(domain, group, > + iommu_group); > + if (ret < 0) > + goto out_free; > + else if (ret > 0) > + goto replay_check; Here you get the domain from the parent device and save it for later use. The actual attaching is ignored. I don't think this follows the philosophy of this function. It actually make all devices in the group with the same bus type to share a single domain. Further more, the parent domain might be a domain of type IOMMU_DOMAIN_DMA. That will not be able to use as an IOMMU_DOMAIN_UNMANAGED domain for iommu APIs. Best regards, Lu Baolu
Hi Kenneth,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on cryptodev/master]
[also build test WARNING on v4.19-rc2 next-20180831]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Kenneth-Lee/A-General-Accelerator-Framework-WarpDrive/20180903-162733
base: https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master
reproduce:
# apt-get install sparse
make ARCH=x86_64 allmodconfig
make C=1 CF=-D__CHECK_ENDIAN__
:::::: branch date: 3 hours ago
:::::: commit date: 3 hours ago
>> drivers/vfio/sdmdev/vfio_sdmdev.c:106:30: sparse: symbol 'vfio_sdmdev_groups' was not declared. Should it be static?
drivers/vfio/sdmdev/vfio_sdmdev.c: In function 'vfio_sdmdev_mdev_remove':
drivers/vfio/sdmdev/vfio_sdmdev.c:178:2: warning: this 'if' clause does not guard... [-Wmisleading-indentation]
if (sdmdev->ops->put_queue);
^~
drivers/vfio/sdmdev/vfio_sdmdev.c:179:3: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'
sdmdev->ops->put_queue(q);
^~~~~~
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Kenneth,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on cryptodev/master]
[also build test WARNING on v4.19-rc2 next-20180831]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Kenneth-Lee/A-General-Accelerator-Framework-WarpDrive/20180903-162733
base: https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master
config: i386-allmodconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
:::::: branch date: 2 hours ago
:::::: commit date: 2 hours ago
All warnings (new ones prefixed by >>):
drivers/vfio/sdmdev/vfio_sdmdev.c: In function 'vfio_sdmdev_mdev_remove':
>> drivers/vfio/sdmdev/vfio_sdmdev.c:178:2: warning: this 'if' clause does not guard... [-Wmisleading-indentation]
if (sdmdev->ops->put_queue);
^~
drivers/vfio/sdmdev/vfio_sdmdev.c:179:3: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'
sdmdev->ops->put_queue(q);
^~~~~~
# https://github.com/0day-ci/linux/commit/1e47d5e608652b4a2c813dbeaf5aa6811f6ceaf7
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 1e47d5e608652b4a2c813dbeaf5aa6811f6ceaf7
vim +/if +178 drivers/vfio/sdmdev/vfio_sdmdev.c
1e47d5e6 Kenneth Lee 2018-09-03 168
1e47d5e6 Kenneth Lee 2018-09-03 169 static int vfio_sdmdev_mdev_remove(struct mdev_device *mdev)
1e47d5e6 Kenneth Lee 2018-09-03 170 {
1e47d5e6 Kenneth Lee 2018-09-03 171 struct vfio_sdmdev_queue *q =
1e47d5e6 Kenneth Lee 2018-09-03 172 (struct vfio_sdmdev_queue *)mdev_get_drvdata(mdev);
1e47d5e6 Kenneth Lee 2018-09-03 173 struct vfio_sdmdev *sdmdev = q->sdmdev;
1e47d5e6 Kenneth Lee 2018-09-03 174 struct device *pdev = mdev_parent_dev(mdev);
1e47d5e6 Kenneth Lee 2018-09-03 175
1e47d5e6 Kenneth Lee 2018-09-03 176 put_device(pdev);
1e47d5e6 Kenneth Lee 2018-09-03 177
1e47d5e6 Kenneth Lee 2018-09-03 @178 if (sdmdev->ops->put_queue);
1e47d5e6 Kenneth Lee 2018-09-03 179 sdmdev->ops->put_queue(q);
1e47d5e6 Kenneth Lee 2018-09-03 180
1e47d5e6 Kenneth Lee 2018-09-03 181 return 0;
1e47d5e6 Kenneth Lee 2018-09-03 182 }
1e47d5e6 Kenneth Lee 2018-09-03 183
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Kenneth, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on cryptodev/master] [also build test WARNING on v4.19-rc2 next-20180905] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Kenneth-Lee/A-General-Accelerator-Framework-WarpDrive/20180903-162733 base: https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master smatch warnings: drivers/vfio/sdmdev/vfio_sdmdev.c:78 iommu_type_show() error: 'sdmdev' dereferencing possible ERR_PTR() drivers/vfio/sdmdev/vfio_sdmdev.c:91 dma_flag_show() error: 'sdmdev' dereferencing possible ERR_PTR() drivers/vfio/sdmdev/vfio_sdmdev.c:127 flags_show() error: 'sdmdev' dereferencing possible ERR_PTR() drivers/vfio/sdmdev/vfio_sdmdev.c:128 name_show() error: 'sdmdev' dereferencing possible ERR_PTR() drivers/vfio/sdmdev/vfio_sdmdev.c:130 device_api_show() error: 'sdmdev' dereferencing possible ERR_PTR() drivers/vfio/sdmdev/vfio_sdmdev.c:138 available_instances_show() error: 'sdmdev' dereferencing possible ERR_PTR() drivers/vfio/sdmdev/vfio_sdmdev.c:178 vfio_sdmdev_mdev_remove() warn: if(); # https://github.com/0day-ci/linux/commit/1e47d5e608652b4a2c813dbeaf5aa6811f6ceaf7 git remote add linux-review https://github.com/0day-ci/linux git remote update linux-review git checkout 1e47d5e608652b4a2c813dbeaf5aa6811f6ceaf7 vim +/sdmdev +78 drivers/vfio/sdmdev/vfio_sdmdev.c 1e47d5e6 Kenneth Lee 2018-09-03 69 1e47d5e6 Kenneth Lee 2018-09-03 70 static ssize_t iommu_type_show(struct device *dev, 1e47d5e6 Kenneth Lee 2018-09-03 71 struct device_attribute *attr, char *buf) 1e47d5e6 Kenneth Lee 2018-09-03 72 { 1e47d5e6 Kenneth Lee 2018-09-03 73 struct vfio_sdmdev *sdmdev = vfio_sdmdev_pdev_sdmdev(dev); ^^^^^^^^^^^^^^^^^^^^^^^ Presumably this returns error pointers instead of NULL? 1e47d5e6 Kenneth Lee 2018-09-03 74 1e47d5e6 Kenneth Lee 2018-09-03 75 if (!sdmdev) 1e47d5e6 Kenneth Lee 2018-09-03 76 return -ENODEV; 1e47d5e6 Kenneth Lee 2018-09-03 77 1e47d5e6 Kenneth Lee 2018-09-03 @78 return sprintf(buf, "%d\n", sdmdev->iommu_type); 1e47d5e6 Kenneth Lee 2018-09-03 79 } 1e47d5e6 Kenneth Lee 2018-09-03 80 1e47d5e6 Kenneth Lee 2018-09-03 81 static DEVICE_ATTR_RO(iommu_type); 1e47d5e6 Kenneth Lee 2018-09-03 82 1e47d5e6 Kenneth Lee 2018-09-03 83 static ssize_t dma_flag_show(struct device *dev, 1e47d5e6 Kenneth Lee 2018-09-03 84 struct device_attribute *attr, char *buf) 1e47d5e6 Kenneth Lee 2018-09-03 85 { 1e47d5e6 Kenneth Lee 2018-09-03 86 struct vfio_sdmdev *sdmdev = vfio_sdmdev_pdev_sdmdev(dev); 1e47d5e6 Kenneth Lee 2018-09-03 87 1e47d5e6 Kenneth Lee 2018-09-03 88 if (!sdmdev) 1e47d5e6 Kenneth Lee 2018-09-03 89 return -ENODEV; 1e47d5e6 Kenneth Lee 2018-09-03 90 1e47d5e6 Kenneth Lee 2018-09-03 @91 return sprintf(buf, "%d\n", sdmdev->dma_flag); 1e47d5e6 Kenneth Lee 2018-09-03 92 } 1e47d5e6 Kenneth Lee 2018-09-03 93 1e47d5e6 Kenneth Lee 2018-09-03 94 static DEVICE_ATTR_RO(dma_flag); 1e47d5e6 Kenneth Lee 2018-09-03 95 1e47d5e6 Kenneth Lee 2018-09-03 96 /* mdev->dev_attr_groups */ 1e47d5e6 Kenneth Lee 2018-09-03 97 static struct attribute *vfio_sdmdev_attrs[] = { 1e47d5e6 Kenneth Lee 2018-09-03 98 &dev_attr_iommu_type.attr, 1e47d5e6 Kenneth Lee 2018-09-03 99 &dev_attr_dma_flag.attr, 1e47d5e6 Kenneth Lee 2018-09-03 100 NULL, 1e47d5e6 Kenneth Lee 2018-09-03 101 }; 1e47d5e6 Kenneth Lee 2018-09-03 102 static const struct attribute_group vfio_sdmdev_group = { 1e47d5e6 Kenneth Lee 2018-09-03 103 .name = VFIO_SDMDEV_PDEV_ATTRS_GRP_NAME, 1e47d5e6 Kenneth Lee 2018-09-03 104 .attrs = vfio_sdmdev_attrs, 1e47d5e6 Kenneth Lee 2018-09-03 105 }; 1e47d5e6 Kenneth Lee 2018-09-03 106 const struct attribute_group *vfio_sdmdev_groups[] = { 1e47d5e6 Kenneth Lee 2018-09-03 107 &vfio_sdmdev_group, 1e47d5e6 Kenneth Lee 2018-09-03 108 NULL, 1e47d5e6 Kenneth Lee 2018-09-03 109 }; 1e47d5e6 Kenneth Lee 2018-09-03 110 1e47d5e6 Kenneth Lee 2018-09-03 111 /* default attributes for mdev->supported_type_groups, used by registerer*/ 1e47d5e6 Kenneth Lee 2018-09-03 112 #define MDEV_TYPE_ATTR_RO_EXPORT(name) \ 1e47d5e6 Kenneth Lee 2018-09-03 113 MDEV_TYPE_ATTR_RO(name); \ 1e47d5e6 Kenneth Lee 2018-09-03 114 EXPORT_SYMBOL_GPL(mdev_type_attr_##name); 1e47d5e6 Kenneth Lee 2018-09-03 115 1e47d5e6 Kenneth Lee 2018-09-03 116 #define DEF_SIMPLE_SDMDEV_ATTR(_name, sdmdev_member, format) \ 1e47d5e6 Kenneth Lee 2018-09-03 117 static ssize_t _name##_show(struct kobject *kobj, struct device *dev, \ 1e47d5e6 Kenneth Lee 2018-09-03 118 char *buf) \ 1e47d5e6 Kenneth Lee 2018-09-03 119 { \ 1e47d5e6 Kenneth Lee 2018-09-03 120 struct vfio_sdmdev *sdmdev = vfio_sdmdev_pdev_sdmdev(dev); \ 1e47d5e6 Kenneth Lee 2018-09-03 121 if (!sdmdev) \ 1e47d5e6 Kenneth Lee 2018-09-03 122 return -ENODEV; \ 1e47d5e6 Kenneth Lee 2018-09-03 123 return sprintf(buf, format, sdmdev->sdmdev_member); \ 1e47d5e6 Kenneth Lee 2018-09-03 124 } \ 1e47d5e6 Kenneth Lee 2018-09-03 125 MDEV_TYPE_ATTR_RO_EXPORT(_name) 1e47d5e6 Kenneth Lee 2018-09-03 126 1e47d5e6 Kenneth Lee 2018-09-03 @127 DEF_SIMPLE_SDMDEV_ATTR(flags, flags, "%d"); 1e47d5e6 Kenneth Lee 2018-09-03 @128 DEF_SIMPLE_SDMDEV_ATTR(name, name, "%s"); /* this should be algorithm name, */ 1e47d5e6 Kenneth Lee 2018-09-03 129 /* but you would not care if you have only one algorithm */ 1e47d5e6 Kenneth Lee 2018-09-03 @130 DEF_SIMPLE_SDMDEV_ATTR(device_api, api_ver, "%s"); 1e47d5e6 Kenneth Lee 2018-09-03 131 1e47d5e6 Kenneth Lee 2018-09-03 132 static ssize_t 1e47d5e6 Kenneth Lee 2018-09-03 133 available_instances_show(struct kobject *kobj, struct device *dev, char *buf) 1e47d5e6 Kenneth Lee 2018-09-03 134 { 1e47d5e6 Kenneth Lee 2018-09-03 135 struct vfio_sdmdev *sdmdev = vfio_sdmdev_pdev_sdmdev(dev); 1e47d5e6 Kenneth Lee 2018-09-03 136 int nr_inst = 0; 1e47d5e6 Kenneth Lee 2018-09-03 137 1e47d5e6 Kenneth Lee 2018-09-03 @138 nr_inst = sdmdev->ops->get_available_instances ? 1e47d5e6 Kenneth Lee 2018-09-03 139 sdmdev->ops->get_available_instances(sdmdev) : 0; 1e47d5e6 Kenneth Lee 2018-09-03 140 return sprintf(buf, "%d", nr_inst); 1e47d5e6 Kenneth Lee 2018-09-03 141 } 1e47d5e6 Kenneth Lee 2018-09-03 142 MDEV_TYPE_ATTR_RO_EXPORT(available_instances); 1e47d5e6 Kenneth Lee 2018-09-03 143 1e47d5e6 Kenneth Lee 2018-09-03 144 static int vfio_sdmdev_mdev_create(struct kobject *kobj, 1e47d5e6 Kenneth Lee 2018-09-03 145 struct mdev_device *mdev) 1e47d5e6 Kenneth Lee 2018-09-03 146 { 1e47d5e6 Kenneth Lee 2018-09-03 147 struct device *pdev = mdev_parent_dev(mdev); 1e47d5e6 Kenneth Lee 2018-09-03 148 struct vfio_sdmdev_queue *q; 1e47d5e6 Kenneth Lee 2018-09-03 149 struct vfio_sdmdev *sdmdev = mdev_sdmdev(mdev); 1e47d5e6 Kenneth Lee 2018-09-03 150 int ret; 1e47d5e6 Kenneth Lee 2018-09-03 151 1e47d5e6 Kenneth Lee 2018-09-03 152 if (!sdmdev->ops->get_queue) 1e47d5e6 Kenneth Lee 2018-09-03 153 return -ENODEV; 1e47d5e6 Kenneth Lee 2018-09-03 154 1e47d5e6 Kenneth Lee 2018-09-03 155 ret = sdmdev->ops->get_queue(sdmdev, &q); 1e47d5e6 Kenneth Lee 2018-09-03 156 if (ret) 1e47d5e6 Kenneth Lee 2018-09-03 157 return ret; 1e47d5e6 Kenneth Lee 2018-09-03 158 1e47d5e6 Kenneth Lee 2018-09-03 159 q->sdmdev = sdmdev; 1e47d5e6 Kenneth Lee 2018-09-03 160 q->mdev = mdev; 1e47d5e6 Kenneth Lee 2018-09-03 161 init_waitqueue_head(&q->wait); 1e47d5e6 Kenneth Lee 2018-09-03 162 1e47d5e6 Kenneth Lee 2018-09-03 163 mdev_set_drvdata(mdev, q); 1e47d5e6 Kenneth Lee 2018-09-03 164 get_device(pdev); 1e47d5e6 Kenneth Lee 2018-09-03 165 1e47d5e6 Kenneth Lee 2018-09-03 166 return 0; 1e47d5e6 Kenneth Lee 2018-09-03 167 } 1e47d5e6 Kenneth Lee 2018-09-03 168 1e47d5e6 Kenneth Lee 2018-09-03 169 static int vfio_sdmdev_mdev_remove(struct mdev_device *mdev) 1e47d5e6 Kenneth Lee 2018-09-03 170 { 1e47d5e6 Kenneth Lee 2018-09-03 171 struct vfio_sdmdev_queue *q = 1e47d5e6 Kenneth Lee 2018-09-03 172 (struct vfio_sdmdev_queue *)mdev_get_drvdata(mdev); 1e47d5e6 Kenneth Lee 2018-09-03 173 struct vfio_sdmdev *sdmdev = q->sdmdev; 1e47d5e6 Kenneth Lee 2018-09-03 174 struct device *pdev = mdev_parent_dev(mdev); 1e47d5e6 Kenneth Lee 2018-09-03 175 1e47d5e6 Kenneth Lee 2018-09-03 176 put_device(pdev); 1e47d5e6 Kenneth Lee 2018-09-03 177 1e47d5e6 Kenneth Lee 2018-09-03 @178 if (sdmdev->ops->put_queue); ^ Extra semicolon breaks the code. 1e47d5e6 Kenneth Lee 2018-09-03 179 sdmdev->ops->put_queue(q); 1e47d5e6 Kenneth Lee 2018-09-03 180 1e47d5e6 Kenneth Lee 2018-09-03 181 return 0; 1e47d5e6 Kenneth Lee 2018-09-03 182 } 1e47d5e6 Kenneth Lee 2018-09-03 183 --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
On Sun, Sep 02, 2018 at 07:11:12PM -0700, Randy Dunlap wrote: > Date: Sun, 2 Sep 2018 19:11:12 -0700 > From: Randy Dunlap <rdunlap@infradead.org> > To: Kenneth Lee <nek.in.cn@gmail.com>, Jonathan Corbet <corbet@lwn.net>, > Herbert Xu <herbert@gondor.apana.org.au>, "David S . Miller" > <davem@davemloft.net>, Joerg Roedel <joro@8bytes.org>, Alex Williamson > <alex.williamson@redhat.com>, Kenneth Lee <liguozhu@hisilicon.com>, Hao > Fang <fanghao11@huawei.com>, Zhou Wang <wangzhou1@hisilicon.com>, Zaibo Xu > <xuzaibo@huawei.com>, Philippe Ombredanne <pombredanne@nexb.com>, Greg > Kroah-Hartman <gregkh@linuxfoundation.org>, Thomas Gleixner > <tglx@linutronix.de>, linux-doc@vger.kernel.org, > linux-kernel@vger.kernel.org, linux-crypto@vger.kernel.org, > iommu@lists.linux-foundation.org, kvm@vger.kernel.org, > linux-accelerators@lists.ozlabs.org, Lu Baolu <baolu.lu@linux.intel.com>, > Sanjay Kumar <sanjay.k.kumar@intel.com> > CC: linuxarm@huawei.com > Subject: Re: [PATCH 3/7] vfio: add sdmdev support > User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 > Thunderbird/52.9.1 > Message-ID: <0574e7dc-bed2-d2d0-3aa6-93590d54ce81@infradead.org> > > On 09/02/2018 05:52 PM, Kenneth Lee wrote: > > diff --git a/drivers/vfio/sdmdev/Kconfig b/drivers/vfio/sdmdev/Kconfig > > new file mode 100644 > > index 000000000000..51474272870d > > --- /dev/null > > +++ b/drivers/vfio/sdmdev/Kconfig > > @@ -0,0 +1,10 @@ > > +# SPDX-License-Identifier: GPL-2.0 > > +config VFIO_SDMDEV > > + tristate "Support for Share Domain MDEV" > > + depends on VFIO_MDEV_DEVICE > > + help > > + Support for VFIO Share Domain MDEV, which enables the kernel to > > + support light weight hardware accelerator framework, WarpDrive. > > lightweight > Thank you, will fix it. > > + > > + To compile this as a module, choose M here: the module will be called > > + sdmdev. > > > thanks, > -- > ~Randy
On Mon, Sep 03, 2018 at 10:55:57AM +0800, Lu Baolu wrote: > Date: Mon, 3 Sep 2018 10:55:57 +0800 > From: Lu Baolu <baolu.lu@linux.intel.com> > To: Kenneth Lee <nek.in.cn@gmail.com>, Jonathan Corbet <corbet@lwn.net>, > Herbert Xu <herbert@gondor.apana.org.au>, "David S . Miller" > <davem@davemloft.net>, Joerg Roedel <joro@8bytes.org>, Alex Williamson > <alex.williamson@redhat.com>, Kenneth Lee <liguozhu@hisilicon.com>, Hao > Fang <fanghao11@huawei.com>, Zhou Wang <wangzhou1@hisilicon.com>, Zaibo Xu > <xuzaibo@huawei.com>, Philippe Ombredanne <pombredanne@nexb.com>, Greg > Kroah-Hartman <gregkh@linuxfoundation.org>, Thomas Gleixner > <tglx@linutronix.de>, linux-doc@vger.kernel.org, > linux-kernel@vger.kernel.org, linux-crypto@vger.kernel.org, > iommu@lists.linux-foundation.org, kvm@vger.kernel.org, > linux-accelerators@lists.ozlabs.org, Sanjay Kumar > <sanjay.k.kumar@intel.com> > CC: linuxarm@huawei.com, baolu.lu@linux.intel.com > Subject: Re: [PATCH 3/7] vfio: add sdmdev support > User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 > Thunderbird/52.9.1 > Message-ID: <4ea51b20-dcc1-db32-18eb-24a004ab9085@linux.intel.com> > > Hi, > > On 09/03/2018 08:52 AM, Kenneth Lee wrote: > >From: Kenneth Lee <liguozhu@hisilicon.com> > > > >SDMDEV is "Share Domain Mdev". It is a vfio-mdev. But differ from > >the general vfio-mdev, it shares its parent's IOMMU. If Multi-PASID > >support is enabled in the IOMMU (not yet in the current kernel HEAD), > >multiple process can share the IOMMU by different PASID. If it is not > >support, only one process can share the IOMMU with the kernel driver. > > > > If only for share domain purpose, I don't think it's necessary to create > a new device type. > Yes, if ONLY for share domain purpose. But we need also to share the interrupt. > >Currently only the vfio type-1 driver is updated to make it to be aware > >of. > > > >Signed-off-by: Kenneth Lee <liguozhu@hisilicon.com> > >Signed-off-by: Zaibo Xu <xuzaibo@huawei.com> > >Signed-off-by: Zhou Wang <wangzhou1@hisilicon.com> > >--- > > drivers/vfio/Kconfig | 1 + > > drivers/vfio/Makefile | 1 + > > drivers/vfio/sdmdev/Kconfig | 10 + > > drivers/vfio/sdmdev/Makefile | 3 + > > drivers/vfio/sdmdev/vfio_sdmdev.c | 363 ++++++++++++++++++++++++++++++ > > drivers/vfio/vfio_iommu_type1.c | 151 ++++++++++++- > > include/linux/vfio_sdmdev.h | 96 ++++++++ > > include/uapi/linux/vfio_sdmdev.h | 29 +++ > > 8 files changed, 648 insertions(+), 6 deletions(-) > > create mode 100644 drivers/vfio/sdmdev/Kconfig > > create mode 100644 drivers/vfio/sdmdev/Makefile > > create mode 100644 drivers/vfio/sdmdev/vfio_sdmdev.c > > create mode 100644 include/linux/vfio_sdmdev.h > > create mode 100644 include/uapi/linux/vfio_sdmdev.h > > > > [--cut for short --] > > >diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c > >index d9fd3188615d..ba73231d8692 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_sdmdev.h> > > #define DRIVER_VERSION "0.2" > > #define DRIVER_AUTHOR "Alex Williamson <alex.williamson@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 sdmdev. > >+ * return 1 if the device is sdmdev, the data will be updated with parent > >+ * device's group. > >+ * return -errno if other error. > >+ */ > >+static int vfio_sdmdev_type(struct device *dev, void *data) > >+{ > >+ struct iommu_group **group = data; > >+ struct iommu_group *pgroup; > >+ int (*_is_sdmdev)(struct device *dev); > >+ struct device *pdev; > >+ int ret = 1; > >+ > >+ /* vfio_sdmdev module is not configurated */ > >+ _is_sdmdev = symbol_get(vfio_sdmdev_is_sdmdev); > >+ if (!_is_sdmdev) > >+ return 0; > >+ > >+ /* check if it belongs to vfio_sdmdev device */ > >+ if (!_is_sdmdev(dev)) { > >+ ret = 0; > >+ goto out; > >+ } > >+ > >+ pdev = dev->parent; > >+ pgroup = iommu_group_get(pdev); > >+ if (!pgroup) { > >+ ret = -ENODEV; > >+ goto out; > >+ } > >+ > >+ if (group) { > >+ /* check if all parent devices is the same */ > >+ if (*group && *group != pgroup) > >+ ret = -ENODEV; > >+ else > >+ *group = pgroup; > >+ } > >+ > >+ iommu_group_put(pgroup); > >+ > >+out: > >+ symbol_put(vfio_sdmdev_is_sdmdev); > >+ > >+ return ret; > >+} > >+ > >+/* return 0 or -errno */ > >+static int vfio_sdmdev_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 sd group, 1 means it is, or -EXXX for error */ > >+static int vfio_iommu_type1_attach_sdgroup(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_sdmdev_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_sdmdev_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; > >+ } > > This doesn't match the function name. It only gets the domain from the > parent device. It hasn't been really attached. > > >+ > >+ 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_sdgroup(domain, group, > >+ iommu_group); > >+ if (ret < 0) > >+ goto out_free; > >+ else if (ret > 0) > >+ goto replay_check; > > Here you get the domain from the parent device and save it for later > use. The actual attaching is ignored. > > I don't think this follows the philosophy of this function. It actually > make all devices in the group with the same bus type to share a single > domain. I think the original logic here is: 1. Create a new vfio_domain along with a iommu_domain for the group attached to the container 2. Try to match the vfio_domain with the domain list in the container. If there is a match, free the created one and and reuse it, or add the new vfio_domain to the list. With this design, the same configuration to the IOMMU(unit) will be applied only once. For iommu_group that shares IOMMU with its parent, the configuration will never be the same (The PASID will be different), so it is not necessary to merge them. > > Further more, the parent domain might be a domain of type > IOMMU_DOMAIN_DMA. That will not be able to use as an > IOMMU_DOMAIN_UNMANAGED domain for iommu APIs. Indeed, it should be checked when the domain is shared. Unmanaged domain should not be used for sharing. I will update it in the future. > > Best regards, > Lu Baolu
diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index c84333eb5eb5..5af7d1db505e 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/sdmdev/Kconfig" source "virt/lib/Kconfig" diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index de67c4725cce..678592360a7a 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_SDMDEV) += sdmdev/ diff --git a/drivers/vfio/sdmdev/Kconfig b/drivers/vfio/sdmdev/Kconfig new file mode 100644 index 000000000000..51474272870d --- /dev/null +++ b/drivers/vfio/sdmdev/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +config VFIO_SDMDEV + tristate "Support for Share Domain MDEV" + depends on VFIO_MDEV_DEVICE + help + Support for VFIO Share Domain MDEV, which enables the kernel to + support light weight hardware accelerator framework, WarpDrive. + + To compile this as a module, choose M here: the module will be called + sdmdev. diff --git a/drivers/vfio/sdmdev/Makefile b/drivers/vfio/sdmdev/Makefile new file mode 100644 index 000000000000..ccaaa03f3184 --- /dev/null +++ b/drivers/vfio/sdmdev/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +sdmdev-y := sdmdev.o +obj-$(CONFIG_VFIO_SDMDEV) += vfio_sdmdev.o diff --git a/drivers/vfio/sdmdev/vfio_sdmdev.c b/drivers/vfio/sdmdev/vfio_sdmdev.c new file mode 100644 index 000000000000..c6eb5d4bdab0 --- /dev/null +++ b/drivers/vfio/sdmdev/vfio_sdmdev.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include <linux/module.h> +#include <linux/vfio_sdmdev.h> + +static struct class *sdmdev_class; + +static int vfio_sdmdev_dev_exist(struct device *dev, void *data) +{ + return !strcmp(dev_name(dev), dev_name((struct device *)data)); +} + +#ifdef CONFIG_IOMMU_SVA +static bool vfio_sdmdev_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_sdmdev */ +int vfio_sdmdev_is_sdmdev(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(sdmdev_class, NULL, pdev, + vfio_sdmdev_dev_exist); +} +EXPORT_SYMBOL_GPL(vfio_sdmdev_is_sdmdev); + +struct vfio_sdmdev *vfio_sdmdev_pdev_sdmdev(struct device *dev) +{ + struct device *class_dev; + + if (!dev) + return ERR_PTR(-EINVAL); + + class_dev = class_find_device(sdmdev_class, NULL, dev, + (int(*)(struct device *, const void *))vfio_sdmdev_dev_exist); + if (!class_dev) + return ERR_PTR(-ENODEV); + + return container_of(class_dev, struct vfio_sdmdev, cls_dev); +} +EXPORT_SYMBOL_GPL(vfio_sdmdev_pdev_sdmdev); + +struct vfio_sdmdev *mdev_sdmdev(struct mdev_device *mdev) +{ + struct device *pdev = mdev_parent_dev(mdev); + + return vfio_sdmdev_pdev_sdmdev(pdev); +} +EXPORT_SYMBOL_GPL(mdev_sdmdev); + +static ssize_t iommu_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vfio_sdmdev *sdmdev = vfio_sdmdev_pdev_sdmdev(dev); + + if (!sdmdev) + return -ENODEV; + + return sprintf(buf, "%d\n", sdmdev->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_sdmdev *sdmdev = vfio_sdmdev_pdev_sdmdev(dev); + + if (!sdmdev) + return -ENODEV; + + return sprintf(buf, "%d\n", sdmdev->dma_flag); +} + +static DEVICE_ATTR_RO(dma_flag); + +/* mdev->dev_attr_groups */ +static struct attribute *vfio_sdmdev_attrs[] = { + &dev_attr_iommu_type.attr, + &dev_attr_dma_flag.attr, + NULL, +}; +static const struct attribute_group vfio_sdmdev_group = { + .name = VFIO_SDMDEV_PDEV_ATTRS_GRP_NAME, + .attrs = vfio_sdmdev_attrs, +}; +const struct attribute_group *vfio_sdmdev_groups[] = { + &vfio_sdmdev_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_SDMDEV_ATTR(_name, sdmdev_member, format) \ +static ssize_t _name##_show(struct kobject *kobj, struct device *dev, \ + char *buf) \ +{ \ + struct vfio_sdmdev *sdmdev = vfio_sdmdev_pdev_sdmdev(dev); \ + if (!sdmdev) \ + return -ENODEV; \ + return sprintf(buf, format, sdmdev->sdmdev_member); \ +} \ +MDEV_TYPE_ATTR_RO_EXPORT(_name) + +DEF_SIMPLE_SDMDEV_ATTR(flags, flags, "%d"); +DEF_SIMPLE_SDMDEV_ATTR(name, name, "%s"); /* this should be algorithm name, */ + /* but you would not care if you have only one algorithm */ +DEF_SIMPLE_SDMDEV_ATTR(device_api, api_ver, "%s"); + +static ssize_t +available_instances_show(struct kobject *kobj, struct device *dev, char *buf) +{ + struct vfio_sdmdev *sdmdev = vfio_sdmdev_pdev_sdmdev(dev); + int nr_inst = 0; + + nr_inst = sdmdev->ops->get_available_instances ? + sdmdev->ops->get_available_instances(sdmdev) : 0; + return sprintf(buf, "%d", nr_inst); +} +MDEV_TYPE_ATTR_RO_EXPORT(available_instances); + +static int vfio_sdmdev_mdev_create(struct kobject *kobj, + struct mdev_device *mdev) +{ + struct device *pdev = mdev_parent_dev(mdev); + struct vfio_sdmdev_queue *q; + struct vfio_sdmdev *sdmdev = mdev_sdmdev(mdev); + int ret; + + if (!sdmdev->ops->get_queue) + return -ENODEV; + + ret = sdmdev->ops->get_queue(sdmdev, &q); + if (ret) + return ret; + + q->sdmdev = sdmdev; + q->mdev = mdev; + init_waitqueue_head(&q->wait); + + mdev_set_drvdata(mdev, q); + get_device(pdev); + + return 0; +} + +static int vfio_sdmdev_mdev_remove(struct mdev_device *mdev) +{ + struct vfio_sdmdev_queue *q = + (struct vfio_sdmdev_queue *)mdev_get_drvdata(mdev); + struct vfio_sdmdev *sdmdev = q->sdmdev; + struct device *pdev = mdev_parent_dev(mdev); + + put_device(pdev); + + if (sdmdev->ops->put_queue); + sdmdev->ops->put_queue(q); + + return 0; +} + +/* Wake up the process who is waiting this queue */ +void vfio_sdmdev_wake_up(struct vfio_sdmdev_queue *q) +{ + wake_up_all(&q->wait); +} +EXPORT_SYMBOL_GPL(vfio_sdmdev_wake_up); + +static int vfio_sdmdev_mdev_mmap(struct mdev_device *mdev, + struct vm_area_struct *vma) +{ + struct vfio_sdmdev_queue *q = + (struct vfio_sdmdev_queue *)mdev_get_drvdata(mdev); + struct vfio_sdmdev *sdmdev = q->sdmdev; + + if (sdmdev->ops->mmap) + return sdmdev->ops->mmap(q, vma); + + dev_err(sdmdev->dev, "no driver mmap!\n"); + return -EINVAL; +} + +static inline int vfio_sdmdev_wait(struct vfio_sdmdev_queue *q, + unsigned long timeout) +{ + int ret; + struct vfio_sdmdev *sdmdev = q->sdmdev; + + if (!sdmdev->ops->mask_notify) + return -ENODEV; + + sdmdev->ops->mask_notify(q, VFIO_SDMDEV_EVENT_Q_UPDATE); + + ret = timeout ? wait_event_interruptible_timeout(q->wait, + sdmdev->ops->is_q_updated(q), timeout) : + wait_event_interruptible(q->wait, + sdmdev->ops->is_q_updated(q)); + + sdmdev->ops->mask_notify(q, 0); + + return ret; +} + +static long vfio_sdmdev_mdev_ioctl(struct mdev_device *mdev, unsigned int cmd, + unsigned long arg) +{ + struct vfio_sdmdev_queue *q = + (struct vfio_sdmdev_queue *)mdev_get_drvdata(mdev); + struct vfio_sdmdev *sdmdev = q->sdmdev; + + switch (cmd) { + case VFIO_SDMDEV_CMD_WAIT: + return vfio_sdmdev_wait(q, arg); + +#ifdef CONFIG_IOMMU_SVA + case VFIO_SDMDEV_CMD_BIND_PASID: + int ret; + + if (!vfio_sdmdev_is_valid_pasid(arg)) + return -EINVAL; + + mutex_lock(&q->mutex); + q->pasid = arg; + + if (sdmdev->ops->start_queue) + ret = sdmdev->ops->start_queue(q); + + mutex_unlock(&q->mutex); + + return ret; +#endif + + default: + if (sdmdev->ops->ioctl) + return sdmdev->ops->ioctl(q, cmd, arg); + + dev_err(sdmdev->dev, "ioctl cmd (%d) is not supported!\n", cmd); + return -EINVAL; + } +} + +static void vfio_sdmdev_release(struct device *dev) { } + +static void vfio_sdmdev_mdev_release(struct mdev_device *mdev) +{ + struct vfio_sdmdev_queue *q = + (struct vfio_sdmdev_queue *)mdev_get_drvdata(mdev); + struct vfio_sdmdev *sdmdev = q->sdmdev; + + if (sdmdev->ops->stop_queue) + sdmdev->ops->stop_queue(q); +} + +static int vfio_sdmdev_mdev_open(struct mdev_device *mdev) +{ +#ifndef CONFIG_IOMMU_SVA + struct vfio_sdmdev_queue *q = + (struct vfio_sdmdev_queue *)mdev_get_drvdata(mdev); + struct vfio_sdmdev *sdmdev = q->sdmdev; + + if (sdmdev->ops->start_queue) + sdmdev->ops->start_queue(q); +#endif + + return 0; +} + +/** + * vfio_sdmdev_register - register a sdmdev + * @sdmdev: device structure + */ +int vfio_sdmdev_register(struct vfio_sdmdev *sdmdev) +{ + int ret; + + if (!sdmdev->dev) + return -ENODEV; + + atomic_set(&sdmdev->ref, 0); + sdmdev->cls_dev.parent = sdmdev->dev; + sdmdev->cls_dev.class = sdmdev_class; + sdmdev->cls_dev.release = vfio_sdmdev_release; + dev_set_name(&sdmdev->cls_dev, "%s", dev_name(sdmdev->dev)); + ret = device_register(&sdmdev->cls_dev); + if (ret) + goto err; + + sdmdev->mdev_fops.owner = THIS_MODULE; + sdmdev->mdev_fops.dev_attr_groups = vfio_sdmdev_groups; + WARN_ON(!sdmdev->mdev_fops.supported_type_groups); + sdmdev->mdev_fops.create = vfio_sdmdev_mdev_create; + sdmdev->mdev_fops.remove = vfio_sdmdev_mdev_remove; + sdmdev->mdev_fops.ioctl = vfio_sdmdev_mdev_ioctl; + sdmdev->mdev_fops.open = vfio_sdmdev_mdev_open; + sdmdev->mdev_fops.release = vfio_sdmdev_mdev_release; + sdmdev->mdev_fops.mmap = vfio_sdmdev_mdev_mmap, + + ret = mdev_register_device(sdmdev->dev, &sdmdev->mdev_fops); + if (ret) + goto err_with_cls_dev; + + return 0; + +err_with_cls_dev: + device_unregister(&sdmdev->cls_dev); +err: + return ret; +} +EXPORT_SYMBOL_GPL(vfio_sdmdev_register); + +/** + * vfio_sdmdev_unregister - unregisters a sdmdev + * @sdmdev: device to unregister + * + * Unregister a sdmdev that wat previously successully registered with + * vfio_sdmdev_register(). + */ +void vfio_sdmdev_unregister(struct vfio_sdmdev *sdmdev) +{ + mdev_unregister_device(sdmdev->dev); + device_unregister(&sdmdev->cls_dev); +} +EXPORT_SYMBOL_GPL(vfio_sdmdev_unregister); + +static int __init vfio_sdmdev_init(void) +{ + sdmdev_class = class_create(THIS_MODULE, VFIO_SDMDEV_CLASS_NAME); + return PTR_ERR_OR_ZERO(sdmdev_class); +} + +static __exit void vfio_sdmdev_exit(void) +{ + class_destroy(sdmdev_class); +} + +module_init(vfio_sdmdev_init); +module_exit(vfio_sdmdev_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hisilicon Tech. Co., Ltd."); +MODULE_DESCRIPTION("VFIO Share Domain Mediated Device"); diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index d9fd3188615d..ba73231d8692 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_sdmdev.h> #define DRIVER_VERSION "0.2" #define DRIVER_AUTHOR "Alex Williamson <alex.williamson@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 sdmdev. + * return 1 if the device is sdmdev, the data will be updated with parent + * device's group. + * return -errno if other error. + */ +static int vfio_sdmdev_type(struct device *dev, void *data) +{ + struct iommu_group **group = data; + struct iommu_group *pgroup; + int (*_is_sdmdev)(struct device *dev); + struct device *pdev; + int ret = 1; + + /* vfio_sdmdev module is not configurated */ + _is_sdmdev = symbol_get(vfio_sdmdev_is_sdmdev); + if (!_is_sdmdev) + return 0; + + /* check if it belongs to vfio_sdmdev device */ + if (!_is_sdmdev(dev)) { + ret = 0; + goto out; + } + + pdev = dev->parent; + pgroup = iommu_group_get(pdev); + if (!pgroup) { + ret = -ENODEV; + goto out; + } + + if (group) { + /* check if all parent devices is the same */ + if (*group && *group != pgroup) + ret = -ENODEV; + else + *group = pgroup; + } + + iommu_group_put(pgroup); + +out: + symbol_put(vfio_sdmdev_is_sdmdev); + + return ret; +} + +/* return 0 or -errno */ +static int vfio_sdmdev_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 sd group, 1 means it is, or -EXXX for error */ +static int vfio_iommu_type1_attach_sdgroup(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_sdmdev_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_sdmdev_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_sdgroup(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); @@ -1527,12 +1645,25 @@ static void vfio_sanity_check_pfn_list(struct vfio_iommu *iommu) WARN_ON(iommu->notifier.head); } +static void vfio_iommu_undo(struct vfio_iommu *iommu, + struct iommu_domain *domain) +{ + struct rb_node *n = rb_first(&iommu->dma_list); + struct vfio_dma *dma; + + for (; n; n = rb_next(n)) { + dma = rb_entry(n, struct vfio_dma, node); + iommu_unmap(domain, dma->iova, dma->size); + } +} + static void vfio_iommu_type1_detach_group(void *iommu_data, struct iommu_group *iommu_group) { struct vfio_iommu *iommu = iommu_data; struct vfio_domain *domain; struct vfio_group *group; + struct iommu_domain *sdomain = NULL; mutex_lock(&iommu->lock); @@ -1560,7 +1691,12 @@ static void vfio_iommu_type1_detach_group(void *iommu_data, if (!group) continue; - iommu_detach_group(domain->domain, iommu_group); + if (group->parent_group) + sdomain = iommu_group_unshare_domain( + group->parent_group); + else + iommu_detach_group(domain->domain, iommu_group); + list_del(&group->next); kfree(group); /* @@ -1577,7 +1713,10 @@ static void vfio_iommu_type1_detach_group(void *iommu_data, else vfio_iommu_unmap_unpin_reaccount(iommu); } - iommu_domain_free(domain->domain); + if (domain->domain != sdomain) + iommu_domain_free(domain->domain); + else + vfio_iommu_undo(iommu, sdomain); list_del(&domain->next); kfree(domain); } diff --git a/include/linux/vfio_sdmdev.h b/include/linux/vfio_sdmdev.h new file mode 100644 index 000000000000..fbc9fb3f4abc --- /dev/null +++ b/include/linux/vfio_sdmdev.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef __VFIO_SDMDEV_H +#define __VFIO_SDMDEV_H + +#include <linux/device.h> +#include <linux/iommu.h> +#include <linux/mdev.h> +#include <linux/vfio.h> +#include <uapi/linux/vfio_sdmdev.h> + +struct vfio_sdmdev_queue; +struct vfio_sdmdev; + +/* event bit used to mask the hardware irq */ +#define VFIO_SDMDEV_EVENT_Q_UPDATE BIT(0) /* irq if queue is updated */ + +/** + * struct vfio_sdmdev_ops - WD device operations + * @get_queue: get a queue from the device according to algorithm + * @put_queue: free a queue to the device + * @start_queue: put queue into action with current process's pasid. + * @stop_queue: stop queue from running state + * @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_sdmdev_ops { + int (*get_queue)(struct vfio_sdmdev *sdmdev, + struct vfio_sdmdev_queue **q); + void (*put_queue)(struct vfio_sdmdev_queue *q); + int (*start_queue)(struct vfio_sdmdev_queue *q); + void (*stop_queue)(struct vfio_sdmdev_queue *q); + int (*is_q_updated)(struct vfio_sdmdev_queue *q); + void (*mask_notify)(struct vfio_sdmdev_queue *q, int event_mask); + int (*mmap)(struct vfio_sdmdev_queue *q, struct vm_area_struct *vma); + int (*reset)(struct vfio_sdmdev *sdmdev); + int (*reset_queue)(struct vfio_sdmdev_queue *q); + long (*ioctl)(struct vfio_sdmdev_queue *q, unsigned int cmd, + unsigned long arg); + int (*get_available_instances)(struct vfio_sdmdev *sdmdev); +}; + +struct vfio_sdmdev_queue { + struct mutex mutex; + struct vfio_sdmdev *sdmdev; + __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_sdmdev { + const char *name; + int status; + atomic_t ref; + const struct vfio_sdmdev_ops *ops; + struct device *dev; + struct device cls_dev; + bool is_vf; + u32 iommu_type; + u32 dma_flag; + void *priv; + int flags; + const char *api_ver; + struct mdev_parent_ops mdev_fops; +}; + +int vfio_sdmdev_register(struct vfio_sdmdev *sdmdev); +void vfio_sdmdev_unregister(struct vfio_sdmdev *sdmdev); +void vfio_sdmdev_wake_up(struct vfio_sdmdev_queue *q); +int vfio_sdmdev_is_sdmdev(struct device *dev); +struct vfio_sdmdev *vfio_sdmdev_pdev_sdmdev(struct device *dev); +struct vfio_sdmdev *mdev_sdmdev(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_SDMDEV_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_SDMDEV_REGION(vm_pgoff) (vm_pgoff & 0xf) + +#endif diff --git a/include/uapi/linux/vfio_sdmdev.h b/include/uapi/linux/vfio_sdmdev.h new file mode 100644 index 000000000000..79fa33fbc8c0 --- /dev/null +++ b/include/uapi/linux/vfio_sdmdev.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef _UAPIVFIO_SDMDEV_H +#define _UAPIVFIO_SDMDEV_H + +#include <linux/ioctl.h> + +#define VFIO_SDMDEV_CLASS_NAME "sdmdev" + +/* Device ATTRs in parent dev SYSFS DIR */ +#define VFIO_SDMDEV_PDEV_ATTRS_GRP_NAME "params" + +/* Parent device attributes */ +#define SDMDEV_IOMMU_TYPE "iommu_type" +#define SDMDEV_DMA_FLAG "dma_flag" + +/* Maximum length of algorithm name string */ +#define VFIO_SDMDEV_ALG_NAME_SIZE 64 + +/* the bits used in SDMDEV_DMA_FLAG attributes */ +#define VFIO_SDMDEV_DMA_INVALID 0 +#define VFIO_SDMDEV_DMA_SINGLE_PROC_MAP 1 +#define VFIO_SDMDEV_DMA_MULTI_PROC_MAP 2 +#define VFIO_SDMDEV_DMA_SVM 4 +#define VFIO_SDMDEV_DMA_SVM_NO_FAULT 8 +#define VFIO_SDMDEV_DMA_PHY 16 + +#define VFIO_SDMDEV_CMD_WAIT _IO('W', 1) +#define VFIO_SDMDEV_CMD_BIND_PASID _IO('W', 2) +#endif