VFIO updates for v3.17-rc1
- Enable support for bus reset on device release - Fixes for EEH support -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJT64V6AAoJECObm247sIsimLkQAKL8sdGu2xwjujB/JFQrTXq0 9jN42aNhv50fVE9p2FKoqet/6VlQmRxLUvA9By94iMIKsUb3w0Rziti1FI3iyKtb 7lPzhCY9GWFnMD7Ero0nx+ljYrDiMlDdjeJ1ke25f1WZKMv524FJvA3cbiCvnAm0 3CRV0buq69deiiy5j1dbJdwYfPTJU7Jepqg8/OkjgJYokKRwqng9QfUgRbNwfL5d FuVWaiGKvkxj4Sil0M3m4Ogf2yqqweX0am/rC3pGoO9woc9XFV560ABoEvXe3PI/ TPq3X63eqiBtu+t0qnYdi9jAfV62NMQEOwgb1z3IrIRoR9pwCOR2UFVQSr9X+rMp EGrGUNkgU9cTzGeiSPmTII/ccYgsFpivy2UV+lNeDs4kraU++Q3QoJ2jwbzx8unF nzyEfW8BizUenBZHsLvBDrlDddn5uEgao9ivdc9yBhjtOQshA8xMUKFG1PHzqtYg Vis9/K2TKrZBcQTBD7nwCWWPLUg11N7stD6Ui0DEAEqJtGWxCNSqcKGTen1NvsOp hpFdlySYORNvPQDMMBmjMc+YkngBwcwDaITE5FFMbyDJYymKA28MtkYaDVLZp5kZ yYua3afxxDDaAzh7ikzUMpBlsSxbRpanAeTtEMQ0s2+ZfkzdvKztmTe9S26swQXG p9hb4pIJNflk014lnNt1 =ADuE -----END PGP SIGNATURE----- Merge tag 'vfio-v3.17-rc1' of git://github.com/awilliam/linux-vfio Pull VFIO updates from Alex Williamson: - enable support for bus reset on device release - fixes for EEH support * tag 'vfio-v3.17-rc1' of git://github.com/awilliam/linux-vfio: drivers/vfio: Enable VFIO if EEH is not supported drivers/vfio: Allow EEH to be built as module drivers/vfio: Fix EEH build error vfio-pci: Attempt bus/slot reset on release vfio-pci: Use mutex around open, release, and remove vfio-pci: Release devices with BusMaster disabled
This commit is contained in:
commit
cc8a44c671
6 changed files with 171 additions and 26 deletions
|
@ -8,11 +8,17 @@ config VFIO_IOMMU_SPAPR_TCE
|
|||
depends on VFIO && SPAPR_TCE_IOMMU
|
||||
default n
|
||||
|
||||
config VFIO_SPAPR_EEH
|
||||
tristate
|
||||
depends on EEH && VFIO_IOMMU_SPAPR_TCE
|
||||
default n
|
||||
|
||||
menuconfig VFIO
|
||||
tristate "VFIO Non-Privileged userspace driver framework"
|
||||
depends on IOMMU_API
|
||||
select VFIO_IOMMU_TYPE1 if X86
|
||||
select VFIO_IOMMU_SPAPR_TCE if (PPC_POWERNV || PPC_PSERIES)
|
||||
select VFIO_SPAPR_EEH if (PPC_POWERNV || PPC_PSERIES)
|
||||
select ANON_INODES
|
||||
help
|
||||
VFIO provides a framework for secure userspace device drivers.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
obj-$(CONFIG_VFIO) += vfio.o
|
||||
obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
|
||||
obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o
|
||||
obj-$(CONFIG_EEH) += vfio_spapr_eeh.o
|
||||
obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spapr_eeh.o
|
||||
obj-$(CONFIG_VFIO_PCI) += pci/
|
||||
|
|
|
@ -37,6 +37,10 @@ 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 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;
|
||||
|
@ -44,6 +48,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 +106,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,
|
||||
|
@ -117,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
|
||||
|
@ -128,7 +138,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);
|
||||
}
|
||||
|
@ -148,46 +158,55 @@ 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)
|
||||
{
|
||||
struct vfio_pci_device *vdev = device_data;
|
||||
|
||||
if (atomic_dec_and_test(&vdev->refcnt)) {
|
||||
mutex_lock(&driver_lock);
|
||||
|
||||
if (!(--vdev->refcnt)) {
|
||||
vfio_spapr_pci_eeh_release(vdev->pdev);
|
||||
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;
|
||||
int ret = 0;
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -ENODEV;
|
||||
|
||||
if (atomic_inc_return(&vdev->refcnt) == 1) {
|
||||
mutex_lock(&driver_lock);
|
||||
|
||||
if (!vdev->refcnt) {
|
||||
ret = vfio_pci_enable(vdev);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
ret = vfio_spapr_pci_eeh_open(vdev->pdev);
|
||||
if (ret) {
|
||||
vfio_pci_disable(vdev);
|
||||
goto error;
|
||||
}
|
||||
vfio_spapr_pci_eeh_open(vdev->pdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
vdev->refcnt++;
|
||||
error:
|
||||
module_put(THIS_MODULE);
|
||||
mutex_unlock(&driver_lock);
|
||||
if (ret)
|
||||
module_put(THIS_MODULE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -843,7 +862,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) {
|
||||
|
@ -858,12 +876,15 @@ static void vfio_pci_remove(struct pci_dev *pdev)
|
|||
{
|
||||
struct vfio_pci_device *vdev;
|
||||
|
||||
vdev = vfio_del_group_dev(&pdev->dev);
|
||||
if (!vdev)
|
||||
return;
|
||||
mutex_lock(&driver_lock);
|
||||
|
||||
iommu_group_put(pdev->dev.iommu_group);
|
||||
kfree(vdev);
|
||||
vdev = vfio_del_group_dev(&pdev->dev);
|
||||
if (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,
|
||||
|
@ -906,6 +927,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);
|
||||
|
|
|
@ -54,8 +54,9 @@ struct vfio_pci_device {
|
|||
bool extended_caps;
|
||||
bool bardirty;
|
||||
bool has_vga;
|
||||
bool needs_reset;
|
||||
struct pci_saved_state *pci_saved_state;
|
||||
atomic_t refcnt;
|
||||
int refcnt;
|
||||
struct eventfd_ctx *err_trigger;
|
||||
};
|
||||
|
||||
|
|
|
@ -9,20 +9,27 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/vfio.h>
|
||||
#include <asm/eeh.h>
|
||||
|
||||
#define DRIVER_VERSION "0.1"
|
||||
#define DRIVER_AUTHOR "Gavin Shan, IBM Corporation"
|
||||
#define DRIVER_DESC "VFIO IOMMU SPAPR EEH"
|
||||
|
||||
/* We might build address mapping here for "fast" path later */
|
||||
int vfio_spapr_pci_eeh_open(struct pci_dev *pdev)
|
||||
void vfio_spapr_pci_eeh_open(struct pci_dev *pdev)
|
||||
{
|
||||
return eeh_dev_open(pdev);
|
||||
eeh_dev_open(pdev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vfio_spapr_pci_eeh_open);
|
||||
|
||||
void vfio_spapr_pci_eeh_release(struct pci_dev *pdev)
|
||||
{
|
||||
eeh_dev_release(pdev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vfio_spapr_pci_eeh_release);
|
||||
|
||||
long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
|
@ -85,3 +92,9 @@ long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group,
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(vfio_spapr_iommu_eeh_ioctl);
|
||||
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
|
|
|
@ -98,16 +98,16 @@ extern int vfio_external_user_iommu_id(struct vfio_group *group);
|
|||
extern long vfio_external_check_extension(struct vfio_group *group,
|
||||
unsigned long arg);
|
||||
|
||||
struct pci_dev;
|
||||
#ifdef CONFIG_EEH
|
||||
extern int vfio_spapr_pci_eeh_open(struct pci_dev *pdev);
|
||||
extern void vfio_spapr_pci_eeh_open(struct pci_dev *pdev);
|
||||
extern void vfio_spapr_pci_eeh_release(struct pci_dev *pdev);
|
||||
extern long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group,
|
||||
unsigned int cmd,
|
||||
unsigned long arg);
|
||||
#else
|
||||
static inline int vfio_spapr_pci_eeh_open(struct pci_dev *pdev)
|
||||
static inline void vfio_spapr_pci_eeh_open(struct pci_dev *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void vfio_spapr_pci_eeh_release(struct pci_dev *pdev)
|
||||
|
|
Loading…
Reference in a new issue