[PATCH] PCI: endpoint: pci-epf-test: Free doorbell IRQ on unbind and deinit

From: Fan Wu

Date: Fri Sep 04 2026 - 21:46:53 EST


Neither pci_epf_test_unbind() nor pci_epf_test_epc_deinit() frees the
doorbell IRQ. epc_deinit() clears the BARs, while unbind() can free the
BAR backing store; if the doorbell is enabled, its IRQ action remains
registered with epf_test as dev_id and epf->db_msg stays allocated. A
doorbell interrupt may already have awakened the threaded handler when
teardown starts, and since cancel_delayed_work_sync() drains only the
command worker and clear_bar() does not wait for the IRQ thread,
unbind() can free that backing while the handler still dereferences
epf_test->reg[]. The leftover IRQ also makes the next doorbell
allocation on the same function fail with -EBUSY.

Free the doorbell IRQ in both paths, guarded by a
doorbell_irq_registered flag set once request_threaded_irq() has
succeeded and cleared in pci_epf_test_doorbell_cleanup(), the
chokepoint shared by the enable error path, disable_doorbell() and the
teardown sites, so no disarm path can double-free. In unbind(), the
drain depends on the driver's own flag, not on epc->init_complete.

This issue was found by an in-house static analysis tool.

Fixes: eff0c286aa91 ("PCI: endpoint: pci-epf-test: Add doorbell test support")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: Codex:gpt-5.6
Co-developed-by: Song Li <songl@xxxxxxxxxx>
Signed-off-by: Song Li <songl@xxxxxxxxxx>
Signed-off-by: Fan Wu <fanwu01@xxxxxxxxxx>
---
drivers/pci/endpoint/functions/pci-epf-test.c | 11 +++++++++++
1 file changed, 11 insertions(+)

diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index d4905aa..ab8df91 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -95,6 +95,7 @@ struct pci_epf_test {
const struct pci_epc_features *epc_features;
struct pci_epf_bar db_bar;
bool db_bar_programmed;
+ bool doorbell_irq_registered;
size_t bar_size[PCI_STD_NUM_BARS];
};

@@ -721,6 +722,7 @@ static void pci_epf_test_doorbell_cleanup(struct pci_epf_test *epf_test)
struct pci_epf *epf = epf_test->epf;

reg->doorbell_bar = cpu_to_le32(NO_BAR);
+ epf_test->doorbell_irq_registered = false;

pci_epf_free_doorbell(epf);
}
@@ -772,6 +774,7 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
goto err_doorbell_cleanup;
}

+ epf_test->doorbell_irq_registered = true;
reg->doorbell_data = cpu_to_le32(msg->data);
reg->doorbell_bar = cpu_to_le32(bar);

@@ -1238,6 +1241,10 @@ static void pci_epf_test_epc_deinit(struct pci_epf *epf)

cancel_delayed_work_sync(&epf_test->cmd_handler);
pci_epf_test_clean_dma_chan(epf_test);
+ if (epf_test->doorbell_irq_registered) {
+ free_irq(epf->db_msg[0].virq, epf_test);
+ pci_epf_test_doorbell_cleanup(epf_test);
+ }
pci_epf_test_clear_bar(epf);
}

@@ -1374,6 +1381,10 @@ static void pci_epf_test_unbind(struct pci_epf *epf)
struct pci_epc *epc = epf->epc;

cancel_delayed_work_sync(&epf_test->cmd_handler);
+ if (epf_test->doorbell_irq_registered) {
+ free_irq(epf->db_msg[0].virq, epf_test);
+ pci_epf_test_doorbell_cleanup(epf_test);
+ }
if (epc->init_complete) {
pci_epf_test_clean_dma_chan(epf_test);
pci_epf_test_clear_bar(epf);