From 8f7e505394e41e6069164a3d37e3232dcf3a871c Mon Sep 17 00:00:00 2001
From: Xiongfeng Wang <wangxiongfeng2@huawei.com>
Date: Wed, 17 Jul 2019 18:44:41 +0800
Subject: [PATCH] pciehp: do not wake up irq_thread for sysfs operation

hulk inclusion
category: bugfix
bugzilla: NA
CVE: NA
-------------------

This patch fix the same issue as commit cdda3bbfd0ca ("pciehp: use
completion to wait irq_thread 'pciehp_ist'"). But the previous patch
didn't fix the issue completely. This patch power off the slot directly
instead of waking up the irq_thread 'pciehp_ist'

This patch also check 'slot_being_removed_rescanned' before powering off
the slot to avoid the dead lock issue similar as commit 764cafd9875e
("pciehp: fix a race between pciehp and removing operations by sysfs")

Signed-off-by: Xiongfeng Wang <wangxiongfeng2@huawei.com>
Reviewed-by: Yao Hongbo <yaohongbo@huawei.com>
Signed-off-by: Yang Yingliang <yangyingliang@huawei.com>
---
 drivers/pci/hotplug/pciehp_ctrl.c | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index b6274e58f95d..5c26328da66c 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -392,6 +392,7 @@ static int pciehp_disable_slot(struct slot *slot, bool safe_removal)
 int pciehp_sysfs_enable_slot(struct slot *p_slot)
 {
 	struct controller *ctrl = p_slot->ctrl;
+	struct pci_dev *pdev = ctrl->pcie->port;
 
 	mutex_lock(&p_slot->lock);
 	switch (p_slot->state) {
@@ -403,9 +404,12 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
 		 * card before the thread wakes up, so initialize to -ENODEV.
 		 */
 		ctrl->request_result = -ENODEV;
-		pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
-		wait_event(ctrl->requester,
-			   !atomic_read(&ctrl->pending_events));
+		pci_config_pm_runtime_get(pdev);
+		down_read(&ctrl->reset_lock);
+		pciehp_handle_presence_or_link_change(p_slot,
+				PCI_EXP_SLTSTA_PDC);
+		up_read(&ctrl->reset_lock);
+		pci_config_pm_runtime_put(pdev);
 		return ctrl->request_result;
 	case POWERON_STATE:
 		ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
@@ -430,15 +434,25 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
 int pciehp_sysfs_disable_slot(struct slot *p_slot)
 {
 	struct controller *ctrl = p_slot->ctrl;
+	struct pci_dev *pdev = ctrl->pcie->port;
+
+	if (test_and_set_bit(0, &slot_being_removed_rescanned)) {
+		ctrl_info(ctrl, "Slot(%s): Slot is being removed or rescanned, please try later!\n",
+			  slot_name(p_slot));
+		return -EINVAL;
+	}
 
 	mutex_lock(&p_slot->lock);
 	switch (p_slot->state) {
 	case BLINKINGOFF_STATE:
 	case ON_STATE:
 		mutex_unlock(&p_slot->lock);
-		pciehp_request(ctrl, DISABLE_SLOT);
-		wait_event(ctrl->requester,
-			   !atomic_read(&ctrl->pending_events));
+		pci_config_pm_runtime_get(pdev);
+		down_read(&ctrl->reset_lock);
+		pciehp_handle_disable_request(p_slot);
+		up_read(&ctrl->reset_lock);
+		pci_config_pm_runtime_put(pdev);
+		slot_being_removed_rescanned = 0;
 		return ctrl->request_result;
 	case POWEROFF_STATE:
 		ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
@@ -457,5 +471,7 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot)
 	}
 	mutex_unlock(&p_slot->lock);
 
+	slot_being_removed_rescanned = 0;
+
 	return -ENODEV;
 }
-- 
GitLab