we introduce a has_bus_reset capability to sign the vfio devices if support host bus reset.
Signed-off-by: Chen Fan <chen.fan.f...@cn.fujitsu.com> --- hw/vfio/pci.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index f4e7855..15f182c 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -33,6 +33,7 @@ #include "hw/pci/msix.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/event_notifier.h" @@ -170,6 +171,7 @@ typedef struct VFIOPCIDevice { bool req_enabled; bool has_flr; bool has_pm_reset; + bool has_bus_reset; bool rom_read_failed; } VFIOPCIDevice; @@ -203,6 +205,7 @@ static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, uint32_t val, int len); static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled); +static void vfio_check_host_bus_reset(VFIOPCIDevice *vdev); /* * Disabling BAR mmaping can be slow, but toggling it around INTx can @@ -2853,6 +2856,20 @@ static int vfio_setup_aer(VFIOPCIDevice *vdev, int pos, uint16_t size) dev_iter = pci_bridge_get_device(dev_iter->bus); } + /* + * Don't check reset when device is enabled during qemu machine creation: + * which is done by machine init function. + */ + if (DEVICE(vdev)->hotplugged) { + vfio_check_host_bus_reset(vdev); + if (!vdev->has_bus_reset) { + error_report("vfio: Cannot enable AER for device %s, " + "which is not support host bus reset.", + vdev->vbasedev.name); + goto error; + } + } + errcap = vfio_pci_read_config(pdev, pdev->exp.aer_cap + PCI_ERR_CAP, 4); /* * The ability to record multiple headers is depending on @@ -3678,6 +3695,102 @@ static void vfio_setup_resetfn(VFIOPCIDevice *vdev) } } +struct VfioDeviceFind { + PCIDevice *pdev; + bool found; +}; + +static void find_devices(PCIBus *bus, void *opaque) +{ + struct VfioDeviceFind *find = opaque; + int i; + + if (find->found == true) { + return; + } + + for (i = 0; i < ARRAY_SIZE(bus->devices); i++) { + if (!bus->devices[i]) { + continue; + } + + if (bus->devices[i] == find->pdev) { + find->found = true; + break; + } + } +} + +static void vfio_check_host_bus_reset(VFIOPCIDevice *vdev) +{ + PCIBus *bus = vdev->pdev.bus; + struct vfio_pci_hot_reset_info *info = NULL; + struct vfio_pci_dependent_device *devices; + VFIOGroup *group; + int ret, i; + + ret = vfio_get_hot_reset_info(vdev, &info); + if (ret < 0) { + goto out; + } + + /* List all affected devices by bus reset */ + devices = &info->devices[0]; + + /* Verify that we have all the groups required */ + for (i = 0; i < info->count; i++) { + PCIHostDeviceAddress host; + VFIOPCIDevice *tmp; + VFIODevice *vbasedev_iter; + bool found = false; + + host.domain = devices[i].segment; + host.bus = devices[i].bus; + host.slot = PCI_SLOT(devices[i].devfn); + host.function = PCI_FUNC(devices[i].devfn); + + /* Skip the current device */ + if (vfio_pci_host_match(&host, &vdev->host)) { + continue; + } + + /* Ensure we own the group of the affected device */ + QLIST_FOREACH(group, &vfio_group_list, next) { + if (group->groupid == devices[i].group_id) { + break; + } + } + + if (!group) { + goto out; + } + + /* Ensure affected devices for reset under the same bus */ + QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { + if (vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { + continue; + } + tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); + if (vfio_pci_host_match(&host, &tmp->host)) { + struct VfioDeviceFind find = { .pdev = &tmp->pdev, .found = false }; + + pci_for_each_bus(bus, find_devices, &find); + found = find.found; + break; + } + } + + if (!found) { + goto out; + } + } + + vdev->has_bus_reset = true; + +out: + g_free(info); +} + static int vfio_initfn(PCIDevice *pdev) { VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); -- 1.9.3