PCI: hv: Add a per-bus mutex state_lock
[linux-2.6-microblaze.git] / drivers / pci / controller / pci-hyperv.c
index 1a5296f..2d93d0c 100644 (file)
@@ -489,7 +489,10 @@ struct hv_pcibus_device {
        struct fwnode_handle *fwnode;
        /* Protocol version negotiated with the host */
        enum pci_protocol_version_t protocol_version;
+
+       struct mutex state_lock;
        enum hv_pcibus_state state;
+
        struct hv_device *hdev;
        resource_size_t low_mmio_space;
        resource_size_t high_mmio_space;
@@ -2605,6 +2608,8 @@ static void pci_devices_present_work(struct work_struct *work)
        if (!dr)
                return;
 
+       mutex_lock(&hbus->state_lock);
+
        /* First, mark all existing children as reported missing. */
        spin_lock_irqsave(&hbus->device_list_lock, flags);
        list_for_each_entry(hpdev, &hbus->children, list_entry) {
@@ -2686,6 +2691,8 @@ static void pci_devices_present_work(struct work_struct *work)
                break;
        }
 
+       mutex_unlock(&hbus->state_lock);
+
        kfree(dr);
 }
 
@@ -2834,6 +2841,8 @@ static void hv_eject_device_work(struct work_struct *work)
        hpdev = container_of(work, struct hv_pci_dev, wrk);
        hbus = hpdev->hbus;
 
+       mutex_lock(&hbus->state_lock);
+
        /*
         * Ejection can come before or after the PCI bus has been set up, so
         * 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)
        put_pcichild(hpdev);
        put_pcichild(hpdev);
        /* hpdev has been freed. Do not use it any more. */
+
+       mutex_unlock(&hbus->state_lock);
 }
 
 /**
@@ -3636,6 +3647,7 @@ static int hv_pci_probe(struct hv_device *hdev,
                return -ENOMEM;
 
        hbus->bridge = bridge;
+       mutex_init(&hbus->state_lock);
        hbus->state = hv_pcibus_init;
        hbus->wslot_res_allocated = -1;
 
@@ -3745,9 +3757,11 @@ static int hv_pci_probe(struct hv_device *hdev,
        if (ret)
                goto free_irq_domain;
 
+       mutex_lock(&hbus->state_lock);
+
        ret = hv_pci_enter_d0(hdev);
        if (ret)
-               goto free_irq_domain;
+               goto release_state_lock;
 
        ret = hv_pci_allocate_bridge_windows(hbus);
        if (ret)
@@ -3765,12 +3779,15 @@ static int hv_pci_probe(struct hv_device *hdev,
        if (ret)
                goto free_windows;
 
+       mutex_unlock(&hbus->state_lock);
        return 0;
 
 free_windows:
        hv_pci_free_bridge_windows(hbus);
 exit_d0:
        (void) hv_pci_bus_exit(hdev, true);
+release_state_lock:
+       mutex_unlock(&hbus->state_lock);
 free_irq_domain:
        irq_domain_remove(hbus->irq_domain);
 free_fwnode:
@@ -4020,20 +4037,26 @@ static int hv_pci_resume(struct hv_device *hdev)
        if (ret)
                goto out;
 
+       mutex_lock(&hbus->state_lock);
+
        ret = hv_pci_enter_d0(hdev);
        if (ret)
-               goto out;
+               goto release_state_lock;
 
        ret = hv_send_resources_allocated(hdev);
        if (ret)
-               goto out;
+               goto release_state_lock;
 
        prepopulate_bars(hbus);
 
        hv_pci_restore_msi_state(hbus);
 
        hbus->state = hv_pcibus_installed;
+       mutex_unlock(&hbus->state_lock);
        return 0;
+
+release_state_lock:
+       mutex_unlock(&hbus->state_lock);
 out:
        vmbus_close(hdev->channel);
        return ret;