* [PATCH 0/3] PowerNV PCIe Hotplug Driver Fixes
@ 2025-04-04 4:18 Shawn Anastasio
2025-04-04 4:18 ` [PATCH 1/3] pci/hotplug/pnv_php: Properly clean up allocated IRQs on unplug Shawn Anastasio
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Shawn Anastasio @ 2025-04-04 4:18 UTC (permalink / raw)
To: linuxppc-dev
Cc: linux-kernel, linux-pci, tpearson, Madhavan Srinivasan,
Michael Ellerman, Christophe Leroy, Naveen N Rao, Bjorn Helgaas,
Shawn Anastasio
Hello all,
This series includes two fixes for bugs in the PowerNV PCIe hotplug
driver that were discovered in testing with a Microsemi Switchtec PM8533
PFX 48xG3 PCIe switch on a PowerNV system, as well as one workaround for
PCIe switches that don't correctly implement slot presence detection
such as the aforementioned one. Without the workaround, the switch works
and downstream devices can be hot-unplugged, but the devices never come
back online after being plugged in again until the system is rebooted.
Other hotplug drivers (like pciehp_hpc) use a similar workaround.
Thanks,
Shawn Anastasio (3):
pci/hotplug/pnv_php: Properly clean up allocated IRQs on unplug
pci/hotplug/pnv_php: Work around switches with broken presence
detection
pci/hotplug/pnv_php: Fix refcount underflow on hot unplug
drivers/pci/hotplug/pnv_php.c | 110 +++++++++++++++++++++++++++-------
1 file changed, 88 insertions(+), 22 deletions(-)
--
2.30.2
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/3] pci/hotplug/pnv_php: Properly clean up allocated IRQs on unplug
2025-04-04 4:18 [PATCH 0/3] PowerNV PCIe Hotplug Driver Fixes Shawn Anastasio
@ 2025-04-04 4:18 ` Shawn Anastasio
2025-04-04 4:18 ` [PATCH 2/3] pci/hotplug/pnv_php: Work around switches with broken presence detection Shawn Anastasio
2025-04-04 4:18 ` [PATCH 3/3] pci/hotplug/pnv_php: Fix refcount underflow on hot unplug Shawn Anastasio
2 siblings, 0 replies; 6+ messages in thread
From: Shawn Anastasio @ 2025-04-04 4:18 UTC (permalink / raw)
To: linuxppc-dev
Cc: linux-kernel, linux-pci, tpearson, Madhavan Srinivasan,
Michael Ellerman, Christophe Leroy, Naveen N Rao, Bjorn Helgaas,
Shawn Anastasio
In cases where the root of a nested PCIe bridge configuration is
unplugged, the pnv_php driver would leak the allocated IRQ resources for
the child bridges' hotplug event notifications, resulting in a panic.
Fix this by walking all child buses and deallocating all it's IRQ
resources before calling pci_hp_remove_devices.
Also modify the lifetime of the workqueue at struct pnv_php_slot::wq so
that it is only destroyed in pnv_php_free_slot, instead of
pnv_php_disable_irq. This is required since pnv_php_disable_irq will now
be called by workers triggered by hot unplug interrupts, so the
workqueue needs to stay allocated.
The abridged kernel panic that occurs without this patch is as follows:
WARNING: CPU: 0 PID: 687 at kernel/irq/msi.c:292 msi_device_data_release+0x6c/0x9c
CPU: 0 UID: 0 PID: 687 Comm: bash Not tainted 6.14.0-rc5+ #2
Call Trace:
msi_device_data_release+0x34/0x9c (unreliable)
release_nodes+0x64/0x13c
devres_release_all+0xc0/0x140
device_del+0x2d4/0x46c
pci_destroy_dev+0x5c/0x194
pci_hp_remove_devices+0x90/0x128
pci_hp_remove_devices+0x44/0x128
pnv_php_disable_slot+0x54/0xd4
power_write_file+0xf8/0x18c
pci_slot_attr_store+0x40/0x5c
sysfs_kf_write+0x64/0x78
kernfs_fop_write_iter+0x1b0/0x290
vfs_write+0x3bc/0x50c
ksys_write+0x84/0x140
system_call_exception+0x124/0x230
system_call_vectored_common+0x15c/0x2ec
Signed-off-by: Shawn Anastasio <sanastasio@raptorengineering.com>
---
drivers/pci/hotplug/pnv_php.c | 76 ++++++++++++++++++++++++++---------
1 file changed, 57 insertions(+), 19 deletions(-)
diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c
index 573a41869c15..2c07544216fb 100644
--- a/drivers/pci/hotplug/pnv_php.c
+++ b/drivers/pci/hotplug/pnv_php.c
@@ -36,8 +36,10 @@ static void pnv_php_register(struct device_node *dn);
static void pnv_php_unregister_one(struct device_node *dn);
static void pnv_php_unregister(struct device_node *dn);
+static void pnv_php_enable_irq(struct pnv_php_slot *php_slot);
+
static void pnv_php_disable_irq(struct pnv_php_slot *php_slot,
- bool disable_device)
+ bool disable_device, bool disable_msi)
{
struct pci_dev *pdev = php_slot->pdev;
u16 ctrl;
@@ -53,19 +55,15 @@ static void pnv_php_disable_irq(struct pnv_php_slot *php_slot,
php_slot->irq = 0;
}
- if (php_slot->wq) {
- destroy_workqueue(php_slot->wq);
- php_slot->wq = NULL;
- }
-
- if (disable_device) {
+ if (disable_device || disable_msi) {
if (pdev->msix_enabled)
pci_disable_msix(pdev);
else if (pdev->msi_enabled)
pci_disable_msi(pdev);
+ }
+ if (disable_device)
pci_disable_device(pdev);
- }
}
static void pnv_php_free_slot(struct kref *kref)
@@ -74,7 +72,8 @@ static void pnv_php_free_slot(struct kref *kref)
struct pnv_php_slot, kref);
WARN_ON(!list_empty(&php_slot->children));
- pnv_php_disable_irq(php_slot, false);
+ pnv_php_disable_irq(php_slot, false, false);
+ destroy_workqueue(php_slot->wq);
kfree(php_slot->name);
kfree(php_slot);
}
@@ -561,8 +560,42 @@ static int pnv_php_reset_slot(struct hotplug_slot *slot, bool probe)
static int pnv_php_enable_slot(struct hotplug_slot *slot)
{
struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
+ u32 prop32;
+ int ret;
+
+ ret = pnv_php_enable(php_slot, true);
+ if (ret)
+ return ret;
- return pnv_php_enable(php_slot, true);
+ /* (Re-)enable interrupt if the slot supports surprise hotplug */
+ ret = of_property_read_u32(php_slot->dn, "ibm,slot-surprise-pluggable", &prop32);
+ if (!ret && prop32)
+ pnv_php_enable_irq(php_slot);
+
+ return 0;
+}
+
+/**
+ * Disable any hotplug interrupts for all slots on the provided bus, as well as
+ * all downstream slots in preparation for a hot unplug.
+ */
+static int pnv_php_disable_all_irqs(struct pci_bus *bus)
+{
+ struct pci_bus *child_bus;
+ struct pci_slot *cur_slot;
+
+ /* First go down child busses */
+ list_for_each_entry(child_bus, &bus->children, node)
+ pnv_php_disable_all_irqs(child_bus);
+
+ /* Disable IRQs for all pnv_php slots on this bus */
+ list_for_each_entry(cur_slot, &bus->slots, list) {
+ struct pnv_php_slot *php_slot = to_pnv_php_slot(cur_slot->hotplug);
+
+ pnv_php_disable_irq(php_slot, false, true);
+ }
+
+ return 0;
}
static int pnv_php_disable_slot(struct hotplug_slot *slot)
@@ -579,6 +612,10 @@ static int pnv_php_disable_slot(struct hotplug_slot *slot)
php_slot->state != PNV_PHP_STATE_REGISTERED)
return 0;
+
+ /* Free all irq resources from slot and all child slots before remove */
+ pnv_php_disable_all_irqs(php_slot->bus);
+
/* Remove all devices behind the slot */
pci_lock_rescan_remove();
pci_hp_remove_devices(php_slot->bus);
@@ -647,6 +684,15 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn)
return NULL;
}
+ /* Allocate workqueue for this slot's interrupt handling */
+ php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name);
+ if (!php_slot->wq) {
+ SLOT_WARN(php_slot, "Cannot alloc workqueue\n");
+ kfree(php_slot->name);
+ kfree(php_slot);
+ return NULL;
+ }
+
if (dn->child && PCI_DN(dn->child))
php_slot->slot_no = PCI_SLOT(PCI_DN(dn->child)->devfn);
else
@@ -843,14 +889,6 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
u16 sts, ctrl;
int ret;
- /* Allocate workqueue */
- php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name);
- if (!php_slot->wq) {
- SLOT_WARN(php_slot, "Cannot alloc workqueue\n");
- pnv_php_disable_irq(php_slot, true);
- return;
- }
-
/* Check PDC (Presence Detection Change) is broken or not */
ret = of_property_read_u32(php_slot->dn, "ibm,slot-broken-pdc",
&broken_pdc);
@@ -869,7 +907,7 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
ret = request_irq(irq, pnv_php_interrupt, IRQF_SHARED,
php_slot->name, php_slot);
if (ret) {
- pnv_php_disable_irq(php_slot, true);
+ pnv_php_disable_irq(php_slot, true, true);
SLOT_WARN(php_slot, "Error %d enabling IRQ %d\n", ret, irq);
return;
}
--
2.30.2
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/3] pci/hotplug/pnv_php: Work around switches with broken presence detection
2025-04-04 4:18 [PATCH 0/3] PowerNV PCIe Hotplug Driver Fixes Shawn Anastasio
2025-04-04 4:18 ` [PATCH 1/3] pci/hotplug/pnv_php: Properly clean up allocated IRQs on unplug Shawn Anastasio
@ 2025-04-04 4:18 ` Shawn Anastasio
2025-04-04 4:42 ` Lukas Wunner
2025-04-04 4:18 ` [PATCH 3/3] pci/hotplug/pnv_php: Fix refcount underflow on hot unplug Shawn Anastasio
2 siblings, 1 reply; 6+ messages in thread
From: Shawn Anastasio @ 2025-04-04 4:18 UTC (permalink / raw)
To: linuxppc-dev
Cc: linux-kernel, linux-pci, tpearson, Madhavan Srinivasan,
Michael Ellerman, Christophe Leroy, Naveen N Rao, Bjorn Helgaas,
Shawn Anastasio
The Microsemi Switchtec PM8533 PFX 48xG3 [11f8:8533] PCIe switch system
was observed to incorrectly assert the Presence Detect Set bit in its
capabilities when tested on a Raptor Computing Systems Blackbird system,
resulting in the hot insert path never attempting a rescan of the bus
and any downstream devices not being re-detected.
Work around this by additionally checking whether the PCIe data link is
active or not when performing presence detection on downstream switches'
ports, similar to the pciehp_hpc.c driver.
Signed-off-by: Shawn Anastasio <sanastasio@raptorengineering.com>
---
drivers/pci/hotplug/pnv_php.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c
index 2c07544216fb..1a734adb5b10 100644
--- a/drivers/pci/hotplug/pnv_php.c
+++ b/drivers/pci/hotplug/pnv_php.c
@@ -390,6 +390,20 @@ static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state)
return 0;
}
+static int pcie_check_link_active(struct pci_dev *pdev)
+{
+ u16 lnk_status;
+ int ret;
+
+ ret = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
+ if (ret == PCIBIOS_DEVICE_NOT_FOUND || PCI_POSSIBLE_ERROR(lnk_status))
+ return -ENODEV;
+
+ ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
+
+ return ret;
+}
+
static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
{
struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
@@ -402,6 +416,19 @@ static int pnv_php_get_adapter_state(struct hotplug_slot *slot, u8 *state)
*/
ret = pnv_pci_get_presence_state(php_slot->id, &presence);
if (ret >= 0) {
+ if (pci_pcie_type(php_slot->pdev) == PCI_EXP_TYPE_DOWNSTREAM &&
+ presence == OPAL_PCI_SLOT_EMPTY) {
+ /*
+ * Similar to pciehp_hpc, check whether the Link Active
+ * bit is set to account for broken downstream bridges
+ * that don't properly assert Presence Detect State, as
+ * was observed on the Microsemi Switchtec PM8533 PFX
+ * [11f8:8533].
+ */
+ if (pcie_check_link_active(php_slot->pdev) > 0)
+ presence = OPAL_PCI_SLOT_PRESENT;
+ }
+
*state = presence;
ret = 0;
} else {
--
2.30.2
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/3] pci/hotplug/pnv_php: Fix refcount underflow on hot unplug
2025-04-04 4:18 [PATCH 0/3] PowerNV PCIe Hotplug Driver Fixes Shawn Anastasio
2025-04-04 4:18 ` [PATCH 1/3] pci/hotplug/pnv_php: Properly clean up allocated IRQs on unplug Shawn Anastasio
2025-04-04 4:18 ` [PATCH 2/3] pci/hotplug/pnv_php: Work around switches with broken presence detection Shawn Anastasio
@ 2025-04-04 4:18 ` Shawn Anastasio
2 siblings, 0 replies; 6+ messages in thread
From: Shawn Anastasio @ 2025-04-04 4:18 UTC (permalink / raw)
To: linuxppc-dev
Cc: linux-kernel, linux-pci, tpearson, Madhavan Srinivasan,
Michael Ellerman, Christophe Leroy, Naveen N Rao, Bjorn Helgaas,
Shawn Anastasio
When hot unplugging a slot containing a PCIe switch on a PowerNV system,
the reference count of the device_node corresponding to the root will
underflow. This is due to improper handling of the device_nodes'
refcounts in pnv_php_detach_device nodes that occurs on each unplug
event.
When iterating through children nodes, pnv_php_detach_nodes first
recursively detach each child's children, then it would decrement the
child's refcount and finally call of_detach_node on it, which in turn
would decrement the refcount further and result in an underflow. Fix
this by dropping the explicit of_put call and by moving the final
of_detach_node call after the loop.
The underflow that occurs without this patch produces the following
backtrace on unplug events:
refcount_t: underflow; use-after-free.
WARNING: CPU: 4 PID: 669 at lib/refcount.c:28 refcount_warn_saturate+0x214/0x224
Call Trace:
refcount_warn_saturate+0x210/0x224 (unreliable)
kobject_put+0x154/0x2d4
of_node_put+0x2c/0x40
of_get_next_child+0x74/0xd0
pnv_php_detach_device_nodes+0x2a4/0x30c
pnv_php_set_slot_power_state+0x20c/0x500
pnv_php_disable_slot+0xb8/0xdc
power_write_file+0xf8/0x18c
pci_slot_attr_store+0x40/0x5c
sysfs_kf_write+0x64/0x78
kernfs_fop_write_iter+0x1b4/0x2a4
vfs_write+0x3bc/0x50c
ksys_write+0x84/0x140
system_call_exception+0x124/0x230
system_call_vectored_common+0x15c/0x2ec
Signed-off-by: Shawn Anastasio <sanastasio@raptorengineering.com>
---
drivers/pci/hotplug/pnv_php.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c
index 1a734adb5b10..a3fa44f7bf1a 100644
--- a/drivers/pci/hotplug/pnv_php.c
+++ b/drivers/pci/hotplug/pnv_php.c
@@ -156,11 +156,12 @@ static void pnv_php_detach_device_nodes(struct device_node *parent)
struct device_node *dn;
for_each_child_of_node(parent, dn) {
+ /* Detach any children of the parent node first */
pnv_php_detach_device_nodes(dn);
-
- of_node_put(dn);
- of_detach_node(dn);
}
+
+ /* Finally, detach the parent */
+ of_detach_node(parent);
}
static void pnv_php_rmv_devtree(struct pnv_php_slot *php_slot)
--
2.30.2
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 2/3] pci/hotplug/pnv_php: Work around switches with broken presence detection
2025-04-04 4:18 ` [PATCH 2/3] pci/hotplug/pnv_php: Work around switches with broken presence detection Shawn Anastasio
@ 2025-04-04 4:42 ` Lukas Wunner
2025-04-12 4:31 ` Lukas Wunner
0 siblings, 1 reply; 6+ messages in thread
From: Lukas Wunner @ 2025-04-04 4:42 UTC (permalink / raw)
To: Shawn Anastasio
Cc: linuxppc-dev, linux-kernel, linux-pci, tpearson,
Madhavan Srinivasan, Michael Ellerman, Christophe Leroy,
Naveen N Rao, Bjorn Helgaas, Krishna Chaitanya Chundru
[cc += Krishna]
On Thu, Apr 03, 2025 at 11:18:09PM -0500, Shawn Anastasio wrote:
> The Microsemi Switchtec PM8533 PFX 48xG3 [11f8:8533] PCIe switch system
> was observed to incorrectly assert the Presence Detect Set bit in its
> capabilities when tested on a Raptor Computing Systems Blackbird system,
> resulting in the hot insert path never attempting a rescan of the bus
> and any downstream devices not being re-detected.
>
> Work around this by additionally checking whether the PCIe data link is
> active or not when performing presence detection on downstream switches'
> ports, similar to the pciehp_hpc.c driver.
[...]
> --- a/drivers/pci/hotplug/pnv_php.c
> +++ b/drivers/pci/hotplug/pnv_php.c
> @@ -390,6 +390,20 @@ static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state)
> return 0;
> }
>
> +static int pcie_check_link_active(struct pci_dev *pdev)
> +{
> + u16 lnk_status;
> + int ret;
> +
> + ret = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
> + if (ret == PCIBIOS_DEVICE_NOT_FOUND || PCI_POSSIBLE_ERROR(lnk_status))
> + return -ENODEV;
> +
> + ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
> +
> + return ret;
> +}
> +
This appears to be a 1:1 copy of pciehp_check_link_active(),
save for the ctrl_dbg() call.
For the sake of code-reuse, please move the function into the
PCI library drivers/pci/pci.c so that it can be used everywhere.
Note that there's another patch pending which does exactly that:
https://lore.kernel.org/r/20250225-qps615_v4_1-v4-7-e08633a7bdf8@oss.qualcomm.com/
So either include that patch in your series (addressing the review
feedback I sent for it and cc'ing the original submitter) or wait
for it to be respun by the original submitter.
Thanks,
Lukas
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/3] pci/hotplug/pnv_php: Work around switches with broken presence detection
2025-04-04 4:42 ` Lukas Wunner
@ 2025-04-12 4:31 ` Lukas Wunner
0 siblings, 0 replies; 6+ messages in thread
From: Lukas Wunner @ 2025-04-12 4:31 UTC (permalink / raw)
To: Shawn Anastasio
Cc: linuxppc-dev, linux-kernel, linux-pci, tpearson,
Madhavan Srinivasan, Michael Ellerman, Christophe Leroy,
Naveen N Rao, Bjorn Helgaas, Krishna Chaitanya Chundru
On Fri, Apr 04, 2025 at 06:42:32AM +0200, Lukas Wunner wrote:
> On Thu, Apr 03, 2025 at 11:18:09PM -0500, Shawn Anastasio wrote:
> > The Microsemi Switchtec PM8533 PFX 48xG3 [11f8:8533] PCIe switch system
> > was observed to incorrectly assert the Presence Detect Set bit in its
> > capabilities when tested on a Raptor Computing Systems Blackbird system,
> > resulting in the hot insert path never attempting a rescan of the bus
> > and any downstream devices not being re-detected.
> >
> > Work around this by additionally checking whether the PCIe data link is
> > active or not when performing presence detection on downstream switches'
> > ports, similar to the pciehp_hpc.c driver.
> [...]
> > --- a/drivers/pci/hotplug/pnv_php.c
> > +++ b/drivers/pci/hotplug/pnv_php.c
> > @@ -390,6 +390,20 @@ static int pnv_php_get_power_state(struct hotplug_slot *slot, u8 *state)
> > return 0;
> > }
> >
> > +static int pcie_check_link_active(struct pci_dev *pdev)
> > +{
> > + u16 lnk_status;
> > + int ret;
> > +
> > + ret = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
> > + if (ret == PCIBIOS_DEVICE_NOT_FOUND || PCI_POSSIBLE_ERROR(lnk_status))
> > + return -ENODEV;
> > +
> > + ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
> > +
> > + return ret;
> > +}
> > +
>
> This appears to be a 1:1 copy of pciehp_check_link_active(),
> save for the ctrl_dbg() call.
>
> For the sake of code-reuse, please move the function into the
> PCI library drivers/pci/pci.c so that it can be used everywhere.
>
> Note that there's another patch pending which does exactly that:
>
> https://lore.kernel.org/r/20250225-qps615_v4_1-v4-7-e08633a7bdf8@oss.qualcomm.com/
>
> So either include that patch in your series (addressing the review
> feedback I sent for it and cc'ing the original submitter) or wait
> for it to be respun by the original submitter.
Update -- Krishna respun the patch:
https://lore.kernel.org/r/20250412-qps615_v4_1-v5-7-5b6a06132fec@oss.qualcomm.com/
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-04-12 4:31 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-04 4:18 [PATCH 0/3] PowerNV PCIe Hotplug Driver Fixes Shawn Anastasio
2025-04-04 4:18 ` [PATCH 1/3] pci/hotplug/pnv_php: Properly clean up allocated IRQs on unplug Shawn Anastasio
2025-04-04 4:18 ` [PATCH 2/3] pci/hotplug/pnv_php: Work around switches with broken presence detection Shawn Anastasio
2025-04-04 4:42 ` Lukas Wunner
2025-04-12 4:31 ` Lukas Wunner
2025-04-04 4:18 ` [PATCH 3/3] pci/hotplug/pnv_php: Fix refcount underflow on hot unplug Shawn Anastasio
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).