[PATCH 06/17] dmaengine: idxd: keep the interrupt handle across terminate_all

From: Daniel Byrne

Date: Mon Oct 19 2026 - 11:35:34 EST


idxd_wq_flush_descs(), the device_terminate_all() implementation,
released the work queue's interrupt handle and cleared its permission
entry after flushing the pending descriptors. The handle is only
requested again when the driver rebinds. On a live channel every
descriptor submitted with a completion interrupt after a terminate
therefore fails with DSA_COMP_INT_HANDLE_INVAL, and since no
interrupt is raised for such a descriptor the client never learns of
it and waits forever. A single dmaengine_terminate_sync() on an error
path, which the API allows, disabled the channel for good.

Flush the pending descriptors only. Releasing the handle and clearing
the permission entry belong to idxd_wq_free_irq(), which already does
both when the interrupt is torn down. idxd_wq_flush_descs() is also the
flush of the FLR halt path, which relied on it to mark the handles
invalid. The FLR revokes every handle, so that path now marks them
invalid itself, without a device command, and the teardown after the
reset skips the release of a handle the reset has already revoked.

At this point idxd_flush_pending_descs() still frees the slots of
in-flight interrupt-armed descriptors while the device may hold them.
A later patch in this series, "dmaengine: idxd: quarantine
terminate_all's descriptor slots until the device has drained",
addresses that.

Fixes: 2a93f5747d0e ("dmaengine: idxd: Flush all pending descriptors")
Cc: stable@xxxxxxxxxxxxxxx # 7.0+
Assisted-by: Claude:claude-opus-5-5
Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Daniel Byrne <daniel1.byrne@xxxxxxxxx>
---
drivers/dma/idxd/device.c | 7 +------
drivers/dma/idxd/irq.c | 16 +++++++++++++---
2 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/drivers/dma/idxd/device.c b/drivers/dma/idxd/device.c
index a83b5da88f8c..a461cae7a409 100644
--- a/drivers/dma/idxd/device.c
+++ b/drivers/dma/idxd/device.c
@@ -1336,7 +1336,7 @@ void idxd_wq_free_irq(struct idxd_wq *wq)
free_irq(ie->vector, ie);
idxd_flush_pending_descs(ie);

- /* The interrupt might have been already released by FLR */
+ /* FLR revokes every handle; the halt path marks them invalid. */
if (ie->int_handle == INVALID_INT_HANDLE)
return;

@@ -1351,7 +1351,6 @@ void idxd_wq_free_irq(struct idxd_wq *wq)
void idxd_wq_flush_descs(struct idxd_wq *wq)
{
struct idxd_irq_entry *ie = &wq->ie;
- struct idxd_device *idxd = wq->idxd;

guard(mutex)(&wq->wq_lock);

@@ -1359,10 +1358,6 @@ void idxd_wq_flush_descs(struct idxd_wq *wq)
return;

idxd_flush_pending_descs(ie);
- if (idxd->request_int_handles)
- idxd_device_release_int_handle(idxd, ie->int_handle, IDXD_IRQ_MSIX);
- idxd_device_clear_perm_entry(idxd, ie);
- ie->int_handle = INVALID_INT_HANDLE;
}

int idxd_wq_request_irq(struct idxd_wq *wq)
diff --git a/drivers/dma/idxd/irq.c b/drivers/dma/idxd/irq.c
index 647969920a06..915304ee7099 100644
--- a/drivers/dma/idxd/irq.c
+++ b/drivers/dma/idxd/irq.c
@@ -442,13 +442,23 @@ static irqreturn_t idxd_halt(struct idxd_device *idxd)
INIT_WORK(&idxd->work, idxd_device_reinit);
queue_work(idxd->wq, &idxd->work);
} else if (gensts.reset_type == IDXD_DEVICE_RESET_FLR) {
+ int i;
+
idxd->state = IDXD_DEV_HALTED;
idxd_mask_error_interrupts(idxd);
- /* Flush all pending descriptors, and disable
- * interrupts, they will be re-enabled when FLR
- * concludes.
+ /*
+ * Flush all pending descriptors. The FLR revokes every
+ * interrupt handle, so mark them invalid here without a
+ * device command; they are requested again when a work
+ * queue is re-enabled after the reset.
*/
idxd_wqs_flush_descs(idxd);
+ for (i = 0; i < idxd->max_wqs; i++) {
+ struct idxd_wq *wq = idxd->wqs[i];
+
+ if (wq->type == IDXD_WQT_KERNEL)
+ WRITE_ONCE(wq->ie.int_handle, INVALID_INT_HANDLE);
+ }
dev_dbg(&idxd->pdev->dev,
"idxd halted, doing FLR. After FLR, configs are restored\n");
INIT_WORK(&idxd->work, idxd_device_flr);
--
2.43.0