[PATCH 05/12] i3c: mipi-i3c-hci: Fix race between DMA ring dequeue and the interrupt handler
Frank Li
Frank.li at nxp.com
Fri Feb 27 08:21:32 PST 2026
On Fri, Feb 27, 2026 at 04:11:42PM +0200, Adrian Hunter wrote:
> The DMA ring bookkeeping in the MIPI I3C HCI driver is updated from two
> contexts: the DMA ring dequeue path (hci_dma_dequeue_xfer()) and the
> interrupt handler (hci_dma_xfer_done()). Both modify the ring’s
> in‑flight transfer state - specifically rh->src_xfers[] and
> xfer->ring_entry - but without any serialization. This allows the two
> paths to race, potentially leading to inconsistent ring state.
>
> Serialize access to the shared ring state by extending the existing ring
> spinlock to cover the dequeue path and the relevant parts of the
> interrupt handler. In the interrupt handler, clear the completed entry in
> src_xfers[] so it cannot be matched or completed again.
>
> Finally, place the ring restart sequence under the same lock in
> hci_dma_dequeue_xfer() to avoid concurrent enqueue or completion
> operations while the ring state is being modified.
>
> Fixes: 9ad9a52cce282 ("i3c/master: introduce the mipi-i3c-hci driver")
> Cc: stable at vger.kernel.org
> Signed-off-by: Adrian Hunter <adrian.hunter at intel.com>
> ---
Reviewed-by: Frank Li <Frank.Li at nxp.com>
> drivers/i3c/master/mipi-i3c-hci/dma.c | 9 +++++++--
> 1 file changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
> index 5a9af561e4cb..8d5f808e03ea 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/dma.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
> @@ -564,6 +564,8 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
> WARN_ON(1);
> }
>
> + spin_lock_irq(&rh->lock);
> +
> for (i = 0; i < n; i++) {
> struct hci_xfer *xfer = xfer_list + i;
> int idx = xfer->ring_entry;
> @@ -597,6 +599,8 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
> /* restart the ring */
> rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE);
>
> + spin_unlock_irq(&rh->lock);
> +
> return did_unqueue;
> }
>
> @@ -607,6 +611,8 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh)
> unsigned int done_cnt = 0;
> struct hci_xfer *xfer;
>
> + spin_lock(&rh->lock);
> +
> for (;;) {
> op2_val = rh_reg_read(RING_OPERATION2);
> if (done_ptr == FIELD_GET(RING_OP2_CR_DEQ_PTR, op2_val))
> @@ -622,6 +628,7 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh)
> dev_dbg(&hci->master.dev, "orphaned ring entry");
> } else {
> hci_dma_unmap_xfer(hci, xfer, 1);
> + rh->src_xfers[done_ptr] = NULL;
> xfer->ring_entry = -1;
> xfer->response = resp;
> if (tid != xfer->cmd_tid) {
> @@ -639,8 +646,6 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh)
> done_cnt += 1;
> }
>
> - /* take care to update the software dequeue pointer atomically */
> - spin_lock(&rh->lock);
> rh->xfer_space += done_cnt;
> op1_val = rh_reg_read(RING_OPERATION1);
> op1_val &= ~RING_OP1_CR_SW_DEQ_PTR;
> --
> 2.51.0
>
>
> --
> linux-i3c mailing list
> linux-i3c at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-i3c
More information about the linux-i3c
mailing list