Commit cc8a44c6 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 4dacb91c 9b936c96
...@@ -8,11 +8,17 @@ config VFIO_IOMMU_SPAPR_TCE ...@@ -8,11 +8,17 @@ config VFIO_IOMMU_SPAPR_TCE
depends on VFIO && SPAPR_TCE_IOMMU depends on VFIO && SPAPR_TCE_IOMMU
default n default n
config VFIO_SPAPR_EEH
tristate
depends on EEH && VFIO_IOMMU_SPAPR_TCE
default n
menuconfig VFIO menuconfig VFIO
tristate "VFIO Non-Privileged userspace driver framework" tristate "VFIO Non-Privileged userspace driver framework"
depends on IOMMU_API depends on IOMMU_API
select VFIO_IOMMU_TYPE1 if X86 select VFIO_IOMMU_TYPE1 if X86
select VFIO_IOMMU_SPAPR_TCE if (PPC_POWERNV || PPC_PSERIES) select VFIO_IOMMU_SPAPR_TCE if (PPC_POWERNV || PPC_PSERIES)
select VFIO_SPAPR_EEH if (PPC_POWERNV || PPC_PSERIES)
select ANON_INODES select ANON_INODES
help help
VFIO provides a framework for secure userspace device drivers. VFIO provides a framework for secure userspace device drivers.
......
obj-$(CONFIG_VFIO) += vfio.o obj-$(CONFIG_VFIO) += vfio.o
obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.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/ obj-$(CONFIG_VFIO_PCI) += pci/
...@@ -37,6 +37,10 @@ module_param_named(nointxmask, nointxmask, bool, S_IRUGO | S_IWUSR); ...@@ -37,6 +37,10 @@ module_param_named(nointxmask, nointxmask, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(nointxmask, 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."); "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) static int vfio_pci_enable(struct vfio_pci_device *vdev)
{ {
struct pci_dev *pdev = vdev->pdev; struct pci_dev *pdev = vdev->pdev;
...@@ -44,6 +48,9 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) ...@@ -44,6 +48,9 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev)
u16 cmd; u16 cmd;
u8 msix_pos; u8 msix_pos;
/* Don't allow our initial saved state to include busmaster */
pci_clear_master(pdev);
ret = pci_enable_device(pdev); ret = pci_enable_device(pdev);
if (ret) if (ret)
return ret; return ret;
...@@ -99,7 +106,8 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) ...@@ -99,7 +106,8 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
struct pci_dev *pdev = vdev->pdev; struct pci_dev *pdev = vdev->pdev;
int bar; 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_pci_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE |
VFIO_IRQ_SET_ACTION_TRIGGER, VFIO_IRQ_SET_ACTION_TRIGGER,
...@@ -117,6 +125,8 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) ...@@ -117,6 +125,8 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
vdev->barmap[bar] = NULL; vdev->barmap[bar] = NULL;
} }
vdev->needs_reset = true;
/* /*
* If we have saved state, restore it. If we can reset the device, * If we have saved state, restore it. If we can reset the device,
* even better. Resetting with current state seems better than * even better. Resetting with current state seems better than
...@@ -128,7 +138,7 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) ...@@ -128,7 +138,7 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
__func__, dev_name(&pdev->dev)); __func__, dev_name(&pdev->dev));
if (!vdev->reset_works) if (!vdev->reset_works)
return; goto out;
pci_save_state(pdev); pci_save_state(pdev);
} }
...@@ -148,46 +158,55 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) ...@@ -148,46 +158,55 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
if (ret) if (ret)
pr_warn("%s: Failed to reset device %s (%d)\n", pr_warn("%s: Failed to reset device %s (%d)\n",
__func__, dev_name(&pdev->dev), ret); __func__, dev_name(&pdev->dev), ret);
else
vdev->needs_reset = false;
} }
pci_restore_state(pdev); pci_restore_state(pdev);
out:
pci_disable_device(pdev);
vfio_pci_try_bus_reset(vdev);
} }
static void vfio_pci_release(void *device_data) static void vfio_pci_release(void *device_data)
{ {
struct vfio_pci_device *vdev = 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_spapr_pci_eeh_release(vdev->pdev);
vfio_pci_disable(vdev); vfio_pci_disable(vdev);
} }
mutex_unlock(&driver_lock);
module_put(THIS_MODULE); module_put(THIS_MODULE);
} }
static int vfio_pci_open(void *device_data) static int vfio_pci_open(void *device_data)
{ {
struct vfio_pci_device *vdev = device_data; struct vfio_pci_device *vdev = device_data;
int ret; int ret = 0;
if (!try_module_get(THIS_MODULE)) if (!try_module_get(THIS_MODULE))
return -ENODEV; return -ENODEV;
if (atomic_inc_return(&vdev->refcnt) == 1) { mutex_lock(&driver_lock);
if (!vdev->refcnt) {
ret = vfio_pci_enable(vdev); ret = vfio_pci_enable(vdev);
if (ret) if (ret)
goto error; goto error;
ret = vfio_spapr_pci_eeh_open(vdev->pdev); vfio_spapr_pci_eeh_open(vdev->pdev);
if (ret) {
vfio_pci_disable(vdev);
goto error;
}
} }
vdev->refcnt++;
return 0;
error: error:
module_put(THIS_MODULE); mutex_unlock(&driver_lock);
if (ret)
module_put(THIS_MODULE);
return ret; return ret;
} }
...@@ -843,7 +862,6 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -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; vdev->irq_type = VFIO_PCI_NUM_IRQS;
mutex_init(&vdev->igate); mutex_init(&vdev->igate);
spin_lock_init(&vdev->irqlock); spin_lock_init(&vdev->irqlock);
atomic_set(&vdev->refcnt, 0);
ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev); ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev);
if (ret) { if (ret) {
...@@ -858,12 +876,15 @@ static void vfio_pci_remove(struct pci_dev *pdev) ...@@ -858,12 +876,15 @@ static void vfio_pci_remove(struct pci_dev *pdev)
{ {
struct vfio_pci_device *vdev; struct vfio_pci_device *vdev;
mutex_lock(&driver_lock);
vdev = vfio_del_group_dev(&pdev->dev); vdev = vfio_del_group_dev(&pdev->dev);
if (!vdev) if (vdev) {
return; iommu_group_put(pdev->dev.iommu_group);
kfree(vdev);
}
iommu_group_put(pdev->dev.iommu_group); mutex_unlock(&driver_lock);
kfree(vdev);
} }
static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev, 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 = { ...@@ -906,6 +927,110 @@ static struct pci_driver vfio_pci_driver = {
.err_handler = &vfio_err_handlers, .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) static void __exit vfio_pci_cleanup(void)
{ {
pci_unregister_driver(&vfio_pci_driver); pci_unregister_driver(&vfio_pci_driver);
......
...@@ -54,8 +54,9 @@ struct vfio_pci_device { ...@@ -54,8 +54,9 @@ struct vfio_pci_device {
bool extended_caps; bool extended_caps;
bool bardirty; bool bardirty;
bool has_vga; bool has_vga;
bool needs_reset;
struct pci_saved_state *pci_saved_state; struct pci_saved_state *pci_saved_state;
atomic_t refcnt; int refcnt;
struct eventfd_ctx *err_trigger; struct eventfd_ctx *err_trigger;
}; };
......
...@@ -9,20 +9,27 @@ ...@@ -9,20 +9,27 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/module.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/vfio.h> #include <linux/vfio.h>
#include <asm/eeh.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 */ /* 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) void vfio_spapr_pci_eeh_release(struct pci_dev *pdev)
{ {
eeh_dev_release(pdev); eeh_dev_release(pdev);
} }
EXPORT_SYMBOL_GPL(vfio_spapr_pci_eeh_release);
long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group, long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
...@@ -85,3 +92,9 @@ long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group, ...@@ -85,3 +92,9 @@ long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group,
return ret; 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); ...@@ -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, extern long vfio_external_check_extension(struct vfio_group *group,
unsigned long arg); unsigned long arg);
struct pci_dev;
#ifdef CONFIG_EEH #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 void vfio_spapr_pci_eeh_release(struct pci_dev *pdev);
extern long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group, extern long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group,
unsigned int cmd, unsigned int cmd,
unsigned long arg); unsigned long arg);
#else #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) static inline void vfio_spapr_pci_eeh_release(struct pci_dev *pdev)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment