Skip to content

Commit f0b5159

Browse files
ahunter6alexandrebelloni
authored andcommitted
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: 9ad9a52 ("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>
1 parent 1dca8ae commit f0b5159

3 files changed

Lines changed: 8 additions & 11 deletions

File tree

drivers/i3c/master/mipi-i3c-hci/core.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,8 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
567567
irqreturn_t result = IRQ_NONE;
568568
u32 val;
569569

570+
guard(spinlock)(&hci->lock);
571+
570572
/*
571573
* The IRQ can be shared, so the handler may be called when the IRQ is
572574
* due to a different device. That could happen when runtime suspended,

drivers/i3c/master/mipi-i3c-hci/dma.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,8 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
560560
WARN_ON(1);
561561
}
562562

563+
spin_lock_irq(&hci->lock);
564+
563565
for (i = 0; i < n; i++) {
564566
struct hci_xfer *xfer = xfer_list + i;
565567
int idx = xfer->ring_entry;
@@ -593,6 +595,8 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
593595
/* restart the ring */
594596
rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE);
595597

598+
spin_unlock_irq(&hci->lock);
599+
596600
return did_unqueue;
597601
}
598602

@@ -618,6 +622,7 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh)
618622
dev_dbg(&hci->master.dev, "orphaned ring entry");
619623
} else {
620624
hci_dma_unmap_xfer(hci, xfer, 1);
625+
rh->src_xfers[done_ptr] = NULL;
621626
xfer->ring_entry = -1;
622627
xfer->response = resp;
623628
if (tid != xfer->cmd_tid) {
@@ -635,14 +640,11 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh)
635640
done_cnt += 1;
636641
}
637642

638-
/* take care to update the software dequeue pointer atomically */
639-
spin_lock(&hci->lock);
640643
rh->xfer_space += done_cnt;
641644
op1_val = rh_reg_read(RING_OPERATION1);
642645
op1_val &= ~RING_OP1_CR_SW_DEQ_PTR;
643646
op1_val |= FIELD_PREP(RING_OP1_CR_SW_DEQ_PTR, done_ptr);
644647
rh_reg_write(RING_OPERATION1, op1_val);
645-
spin_unlock(&hci->lock);
646648
}
647649

648650
static int hci_dma_request_ibi(struct i3c_hci *hci, struct i3c_dev_desc *dev,
@@ -822,13 +824,10 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
822824
i3c_master_queue_ibi(dev, slot);
823825

824826
done:
825-
/* take care to update the ibi dequeue pointer atomically */
826-
spin_lock(&hci->lock);
827827
op1_val = rh_reg_read(RING_OPERATION1);
828828
op1_val &= ~RING_OP1_IBI_DEQ_PTR;
829829
op1_val |= FIELD_PREP(RING_OP1_IBI_DEQ_PTR, deq_ptr);
830830
rh_reg_write(RING_OPERATION1, op1_val);
831-
spin_unlock(&hci->lock);
832831

833832
/* update the chunk pointer */
834833
rh->ibi_chunk_ptr += ibi_chunks;

drivers/i3c/master/mipi-i3c-hci/pio.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,15 +1014,12 @@ static bool hci_pio_irq_handler(struct i3c_hci *hci)
10141014
struct hci_pio_data *pio = hci->io_data;
10151015
u32 status;
10161016

1017-
spin_lock(&hci->lock);
10181017
status = pio_reg_read(INTR_STATUS);
10191018
dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x",
10201019
status, pio->enabled_irqs);
10211020
status &= pio->enabled_irqs | STAT_LATENCY_WARNINGS;
1022-
if (!status) {
1023-
spin_unlock(&hci->lock);
1021+
if (!status)
10241022
return false;
1025-
}
10261023

10271024
if (status & STAT_IBI_STATUS_THLD)
10281025
hci_pio_process_ibi(hci, pio);
@@ -1056,7 +1053,6 @@ static bool hci_pio_irq_handler(struct i3c_hci *hci)
10561053
pio_reg_write(INTR_SIGNAL_ENABLE, pio->enabled_irqs);
10571054
dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x",
10581055
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
1059-
spin_unlock(&hci->lock);
10601056
return true;
10611057
}
10621058

0 commit comments

Comments
 (0)