From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 00:20:50
Prologue:
This is the first series of three to send the "mlx5_vfio_pci" driver that has
been discussed on the list for a while now.
- Reorganize reflck to support splitting vfio_pci
- Split vfio_pci into vfio_pci/vfio_pci_core and provide infrastructure
for non-generic VFIO PCI drivers
- The new driver mlx5_vfio_pci that is a full implementation of
suspend/resume functionality for mlx5 devices.
A preview of all the patches can be seen here:
https://github.com/jgunthorpe/linux/commits/mlx5_vfio_pci
===============
This is in support of Max's series to split vfio-pci. For that to work the
reflck concept embedded in vfio-pci needs to be sharable across all of the
new VFIO PCI drivers which motivated re-examining how this is
implemented.
Another significant issue is how the VFIO PCI core includes code like:
if (pci_dev_driver(pdev) != &vfio_pci_driver)
Which is not scalable if there are going to be multiple different driver
types.
This series takes the approach of moving the "reflck" mechanism into the
core code as a "device set". Each vfio_device driver can specify how
vfio_devices are grouped into the set using a key and the set comes along
with a set-global mutex. The core code manages creating per-device set
memory and associating it with each vfio_device.
In turn this allows the core code to provide an open/close_device()
operation that is called only for the first/last FD, and is called under
the global device set lock.
Review of all the drivers show that they are either already open coding
the first/last semantic or are buggy and missing it. All drivers are
migrated/fixed to the new open/close_device ops and the unused per-FD
open()/release() ops are deleted.
The special behavior of PCI around the bus/slot "reset group" is recast in
terms of the device set which conslidates the reflck, eliminates two
touches of pci_dev_driver(), and allows the reset mechanism to share
across all VFIO PCI drivers. PCI is changed to acquire devices directly
from the device set instead of trying to work backwards from the struct
pci_device.
Overall a few minor bugs are squashed and quite a bit of code is removed
through consolidation.
Jason Gunthorpe (11):
vfio/samples: Remove module get/put
vfio: Provide better generic support for open/release vfio_device_ops
vfio/samples: Delete useless open/close
vfio/fsl: Move to the device set infrastructure
vfio/platform: Use open_device() instead of open coding a refcnt
scheme
vfio/pci: Change vfio_pci_try_bus_reset() to use the dev_set
vfio/pci: Reorganize VFIO_DEVICE_PCI_HOT_RESET to use the device set
vfio/mbochs: Fix close when multiple device FDs are open
vfio/ap,ccw: Fix open/close when multiple device FDs are open
vfio/gvt: Fix open/close when multiple device FDs are open
vfio: Remove struct vfio_device_ops open/release
Max Gurtovoy (1):
vfio: Introduce a vfio_uninit_group_dev() API call
Yishai Hadas (1):
vfio/pci: Move to the device set infrastructure
Documentation/driver-api/vfio.rst | 4 +-
drivers/gpu/drm/i915/gvt/kvmgt.c | 8 +-
drivers/s390/cio/vfio_ccw_ops.c | 8 +-
drivers/s390/crypto/vfio_ap_ops.c | 8 +-
drivers/vfio/fsl-mc/vfio_fsl_mc.c | 158 ++----
drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c | 6 +-
drivers/vfio/fsl-mc/vfio_fsl_mc_private.h | 7 -
drivers/vfio/mdev/vfio_mdev.c | 29 +-
drivers/vfio/pci/vfio_pci.c | 459 ++++++------------
drivers/vfio/pci/vfio_pci_private.h | 7 -
drivers/vfio/platform/vfio_platform_common.c | 86 ++--
drivers/vfio/platform/vfio_platform_private.h | 1 -
drivers/vfio/vfio.c | 149 +++++-
include/linux/mdev.h | 9 +-
include/linux/vfio.h | 26 +-
samples/vfio-mdev/mbochs.c | 16 +-
samples/vfio-mdev/mdpy.c | 40 +-
samples/vfio-mdev/mtty.c | 40 +-
18 files changed, 439 insertions(+), 622 deletions(-)
--
2.32.0
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 00:20:49
The patch to move the get/put to core and the patch to convert the samples
to use vfio_device crossed in a way that this was missed. When both
patches are together the samples do not need their own get/put.
Fixes: 437e41368c01 ("vfio/mdpy: Convert to use vfio_register_group_dev()")
Fixes: 681c1615f891 ("vfio/mbochs: Convert to use vfio_register_group_dev()")
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
samples/vfio-mdev/mbochs.c | 4 ----
samples/vfio-mdev/mdpy.c | 4 ----
2 files changed, 8 deletions(-)
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 00:20:51
Currently the driver ops have an open/release pair that is called once
each time a device FD is opened or closed. Add an additional set of
open/close_device() ops which are called when the device FD is opened for
the first time and closed for the last time.
An analysis shows that all of the drivers require this semantic. Some are
open coding it as part of their reflck implementation, and some are just
buggy and miss it completely.
To retain the current semantics PCI and FSL depend on, introduce the idea
of a "device set" which is a grouping of vfio_device's that share the same
lock around opening.
The device set is established by providing a 'set_id' pointer. All
vfio_device's that provide the same pointer will be joined to the same
singleton memory and lock across the whole set. This effectively replaces
the oddly named reflck.
After conversion the set_id will be sourced from:
- A struct device from a fsl_mc_device (fsl)
- A struct pci_slot (pci)
- A struct pci_bus (pci)
- The struct vfio_device (everything)
The design ensures that the above pointers are live as long as the
vfio_device is registered, so they form reliable unique keys to group
vfio_devices into sets.
This implementation uses xarray instead of searching through the driver
core structures, which simplifies the somewhat tricky locking in this
area.
Following patches convert all the drivers.
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
drivers/vfio/mdev/vfio_mdev.c | 22 ++++++
drivers/vfio/vfio.c | 144 ++++++++++++++++++++++++++++------
include/linux/mdev.h | 2 +
include/linux/vfio.h | 19 +++++
4 files changed, 165 insertions(+), 22 deletions(-)
@@ -96,6 +96,74 @@ module_param_named(enable_unsafe_noiommu_mode,MODULE_PARM_DESC(enable_unsafe_noiommu_mode,"Enable UNSAFE, no-IOMMU mode. This mode provides no device isolation, no DMA translation, no host kernel protection, cannot be used for device assignment to virtual machines, requires RAWIO permissions, and will taint the kernel. If you do not know what this is for, step away. (default: false)");#endif+staticDEFINE_XARRAY(vfio_device_set_xa);++intvfio_assign_device_set(structvfio_device*device,void*set_id)+{+structvfio_device_set*alloc_dev_set=NULL;+structvfio_device_set*dev_set;++if(WARN_ON(!set_id))+return-EINVAL;++/*+*Atomicallyacquireasingletonobjectinthexarrayforthisset_id+*/+again:+xa_lock(&vfio_device_set_xa);+if(alloc_dev_set){+dev_set=__xa_cmpxchg(&vfio_device_set_xa,+(unsignedlong)set_id,NULL,+alloc_dev_set,GFP_KERNEL);+if(xa_is_err(dev_set)){+xa_unlock(&vfio_device_set_xa);+kfree(alloc_dev_set);+returnxa_err(dev_set);+}+if(!dev_set)+dev_set=alloc_dev_set;+}else+dev_set=xa_load(&vfio_device_set_xa,(unsignedlong)set_id);+if(dev_set){+dev_set->device_count++;+xa_unlock(&vfio_device_set_xa);+device->dev_set=dev_set;+if(dev_set!=alloc_dev_set)+kfree(alloc_dev_set);+return0;+}+xa_unlock(&vfio_device_set_xa);++if(WARN_ON(alloc_dev_set))+return-EINVAL;++alloc_dev_set=kzalloc(sizeof(*alloc_dev_set),GFP_KERNEL);+if(!alloc_dev_set)+return-ENOMEM;+mutex_init(&alloc_dev_set->lock);+alloc_dev_set->set_id=set_id;+gotoagain;+}+EXPORT_SYMBOL_GPL(vfio_assign_device_set);++staticvoidvfio_release_device_set(structvfio_device*device)+{+structvfio_device_set*dev_set=device->dev_set;++if(!dev_set)+return;++xa_lock(&vfio_device_set_xa);+dev_set->device_count--;+if(!dev_set->device_count){+__xa_erase(&vfio_device_set_xa,+(unsignedlong)dev_set->set_id);+mutex_destroy(&dev_set->lock);+kfree(dev_set);+}+xa_unlock(&vfio_device_set_xa);+}+/**vfio_iommu_group_{get,put}areonlyintendedforVFIObusdriverprobe*andremovefunctions,anyusecasesotherthanacquiringthefirst
@@ -1418,12 +1495,28 @@ static int vfio_group_get_device_fd(struct vfio_group *group, char *buf)atomic_inc(&group->container_users);-fd_install(ret,filep);+fd_install(fdno,filep);if(group->noiommu)dev_warn(device->dev,"vfio-noiommu device opened by user ""(%s:%d)\n",current->comm,task_pid_nr(current));+returnfdno;+err_fd:+put_unused_fd(fdno);+err_release:+if(device->ops->release)+device->ops->release(device);+err_close_device:+mutex_lock(&device->dev_set->lock);+if(device->open_count==1&&device->ops->close_device)+device->ops->close_device(device);+err_undo_count:+device->open_count--;+mutex_unlock(&device->dev_set->lock);+module_put(device->dev->driver->owner);+err_device_put:+vfio_device_put(device);returnret;}
@@ -15,13 +15,26 @@#include<linux/poll.h>#include<uapi/linux/vfio.h>+/*+*VFIOdevicescanbeplacedinaset,thisallowsalldevicestosharethis+*structureandtheVFIOcorewillprovidealockthatisheldaround+*open_device()/close_device()foralldevicesintheset.+*/+structvfio_device_set{+void*set_id;+structmutexlock;+unsignedintdevice_count;+};+structvfio_device{structdevice*dev;conststructvfio_device_ops*ops;structvfio_group*group;+structvfio_device_set*dev_set;/* Members below here are private, not for driver use */refcount_trefcount;+unsignedintopen_count;structcompletioncomp;structlist_headgroup_next;};
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 00:20:52
From: Max Gurtovoy <mgurtovoy@nvidia.com>
This pairs with vfio_init_group_dev() and allows undoing any state that is
stored in the vfio_device unrelated to registration. Add appropriately
placed calls to all the drivers.
The following patch will use this to add pre-registration state for the
device set.
Signed-off-by: Max Gurtovoy <mgurtovoy@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
Documentation/driver-api/vfio.rst | 4 ++-
drivers/vfio/fsl-mc/vfio_fsl_mc.c | 6 +++--
drivers/vfio/mdev/vfio_mdev.c | 13 +++++++---
drivers/vfio/pci/vfio_pci.c | 6 +++--
drivers/vfio/platform/vfio_platform_common.c | 7 +++--
drivers/vfio/vfio.c | 5 ++++
include/linux/vfio.h | 1 +
samples/vfio-mdev/mbochs.c | 2 ++
samples/vfio-mdev/mdpy.c | 25 ++++++++++--------
samples/vfio-mdev/mtty.c | 27 ++++++++++++--------
10 files changed, 64 insertions(+), 32 deletions(-)
@@ -255,11 +255,13 @@ vfio_unregister_group_dev() respectively:: void vfio_init_group_dev(struct vfio_device *device, struct device *dev, const struct vfio_device_ops *ops);+ void vfio_uninit_group_dev(struct vfio_device *device); int vfio_register_group_dev(struct vfio_device *device); void vfio_unregister_group_dev(struct vfio_device *device); The driver should embed the vfio_device in its own structure and call-vfio_init_group_dev() to pre-configure it before going to registration.+vfio_init_group_dev() to pre-configure it before going to registration+and call vfio_uninit_group_dev() after completing the un-registration. vfio_register_group_dev() indicates to the core to begin tracking the iommu_group of the specified dev and register the dev as owned by a VFIO bus driver. Once vfio_register_group_dev() returns it is possible for userspace to
@@ -667,7 +667,7 @@ int vfio_platform_probe_common(struct vfio_platform_device *vdev,ret=vfio_platform_of_probe(vdev,dev);if(ret)-returnret;+gotoout_uninit;vdev->device=dev;
@@ -675,7 +675,7 @@ int vfio_platform_probe_common(struct vfio_platform_device *vdev,if(ret&&vdev->reset_required){dev_err(dev,"No reset function found for device %s\n",vdev->name);-returnret;+gotoout_uninit;}group=vfio_iommu_group_get(dev);
@@ -698,6 +698,8 @@ int vfio_platform_probe_common(struct vfio_platform_device *vdev,vfio_iommu_group_put(group,dev);put_reset:vfio_platform_put_reset(vdev);+out_uninit:+vfio_uninit_group_dev(&vdev->vdev);returnret;}EXPORT_SYMBOL_GPL(vfio_platform_probe_common);
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 00:20:52
mbochs_close() iterates over global device state and frees it. Currently
this is done every time a device FD is closed, but if multiple device FDs
are open this could corrupt other still active FDs.
Change this to use close_device() so it only runs on the last close.
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
samples/vfio-mdev/mbochs.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 00:20:53
FSL uses the internal reflck to implement the open_device() functionality,
conversion to the core code is straightforward.
The decision on which set to be part of is trivially based on the
is_fsl_mc_bus_dprc() and we use a 'struct device *' pointer as the set_id.
It isn't entirely clear what the device set lock is actually protecting,
but I think it is related to the interrupt setup.
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
drivers/vfio/fsl-mc/vfio_fsl_mc.c | 152 ++++------------------
drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c | 6 +-
drivers/vfio/fsl-mc/vfio_fsl_mc_private.h | 7 -
3 files changed, 26 insertions(+), 139 deletions(-)
@@ -136,58 +65,30 @@ static void vfio_fsl_mc_regions_cleanup(struct vfio_fsl_mc_device *vdev)kfree(vdev->regions);}-staticintvfio_fsl_mc_open(structvfio_device*core_vdev)-{-structvfio_fsl_mc_device*vdev=-container_of(core_vdev,structvfio_fsl_mc_device,vdev);-intret=0;--mutex_lock(&vdev->reflck->lock);-if(!vdev->refcnt){-ret=vfio_fsl_mc_regions_init(vdev);-if(ret)-gotoout;-}-vdev->refcnt++;-out:-mutex_unlock(&vdev->reflck->lock);-returnret;-}--staticvoidvfio_fsl_mc_release(structvfio_device*core_vdev)+staticvoidvfio_fsl_mc_close_device(structvfio_device*core_vdev){structvfio_fsl_mc_device*vdev=container_of(core_vdev,structvfio_fsl_mc_device,vdev);+structfsl_mc_device*mc_dev=vdev->mc_dev;+structdevice*cont_dev=fsl_mc_cont_dev(&mc_dev->dev);+structfsl_mc_device*mc_cont=to_fsl_mc_device(cont_dev);intret;-mutex_lock(&vdev->reflck->lock);+vfio_fsl_mc_regions_cleanup(vdev);-if(!(--vdev->refcnt)){-structfsl_mc_device*mc_dev=vdev->mc_dev;-structdevice*cont_dev=fsl_mc_cont_dev(&mc_dev->dev);-structfsl_mc_device*mc_cont=to_fsl_mc_device(cont_dev);--vfio_fsl_mc_regions_cleanup(vdev);+/* reset the device before cleaning up the interrupts */+ret=dprc_reset_container(mc_cont->mc_io,0,mc_cont->mc_handle,+mc_cont->obj_desc.id,+DPRC_RESET_OPTION_NON_RECURSIVE);-/* reset the device before cleaning up the interrupts */-ret=dprc_reset_container(mc_cont->mc_io,0,-mc_cont->mc_handle,-mc_cont->obj_desc.id,-DPRC_RESET_OPTION_NON_RECURSIVE);+if(WARN_ON(ret))+dev_warn(&mc_cont->dev,+"VFIO_FLS_MC: reset device has failed (%d)\n",ret);-if(ret){-dev_warn(&mc_cont->dev,"VFIO_FLS_MC: reset device has failed (%d)\n",-ret);-WARN_ON(1);-}+vfio_fsl_mc_irqs_cleanup(vdev);-vfio_fsl_mc_irqs_cleanup(vdev);--fsl_mc_cleanup_irq_pool(mc_cont);-}--mutex_unlock(&vdev->reflck->lock);+fsl_mc_cleanup_irq_pool(mc_cont);}staticlongvfio_fsl_mc_ioctl(structvfio_device*core_vdev,
@@ -504,8 +405,8 @@ static int vfio_fsl_mc_mmap(struct vfio_device *core_vdev,staticconststructvfio_device_opsvfio_fsl_mc_ops={.name="vfio-fsl-mc",-.open=vfio_fsl_mc_open,-.release=vfio_fsl_mc_release,+.open_device=vfio_fsl_mc_open_device,+.close_device=vfio_fsl_mc_close_device,.ioctl=vfio_fsl_mc_ioctl,.read=vfio_fsl_mc_read,.write=vfio_fsl_mc_write,
@@ -625,13 +526,15 @@ static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev)vdev->mc_dev=mc_dev;mutex_init(&vdev->igate);-ret=vfio_fsl_mc_reflck_attach(vdev);+ret=vfio_assign_device_set(&vdev->vdev,is_fsl_mc_bus_dprc(mc_dev)?+&mc_dev->dev:+mc_dev->dev.parent);if(ret)gotoout_uninit;ret=vfio_fsl_mc_init_device(vdev);if(ret)-gotoout_reflck;+gotoout_uninit;ret=vfio_register_group_dev(&vdev->vdev);if(ret){
@@ -639,12 +542,6 @@ static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev)gotoout_device;}-/*-*Thistriggersrecursionintovfio_fsl_mc_probe()onanotherdevice-*andthevfio_fsl_mc_reflck_attach()mustsucceed,whichreliesonthe-*vfio_add_group_dev()above.Ithasnoimpactonthisvdev,soitis-*safetobeafterthevfiodeviceismadelive.-*/ret=vfio_fsl_mc_scan_container(mc_dev);if(ret)gotoout_group_dev;
@@ -655,8 +552,6 @@ static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev)vfio_unregister_group_dev(&vdev->vdev);out_device:vfio_fsl_uninit_device(vdev);-out_reflck:-vfio_fsl_mc_reflck_put(vdev->reflck);out_uninit:vfio_uninit_group_dev(&vdev->vdev);kfree(vdev);
@@ -676,7 +571,6 @@ static int vfio_fsl_mc_remove(struct fsl_mc_device *mc_dev)dprc_remove_devices(mc_dev,NULL,0);vfio_fsl_uninit_device(vdev);vfio_uninit_group_dev(&vdev->vdev);-vfio_fsl_mc_reflck_put(vdev->reflck);kfree(vdev);vfio_iommu_group_put(mc_dev->dev.iommu_group,dev);
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 00:20:54
The core code no longer requires these ops to be defined, so delete these
empty functions and leave the op as NULL. mtty's functions only log a
pointless message, delete that entirely.
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
samples/vfio-mdev/mbochs.c | 6 ------
samples/vfio-mdev/mdpy.c | 11 -----------
samples/vfio-mdev/mtty.c | 13 -------------
3 files changed, 30 deletions(-)
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 00:20:56
Platform simply wants to run some code when the device is first
opened/last closed. Use the core framework and locking for this. Aside
from removing a bit of code this narrows the locking scope from a global
lock.
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
drivers/vfio/platform/vfio_platform_common.c | 79 ++++++++-----------
drivers/vfio/platform/vfio_platform_private.h | 1 -
2 files changed, 32 insertions(+), 48 deletions(-)
@@ -218,65 +218,52 @@ static int vfio_platform_call_reset(struct vfio_platform_device *vdev,return-EINVAL;}-staticvoidvfio_platform_release(structvfio_device*core_vdev)+staticvoidvfio_platform_close_device(structvfio_device*core_vdev){structvfio_platform_device*vdev=container_of(core_vdev,structvfio_platform_device,vdev);+constchar*extra_dbg=NULL;+intret;-mutex_lock(&driver_lock);--if(!(--vdev->refcnt)){-constchar*extra_dbg=NULL;-intret;--ret=vfio_platform_call_reset(vdev,&extra_dbg);-if(ret&&vdev->reset_required){-dev_warn(vdev->device,"reset driver is required and reset call failed in release (%d) %s\n",-ret,extra_dbg?extra_dbg:"");-WARN_ON(1);-}-pm_runtime_put(vdev->device);-vfio_platform_regions_cleanup(vdev);-vfio_platform_irq_cleanup(vdev);+ret=vfio_platform_call_reset(vdev,&extra_dbg);+if(WARN_ON(ret&&vdev->reset_required)){+dev_warn(+vdev->device,+"reset driver is required and reset call failed in release (%d) %s\n",+ret,extra_dbg?extra_dbg:"");}--mutex_unlock(&driver_lock);+pm_runtime_put(vdev->device);+vfio_platform_regions_cleanup(vdev);+vfio_platform_irq_cleanup(vdev);}-staticintvfio_platform_open(structvfio_device*core_vdev)+staticintvfio_platform_open_device(structvfio_device*core_vdev){structvfio_platform_device*vdev=container_of(core_vdev,structvfio_platform_device,vdev);+constchar*extra_dbg=NULL;intret;-mutex_lock(&driver_lock);--if(!vdev->refcnt){-constchar*extra_dbg=NULL;--ret=vfio_platform_regions_init(vdev);-if(ret)-gotoerr_reg;+ret=vfio_platform_regions_init(vdev);+if(ret)+returnret;-ret=vfio_platform_irq_init(vdev);-if(ret)-gotoerr_irq;+ret=vfio_platform_irq_init(vdev);+if(ret)+gotoerr_irq;-ret=pm_runtime_get_sync(vdev->device);-if(ret<0)-gotoerr_rst;+ret=pm_runtime_get_sync(vdev->device);+if(ret<0)+gotoerr_rst;-ret=vfio_platform_call_reset(vdev,&extra_dbg);-if(ret&&vdev->reset_required){-dev_warn(vdev->device,"reset driver is required and reset call failed in open (%d) %s\n",-ret,extra_dbg?extra_dbg:"");-gotoerr_rst;-}+ret=vfio_platform_call_reset(vdev,&extra_dbg);+if(ret&&vdev->reset_required){+dev_warn(+vdev->device,+"reset driver is required and reset call failed in open (%d) %s\n",+ret,extra_dbg?extra_dbg:"");+gotoerr_rst;}--vdev->refcnt++;--mutex_unlock(&driver_lock);return0;err_rst:
@@ -284,8 +271,6 @@ static int vfio_platform_open(struct vfio_device *core_vdev)vfio_platform_irq_cleanup(vdev);err_irq:vfio_platform_regions_cleanup(vdev);-err_reg:-mutex_unlock(&driver_lock);returnret;}
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 00:20:58
From: Yishai Hadas <yishaih@nvidia.com>
PCI wants to have the usual open/close_device() logic with the slight
twist that the open/close_device() must be done under a singelton lock
shared by all of the vfio_devices that are in the PCI "reset group".
The reset group, and thus the device set, is determined by what devices
pci_reset_bus() touches, which is either the entire bus or only the slot.
Rely on the core code to do everything reflck was doing and delete reflck
entirely.
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
drivers/vfio/pci/vfio_pci.c | 156 ++++++----------------------
drivers/vfio/pci/vfio_pci_private.h | 7 --
2 files changed, 31 insertions(+), 132 deletions(-)
@@ -2254,7 +2160,7 @@ static int vfio_pci_get_unused_devs(struct pci_dev *pdev, void *data)vdev=container_of(device,structvfio_pci_device,vdev);/* Fault if the device is not unused */-if(vdev->refcnt){+if(device->open_count){vfio_device_put(device);return-EBUSY;}
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 00:20:59
The user can open multiple device FDs if it likes, however these open()
functions call vfio_register_notifier() on some device global
state. Calling vfio_register_notifier() twice in will trigger a WARN_ON
from notifier_chain_register() and the first close will wrongly delete the
notifier and more.
Since these really want the new open/close_device() semantics just change
the functions over.
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
drivers/s390/cio/vfio_ccw_ops.c | 8 ++++----
drivers/s390/crypto/vfio_ap_ops.c | 8 ++++----
2 files changed, 8 insertions(+), 8 deletions(-)
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 00:21:11
Keep track of all the vfio_devices that have been added to the device set
and use this list in vfio_pci_try_bus_reset() instead of trying to work
backwards from the pci_device.
The dev_set->lock directly prevents devices from joining/leaving the set,
which further implies the pci_device cannot change drivers or that the
vfio_device be freed, eliminating the need for get/put's.
Completeness of the device set can be directly measured by checking if
every PCI device in the reset group is also in the device set - which
proves that VFIO drivers are attached to everything.
This restructuring corrects a call to pci_dev_driver() without holding the
device_lock() and removes a hard wiring to &vfio_pci_driver.
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
drivers/vfio/pci/vfio_pci.c | 110 ++++++++++++++----------------------
drivers/vfio/vfio.c | 10 ++++
include/linux/vfio.h | 2 +
3 files changed, 53 insertions(+), 69 deletions(-)
@@ -404,6 +404,9 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)structvfio_pci_ioeventfd*ioeventfd,*ioeventfd_tmp;inti,bar;+/* For needs_reset */+lockdep_assert_held(&vdev->vdev.dev_set->lock);+/* Stop the device from further DMA */pci_clear_master(pdev);
@@ -2139,34 +2142,17 @@ static struct pci_driver vfio_pci_driver = {.err_handler=&vfio_err_handlers,};-staticintvfio_pci_get_unused_devs(structpci_dev*pdev,void*data)+staticintvfio_pci_check_all_devices_bound(structpci_dev*pdev,void*data){-structvfio_devices*devs=data;-structvfio_device*device;-structvfio_pci_device*vdev;--if(devs->cur_index==devs->max_index)-return-ENOSPC;+structvfio_device_set*dev_set=data;+structvfio_device*cur;-device=vfio_device_get_from_dev(&pdev->dev);-if(!device)-return-EINVAL;--if(pci_dev_driver(pdev)!=&vfio_pci_driver){-vfio_device_put(device);-return-EBUSY;-}--vdev=container_of(device,structvfio_pci_device,vdev);--/* Fault if the device is not unused */-if(device->open_count){-vfio_device_put(device);-return-EBUSY;-}+lockdep_assert_held(&dev_set->lock);-devs->devices[devs->cur_index++]=vdev;-return0;+list_for_each_entry(cur,&dev_set->device_list,dev_set_list)+if(cur->dev==&pdev->dev)+return0;+return-EBUSY;}staticintvfio_pci_try_zap_and_vma_lock_cb(structpci_dev*pdev,void*data)
@@ -2220,61 +2205,48 @@ static int vfio_pci_try_zap_and_vma_lock_cb(struct pci_dev *pdev, void *data)*/staticvoidvfio_pci_try_bus_reset(structvfio_pci_device*vdev){-structvfio_devicesdevs={.cur_index=0};-inti=0,ret=-EINVAL;-boolslot=false;-structvfio_pci_device*tmp;--if(!pci_probe_reset_slot(vdev->pdev->slot))-slot=true;-elseif(pci_probe_reset_bus(vdev->pdev->bus))-return;+structvfio_device_set*dev_set=vdev->vdev.dev_set;+structvfio_pci_device*to_reset=NULL;+structvfio_pci_device*cur;+intret;-if(vfio_pci_for_each_slot_or_bus(vdev->pdev,vfio_pci_count_devs,-&i,slot)||!i)+if(pci_probe_reset_slot(vdev->pdev->slot)&&+pci_probe_reset_bus(vdev->pdev->bus))return;-devs.max_index=i;-devs.devices=kcalloc(i,sizeof(structvfio_device*),GFP_KERNEL);-if(!devs.devices)-return;+lockdep_assert_held(&vdev->vdev.dev_set->lock);-if(vfio_pci_for_each_slot_or_bus(vdev->pdev,-vfio_pci_get_unused_devs,-&devs,slot))-gotoput_devs;+/* All VFIO devices have a closed FD */+list_for_each_entry(cur,&dev_set->device_list,vdev.dev_set_list)+if(cur->vdev.open_count)+return;++/* All devices in the group to be reset need VFIO devices */+if(vfio_pci_for_each_slot_or_bus(+vdev->pdev,vfio_pci_check_all_devices_bound,dev_set,+!pci_probe_reset_slot(vdev->pdev->slot)))+return;/* Does at least one need a reset? */-for(i=0;i<devs.cur_index;i++){-tmp=devs.devices[i];-if(tmp->needs_reset){-ret=pci_reset_bus(vdev->pdev);+list_for_each_entry(cur,&dev_set->device_list,vdev.dev_set_list){+if(cur->needs_reset){+to_reset=cur;break;}}+if(!to_reset)+return;-put_devs:-for(i=0;i<devs.cur_index;i++){-tmp=devs.devices[i];--/*-*Ifresetwassuccessful,affecteddevicesnolongerneed-*aresetandweshouldreturnallthecollateraldevices-*tolowpower.Ifnotsuccessful,weeitherdidn'treset-*thebusortimedoutwaitingforit,solet'snottouch-*thepowerstate.-*/-if(!ret){-tmp->needs_reset=false;+ret=pci_reset_bus(to_reset->pdev);+if(ret)+return;-if(tmp!=vdev&&!disable_idle_d3)-vfio_pci_set_power_state(tmp,PCI_D3hot);-}+list_for_each_entry(cur,&dev_set->device_list,vdev.dev_set_list){+cur->needs_reset=false;-vfio_device_put(&tmp->vdev);+if(cur!=to_reset&&!disable_idle_d3)+vfio_pci_set_power_state(cur,PCI_D3hot);}--kfree(devs.devices);}staticvoid__exitvfio_pci_cleanup(void)
@@ -31,6 +32,7 @@ struct vfio_device {conststructvfio_device_ops*ops;structvfio_group*group;structvfio_device_set*dev_set;+structlist_headdev_set_list;/* Members below here are private, not for driver use */refcount_trefcount;
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 00:22:01
The user can open multiple device FDs if it likes, however the open
function calls vfio_register_notifier() on device global state. Calling
vfio_register_notifier() twice will trigger a WARN_ON from
notifier_chain_register() and the first close will wrongly delete the
notifier and more.
Since these really want the new open/close_device() semantics just change
the function over.
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
drivers/gpu/drm/i915/gvt/kvmgt.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 00:22:02
Like vfio_pci_try_bus_reset() this code wants to reset all of the devices
in the "reset group" which is the same membership as the device set.
Instead of trying to reconstruct the device set from the PCI list go
directly from the device set's device list to execute the reset.
The same basic structure as vfio_pci_try_bus_reset() is used. The
'vfio_devices' struct is replaced with the device set linked list and we
simply sweep it multiple times under the lock.
This eliminates a memory allocation and get/put traffic and another
improperly locked test of pci_dev_driver().
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
drivers/vfio/pci/vfio_pci.c | 205 ++++++++++++++++--------------------
1 file changed, 88 insertions(+), 117 deletions(-)
@@ -753,12 +729,6 @@ int vfio_pci_register_dev_region(struct vfio_pci_device *vdev,return0;}-structvfio_devices{-structvfio_pci_device**devices;-intcur_index;-intmax_index;-};-staticlongvfio_pci_ioctl(structvfio_device*core_vdev,unsignedintcmd,unsignedlongarg){
@@ -1127,11 +1097,10 @@ static long vfio_pci_ioctl(struct vfio_device *core_vdev,}elseif(cmd==VFIO_DEVICE_PCI_HOT_RESET){structvfio_pci_hot_resethdr;int32_t*group_fds;-structvfio_pci_group_entry*groups;+structvfio_group**groups;structvfio_pci_group_infoinfo;-structvfio_devicesdevs={.cur_index=0};boolslot=false;-inti,group_idx,mem_idx=0,count=0,ret=0;+intgroup_idx,count=0,ret=0;minsz=offsetofend(structvfio_pci_hot_reset,count);
@@ -1198,9 +1167,7 @@ static long vfio_pci_ioctl(struct vfio_device *core_vdev,break;}-groups[group_idx].group=group;-groups[group_idx].id=-vfio_external_user_iommu_id(group);+groups[group_idx]=group;}kfree(group_fds);
@@ -1212,64 +1179,11 @@ static long vfio_pci_ioctl(struct vfio_device *core_vdev,info.count=hdr.count;info.groups=groups;-/*-*Testwhetheralltheaffecteddevicesarecontained-*bythesetofgroupsprovidedbytheuser.-*/-ret=vfio_pci_for_each_slot_or_bus(vdev->pdev,-vfio_pci_validate_devs,-&info,slot);-if(ret)-gotohot_reset_release;--devs.max_index=count;-devs.devices=kcalloc(count,sizeof(structvfio_device*),-GFP_KERNEL);-if(!devs.devices){-ret=-ENOMEM;-gotohot_reset_release;-}--/*-*Weneedtogetmemory_lockforeachdevice,butdevices-*cansharemmap_lock,thereforeweneedtozapandhold-*thevma_lockforeachdevice,andonlythengeteach-*memory_lock.-*/-ret=vfio_pci_for_each_slot_or_bus(vdev->pdev,-vfio_pci_try_zap_and_vma_lock_cb,-&devs,slot);-if(ret)-gotohot_reset_release;--for(;mem_idx<devs.cur_index;mem_idx++){-structvfio_pci_device*tmp=devs.devices[mem_idx];--ret=down_write_trylock(&tmp->memory_lock);-if(!ret){-ret=-EBUSY;-gotohot_reset_release;-}-mutex_unlock(&tmp->vma_lock);-}--/* User has access, do the reset */-ret=pci_reset_bus(vdev->pdev);+ret=vfio_hot_reset_device_set(vdev,&info);hot_reset_release:-for(i=0;i<devs.cur_index;i++){-structvfio_pci_device*tmp=devs.devices[i];--if(i<mem_idx)-up_write(&tmp->memory_lock);-else-mutex_unlock(&tmp->vma_lock);-vfio_device_put(&tmp->vdev);-}-kfree(devs.devices);-for(group_idx--;group_idx>=0;group_idx--)-vfio_group_put_external_user(groups[group_idx].group);+vfio_group_put_external_user(groups[group_idx]);kfree(groups);returnret;
@@ -2155,37 +2069,94 @@ static int vfio_pci_check_all_devices_bound(struct pci_dev *pdev, void *data)return-EBUSY;}-staticintvfio_pci_try_zap_and_vma_lock_cb(structpci_dev*pdev,void*data)+staticboolvfio_dev_in_groups(structvfio_pci_device*vdev,+structvfio_pci_group_info*groups){-structvfio_devices*devs=data;-structvfio_device*device;-structvfio_pci_device*vdev;+unsignedinti;-if(devs->cur_index==devs->max_index)-return-ENOSPC;+for(i=0;i<groups->count;i++)+if(groups->groups[i]==vdev->vdev.group)+returntrue;+returnfalse;+}-device=vfio_device_get_from_dev(&pdev->dev);-if(!device)-return-EINVAL;+/*+*Weneedtogetmemory_lockforeachdevice,butdevicescansharemmap_lock,+*thereforeweneedtozapandholdthevma_lockforeachdevice,andonlythen+*geteachmemory_lock.+*/+staticintvfio_hot_reset_device_set(structvfio_pci_device*vdev,+structvfio_pci_group_info*groups)+{+structvfio_device_set*dev_set=vdev->vdev.dev_set;+structvfio_pci_device*cur_mem=+list_first_entry(&dev_set->device_list,structvfio_pci_device,+vdev.dev_set_list);+structvfio_pci_device*cur_vma;+structvfio_pci_device*cur;+boolis_mem=true;+intret;-if(pci_dev_driver(pdev)!=&vfio_pci_driver){-vfio_device_put(device);-return-EBUSY;+mutex_lock(&dev_set->lock);++/* All devices in the group to be reset need VFIO devices */+if(vfio_pci_for_each_slot_or_bus(+vdev->pdev,vfio_pci_check_all_devices_bound,dev_set,+!pci_probe_reset_slot(vdev->pdev->slot))){+ret=-EINVAL;+gotoerr_unlock;}-vdev=container_of(device,structvfio_pci_device,vdev);+list_for_each_entry(cur_vma,&dev_set->device_list,vdev.dev_set_list){+/*+*Testwhetheralltheaffecteddevicesarecontainedbythe+*setofgroupsprovidedbytheuser.+*/+if(!vfio_dev_in_groups(cur_vma,groups)){+ret=-EINVAL;+gotoerr_undo;+}-/*-*Lockingmultipledevicesispronetodeadlock,runawayand-*unwindifwehitcontention.-*/-if(!vfio_pci_zap_and_vma_lock(vdev,true)){-vfio_device_put(device);-return-EBUSY;+/*+*Lockingmultipledevicesispronetodeadlock,runawayand+*unwindifwehitcontention.+*/+if(!vfio_pci_zap_and_vma_lock(cur_vma,true)){+ret=-EBUSY;+gotoerr_undo;+}}-devs->devices[devs->cur_index++]=vdev;-return0;+list_for_each_entry(cur_mem,&dev_set->device_list,vdev.dev_set_list){+if(!down_write_trylock(&cur_mem->memory_lock)){+ret=-EBUSY;+gotoerr_undo;+}+mutex_unlock(&cur_mem->vma_lock);+}++ret=pci_reset_bus(vdev->pdev);++list_for_each_entry(cur,&dev_set->device_list,vdev.dev_set_list)+up_write(&cur->memory_lock);+mutex_unlock(&dev_set->lock);++returnret;++err_undo:+list_for_each_entry(cur,&dev_set->device_list,vdev.dev_set_list){+if(cur==cur_mem)+is_mem=false;+if(cur==cur_vma)+break;+if(is_mem)+up_write(&cur->memory_lock);+else+mutex_unlock(&cur->vma_lock);+}+err_unlock:+mutex_unlock(&dev_set->lock);+returnret;}/*
From: Leon Romanovsky <leonro@nvidia.com> Date: 2021-07-15 03:49:13
On Wed, Jul 14, 2021 at 09:20:31PM -0300, Jason Gunthorpe wrote:
From: Max Gurtovoy <mgurtovoy@nvidia.com>
This pairs with vfio_init_group_dev() and allows undoing any state that is
stored in the vfio_device unrelated to registration. Add appropriately
placed calls to all the drivers.
The following patch will use this to add pre-registration state for the
device set.
Signed-off-by: Max Gurtovoy <mgurtovoy@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
Documentation/driver-api/vfio.rst | 4 ++-
drivers/vfio/fsl-mc/vfio_fsl_mc.c | 6 +++--
drivers/vfio/mdev/vfio_mdev.c | 13 +++++++---
drivers/vfio/pci/vfio_pci.c | 6 +++--
drivers/vfio/platform/vfio_platform_common.c | 7 +++--
drivers/vfio/vfio.c | 5 ++++
include/linux/vfio.h | 1 +
samples/vfio-mdev/mbochs.c | 2 ++
samples/vfio-mdev/mdpy.c | 25 ++++++++++--------
samples/vfio-mdev/mtty.c | 27 ++++++++++++--------
10 files changed, 64 insertions(+), 32 deletions(-)
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 12:45:17
On Thu, Jul 15, 2021 at 06:49:05AM +0300, Leon Romanovsky wrote:
On Wed, Jul 14, 2021 at 09:20:31PM -0300, Jason Gunthorpe wrote:
quoted
From: Max Gurtovoy <mgurtovoy@nvidia.com>
This pairs with vfio_init_group_dev() and allows undoing any state that is
stored in the vfio_device unrelated to registration. Add appropriately
placed calls to all the drivers.
The following patch will use this to add pre-registration state for the
device set.
Signed-off-by: Max Gurtovoy <mgurtovoy@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Documentation/driver-api/vfio.rst | 4 ++-
drivers/vfio/fsl-mc/vfio_fsl_mc.c | 6 +++--
drivers/vfio/mdev/vfio_mdev.c | 13 +++++++---
drivers/vfio/pci/vfio_pci.c | 6 +++--
drivers/vfio/platform/vfio_platform_common.c | 7 +++--
drivers/vfio/vfio.c | 5 ++++
include/linux/vfio.h | 1 +
samples/vfio-mdev/mbochs.c | 2 ++
samples/vfio-mdev/mdpy.c | 25 ++++++++++--------
samples/vfio-mdev/mtty.c | 27 ++++++++++++--------
10 files changed, 64 insertions(+), 32 deletions(-)
This is wrong place, the _uninit_ should be after vfio_fsl_mc_reflck_put().
Well, maybe, but it doesn't matter, the uninit doesn't effect the
reflck and the next fsl patch deletes the line below. I can switch it
if there is a v2
Prologue:
This is the first series of three to send the "mlx5_vfio_pci" driver that has
been discussed on the list for a while now.
- Reorganize reflck to support splitting vfio_pci
- Split vfio_pci into vfio_pci/vfio_pci_core and provide infrastructure
for non-generic VFIO PCI drivers
- The new driver mlx5_vfio_pci that is a full implementation of
suspend/resume functionality for mlx5 devices.
A preview of all the patches can be seen here:
https://github.com/jgunthorpe/linux/commits/mlx5_vfio_pci
===============
This is in support of Max's series to split vfio-pci. For that to work the
reflck concept embedded in vfio-pci needs to be sharable across all of the
new VFIO PCI drivers which motivated re-examining how this is
implemented.
Another significant issue is how the VFIO PCI core includes code like:
if (pci_dev_driver(pdev) != &vfio_pci_driver)
Which is not scalable if there are going to be multiple different driver
types.
This series takes the approach of moving the "reflck" mechanism into the
core code as a "device set". Each vfio_device driver can specify how
vfio_devices are grouped into the set using a key and the set comes along
with a set-global mutex. The core code manages creating per-device set
memory and associating it with each vfio_device.
In turn this allows the core code to provide an open/close_device()
operation that is called only for the first/last FD, and is called under
the global device set lock.
Review of all the drivers show that they are either already open coding
the first/last semantic or are buggy and missing it. All drivers are
migrated/fixed to the new open/close_device ops and the unused per-FD
open()/release() ops are deleted.
Why can't open()/release() ops be reused instead of adding
open_device()/close_device().
Thanks,
Kirti
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 14:55:54
On Thu, Jul 15, 2021 at 06:58:31PM +0530, Kirti Wankhede wrote:
quoted
Review of all the drivers show that they are either already open coding
the first/last semantic or are buggy and missing it. All drivers are
migrated/fixed to the new open/close_device ops and the unused per-FD
open()/release() ops are deleted.
Why can't open()/release() ops be reused instead of adding
open_device()/close_device().
It could be done but it would ruin the structure of the patch series,
obfuscate the naming of the ops, and complicate backporting as this is
a significant semantic difference.
Overall when funtionality changes significantly it is better to change
the name along with it
Jason
From: Alex Williamson <hidden> Date: 2021-07-15 21:01:02
On Wed, 14 Jul 2021 21:20:38 -0300
Jason Gunthorpe [off-list ref] wrote:
+/*
+ * We need to get memory_lock for each device, but devices can share mmap_lock,
+ * therefore we need to zap and hold the vma_lock for each device, and only then
+ * get each memory_lock.
+ */
+static int vfio_hot_reset_device_set(struct vfio_pci_device *vdev,
+ struct vfio_pci_group_info *groups)
+{
+ struct vfio_device_set *dev_set = vdev->vdev.dev_set;
+ struct vfio_pci_device *cur_mem =
+ list_first_entry(&dev_set->device_list, struct vfio_pci_device,
+ vdev.dev_set_list);
We shouldn't be looking at the list outside of the lock, if the first
entry got removed we'd break our unwind code.
+
+ /* All devices in the group to be reset need VFIO devices */
+ if (vfio_pci_for_each_slot_or_bus(
+ vdev->pdev, vfio_pci_check_all_devices_bound, dev_set,
+ !pci_probe_reset_slot(vdev->pdev->slot))) {
+ ret = -EINVAL;
+ goto err_unlock;
}
- vdev = container_of(device, struct vfio_pci_device, vdev);
+ list_for_each_entry(cur_vma, &dev_set->device_list, vdev.dev_set_list) {
+ /*
+ * Test whether all the affected devices are contained by the
+ * set of groups provided by the user.
+ */
+ if (!vfio_dev_in_groups(cur_vma, groups)) {
+ ret = -EINVAL;
+ goto err_undo;
+ }
- /*
- * Locking multiple devices is prone to deadlock, runaway and
- * unwind if we hit contention.
- */
- if (!vfio_pci_zap_and_vma_lock(vdev, true)) {
- vfio_device_put(device);
- return -EBUSY;
+ /*
+ * Locking multiple devices is prone to deadlock, runaway and
+ * unwind if we hit contention.
+ */
+ if (!vfio_pci_zap_and_vma_lock(cur_vma, true)) {
+ ret = -EBUSY;
+ goto err_undo;
+ }
}
- devs->devices[devs->cur_index++] = vdev;
- return 0;
+ list_for_each_entry(cur_mem, &dev_set->device_list, vdev.dev_set_list) {
+ if (!down_write_trylock(&cur_mem->memory_lock)) {
+ ret = -EBUSY;
+ goto err_undo;
+ }
+ mutex_unlock(&cur_mem->vma_lock);
+ }
+
+ ret = pci_reset_bus(vdev->pdev);
+
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-15 22:11:55
On Thu, Jul 15, 2021 at 03:00:55PM -0600, Alex Williamson wrote:
On Wed, 14 Jul 2021 21:20:38 -0300
Jason Gunthorpe [off-list ref] wrote:
quoted
+/*
+ * We need to get memory_lock for each device, but devices can share mmap_lock,
+ * therefore we need to zap and hold the vma_lock for each device, and only then
+ * get each memory_lock.
+ */
+static int vfio_hot_reset_device_set(struct vfio_pci_device *vdev,
+ struct vfio_pci_group_info *groups)
+{
+ struct vfio_device_set *dev_set = vdev->vdev.dev_set;
+ struct vfio_pci_device *cur_mem =
+ list_first_entry(&dev_set->device_list, struct vfio_pci_device,
+ vdev.dev_set_list);
We shouldn't be looking at the list outside of the lock, if the first
entry got removed we'd break our unwind code.
From: Alex Williamson <hidden> Date: 2021-07-15 22:27:54
On Thu, 15 Jul 2021 19:11:49 -0300
Jason Gunthorpe [off-list ref] wrote:
On Thu, Jul 15, 2021 at 03:00:55PM -0600, Alex Williamson wrote:
quoted
On Wed, 14 Jul 2021 21:20:38 -0300
Jason Gunthorpe [off-list ref] wrote:
quoted
+/*
+ * We need to get memory_lock for each device, but devices can share mmap_lock,
+ * therefore we need to zap and hold the vma_lock for each device, and only then
+ * get each memory_lock.
+ */
+static int vfio_hot_reset_device_set(struct vfio_pci_device *vdev,
+ struct vfio_pci_group_info *groups)
+{
+ struct vfio_device_set *dev_set = vdev->vdev.dev_set;
+ struct vfio_pci_device *cur_mem =
+ list_first_entry(&dev_set->device_list, struct vfio_pci_device,
+ vdev.dev_set_list);
We shouldn't be looking at the list outside of the lock, if the first
entry got removed we'd break our unwind code.
Yeah, I think the simpler version just adds to the confusion of what
this oddball logic does. It already handles all cases, up to and
including success, so let's give it more exercise by always using it.
Thanks,
Alex
From: Zhenyu Wang <hidden> Date: 2021-07-16 07:12:34
On 2021.07.14 21:20:41 -0300, Jason Gunthorpe wrote:
quoted hunk
The user can open multiple device FDs if it likes, however the open
function calls vfio_register_notifier() on device global state. Calling
vfio_register_notifier() twice will trigger a WARN_ON from
notifier_chain_register() and the first close will wrongly delete the
notifier and more.
Since these really want the new open/close_device() semantics just change
the function over.
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
drivers/gpu/drm/i915/gvt/kvmgt.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
On Wed, Jul 14 2021, Jason Gunthorpe [off-list ref] wrote:
The patch to move the get/put to core and the patch to convert the samples
to use vfio_device crossed in a way that this was missed. When both
patches are together the samples do not need their own get/put.
Fixes: 437e41368c01 ("vfio/mdpy: Convert to use vfio_register_group_dev()")
Fixes: 681c1615f891 ("vfio/mbochs: Convert to use vfio_register_group_dev()")
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
samples/vfio-mdev/mbochs.c | 4 ----
samples/vfio-mdev/mdpy.c | 4 ----
2 files changed, 8 deletions(-)
On Wed, Jul 14 2021, Jason Gunthorpe [off-list ref] wrote:
From: Max Gurtovoy <mgurtovoy@nvidia.com>
This pairs with vfio_init_group_dev() and allows undoing any state that is
stored in the vfio_device unrelated to registration. Add appropriately
placed calls to all the drivers.
The following patch will use this to add pre-registration state for the
device set.
Signed-off-by: Max Gurtovoy <mgurtovoy@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
Documentation/driver-api/vfio.rst | 4 ++-
drivers/vfio/fsl-mc/vfio_fsl_mc.c | 6 +++--
drivers/vfio/mdev/vfio_mdev.c | 13 +++++++---
drivers/vfio/pci/vfio_pci.c | 6 +++--
drivers/vfio/platform/vfio_platform_common.c | 7 +++--
drivers/vfio/vfio.c | 5 ++++
include/linux/vfio.h | 1 +
samples/vfio-mdev/mbochs.c | 2 ++
samples/vfio-mdev/mdpy.c | 25 ++++++++++--------
samples/vfio-mdev/mtty.c | 27 ++++++++++++--------
10 files changed, 64 insertions(+), 32 deletions(-)
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-19 12:17:35
On Mon, Jul 19, 2021 at 02:11:38PM +0200, Cornelia Huck wrote:
On Wed, Jul 14 2021, Jason Gunthorpe [off-list ref] wrote:
quoted
From: Max Gurtovoy <mgurtovoy@nvidia.com>
This pairs with vfio_init_group_dev() and allows undoing any state that is
stored in the vfio_device unrelated to registration. Add appropriately
placed calls to all the drivers.
The following patch will use this to add pre-registration state for the
device set.
Signed-off-by: Max Gurtovoy <mgurtovoy@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Documentation/driver-api/vfio.rst | 4 ++-
drivers/vfio/fsl-mc/vfio_fsl_mc.c | 6 +++--
drivers/vfio/mdev/vfio_mdev.c | 13 +++++++---
drivers/vfio/pci/vfio_pci.c | 6 +++--
drivers/vfio/platform/vfio_platform_common.c | 7 +++--
drivers/vfio/vfio.c | 5 ++++
include/linux/vfio.h | 1 +
samples/vfio-mdev/mbochs.c | 2 ++
samples/vfio-mdev/mdpy.c | 25 ++++++++++--------
samples/vfio-mdev/mtty.c | 27 ++++++++++++--------
10 files changed, 64 insertions(+), 32 deletions(-)
On Mon, Jul 19 2021, Jason Gunthorpe [off-list ref] wrote:
On Mon, Jul 19, 2021 at 02:11:38PM +0200, Cornelia Huck wrote:
quoted
On Wed, Jul 14 2021, Jason Gunthorpe [off-list ref] wrote:
quoted
From: Max Gurtovoy <mgurtovoy@nvidia.com>
This pairs with vfio_init_group_dev() and allows undoing any state that is
stored in the vfio_device unrelated to registration. Add appropriately
placed calls to all the drivers.
The following patch will use this to add pre-registration state for the
device set.
Signed-off-by: Max Gurtovoy <mgurtovoy@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Documentation/driver-api/vfio.rst | 4 ++-
drivers/vfio/fsl-mc/vfio_fsl_mc.c | 6 +++--
drivers/vfio/mdev/vfio_mdev.c | 13 +++++++---
drivers/vfio/pci/vfio_pci.c | 6 +++--
drivers/vfio/platform/vfio_platform_common.c | 7 +++--
drivers/vfio/vfio.c | 5 ++++
include/linux/vfio.h | 1 +
samples/vfio-mdev/mbochs.c | 2 ++
samples/vfio-mdev/mdpy.c | 25 ++++++++++--------
samples/vfio-mdev/mtty.c | 27 ++++++++++++--------
10 files changed, 64 insertions(+), 32 deletions(-)
On Wed, Jul 14 2021, Jason Gunthorpe [off-list ref] wrote:
Currently the driver ops have an open/release pair that is called once
each time a device FD is opened or closed. Add an additional set of
open/close_device() ops which are called when the device FD is opened for
the first time and closed for the last time.
An analysis shows that all of the drivers require this semantic. Some are
open coding it as part of their reflck implementation, and some are just
buggy and miss it completely.
To retain the current semantics PCI and FSL depend on, introduce the idea
of a "device set" which is a grouping of vfio_device's that share the same
lock around opening.
The device set is established by providing a 'set_id' pointer. All
vfio_device's that provide the same pointer will be joined to the same
singleton memory and lock across the whole set. This effectively replaces
the oddly named reflck.
After conversion the set_id will be sourced from:
- A struct device from a fsl_mc_device (fsl)
- A struct pci_slot (pci)
- A struct pci_bus (pci)
- The struct vfio_device (everything)
The design ensures that the above pointers are live as long as the
vfio_device is registered, so they form reliable unique keys to group
vfio_devices into sets.
This implementation uses xarray instead of searching through the driver
core structures, which simplifies the somewhat tricky locking in this
area.
Following patches convert all the drivers.
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
drivers/vfio/mdev/vfio_mdev.c | 22 ++++++
drivers/vfio/vfio.c | 144 ++++++++++++++++++++++++++++------
include/linux/mdev.h | 2 +
include/linux/vfio.h | 19 +++++
4 files changed, 165 insertions(+), 22 deletions(-)
(...)
quoted hunk
@@ -760,6 +829,13 @@ int vfio_register_group_dev(struct vfio_device *device) struct iommu_group *iommu_group; struct vfio_group *group;+ /*+ * If the driver doesn't specify a set then the device is added to a+ * signleton set just for itself.
s/signleton/singleton/
quoted hunk
+ */
+ if (!device->dev_set)
+ vfio_assign_device_set(device, device);
+
iommu_group = iommu_group_get(device->dev);
if (!iommu_group)
return -EINVAL;
@@ -1361,7 +1437,8 @@ static int vfio_group_get_device_fd(struct vfio_group *group, char *buf) { struct vfio_device *device; struct file *filep;- int ret;+ int fdno;+ int ret = 0; if (0 == atomic_read(&group->container_users) || !group->container->iommu_driver || !vfio_group_viable(group))
@@ -1375,38 +1452,38 @@ static int vfio_group_get_device_fd(struct vfio_group *group, char *buf) return PTR_ERR(device); if (!try_module_get(device->dev->driver->owner)) {- vfio_device_put(device);- return -ENODEV;+ ret = -ENODEV;+ goto err_device_put; }- ret = device->ops->open(device);- if (ret) {- module_put(device->dev->driver->owner);- vfio_device_put(device);- return ret;+ mutex_lock(&device->dev_set->lock);+ device->open_count++;+ if (device->open_count == 1 && device->ops->open_device) {+ ret = device->ops->open_device(device);+ if (ret)+ goto err_undo_count;
Won't that fail for mdev devices, until the patches later in this series
have been applied? (i.e. bad for bisect)
+ }
+ mutex_unlock(&device->dev_set->lock);
+
+ if (device->ops->open) {
+ ret = device->ops->open(device);
+ if (ret)
+ goto err_close_device;
}
On Wed, Jul 14 2021, Jason Gunthorpe [off-list ref] wrote:
The core code no longer requires these ops to be defined, so delete these
empty functions and leave the op as NULL. mtty's functions only log a
pointless message, delete that entirely.
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
samples/vfio-mdev/mbochs.c | 6 ------
samples/vfio-mdev/mdpy.c | 11 -----------
samples/vfio-mdev/mtty.c | 13 -------------
3 files changed, 30 deletions(-)
On Wed, Jul 14 2021, Jason Gunthorpe [off-list ref] wrote:
The user can open multiple device FDs if it likes, however these open()
functions call vfio_register_notifier() on some device global
state. Calling vfio_register_notifier() twice in will trigger a WARN_ON
from notifier_chain_register() and the first close will wrongly delete the
notifier and more.
Since these really want the new open/close_device() semantics just change
the functions over.
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
drivers/s390/cio/vfio_ccw_ops.c | 8 ++++----
drivers/s390/crypto/vfio_ap_ops.c | 8 ++++----
2 files changed, 8 insertions(+), 8 deletions(-)
On Wed, Jul 14 2021, Jason Gunthorpe [off-list ref] wrote:
mbochs_close() iterates over global device state and frees it. Currently
this is done every time a device FD is closed, but if multiple device FDs
are open this could corrupt other still active FDs.
Change this to use close_device() so it only runs on the last close.
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
samples/vfio-mdev/mbochs.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
On Wed, Jul 14 2021, Jason Gunthorpe [off-list ref] wrote:
The user can open multiple device FDs if it likes, however the open
function calls vfio_register_notifier() on device global state. Calling
vfio_register_notifier() twice will trigger a WARN_ON from
notifier_chain_register() and the first close will wrongly delete the
notifier and more.
Since these really want the new open/close_device() semantics just change
the function over.
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
drivers/gpu/drm/i915/gvt/kvmgt.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
FSL uses the internal reflck to implement the open_device() functionality,
conversion to the core code is straightforward.
The decision on which set to be part of is trivially based on the
is_fsl_mc_bus_dprc() and we use a 'struct device *' pointer as the set_id.
It isn't entirely clear what the device set lock is actually protecting,
but I think it is related to the interrupt setup.
Yes, it is protecting the interrupts setup. The FSL MC devices are using
MSIs and only the DPRC device is allocating the MSIs from the MSI
domain. The other devices just take interrupts from a pool. The lock is
protecting the access to this pool.
@@ -136,58 +65,30 @@ static void vfio_fsl_mc_regions_cleanup(struct vfio_fsl_mc_device *vdev)kfree(vdev->regions);}-staticintvfio_fsl_mc_open(structvfio_device*core_vdev)-{-structvfio_fsl_mc_device*vdev=-container_of(core_vdev,structvfio_fsl_mc_device,vdev);-intret=0;--mutex_lock(&vdev->reflck->lock);-if(!vdev->refcnt){-ret=vfio_fsl_mc_regions_init(vdev);-if(ret)-gotoout;-}-vdev->refcnt++;-out:-mutex_unlock(&vdev->reflck->lock);-returnret;-}--staticvoidvfio_fsl_mc_release(structvfio_device*core_vdev)+staticvoidvfio_fsl_mc_close_device(structvfio_device*core_vdev){structvfio_fsl_mc_device*vdev=container_of(core_vdev,structvfio_fsl_mc_device,vdev);+structfsl_mc_device*mc_dev=vdev->mc_dev;+structdevice*cont_dev=fsl_mc_cont_dev(&mc_dev->dev);+structfsl_mc_device*mc_cont=to_fsl_mc_device(cont_dev);intret;-mutex_lock(&vdev->reflck->lock);+vfio_fsl_mc_regions_cleanup(vdev);-if(!(--vdev->refcnt)){-structfsl_mc_device*mc_dev=vdev->mc_dev;-structdevice*cont_dev=fsl_mc_cont_dev(&mc_dev->dev);-structfsl_mc_device*mc_cont=to_fsl_mc_device(cont_dev);--vfio_fsl_mc_regions_cleanup(vdev);+/* reset the device before cleaning up the interrupts */+ret=dprc_reset_container(mc_cont->mc_io,0,mc_cont->mc_handle,+mc_cont->obj_desc.id,+DPRC_RESET_OPTION_NON_RECURSIVE);-/* reset the device before cleaning up the interrupts */-ret=dprc_reset_container(mc_cont->mc_io,0,-mc_cont->mc_handle,-mc_cont->obj_desc.id,-DPRC_RESET_OPTION_NON_RECURSIVE);+if(WARN_ON(ret))+dev_warn(&mc_cont->dev,+"VFIO_FLS_MC: reset device has failed (%d)\n",ret);-if(ret){-dev_warn(&mc_cont->dev,"VFIO_FLS_MC: reset device has failed (%d)\n",-ret);-WARN_ON(1);-}+vfio_fsl_mc_irqs_cleanup(vdev);-vfio_fsl_mc_irqs_cleanup(vdev);--fsl_mc_cleanup_irq_pool(mc_cont);
There is also a need for the lock here. Eventhough the close function is
called only once, there might be a race between the devices in the set.
The lock is protecting the pool of interrupts and releasing interrupts
to the pool might generate races if not protected:
@@ -625,13 +526,15 @@ static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev) vdev->mc_dev = mc_dev; mutex_init(&vdev->igate);- ret = vfio_fsl_mc_reflck_attach(vdev);+ ret = vfio_assign_device_set(&vdev->vdev, is_fsl_mc_bus_dprc(mc_dev) ?+ &mc_dev->dev :+ mc_dev->dev.parent); if (ret) goto out_uninit; ret = vfio_fsl_mc_init_device(vdev); if (ret)- goto out_reflck;+ goto out_uninit; ret = vfio_register_group_dev(&vdev->vdev); if (ret) {
@@ -639,12 +542,6 @@ static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev) goto out_device; }- /*- * This triggers recursion into vfio_fsl_mc_probe() on another device- * and the vfio_fsl_mc_reflck_attach() must succeed, which relies on the- * vfio_add_group_dev() above. It has no impact on this vdev, so it is- * safe to be after the vfio device is made live.- */ ret = vfio_fsl_mc_scan_container(mc_dev); if (ret) goto out_group_dev;
From: Jason Gunthorpe <jgg@nvidia.com> Date: 2021-07-20 16:24:41
On Tue, Jul 20, 2021 at 07:12:26PM +0300, Diana Craciun OSS wrote:
On 7/15/2021 3:20 AM, Jason Gunthorpe wrote:
quoted
FSL uses the internal reflck to implement the open_device() functionality,
conversion to the core code is straightforward.
The decision on which set to be part of is trivially based on the
is_fsl_mc_bus_dprc() and we use a 'struct device *' pointer as the set_id.
It isn't entirely clear what the device set lock is actually protecting,
but I think it is related to the interrupt setup.
Yes, it is protecting the interrupts setup. The FSL MC devices are using
MSIs and only the DPRC device is allocating the MSIs from the MSI domain.
The other devices just take interrupts from a pool. The lock is protecting
the access to this pool.
It would be much clearer if the lock was near the data it was
protecting, the DPRC pool seems in an entirely different layer..
quoted
-static void vfio_fsl_mc_release(struct vfio_device *core_vdev)
+static void vfio_fsl_mc_close_device(struct vfio_device *core_vdev)
{
struct vfio_fsl_mc_device *vdev =
container_of(core_vdev, struct vfio_fsl_mc_device, vdev);
+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
+ struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev);
+ struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev);
int ret;
- mutex_lock(&vdev->reflck->lock);
+ vfio_fsl_mc_regions_cleanup(vdev);
- if (!(--vdev->refcnt)) {
- struct fsl_mc_device *mc_dev = vdev->mc_dev;
- struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev);
- struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev);
-
- vfio_fsl_mc_regions_cleanup(vdev);
+ /* reset the device before cleaning up the interrupts */
+ ret = dprc_reset_container(mc_cont->mc_io, 0, mc_cont->mc_handle,
+ mc_cont->obj_desc.id,
+ DPRC_RESET_OPTION_NON_RECURSIVE);
- /* reset the device before cleaning up the interrupts */
- ret = dprc_reset_container(mc_cont->mc_io, 0,
- mc_cont->mc_handle,
- mc_cont->obj_desc.id,
- DPRC_RESET_OPTION_NON_RECURSIVE);
+ if (WARN_ON(ret))
+ dev_warn(&mc_cont->dev,
+ "VFIO_FLS_MC: reset device has failed (%d)\n", ret);
- if (ret) {
- dev_warn(&mc_cont->dev, "VFIO_FLS_MC: reset device has failed (%d)\n",
- ret);
- WARN_ON(1);
- }
+ vfio_fsl_mc_irqs_cleanup(vdev);
- vfio_fsl_mc_irqs_cleanup(vdev);
-
- fsl_mc_cleanup_irq_pool(mc_cont);
There is also a need for the lock here. Eventhough the close function is
called only once, there might be a race between the devices in the
set.
vfio_fsl_mc_close_device() is already called under this lock:
mutex_lock(&device->dev_set->lock);
if (!--device->open_count && device->ops->close_device)
device->ops->close_device(device);
mutex_unlock(&device->dev_set->lock);
Thanks,
Jason
On Tue, Jul 20, 2021 at 07:12:26PM +0300, Diana Craciun OSS wrote:
quoted
On 7/15/2021 3:20 AM, Jason Gunthorpe wrote:
quoted
FSL uses the internal reflck to implement the open_device() functionality,
conversion to the core code is straightforward.
The decision on which set to be part of is trivially based on the
is_fsl_mc_bus_dprc() and we use a 'struct device *' pointer as the set_id.
It isn't entirely clear what the device set lock is actually protecting,
but I think it is related to the interrupt setup.
Yes, it is protecting the interrupts setup. The FSL MC devices are using
MSIs and only the DPRC device is allocating the MSIs from the MSI domain.
The other devices just take interrupts from a pool. The lock is protecting
the access to this pool.
It would be much clearer if the lock was near the data it was
protecting, the DPRC pool seems in an entirely different layer..
Yes, I agree. I will think about of a more clearer design for a future
improvement.
quoted
quoted
-static void vfio_fsl_mc_release(struct vfio_device *core_vdev)
+static void vfio_fsl_mc_close_device(struct vfio_device *core_vdev)
{
struct vfio_fsl_mc_device *vdev =
container_of(core_vdev, struct vfio_fsl_mc_device, vdev);
+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
+ struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev);
+ struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev);
int ret;
- mutex_lock(&vdev->reflck->lock);
+ vfio_fsl_mc_regions_cleanup(vdev);
- if (!(--vdev->refcnt)) {
- struct fsl_mc_device *mc_dev = vdev->mc_dev;
- struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev);
- struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev);
-
- vfio_fsl_mc_regions_cleanup(vdev);
+ /* reset the device before cleaning up the interrupts */
+ ret = dprc_reset_container(mc_cont->mc_io, 0, mc_cont->mc_handle,
+ mc_cont->obj_desc.id,
+ DPRC_RESET_OPTION_NON_RECURSIVE);
- /* reset the device before cleaning up the interrupts */
- ret = dprc_reset_container(mc_cont->mc_io, 0,
- mc_cont->mc_handle,
- mc_cont->obj_desc.id,
- DPRC_RESET_OPTION_NON_RECURSIVE);
+ if (WARN_ON(ret))
+ dev_warn(&mc_cont->dev,
+ "VFIO_FLS_MC: reset device has failed (%d)\n", ret);
- if (ret) {
- dev_warn(&mc_cont->dev, "VFIO_FLS_MC: reset device has failed (%d)\n",
- ret);
- WARN_ON(1);
- }
+ vfio_fsl_mc_irqs_cleanup(vdev);
- vfio_fsl_mc_irqs_cleanup(vdev);
-
- fsl_mc_cleanup_irq_pool(mc_cont);
There is also a need for the lock here. Eventhough the close function is
called only once, there might be a race between the devices in the
set.
vfio_fsl_mc_close_device() is already called under this lock:
mutex_lock(&device->dev_set->lock);
if (!--device->open_count && device->ops->close_device)
device->ops->close_device(device);
mutex_unlock(&device->dev_set->lock);
OK, I missed that.
Thanks,
Jason
I have tested the changes and everything works as expected.
Thanks,
Diana