[PATCH v3 1/4] s390/pci: Fix leak of struct pci_dev reference in zpci_report_status()

From: Niklas Schnelle

Date: Wed Sep 16 2026 - 11:29:58 EST


In zpci_report_status(), a reference to the pdev associated with the
zdev being reported about is acquired using pci_get_slot(). This
reference needs to be dropped with pci_dev_put(), but this call is
missing, thus leaking the reference. On subsequent hot unplug, this will
cause the struct pci_dev to not be released, leaking memory and
preventing reattach.

At the same time, the only existing caller already holds a pdev
reference. So instead of reacquiring and then dropping another reference,
simply pass the existing pdev pointer to zpci_report_status(). This gets
rid of the need for pci_get_slot() as well as the zdev->zbus check.

Cc: stable@xxxxxxxxxxxxxxx
Fixes: 4ec6054e7321 ("s390/pci: Report PCI error recovery results via SCLP")
Signed-off-by: Niklas Schnelle <schnelle@xxxxxxxxxxxxx>
---
arch/s390/pci/pci_event.c | 2 +-
arch/s390/pci/pci_report.c | 11 +++++------
arch/s390/pci/pci_report.h | 4 +++-
3 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c
index f317a1465dad..3b4941b65840 100644
--- a/arch/s390/pci/pci_event.c
+++ b/arch/s390/pci/pci_event.c
@@ -298,7 +298,7 @@ static pci_ers_result_t zpci_event_attempt_error_recovery(struct pci_dev *pdev,
pci_uevent_ers(pdev, PCI_ERS_RESULT_RECOVERED);
out_unlock:
device_unlock(&pdev->dev);
- zpci_report_status(zdev, "recovery", status_str);
+ zpci_report_status(zdev, pdev, "recovery", status_str);

return ers_res;
}
diff --git a/arch/s390/pci/pci_report.c b/arch/s390/pci/pci_report.c
index 7030f7052926..867419779219 100644
--- a/arch/s390/pci/pci_report.c
+++ b/arch/s390/pci/pci_report.c
@@ -89,7 +89,8 @@ static struct debug_view debug_log_view = {

/**
* zpci_report_status - Report the status of operations on a PCI device
- * @zdev: The PCI device for which to report status
+ * @zdev: The zPCI device for which to report status
+ * @pdev: The PCI device associated with the zdev if any, NULL otherwise
* @operation: A string representing the operation reported
* @status: A string representing the status of the operation
*
@@ -103,15 +104,15 @@ static struct debug_view debug_log_view = {
*
* Return: 0 on success an error code < 0 otherwise.
*/
-int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char *status)
+int zpci_report_status(struct zpci_dev *zdev, struct pci_dev *pdev,
+ const char *operation, const char *status)
{
struct zpci_report_error *report;
struct pci_driver *driver = NULL;
- struct pci_dev *pdev = NULL;
char *buf, *end;
int ret;

- if (!zdev || !zdev->zbus)
+ if (!zdev)
return -ENODEV;

/* Protected virtualization hosts get nothing from us */
@@ -121,8 +122,6 @@ int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char
report = (void *)get_zeroed_page(GFP_KERNEL);
if (!report)
return -ENOMEM;
- if (zdev->zbus->bus)
- pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn);
if (pdev)
driver = to_pci_driver(pdev->dev.driver);

diff --git a/arch/s390/pci/pci_report.h b/arch/s390/pci/pci_report.h
index e08003d51a97..dd7b0b05001c 100644
--- a/arch/s390/pci/pci_report.h
+++ b/arch/s390/pci/pci_report.h
@@ -8,9 +8,11 @@
*/
#ifndef __S390_PCI_REPORT_H
#define __S390_PCI_REPORT_H
+#include <linux/pci.h>

struct zpci_dev;

-int zpci_report_status(struct zpci_dev *zdev, const char *operation, const char *status);
+int zpci_report_status(struct zpci_dev *zdev, struct pci_dev *pdev,
+ const char *operation, const char *status);

#endif /* __S390_PCI_REPORT_H */

--
2.53.0