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 in DMA error handling in interrupt context

The DMA ring halts whenever a transfer encounters an error. The interrupt
handler previously attempted to detect this situation and restart the ring
if a transfer completed at the same time. However, this restart logic runs
entirely in interrupt context and is inherently racy: it interacts with
other paths manipulating the ring state, and fully serializing it within
the interrupt handler is not practical.

Move this error-recovery logic out of the interrupt handler and into the
transfer-processing path (i3c_hci_process_xfer()), where serialization and
state management are already controlled. Introduce a new optional I/O-ops
callback, handle_error(), invoked when a completed transfer reports an
error. For DMA operation, the implementation simply calls the existing
dequeue function, which safely aborts and restarts the ring when needed.

This removes the fragile ring-restart logic from the interrupt handler and
centralizes error handling where proper sequencing can be ensured.

Fixes: ccdb2e0e3b00d ("i3c: mipi-i3c-hci: Add Intel specific quirk to ring resuming")
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-13-adrian.hunter@intel.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Adrian Hunter and committed by
Alexandre Belloni
e44d2719 7ac45bc6

+24 -27
+15 -4
drivers/i3c/master/mipi-i3c-hci/core.c
··· 223 223 if (ret) 224 224 return ret; 225 225 226 - if (!wait_for_completion_timeout(done, timeout) && 227 - hci->io->dequeue_xfer(hci, xfer, n)) { 228 - dev_err(&hci->master.dev, "%s: timeout error\n", __func__); 229 - return -ETIMEDOUT; 226 + if (!wait_for_completion_timeout(done, timeout)) { 227 + if (hci->io->dequeue_xfer(hci, xfer, n)) { 228 + dev_err(&hci->master.dev, "%s: timeout error\n", __func__); 229 + return -ETIMEDOUT; 230 + } 231 + return 0; 232 + } 233 + 234 + if (hci->io->handle_error) { 235 + bool error = false; 236 + 237 + for (int i = 0; i < n && !error; i++) 238 + error = RESP_STATUS(xfer[i].response); 239 + if (error) 240 + return hci->io->handle_error(hci, xfer, n); 230 241 } 231 242 232 243 return 0;
+8 -23
drivers/i3c/master/mipi-i3c-hci/dma.c
··· 609 609 return did_unqueue; 610 610 } 611 611 612 + static int hci_dma_handle_error(struct i3c_hci *hci, struct hci_xfer *xfer_list, int n) 613 + { 614 + return hci_dma_dequeue_xfer(hci, xfer_list, n) ? -EIO : 0; 615 + } 616 + 612 617 static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh) 613 618 { 614 619 u32 op1_val, op2_val, resp, *ring_resp; ··· 875 870 hci_dma_xfer_done(hci, rh); 876 871 if (status & INTR_RING_OP) 877 872 complete(&rh->op_done); 878 - 879 - if (status & INTR_TRANSFER_ABORT) { 880 - u32 ring_status; 881 - 882 - dev_notice_ratelimited(&hci->master.dev, 883 - "Ring %d: Transfer Aborted\n", i); 884 - mipi_i3c_hci_resume(hci); 885 - ring_status = rh_reg_read(RING_STATUS); 886 - if (!(ring_status & RING_STATUS_RUNNING) && 887 - status & INTR_TRANSFER_COMPLETION && 888 - status & INTR_TRANSFER_ERR) { 889 - /* 890 - * Ring stop followed by run is an Intel 891 - * specific required quirk after resuming the 892 - * halted controller. Do it only when the ring 893 - * is not in running state after a transfer 894 - * error. 895 - */ 896 - rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE); 897 - rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | 898 - RING_CTRL_RUN_STOP); 899 - } 900 - } 873 + if (status & INTR_TRANSFER_ABORT) 874 + dev_dbg(&hci->master.dev, "Ring %d: Transfer Aborted\n", i); 901 875 if (status & INTR_IBI_RING_FULL) 902 876 dev_err_ratelimited(&hci->master.dev, 903 877 "Ring %d: IBI Ring Full Condition\n", i); ··· 892 908 .cleanup = hci_dma_cleanup, 893 909 .queue_xfer = hci_dma_queue_xfer, 894 910 .dequeue_xfer = hci_dma_dequeue_xfer, 911 + .handle_error = hci_dma_handle_error, 895 912 .irq_handler = hci_dma_irq_handler, 896 913 .request_ibi = hci_dma_request_ibi, 897 914 .free_ibi = hci_dma_free_ibi,
+1
drivers/i3c/master/mipi-i3c-hci/hci.h
··· 123 123 bool (*irq_handler)(struct i3c_hci *hci); 124 124 int (*queue_xfer)(struct i3c_hci *hci, struct hci_xfer *xfer, int n); 125 125 bool (*dequeue_xfer)(struct i3c_hci *hci, struct hci_xfer *xfer, int n); 126 + int (*handle_error)(struct i3c_hci *hci, struct hci_xfer *xfer, int n); 126 127 int (*request_ibi)(struct i3c_hci *hci, struct i3c_dev_desc *dev, 127 128 const struct i3c_ibi_setup *req); 128 129 void (*free_ibi)(struct i3c_hci *hci, struct i3c_dev_desc *dev);