add host secondary bus reset for vfio when AER occurs, if reset failed, we should stop vm.
Signed-off-by: Chen Fan <[email protected]> --- hw/vfio/pci.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 138 insertions(+), 13 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 060fb47..619daed 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -154,6 +154,8 @@ typedef struct VFIOPCIDevice { PCIHostDeviceAddress host; EventNotifier err_notifier; EventNotifier req_notifier; + + Notifier sec_bus_reset_notifier; uint32_t features; #define VFIO_FEATURE_ENABLE_VGA_BIT 0 #define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT) @@ -2627,6 +2629,13 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size) return pos; } +static bool vfio_pci_host_match(PCIHostDeviceAddress *host1, + PCIHostDeviceAddress *host2) +{ + return (host1->domain == host2->domain && host1->bus == host2->bus && + host1->slot == host2->slot && host1->function == host2->function); +} + static void vfio_check_pcie_flr(VFIOPCIDevice *vdev, uint8_t pos) { uint32_t cap = pci_get_long(vdev->pdev.config + pos + PCI_EXP_DEVCAP); @@ -2819,6 +2828,131 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos) return 0; } +static int vfio_aer_validate_devices(DeviceState *dev, + void *opaque) +{ + VFIOPCIDevice *vdev; + int i; + bool found = false; + struct vfio_pci_hot_reset_info *info = opaque; + struct vfio_pci_dependent_device *devices = &info->devices[0]; + + if (!object_dynamic_cast(OBJECT(dev), "vfio-pci")) { + return 0; + } + + vdev = DO_UPCAST(VFIOPCIDevice, pdev, PCI_DEVICE(dev)); + for (i = 0; i < info->count; i++) { + PCIHostDeviceAddress host; + + host.domain = devices[i].segment; + host.bus = devices[i].bus; + host.slot = PCI_SLOT(devices[i].devfn); + host.function = PCI_FUNC(devices[i].devfn); + + if (vfio_pci_host_match(&host, &vdev->host)) { + found = true; + break; + } + } + + if (!found) { + error_report("vfio: Cannot reset parent bus with AER supported," + "depends on device %s which is not contained.", + vdev->vbasedev.name); + return -1; + } + + return 0; +} + +static void vfio_pci_vm_stop(VFIOPCIDevice *vdev) +{ + error_report("%s(%04x:%02x:%02x.%x) Unrecoverable error detected. " + "Please collect any data possible and then kill the guest", + __func__, vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function); + + vm_stop(RUN_STATE_INTERNAL_ERROR); +} + +static void vfio_pci_host_bus_reset(Notifier *n, void *opaque) +{ + VFIOPCIDevice *vdev = container_of(n, VFIOPCIDevice, sec_bus_reset_notifier); + PCIDevice *pdev = &vdev->pdev; + int ret, i; + struct vfio_pci_hot_reset_info *info; + struct vfio_pci_dependent_device *devices; + VFIOGroup *group; + + if (!(vdev->features & VFIO_FEATURE_ENABLE_AER)) { + return; + } + + /* + * Check the affected devices by virtual bus reset are contained in + * the set of groups. + */ + ret = vfio_get_hot_reset_info(vdev, &info); + if (ret < 0) { + goto stop_vm; + } + + devices = &info->devices[0]; + + /* Verify that we have all the groups required */ + for (i = 0; i < info->count; i++) { + PCIHostDeviceAddress host; + + host.domain = devices[i].segment; + host.bus = devices[i].bus; + host.slot = PCI_SLOT(devices[i].devfn); + host.function = PCI_FUNC(devices[i].devfn); + + if (vfio_pci_host_match(&host, &vdev->host)) { + continue; + } + + QLIST_FOREACH(group, &vfio_group_list, next) { + if (group->groupid == devices[i].group_id) { + break; + } + } + + if (!group) { + if (!vdev->has_pm_reset) { + error_report("vfio: Cannot reset device %s with AER supported," + "depends on group %d which is not owned.", + vdev->vbasedev.name, devices[i].group_id); + } + ret = -EPERM; + goto stop_vm; + } + } + + /* Verify that we have all the affected devices under the bus */ + ret = qbus_walk_children(BUS(pdev->bus), NULL, NULL, + vfio_aer_validate_devices, + NULL, info); + if (ret < 0) { + goto stop_vm; + } + + + /* bus reset! */ + ret = vfio_pci_do_hot_reset(vdev, info); + if (ret < 0) { + goto stop_vm; + } + + g_free(info); + return; + +stop_vm: + g_free(info); + vfio_pci_vm_stop(vdev); +} + static int vfio_setup_aer(VFIOPCIDevice *vdev, int pos, uint16_t size) { PCIDevice *pdev = &vdev->pdev; @@ -2852,6 +2986,9 @@ static int vfio_setup_aer(VFIOPCIDevice *vdev, int pos, uint16_t size) pdev->exp.aer_cap + PCI_ERR_UNCOR_SEVER, 4); pci_long_test_and_clear_mask(exp_cap + PCI_ERR_UNCOR_SEVER, ~severity); + vdev->sec_bus_reset_notifier.notify = vfio_pci_host_bus_reset; + pci_bus_add_reset_notifier(pdev->bus, &vdev->sec_bus_reset_notifier); + return 0; } @@ -2978,13 +3115,6 @@ static void vfio_pci_post_reset(VFIOPCIDevice *vdev) vfio_enable_intx(vdev); } -static bool vfio_pci_host_match(PCIHostDeviceAddress *host1, - PCIHostDeviceAddress *host2) -{ - return (host1->domain == host2->domain && host1->bus == host2->bus && - host1->slot == host2->slot && host1->function == host2->function); -} - static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single) { VFIOGroup *group; @@ -3328,12 +3458,7 @@ static void vfio_err_notifier_handler(void *opaque) * terminate the guest to contain the error. */ - error_report("%s(%04x:%02x:%02x.%x) Unrecoverable error detected. " - "Please collect any data possible and then kill the guest", - __func__, vdev->host.domain, vdev->host.bus, - vdev->host.slot, vdev->host.function); - - vm_stop(RUN_STATE_INTERNAL_ERROR); + vfio_pci_vm_stop(vdev); } /* -- 1.9.3
