Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

i3c: mipi-i3c-hci: Fix race between DMA ring dequeue and interrupt handler

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
spinlock to cover the DMA dequeue path and the entire interrupt handler.
Since the core IRQ handler now holds this lock, remove the per-function
locking from the PIO and DMA sub-handlers.

Additionally, clear the completed entry in rh->src_xfers[] in
hci_dma_xfer_done() 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@vger.kernel.org
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20260306072451.11131-8-adrian.hunter@intel.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Adrian Hunter and committed by
Alexandre Belloni
f0b51596 1dca8aee

+8 -11
+2
drivers/i3c/master/mipi-i3c-hci/core.c
··· 567 567 irqreturn_t result = IRQ_NONE; 568 568 u32 val; 569 569 570 + guard(spinlock)(&hci->lock); 571 + 570 572 /* 571 573 * The IRQ can be shared, so the handler may be called when the IRQ is 572 574 * due to a different device. That could happen when runtime suspended,
+5 -6
drivers/i3c/master/mipi-i3c-hci/dma.c
··· 560 560 WARN_ON(1); 561 561 } 562 562 563 + spin_lock_irq(&hci->lock); 564 + 563 565 for (i = 0; i < n; i++) { 564 566 struct hci_xfer *xfer = xfer_list + i; 565 567 int idx = xfer->ring_entry; ··· 595 593 /* restart the ring */ 596 594 rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE); 597 595 596 + spin_unlock_irq(&hci->lock); 597 + 598 598 return did_unqueue; 599 599 } 600 600 ··· 622 618 dev_dbg(&hci->master.dev, "orphaned ring entry"); 623 619 } else { 624 620 hci_dma_unmap_xfer(hci, xfer, 1); 621 + rh->src_xfers[done_ptr] = NULL; 625 622 xfer->ring_entry = -1; 626 623 xfer->response = resp; 627 624 if (tid != xfer->cmd_tid) { ··· 640 635 done_cnt += 1; 641 636 } 642 637 643 - /* take care to update the software dequeue pointer atomically */ 644 - spin_lock(&hci->lock); 645 638 rh->xfer_space += done_cnt; 646 639 op1_val = rh_reg_read(RING_OPERATION1); 647 640 op1_val &= ~RING_OP1_CR_SW_DEQ_PTR; 648 641 op1_val |= FIELD_PREP(RING_OP1_CR_SW_DEQ_PTR, done_ptr); 649 642 rh_reg_write(RING_OPERATION1, op1_val); 650 - spin_unlock(&hci->lock); 651 643 } 652 644 653 645 static int hci_dma_request_ibi(struct i3c_hci *hci, struct i3c_dev_desc *dev, ··· 824 822 i3c_master_queue_ibi(dev, slot); 825 823 826 824 done: 827 - /* take care to update the ibi dequeue pointer atomically */ 828 - spin_lock(&hci->lock); 829 825 op1_val = rh_reg_read(RING_OPERATION1); 830 826 op1_val &= ~RING_OP1_IBI_DEQ_PTR; 831 827 op1_val |= FIELD_PREP(RING_OP1_IBI_DEQ_PTR, deq_ptr); 832 828 rh_reg_write(RING_OPERATION1, op1_val); 833 - spin_unlock(&hci->lock); 834 829 835 830 /* update the chunk pointer */ 836 831 rh->ibi_chunk_ptr += ibi_chunks;
+1 -5
drivers/i3c/master/mipi-i3c-hci/pio.c
··· 1014 1014 struct hci_pio_data *pio = hci->io_data; 1015 1015 u32 status; 1016 1016 1017 - spin_lock(&hci->lock); 1018 1017 status = pio_reg_read(INTR_STATUS); 1019 1018 dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x", 1020 1019 status, pio->enabled_irqs); 1021 1020 status &= pio->enabled_irqs | STAT_LATENCY_WARNINGS; 1022 - if (!status) { 1023 - spin_unlock(&hci->lock); 1021 + if (!status) 1024 1022 return false; 1025 - } 1026 1023 1027 1024 if (status & STAT_IBI_STATUS_THLD) 1028 1025 hci_pio_process_ibi(hci, pio); ··· 1053 1056 pio_reg_write(INTR_SIGNAL_ENABLE, pio->enabled_irqs); 1054 1057 dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x", 1055 1058 pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); 1056 - spin_unlock(&hci->lock); 1057 1059 return true; 1058 1060 } 1059 1061