linux-pci.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Lukas Wunner <lukas@wunner.de>
To: Bjorn Helgaas <bhelgaas@google.com>
Cc: Mika Westerberg <mika.westerberg@linux.intel.com>,
	"Rafael J. Wysocki" <rafael.j.wysocki@intel.com>,
	Ashok Raj <ashok.raj@intel.com>,
	Keith Busch <keith.busch@intel.com>,
	Yinghai Lu <yinghai@kernel.org>, Sinan Kaya <okaya@kernel.org>,
	linux-pci@vger.kernel.org
Subject: [PATCH 03/32] PCI: pciehp: Fix deadlock on unplug
Date: Sat, 16 Jun 2018 21:25:00 +0200	[thread overview]
Message-ID: <4c882e25194ba8282b78fe963fec8faae7cf23eb.1529173804.git.lukas@wunner.de> (raw)
In-Reply-To: <cover.1529173804.git.lukas@wunner.de>

When pciehp was conceived it apparently wasn't considered that one
hotplug port may be the parent of another, but with Thunderbolt this is
par for the course.

If the sysfs interface is used to initiate a simultaneous card removal
of two hotplug ports where one is a parent of the other, or if the two
ports simultaneously signal interrupts, e.g. because a link or presence
change was detected on both, a deadlock may occur if:

- The parent acquires pci_lock_rescan_remove(), starts removal of the
  child and waits for it to be unbound.
- The child waits to acquire pci_lock_rescan_remove() in order to
  service the pending sysfs command or interrupt before it can unbind.

Fix by using pci_dev_is_disconnected() as an indicator whether a parent
is removing the child, and avoid acquiring the lock in the child if so.
Should the child happen to acquire the lock first, there's no deadlock.

Testing or setting the disconnected flag needs to happen atomically with
acquisition of the lock, so introduce a mutex to protect that critical
section.

Previously the disconnected flag was only set if the slot was no longer
occupied.  That doesn't seem to make sense because the (logical) child
devices are going to be removed regardless of occupancy, so set the flag
unconditionally.  (Why is occupancy checked at all?  Because if the slot
was disabled via sysfs or a button press, the device remains physically
present for the time being, so the Command register is modified to
quiesce the device.)

The deadlock is probably rarely encountered in practice, but I can
easily reproduce it in poll mode:

    INFO: task pciehp_poll-4-2:102 blocked for more than 120 seconds.
    __schedule+0x291/0x880
    schedule+0x28/0x80
    schedule_timeout+0x1e3/0x370
    wait_for_completion+0x123/0x190
    kthread_stop+0x42/0xf0
    pciehp_release_ctrl+0xaa/0xb0
    pcie_port_remove_service+0x2f/0x40
    device_release_driver_internal+0x157/0x220
    bus_remove_device+0xe2/0x150
    device_del+0x124/0x340
    device_unregister+0x16/0x60
    remove_iter+0x1a/0x20
    device_for_each_child+0x4b/0x90
    pcie_port_device_remove+0x1e/0x30
    pci_device_remove+0x36/0xb0
    device_release_driver_internal+0x157/0x220
    pci_stop_bus_device+0x7d/0xa0
    pci_stop_bus_device+0x2b/0xa0
    pci_stop_and_remove_bus_device+0xe/0x20
    pciehp_unconfigure_device+0xb8/0x160
    pciehp_disable_slot+0x84/0x130
    pciehp_handle_card_not_present+0xd6/0x120
    pciehp_ist+0x111/0x150
    pciehp_poll+0x37/0x90
    kthread+0x111/0x130

    INFO: task pciehp_poll-9:104 blocked for more than 120 seconds.
    schedule+0x28/0x80
    schedule_preempt_disabled+0xa/0x10
    __mutex_lock.isra.1+0x1a0/0x4e0
    pciehp_configure_device+0x24/0x130
    pciehp_enable_slot+0x236/0x390
    pciehp_handle_card_present+0xe2/0x160
    pciehp_ist+0x11e/0x150
    pciehp_poll+0x37/0x90
    kthread+0x111/0x130

Cc: stable@vger.kernel.org
Cc: Keith Busch <keith.busch@intel.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
---
 drivers/pci/hotplug/pciehp_pci.c | 38 ++++++++++++++++++++++----------
 1 file changed, 26 insertions(+), 12 deletions(-)

diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c
index 3f518dea856d..242395389293 100644
--- a/drivers/pci/hotplug/pciehp_pci.c
+++ b/drivers/pci/hotplug/pciehp_pci.c
@@ -20,15 +20,27 @@
 #include "../pci.h"
 #include "pciehp.h"
 
+static DEFINE_MUTEX(acquire_unless_disconnected);
+
 int pciehp_configure_device(struct slot *p_slot)
 {
 	struct pci_dev *dev;
-	struct pci_dev *bridge = p_slot->ctrl->pcie->port;
+	struct controller *ctrl = p_slot->ctrl;
+	struct pci_dev *bridge = ctrl->pcie->port;
 	struct pci_bus *parent = bridge->subordinate;
 	int num, ret = 0;
-	struct controller *ctrl = p_slot->ctrl;
 
+	/*
+	 * Avoid deadlock if an upstream hotplug port has already acquired
+	 * pci_lock_rescan_remove() in order to remove this hotplug port.
+	 */
+	mutex_lock(&acquire_unless_disconnected);
+	if (pci_dev_is_disconnected(bridge)) {
+		mutex_unlock(&acquire_unless_disconnected);
+		return -ENODEV;
+	}
 	pci_lock_rescan_remove();
+	mutex_unlock(&acquire_unless_disconnected);
 
 	dev = pci_get_slot(parent, PCI_DEVFN(0, 0));
 	if (dev) {
@@ -67,16 +79,24 @@ int pciehp_unconfigure_device(struct slot *p_slot)
 	int rc = 0;
 	u8 presence = 0;
 	struct pci_dev *dev, *temp;
-	struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate;
-	u16 command;
 	struct controller *ctrl = p_slot->ctrl;
+	struct pci_dev *bridge = ctrl->pcie->port;
+	struct pci_bus *parent = bridge->subordinate;
+	u16 command;
+
+	mutex_lock(&acquire_unless_disconnected);
+	if (pci_dev_is_disconnected(bridge)) {
+		mutex_unlock(&acquire_unless_disconnected);
+		return -ENODEV;
+	}
+	pci_walk_bus(parent, pci_dev_set_disconnected, NULL);
+	pci_lock_rescan_remove();
+	mutex_unlock(&acquire_unless_disconnected);
 
 	ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n",
 		 __func__, pci_domain_nr(parent), parent->number);
 	pciehp_get_adapter_status(p_slot, &presence);
 
-	pci_lock_rescan_remove();
-
 	/*
 	 * Stopping an SR-IOV PF device removes all the associated VFs,
 	 * which will update the bus->devices list and confuse the
@@ -86,12 +106,6 @@ int pciehp_unconfigure_device(struct slot *p_slot)
 	list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
 					 bus_list) {
 		pci_dev_get(dev);
-		if (!presence) {
-			pci_dev_set_disconnected(dev, NULL);
-			if (pci_has_subordinate(dev))
-				pci_walk_bus(dev->subordinate,
-					     pci_dev_set_disconnected, NULL);
-		}
 		pci_stop_and_remove_bus_device(dev);
 		/*
 		 * Ensure that no new Requests will be generated from
-- 
2.17.1

  parent reply	other threads:[~2018-06-16 19:35 UTC|newest]

Thread overview: 66+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-06-16 19:25 [PATCH 00/32] Rework pciehp event handling & add runtime PM Lukas Wunner
2018-06-16 19:25 ` [PATCH 04/32] PCI: pciehp: Fix unprotected list iteration in IRQ handler Lukas Wunner
2018-06-16 19:25 ` [PATCH 08/32] genirq: Synchronize only with single thread on free_irq() Lukas Wunner
2018-07-12 22:21   ` Bjorn Helgaas
2018-07-13  7:21     ` Lukas Wunner
2018-07-13 11:44       ` Bjorn Helgaas
2018-07-16 12:37       ` Bjorn Helgaas
2018-07-16 13:37         ` Lukas Wunner
2018-06-16 19:25 ` [PATCH 31/32] PCI: Whitelist native hotplug ports for runtime D3 Lukas Wunner
2018-06-16 19:25 ` [PATCH 19/32] PCI: pciehp: Declare pciehp_enable/disable_slot() static Lukas Wunner
2018-06-16 19:25 ` [PATCH 11/32] PCI: pciehp: Stop blinking on slot enable failure Lukas Wunner
2018-06-16 19:25 ` [PATCH 23/32] PCI: pciehp: Avoid slot access during reset Lukas Wunner
2018-06-21 12:06   ` Mika Westerberg
2018-06-22  9:23     ` Lukas Wunner
2018-06-25 13:10       ` Mika Westerberg
2018-06-16 19:25 ` [PATCH 01/32] PCI: hotplug: Don't leak pci_slot on registration failure Lukas Wunner
2018-06-16 19:25 ` [PATCH 26/32] PCI: pciehp: Obey compulsory command delay after resume Lukas Wunner
2018-06-16 19:25 ` [PATCH 15/32] PCI: pciehp: Publish to user space last on probe Lukas Wunner
2018-06-16 19:25 ` [PATCH 16/32] PCI: pciehp: Track enable/disable status Lukas Wunner
2018-06-16 19:25 ` [PATCH 32/32] PCI: Whitelist Thunderbolt ports for runtime D3 Lukas Wunner
2018-06-21 11:13   ` Mika Westerberg
2018-07-18 19:30     ` Lukas Wunner
2018-07-20 15:23       ` Mika Westerberg
2018-07-20 16:00         ` Mika Westerberg
2018-07-20 20:33           ` Bjorn Helgaas
2018-06-16 19:25 ` [PATCH 06/32] PCI: pciehp: Declare pciehp_unconfigure_device() void Lukas Wunner
2018-06-16 19:25 ` [PATCH 28/32] PCI: pciehp: Resume to D0 on enable/disable Lukas Wunner
2018-06-16 19:25 ` [PATCH 22/32] PCI: pciehp: Always enable occupied slot on probe Lukas Wunner
2018-06-16 19:25 ` [PATCH 10/32] PCI: pciehp: Convert to threaded polling Lukas Wunner
2018-06-16 19:25 ` [PATCH 13/32] PCI: pciehp: Drop slot workqueue Lukas Wunner
2018-06-16 19:25 ` [PATCH 12/32] PCI: pciehp: Handle events synchronously Lukas Wunner
2018-06-16 19:25 ` [PATCH 21/32] PCI: pciehp: Become resilient to missed events Lukas Wunner
2018-06-16 19:25 ` [PATCH 25/32] PCI: pciehp: Clear spurious events earlier on resume Lukas Wunner
2018-06-16 19:25 ` [PATCH 02/32] PCI: pciehp: Fix UAF on unplug Lukas Wunner
2018-06-16 19:25 ` [PATCH 29/32] PCI: pciehp: Resume parent to D0 on config space access Lukas Wunner
2018-06-16 19:25 ` [PATCH 17/32] PCI: pciehp: Enable/disable exclusively from IRQ thread Lukas Wunner
2018-06-21 11:58   ` Mika Westerberg
2018-06-16 19:25 ` [PATCH 09/32] PCI: pciehp: Convert to threaded IRQ Lukas Wunner
2018-06-19 23:16   ` Keith Busch
2018-06-20 11:01     ` Lukas Wunner
2018-06-16 19:25 ` [PATCH 07/32] PCI: pciehp: Document struct slot and struct controller Lukas Wunner
2018-06-16 19:25 ` [PATCH 20/32] PCI: pciehp: Tolerate initially unstable link Lukas Wunner
2018-06-16 19:25 ` [PATCH 05/32] PCI: pciehp: Drop unnecessary NULL pointer check Lukas Wunner
2018-06-16 19:25 ` [PATCH 30/32] PCI: sysfs: Resume to D0 on function reset Lukas Wunner
2018-06-16 19:25 ` Lukas Wunner [this message]
2018-09-06 16:01   ` [PATCH 03/32] PCI: pciehp: Fix deadlock on unplug Mika Westerberg
2018-09-06 16:26     ` Lukas Wunner
2018-09-06 18:08       ` Mika Westerberg
2018-06-16 19:25 ` [PATCH 18/32] PCI: pciehp: Drop enable/disable lock Lukas Wunner
2018-06-16 19:25 ` [PATCH 14/32] PCI: hotplug: Demidlayer registration with the core Lukas Wunner
2018-06-17 16:44   ` Andy Shevchenko
2018-07-16 12:46     ` Bjorn Helgaas
2018-07-16 14:14       ` Andy Shevchenko
2018-06-16 19:25 ` [PATCH 24/32] PCI: portdrv: Deduplicate PM callback iterator Lukas Wunner
2018-06-16 19:25 ` [PATCH 27/32] PCI: pciehp: Support interrupts sent from D3hot Lukas Wunner
2018-07-12 23:03   ` Bjorn Helgaas
2018-06-21 12:19 ` [PATCH 00/32] Rework pciehp event handling & add runtime PM Mika Westerberg
2018-06-27 13:35   ` Patel, Mayurkumar
2018-07-12 22:28 ` Bjorn Helgaas
2018-07-13  7:54   ` Lukas Wunner
2018-07-13 11:43     ` Bjorn Helgaas
2018-07-16 14:20 ` Bjorn Helgaas
2018-07-19  9:43   ` Lukas Wunner
2018-07-19 19:05     ` Bjorn Helgaas
2018-07-19 22:50     ` Bjorn Helgaas
2018-07-28  5:44       ` Lukas Wunner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4c882e25194ba8282b78fe963fec8faae7cf23eb.1529173804.git.lukas@wunner.de \
    --to=lukas@wunner.de \
    --cc=ashok.raj@intel.com \
    --cc=bhelgaas@google.com \
    --cc=keith.busch@intel.com \
    --cc=linux-pci@vger.kernel.org \
    --cc=mika.westerberg@linux.intel.com \
    --cc=okaya@kernel.org \
    --cc=rafael.j.wysocki@intel.com \
    --cc=yinghai@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).