* [PATCH 0/3] vfio-pci: Reset improvements
@ 2014-07-16 19:01 Alex Williamson
2014-07-16 19:01 ` [PATCH 1/3] vfio-pci: Release devices with BusMaster disabled Alex Williamson
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Alex Williamson @ 2014-07-16 19:01 UTC (permalink / raw)
To: alex.williamson; +Cc: kvm, linux-kernel, linux-pci
This series is intended to improve the state of devices returned back
to the host from vfio-pci or re-used by another user. First we make
sure that busmaster is disabled in the saved state, so the device
cannot continue to do DMA, then we add some serialization, move our
reference counting under it to fix an unlikely bug should we fail to
initialize a device, and add the ability to do bus/slot reset on
device release. To do this, we require all devices affected by the
bus/slot reset to be bound to vfio-pci, therefore users sequestering
devices with pci-stub will need to bind them to vfio-pci to see this
change.
The effect of these changes are perhaps most noticeable with GPU
assignment to a VM, where killing QEMU results in a static image on
the framebuffer since no reset of the device was done. Returning
the GPU to a host device at this point was suspect. Other devices,
like USB controllers, also don't necessarily appreciate being abruptly
disconnected from their IOMMU domain and would generate IOMMU faults
in the event the user process is killed. Both of these cases should
be resolved here, assuming all the devices on the bus are bound to
vfio-pci and at least one of the devices in use does not support
a function-local reset.
Please test and comment. Thanks,
Alex
---
Alex Williamson (3):
vfio-pci: Attempt bus/slot reset on release
vfio-pci: Use mutex around open, release, and remove
vfio-pci: Release devices with BusMaster disabled
drivers/vfio/pci/vfio_pci.c | 157 ++++++++++++++++++++++++++++++++---
drivers/vfio/pci/vfio_pci_private.h | 3 -
2 files changed, 147 insertions(+), 13 deletions(-)
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/3] vfio-pci: Release devices with BusMaster disabled
2014-07-16 19:01 [PATCH 0/3] vfio-pci: Reset improvements Alex Williamson
@ 2014-07-16 19:01 ` Alex Williamson
2014-07-16 19:01 ` [PATCH 2/3] vfio-pci: Use mutex around open, release, and remove Alex Williamson
2014-07-16 19:01 ` [PATCH 3/3] vfio-pci: Attempt bus/slot reset on release Alex Williamson
2 siblings, 0 replies; 4+ messages in thread
From: Alex Williamson @ 2014-07-16 19:01 UTC (permalink / raw)
To: alex.williamson; +Cc: kvm, linux-kernel, linux-pci
Our current open/release path looks like this:
vfio_pci_open
vfio_pci_enable
pci_enable_device
pci_save_state
pci_store_saved_state
vfio_pci_release
vfio_pci_disable
pci_disable_device
pci_restore_state
pci_enable_device() doesn't modify PCI_COMMAND_MASTER, so if a device
comes to us with it enabled, it persists through the open and gets
stored as part of the device saved state. We then restore that saved
state when released, which can allow the device to attempt to continue
to do DMA. When the group is disconnected from the domain, this will
get caught by the IOMMU, but if there are other devices in the group,
the device may continue running and interfere with the user. Even in
the former case, IOMMUs don't necessarily behave well and a stream of
blocked DMA can result in unpleasant behavior on the host.
Explicitly disable Bus Master as we're enabling the device and
slightly re-work release to make sure that pci_disable_device() is
the last thing that touches the device.
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
drivers/vfio/pci/vfio_pci.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 010e0f8..36d8332 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -44,6 +44,9 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev)
u16 cmd;
u8 msix_pos;
+ /* Don't allow our initial saved state to include busmaster */
+ pci_clear_master(pdev);
+
ret = pci_enable_device(pdev);
if (ret)
return ret;
@@ -99,7 +102,8 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
struct pci_dev *pdev = vdev->pdev;
int bar;
- pci_disable_device(pdev);
+ /* Stop the device from further DMA */
+ pci_clear_master(pdev);
vfio_pci_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE |
VFIO_IRQ_SET_ACTION_TRIGGER,
@@ -128,7 +132,7 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
__func__, dev_name(&pdev->dev));
if (!vdev->reset_works)
- return;
+ goto out;
pci_save_state(pdev);
}
@@ -151,6 +155,8 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
}
pci_restore_state(pdev);
+out:
+ pci_disable_device(pdev);
}
static void vfio_pci_release(void *device_data)
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/3] vfio-pci: Use mutex around open, release, and remove
2014-07-16 19:01 [PATCH 0/3] vfio-pci: Reset improvements Alex Williamson
2014-07-16 19:01 ` [PATCH 1/3] vfio-pci: Release devices with BusMaster disabled Alex Williamson
@ 2014-07-16 19:01 ` Alex Williamson
2014-07-16 19:01 ` [PATCH 3/3] vfio-pci: Attempt bus/slot reset on release Alex Williamson
2 siblings, 0 replies; 4+ messages in thread
From: Alex Williamson @ 2014-07-16 19:01 UTC (permalink / raw)
To: alex.williamson; +Cc: kvm, linux-kernel, linux-pci
Serializing open/release allows us to fix a refcnt error if we fail
to enable the device and lets us prevent devices from being unbound
or opened, giving us an opportunity to do bus resets on release. No
restriction added to serialize binding devices to vfio-pci while the
mutex is held though.
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
drivers/vfio/pci/vfio_pci.c | 35 +++++++++++++++++++++++++----------
drivers/vfio/pci/vfio_pci_private.h | 2 +-
2 files changed, 26 insertions(+), 11 deletions(-)
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 36d8332..c4949a7 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -37,6 +37,8 @@ module_param_named(nointxmask, nointxmask, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(nointxmask,
"Disable support for PCI 2.3 style INTx masking. If this resolves problems for specific devices, report lspci -vvvxxx to linux-pci@vger.kernel.org so the device can be fixed automatically via the broken_intx_masking flag.");
+static DEFINE_MUTEX(driver_lock);
+
static int vfio_pci_enable(struct vfio_pci_device *vdev)
{
struct pci_dev *pdev = vdev->pdev;
@@ -163,28 +165,39 @@ static void vfio_pci_release(void *device_data)
{
struct vfio_pci_device *vdev = device_data;
- if (atomic_dec_and_test(&vdev->refcnt))
+ mutex_lock(&driver_lock);
+
+ if (!(--vdev->refcnt))
vfio_pci_disable(vdev);
+ mutex_unlock(&driver_lock);
+
module_put(THIS_MODULE);
}
static int vfio_pci_open(void *device_data)
{
struct vfio_pci_device *vdev = device_data;
+ int ret = 0;
if (!try_module_get(THIS_MODULE))
return -ENODEV;
- if (atomic_inc_return(&vdev->refcnt) == 1) {
- int ret = vfio_pci_enable(vdev);
+ mutex_lock(&driver_lock);
+
+ if (!vdev->refcnt) {
+ ret = vfio_pci_enable(vdev);
if (ret) {
module_put(THIS_MODULE);
- return ret;
+ goto unlock;
}
}
+ vdev->refcnt++;
- return 0;
+unlock:
+ mutex_unlock(&driver_lock);
+
+ return ret;
}
static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type)
@@ -839,7 +852,6 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
vdev->irq_type = VFIO_PCI_NUM_IRQS;
mutex_init(&vdev->igate);
spin_lock_init(&vdev->irqlock);
- atomic_set(&vdev->refcnt, 0);
ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev);
if (ret) {
@@ -854,12 +866,15 @@ static void vfio_pci_remove(struct pci_dev *pdev)
{
struct vfio_pci_device *vdev;
+ mutex_lock(&driver_lock);
+
vdev = vfio_del_group_dev(&pdev->dev);
- if (!vdev)
- return;
+ if (vdev) {
+ iommu_group_put(pdev->dev.iommu_group);
+ kfree(vdev);
+ }
- iommu_group_put(pdev->dev.iommu_group);
- kfree(vdev);
+ mutex_unlock(&driver_lock);
}
static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev,
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h
index 9c6d5d0..31e7a30 100644
--- a/drivers/vfio/pci/vfio_pci_private.h
+++ b/drivers/vfio/pci/vfio_pci_private.h
@@ -55,7 +55,7 @@ struct vfio_pci_device {
bool bardirty;
bool has_vga;
struct pci_saved_state *pci_saved_state;
- atomic_t refcnt;
+ int refcnt;
struct eventfd_ctx *err_trigger;
};
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 3/3] vfio-pci: Attempt bus/slot reset on release
2014-07-16 19:01 [PATCH 0/3] vfio-pci: Reset improvements Alex Williamson
2014-07-16 19:01 ` [PATCH 1/3] vfio-pci: Release devices with BusMaster disabled Alex Williamson
2014-07-16 19:01 ` [PATCH 2/3] vfio-pci: Use mutex around open, release, and remove Alex Williamson
@ 2014-07-16 19:01 ` Alex Williamson
2 siblings, 0 replies; 4+ messages in thread
From: Alex Williamson @ 2014-07-16 19:01 UTC (permalink / raw)
To: alex.williamson; +Cc: kvm, linux-kernel, linux-pci
Each time a device is released, mark whether a local reset was
successful or whether a bus/slot reset is needed. If a reset is
needed and all of the affected devices are bound to vfio-pci and
unused, allow the reset. This is most useful when the userspace
driver is killed and releases all the devices in an unclean state,
such as when a QEMU VM quits.
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
drivers/vfio/pci/vfio_pci.c | 112 +++++++++++++++++++++++++++++++++++
drivers/vfio/pci/vfio_pci_private.h | 1
2 files changed, 113 insertions(+)
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index c4949a7..f95b90f 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -39,6 +39,8 @@ MODULE_PARM_DESC(nointxmask,
static DEFINE_MUTEX(driver_lock);
+static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev);
+
static int vfio_pci_enable(struct vfio_pci_device *vdev)
{
struct pci_dev *pdev = vdev->pdev;
@@ -123,6 +125,8 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
vdev->barmap[bar] = NULL;
}
+ vdev->needs_reset = true;
+
/*
* If we have saved state, restore it. If we can reset the device,
* even better. Resetting with current state seems better than
@@ -154,11 +158,15 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
if (ret)
pr_warn("%s: Failed to reset device %s (%d)\n",
__func__, dev_name(&pdev->dev), ret);
+ else
+ vdev->needs_reset = false;
}
pci_restore_state(pdev);
out:
pci_disable_device(pdev);
+
+ vfio_pci_try_bus_reset(vdev);
}
static void vfio_pci_release(void *device_data)
@@ -917,6 +925,110 @@ static struct pci_driver vfio_pci_driver = {
.err_handler = &vfio_err_handlers,
};
+/*
+ * Test whether a reset is necessary and possible. We mark devices as
+ * needs_reset when they are released, but don't have a function-local reset
+ * available. If any of these exist in the affected devices, we want to do
+ * a bus/slot reset. We also need all of the affected devices to be unused,
+ * so we abort if any device has a non-zero refcnt. driver_lock prevents a
+ * device from being opened during the scan or unbound from vfio-pci.
+ */
+static int vfio_pci_test_bus_reset(struct pci_dev *pdev, void *data)
+{
+ bool *needs_reset = data;
+ struct pci_driver *pci_drv = ACCESS_ONCE(pdev->driver);
+ int ret = -EBUSY;
+
+ if (pci_drv == &vfio_pci_driver) {
+ struct vfio_device *device;
+ struct vfio_pci_device *vdev;
+
+ device = vfio_device_get_from_dev(&pdev->dev);
+ if (!device)
+ return ret;
+
+ vdev = vfio_device_data(device);
+ if (vdev) {
+ if (vdev->needs_reset)
+ *needs_reset = true;
+
+ if (!vdev->refcnt)
+ ret = 0;
+ }
+
+ vfio_device_put(device);
+ }
+
+ /*
+ * TODO: vfio-core considers groups to be viable even if some devices
+ * are attached to known drivers, like pci-stub or pcieport. We can't
+ * freeze devices from being unbound to those drivers like we can
+ * here though, so it would be racy to test for them. We also can't
+ * use device_lock() to prevent changes as that would interfere with
+ * PCI-core taking device_lock during bus reset. For now, we require
+ * devices to be bound to vfio-pci to get a bus/slot reset on release.
+ */
+
+ return ret;
+}
+
+/* Clear needs_reset on all affected devices after successful bus/slot reset */
+static int vfio_pci_clear_needs_reset(struct pci_dev *pdev, void *data)
+{
+ struct pci_driver *pci_drv = ACCESS_ONCE(pdev->driver);
+
+ if (pci_drv == &vfio_pci_driver) {
+ struct vfio_device *device;
+ struct vfio_pci_device *vdev;
+
+ device = vfio_device_get_from_dev(&pdev->dev);
+ if (!device)
+ return 0;
+
+ vdev = vfio_device_data(device);
+ if (vdev)
+ vdev->needs_reset = false;
+
+ vfio_device_put(device);
+ }
+
+ return 0;
+}
+
+/*
+ * Attempt to do a bus/slot reset if there are devices affected by a reset for
+ * this device that are needs_reset and all of the affected devices are unused
+ * (!refcnt). Callers of this function are required to hold driver_lock such
+ * that devices can not be unbound from vfio-pci or opened by a user while we
+ * test for and perform a bus/slot reset.
+ */
+static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev)
+{
+ bool needs_reset = false, slot = false;
+ int ret;
+
+ if (!pci_probe_reset_slot(vdev->pdev->slot))
+ slot = true;
+ else if (pci_probe_reset_bus(vdev->pdev->bus))
+ return;
+
+ if (vfio_pci_for_each_slot_or_bus(vdev->pdev,
+ vfio_pci_test_bus_reset,
+ &needs_reset, slot) || !needs_reset)
+ return;
+
+ if (slot)
+ ret = pci_try_reset_slot(vdev->pdev->slot);
+ else
+ ret = pci_try_reset_bus(vdev->pdev->bus);
+
+ if (ret)
+ return;
+
+ vfio_pci_for_each_slot_or_bus(vdev->pdev,
+ vfio_pci_clear_needs_reset, NULL, slot);
+}
+
static void __exit vfio_pci_cleanup(void)
{
pci_unregister_driver(&vfio_pci_driver);
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h
index 31e7a30..671c17a 100644
--- a/drivers/vfio/pci/vfio_pci_private.h
+++ b/drivers/vfio/pci/vfio_pci_private.h
@@ -54,6 +54,7 @@ struct vfio_pci_device {
bool extended_caps;
bool bardirty;
bool has_vga;
+ bool needs_reset;
struct pci_saved_state *pci_saved_state;
int refcnt;
struct eventfd_ctx *err_trigger;
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2014-07-16 19:01 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-07-16 19:01 [PATCH 0/3] vfio-pci: Reset improvements Alex Williamson
2014-07-16 19:01 ` [PATCH 1/3] vfio-pci: Release devices with BusMaster disabled Alex Williamson
2014-07-16 19:01 ` [PATCH 2/3] vfio-pci: Use mutex around open, release, and remove Alex Williamson
2014-07-16 19:01 ` [PATCH 3/3] vfio-pci: Attempt bus/slot reset on release Alex Williamson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).