]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/pci/hotplug/pciehp_hpc.c
PCI: pciehp: Fix use-after-free on unplug
[mirror_ubuntu-bionic-kernel.git] / drivers / pci / hotplug / pciehp_hpc.c
index 7bab0606f1a9f1d04e9529a22b1354b1f73844c3..2a9a80ddaa62547ee175d671af31fca2b57e1f51 100644 (file)
@@ -673,7 +673,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
        return handled;
 }
 
-void pcie_enable_notification(struct controller *ctrl)
+static void pcie_enable_notification(struct controller *ctrl)
 {
        u16 cmd, mask;
 
@@ -711,6 +711,17 @@ void pcie_enable_notification(struct controller *ctrl)
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd);
 }
 
+void pcie_reenable_notification(struct controller *ctrl)
+{
+       /*
+        * Clear both Presence and Data Link Layer Changed to make sure
+        * those events still fire after we have re-enabled them.
+        */
+       pcie_capability_write_word(ctrl->pcie->port, PCI_EXP_SLTSTA,
+                                  PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
+       pcie_enable_notification(ctrl);
+}
+
 static void pcie_disable_notification(struct controller *ctrl)
 {
        u16 mask;
@@ -774,7 +785,7 @@ int pcie_init_notification(struct controller *ctrl)
        return 0;
 }
 
-static void pcie_shutdown_notification(struct controller *ctrl)
+void pcie_shutdown_notification(struct controller *ctrl)
 {
        if (ctrl->notification_enabled) {
                pcie_disable_notification(ctrl);
@@ -809,7 +820,7 @@ abort:
 static void pcie_cleanup_slot(struct controller *ctrl)
 {
        struct slot *slot = ctrl->slot;
-       cancel_delayed_work(&slot->work);
+
        destroy_workqueue(slot->wq);
        kfree(slot);
 }
@@ -848,6 +859,13 @@ struct controller *pcie_init(struct pcie_device *dev)
        if (pdev->hotplug_user_indicators)
                slot_cap &= ~(PCI_EXP_SLTCAP_AIP | PCI_EXP_SLTCAP_PIP);
 
+       /*
+        * We assume no Thunderbolt controllers support Command Complete events,
+        * but some controllers falsely claim they do.
+        */
+       if (pdev->is_thunderbolt)
+               slot_cap |= PCI_EXP_SLTCAP_NCCS;
+
        ctrl->slot_cap = slot_cap;
        mutex_init(&ctrl->ctrl_lock);
        init_waitqueue_head(&ctrl->queue);
@@ -895,7 +913,6 @@ abort:
 
 void pciehp_release_ctrl(struct controller *ctrl)
 {
-       pcie_shutdown_notification(ctrl);
        pcie_cleanup_slot(ctrl);
        kfree(ctrl);
 }