diff options
author | Long Li <longli@microsoft.com> | 2017-03-23 17:58:10 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-03-24 10:50:25 -0400 |
commit | d3a78d8bf759d8848339dcc367c4c1678b57a08b (patch) | |
tree | d2fa946c39abdfdd6c78e068f44ec3892a9c0de8 | |
parent | c1ae3cfa0e89fa1a7ecc4c99031f5e9ae99d9201 (diff) |
PCI: hv: Properly handle PCI bus remove
hv_pci_devices_present() is called in hv_pci_remove() when we remove a PCI
device from the host, e.g., by disabling SR-IOV on a device. In
hv_pci_remove(), the bus is already removed before the call, so we don't
need to rescan the bus in the workqueue scheduled from
hv_pci_devices_present().
By introducing bus state hv_pcibus_removed, we can avoid this situation.
Reported-by: Xiaofeng Wang <xiaofwan@redhat.com>
Signed-off-by: Long Li <longli@microsoft.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: K. Y. Srinivasan <kys@microsoft.com>
-rw-r--r-- | drivers/pci/host/pci-hyperv.c | 20 |
1 files changed, 17 insertions, 3 deletions
diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index ada98569b78e..39fafda4deff 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c | |||
@@ -350,6 +350,7 @@ enum hv_pcibus_state { | |||
350 | hv_pcibus_init = 0, | 350 | hv_pcibus_init = 0, |
351 | hv_pcibus_probed, | 351 | hv_pcibus_probed, |
352 | hv_pcibus_installed, | 352 | hv_pcibus_installed, |
353 | hv_pcibus_removed, | ||
353 | hv_pcibus_maximum | 354 | hv_pcibus_maximum |
354 | }; | 355 | }; |
355 | 356 | ||
@@ -1504,13 +1505,24 @@ static void pci_devices_present_work(struct work_struct *work) | |||
1504 | put_pcichild(hpdev, hv_pcidev_ref_initial); | 1505 | put_pcichild(hpdev, hv_pcidev_ref_initial); |
1505 | } | 1506 | } |
1506 | 1507 | ||
1507 | /* Tell the core to rescan bus because there may have been changes. */ | 1508 | switch(hbus->state) { |
1508 | if (hbus->state == hv_pcibus_installed) { | 1509 | case hv_pcibus_installed: |
1510 | /* | ||
1511 | * Tell the core to rescan bus | ||
1512 | * because there may have been changes. | ||
1513 | */ | ||
1509 | pci_lock_rescan_remove(); | 1514 | pci_lock_rescan_remove(); |
1510 | pci_scan_child_bus(hbus->pci_bus); | 1515 | pci_scan_child_bus(hbus->pci_bus); |
1511 | pci_unlock_rescan_remove(); | 1516 | pci_unlock_rescan_remove(); |
1512 | } else { | 1517 | break; |
1518 | |||
1519 | case hv_pcibus_init: | ||
1520 | case hv_pcibus_probed: | ||
1513 | survey_child_resources(hbus); | 1521 | survey_child_resources(hbus); |
1522 | break; | ||
1523 | |||
1524 | default: | ||
1525 | break; | ||
1514 | } | 1526 | } |
1515 | 1527 | ||
1516 | up(&hbus->enum_sem); | 1528 | up(&hbus->enum_sem); |
@@ -2185,6 +2197,7 @@ static int hv_pci_probe(struct hv_device *hdev, | |||
2185 | hbus = kzalloc(sizeof(*hbus), GFP_KERNEL); | 2197 | hbus = kzalloc(sizeof(*hbus), GFP_KERNEL); |
2186 | if (!hbus) | 2198 | if (!hbus) |
2187 | return -ENOMEM; | 2199 | return -ENOMEM; |
2200 | hbus->state = hv_pcibus_init; | ||
2188 | 2201 | ||
2189 | /* | 2202 | /* |
2190 | * The PCI bus "domain" is what is called "segment" in ACPI and | 2203 | * The PCI bus "domain" is what is called "segment" in ACPI and |
@@ -2348,6 +2361,7 @@ static int hv_pci_remove(struct hv_device *hdev) | |||
2348 | pci_stop_root_bus(hbus->pci_bus); | 2361 | pci_stop_root_bus(hbus->pci_bus); |
2349 | pci_remove_root_bus(hbus->pci_bus); | 2362 | pci_remove_root_bus(hbus->pci_bus); |
2350 | pci_unlock_rescan_remove(); | 2363 | pci_unlock_rescan_remove(); |
2364 | hbus->state = hv_pcibus_removed; | ||
2351 | } | 2365 | } |
2352 | 2366 | ||
2353 | hv_pci_bus_exit(hdev); | 2367 | hv_pci_bus_exit(hdev); |