Skip to content

Commit 067d6ec

Browse files
dcuiliuw
authored andcommitted
PCI: hv: Add a per-bus mutex state_lock
In the case of fast device addition/removal, it's possible that hv_eject_device_work() can start to run before create_root_hv_pci_bus() starts to run; as a result, the pci_get_domain_bus_and_slot() in hv_eject_device_work() can return a 'pdev' of NULL, and hv_eject_device_work() can remove the 'hpdev', and immediately send a message PCI_EJECTION_COMPLETE to the host, and the host immediately unassigns the PCI device from the guest; meanwhile, create_root_hv_pci_bus() and the PCI device driver can be probing the dead PCI device and reporting timeout errors. Fix the issue by adding a per-bus mutex 'state_lock' and grabbing the mutex before powering on the PCI bus in hv_pci_enter_d0(): when hv_eject_device_work() starts to run, it's able to find the 'pdev' and call pci_stop_and_remove_bus_device(pdev): if the PCI device driver has loaded, the PCI device driver's probe() function is already called in create_root_hv_pci_bus() -> pci_bus_add_devices(), and now hv_eject_device_work() -> pci_stop_and_remove_bus_device() is able to call the PCI device driver's remove() function and remove the device reliably; if the PCI device driver hasn't loaded yet, the function call hv_eject_device_work() -> pci_stop_and_remove_bus_device() is able to remove the PCI device reliably and the PCI device driver's probe() function won't be called; if the PCI device driver's probe() is already running (e.g., systemd-udev is loading the PCI device driver), it must be holding the per-device lock, and after the probe() finishes and releases the lock, hv_eject_device_work() -> pci_stop_and_remove_bus_device() is able to proceed to remove the device reliably. Fixes: 4daace0 ("PCI: hv: Add paravirtual PCI front-end for Microsoft Hyper-V VMs") Signed-off-by: Dexuan Cui <[email protected]> Reviewed-by: Michael Kelley <[email protected]> Acked-by: Lorenzo Pieralisi <[email protected]> Cc: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Wei Liu <[email protected]>
1 parent a847234 commit 067d6ec

File tree

1 file changed

+26
-3
lines changed

1 file changed

+26
-3
lines changed

drivers/pci/controller/pci-hyperv.c

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,10 @@ struct hv_pcibus_device {
489489
struct fwnode_handle *fwnode;
490490
/* Protocol version negotiated with the host */
491491
enum pci_protocol_version_t protocol_version;
492+
493+
struct mutex state_lock;
492494
enum hv_pcibus_state state;
495+
493496
struct hv_device *hdev;
494497
resource_size_t low_mmio_space;
495498
resource_size_t high_mmio_space;
@@ -2605,6 +2608,8 @@ static void pci_devices_present_work(struct work_struct *work)
26052608
if (!dr)
26062609
return;
26072610

2611+
mutex_lock(&hbus->state_lock);
2612+
26082613
/* First, mark all existing children as reported missing. */
26092614
spin_lock_irqsave(&hbus->device_list_lock, flags);
26102615
list_for_each_entry(hpdev, &hbus->children, list_entry) {
@@ -2686,6 +2691,8 @@ static void pci_devices_present_work(struct work_struct *work)
26862691
break;
26872692
}
26882693

2694+
mutex_unlock(&hbus->state_lock);
2695+
26892696
kfree(dr);
26902697
}
26912698

@@ -2834,6 +2841,8 @@ static void hv_eject_device_work(struct work_struct *work)
28342841
hpdev = container_of(work, struct hv_pci_dev, wrk);
28352842
hbus = hpdev->hbus;
28362843

2844+
mutex_lock(&hbus->state_lock);
2845+
28372846
/*
28382847
* Ejection can come before or after the PCI bus has been set up, so
28392848
* attempt to find it and tear down the bus state, if it exists. This
@@ -2870,6 +2879,8 @@ static void hv_eject_device_work(struct work_struct *work)
28702879
put_pcichild(hpdev);
28712880
put_pcichild(hpdev);
28722881
/* hpdev has been freed. Do not use it any more. */
2882+
2883+
mutex_unlock(&hbus->state_lock);
28732884
}
28742885

28752886
/**
@@ -3636,6 +3647,7 @@ static int hv_pci_probe(struct hv_device *hdev,
36363647
return -ENOMEM;
36373648

36383649
hbus->bridge = bridge;
3650+
mutex_init(&hbus->state_lock);
36393651
hbus->state = hv_pcibus_init;
36403652
hbus->wslot_res_allocated = -1;
36413653

@@ -3745,9 +3757,11 @@ static int hv_pci_probe(struct hv_device *hdev,
37453757
if (ret)
37463758
goto free_irq_domain;
37473759

3760+
mutex_lock(&hbus->state_lock);
3761+
37483762
ret = hv_pci_enter_d0(hdev);
37493763
if (ret)
3750-
goto free_irq_domain;
3764+
goto release_state_lock;
37513765

37523766
ret = hv_pci_allocate_bridge_windows(hbus);
37533767
if (ret)
@@ -3765,12 +3779,15 @@ static int hv_pci_probe(struct hv_device *hdev,
37653779
if (ret)
37663780
goto free_windows;
37673781

3782+
mutex_unlock(&hbus->state_lock);
37683783
return 0;
37693784

37703785
free_windows:
37713786
hv_pci_free_bridge_windows(hbus);
37723787
exit_d0:
37733788
(void) hv_pci_bus_exit(hdev, true);
3789+
release_state_lock:
3790+
mutex_unlock(&hbus->state_lock);
37743791
free_irq_domain:
37753792
irq_domain_remove(hbus->irq_domain);
37763793
free_fwnode:
@@ -4020,20 +4037,26 @@ static int hv_pci_resume(struct hv_device *hdev)
40204037
if (ret)
40214038
goto out;
40224039

4040+
mutex_lock(&hbus->state_lock);
4041+
40234042
ret = hv_pci_enter_d0(hdev);
40244043
if (ret)
4025-
goto out;
4044+
goto release_state_lock;
40264045

40274046
ret = hv_send_resources_allocated(hdev);
40284047
if (ret)
4029-
goto out;
4048+
goto release_state_lock;
40304049

40314050
prepopulate_bars(hbus);
40324051

40334052
hv_pci_restore_msi_state(hbus);
40344053

40354054
hbus->state = hv_pcibus_installed;
4055+
mutex_unlock(&hbus->state_lock);
40364056
return 0;
4057+
4058+
release_state_lock:
4059+
mutex_unlock(&hbus->state_lock);
40374060
out:
40384061
vmbus_close(hdev->channel);
40394062
return ret;

0 commit comments

Comments
 (0)