--- a/drivers/xen/xen-pciback/pci_stub.c 2020-03-30 21:08:39.406994339 +0200 +++ b/drivers/xen/xen-pciback/pci_stub.c 2020-03-30 20:56:18.225810279 +0200 @@ -245,6 +245,90 @@ return found_dev; } +struct pcistub_args { + struct pci_dev *dev; + unsigned int dcount; +}; + +static int pcistub_search_dev(struct pci_dev *dev, void *data) +{ + struct pcistub_device *psdev; + struct pcistub_args *arg = data; + bool found_dev = false; + unsigned long flags; + + spin_lock_irqsave(&pcistub_devices_lock, flags); + + list_for_each_entry(psdev, &pcistub_devices, dev_list) { + if (psdev->dev == dev) { + found_dev = true; + arg->dcount++; + break; + } + } + + spin_unlock_irqrestore(&pcistub_devices_lock, flags); + + /* Device not owned by pcistub, someone owns it. Abort the walk */ + if (!found_dev) + arg->dev = dev; + + return found_dev ? 0 : 1; +} + +static int pcistub_reset_dev(struct pci_dev *dev) +{ + struct xen_pcibk_dev_data *dev_data; + bool slot = false, bus = false; + struct pcistub_args arg = {}; + + if (!dev) + return -EINVAL; + + dev_dbg(&dev->dev, "[%s]\n", __func__); + + if (!pci_probe_reset_slot(dev->slot)) + slot = true; + else if ((!pci_probe_reset_bus(dev->bus)) && + (!pci_is_root_bus(dev->bus))) + bus = true; + + if (!bus && !slot) + return -EOPNOTSUPP; + + /* + * Make sure all devices on this bus are owned by the + * PCI backend so that we can safely reset the whole bus. + */ + pci_walk_bus(dev->bus, pcistub_search_dev, &arg); + + /* All devices under the bus should be part of pcistub! */ + if (arg.dev) { + dev_err(&dev->dev, "%s device on bus 0x%x is not owned by pcistub\n", + pci_name(arg.dev), dev->bus->number); + + return -EBUSY; + } + + dev_dbg(&dev->dev, "pcistub owns %d devices on bus 0x%x\n", + arg.dcount, dev->bus->number); + + dev_data = pci_get_drvdata(dev); + if (!pci_load_saved_state(dev, dev_data->pci_saved_state)) + pci_restore_state(dev); + + /* This disables the device. */ + xen_pcibk_reset_device(dev); + + /* Cleanup up any emulated fields */ + xen_pcibk_config_reset_dev(dev); + + dev_dbg(&dev->dev, "resetting %s device using %s reset\n", + pci_name(dev), slot ? "slot" : "bus"); + + return pci_reset_bus(dev); +} + /* * Called when: * - XenBus state has been reconfigure (pci unplug). See xen_pcibk_remove_device @@ -1492,6 +1576,33 @@ } static DRIVER_ATTR_RW(allow_interrupt_control); +static ssize_t reset_store(struct device_driver *drv, const char *buf, + size_t count) +{ + struct pcistub_device *psdev; + int domain, bus, slot, func; + int err; + + err = str_to_slot(buf, &domain, &bus, &slot, &func); + if (err) + return err; + + psdev = pcistub_device_find(domain, bus, slot, func); + if (psdev) { + err = pcistub_reset_dev(psdev->dev); + pcistub_device_put(psdev); + } else { + err = -ENODEV; + } + + if (!err) + err = count; + + return err; +} + +static DRIVER_ATTR_WO(reset); + static void pcistub_exit(void) { driver_remove_file(&xen_pcibk_pci_driver.driver, &driver_attr_new_slot); @@ -1507,6 +1618,8 @@ &driver_attr_irq_handlers); driver_remove_file(&xen_pcibk_pci_driver.driver, &driver_attr_irq_handler_state); + driver_remove_file(&xen_pcibk_pci_driver.driver, + &driver_attr_reset); pci_unregister_driver(&xen_pcibk_pci_driver); } @@ -1603,6 +1716,11 @@ if (!err) err = driver_create_file(&xen_pcibk_pci_driver.driver, &driver_attr_irq_handler_state); + + if (!err) + err = driver_create_file(&xen_pcibk_pci_driver.driver, + &driver_attr_reset); + if (err) pcistub_exit();