Re: [PATCH v3 2/2] spi: qcom-geni: Add panic notifier to cancel and reset DMA during panic
Praveen Talari <[email protected]>
| Newsgroups | org.kernel.vger.linux-spi,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Hi Jyothi, Thank you for review. On 21-08-2026 16:19, Jyothi Kumar Seerapu wrote: > > > On 8/18/2026 6:58 PM, Praveen Talari wrote: >> When a VM crashes with an active SPI DMA transfer in progress, the >> SMMU raises context faults as the DMA engine continues to access >> IOVAs that are invalidated when the VM's memory context is torn down. >> These faults can affect other VMs sharing the same SMMU instance and >> obscure the root cause of the crash. >> >> Register a panic notifier that cancels (or aborts, if cancel doesn't >> complete) the in-flight command and resets the TX/RX DMA FSMs, so the >> DMA engine stops issuing transactions against invalidated IOVAs >> before the system halts. For GPI DMA mode, the DMA channels are >> terminated directly via dmaengine_terminate_async(). >> >> The notifier bails out early if the device is not runtime-active or >> if there's no active GENI command, avoiding unnecessary register >> accesses while the SE is clock-gated or idle. >> >> Since panic notifiers run with IRQs and preemption disabled, >> completion-based waits used by the regular error-handling path >> (handle_se_timeout()) cannot be reused here. Instead, the relevant >> status registers are polled directly with >> readl_poll_timeout_atomic(), which is safe to call in this context. >> >> Signed-off-by: Praveen Talari <[email protected]> >> --- >> drivers/spi/spi-geni-qcom.c | 68 >> +++++++++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 68 insertions(+) >> >> diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c >> index c65c1788325d..f272bd0cb640 100644 >> --- a/drivers/spi/spi-geni-qcom.c >> +++ b/drivers/spi/spi-geni-qcom.c >> @@ -12,8 +12,10 @@ >> #include <linux/dma/qcom-gpi-dma.h> >> #include <linux/interrupt.h> >> #include <linux/io.h> >> +#include <linux/iopoll.h> >> #include <linux/log2.h> >> #include <linux/module.h> >> +#include <linux/panic_notifier.h> >> #include <linux/platform_device.h> >> #include <linux/pm_opp.h> >> #include <linux/pm_runtime.h> >> @@ -115,6 +117,7 @@ struct spi_geni_master { >> struct dma_chan *rx; >> int cur_xfer_mode; >> const struct geni_spi_desc *dev_data; >> + struct notifier_block panic_nb; >> }; >> static void spi_slv_setup(struct spi_geni_master *mas) >> @@ -1073,6 +1076,62 @@ static void spi_geni_shutdown(struct >> platform_device *pdev) >> spi_controller_suspend(spi); >> } >> +static int spi_geni_panic_notifier(struct notifier_block *nb, >> + unsigned long action, void *data) >> +{ >> + struct spi_geni_master *mas = container_of(nb, struct >> spi_geni_master, panic_nb); >> + struct spi_controller *spi = dev_get_drvdata(mas->dev); >> + struct geni_se *se = &mas->se; >> + u32 val; >> + >> + if (!pm_runtime_active(mas->dev)) >> + return NOTIFY_OK; >> + >> + if (mas->cur_xfer_mode == GENI_GPI_DMA) { >> + dmaengine_terminate_async(mas->tx); >> + dmaengine_terminate_async(mas->rx); >> + return NOTIFY_OK; >> + } >> + >> + if (!(readl_relaxed(se->base + SE_GENI_STATUS) & >> M_GENI_CMD_ACTIVE)) >> + return NOTIFY_OK; >> + >> + if (!spi->target) { >> + geni_se_cancel_m_cmd(se); >> + if (!readl_poll_timeout_atomic(se->base + >> SE_GENI_M_IRQ_STATUS, val, >> + val & M_CMD_CANCEL_EN, 10, 50000)) { >> + writel_relaxed(M_CMD_CANCEL_EN, se->base + >> SE_GENI_M_IRQ_CLEAR); >> + return NOTIFY_OK; > Looks like a successful cancel here returns NOTIFY_OK directly, > skipping the FSM reset block below it entirely. Is that the correct > expectation ?> + } If the cancel operation succeeds, an FSM reset is not required. If it fails, the driver proceeds with an abort sequence followed by an FSM reset. Thanks, Praveen Talari >> + } >> + >> + geni_se_abort_m_cmd(se); >> + if (!readl_poll_timeout_atomic(se->base + SE_GENI_M_IRQ_STATUS, >> val, >> + val & M_CMD_ABORT_EN, 10, 50000)) >> + writel_relaxed(M_CMD_ABORT_EN, se->base + SE_GENI_M_IRQ_CLEAR); >> + >> + if (mas->cur_xfer_mode == GENI_SE_DMA) { >> + writel_relaxed(1, se->base + SE_DMA_TX_FSM_RST); >> + readl_poll_timeout_atomic(se->base + SE_DMA_TX_IRQ_STAT, val, >> + val & TX_RESET_DONE, 10, 50000); >> + writel_relaxed(val, se->base + SE_DMA_TX_IRQ_CLR); >> + >> + writel_relaxed(1, se->base + SE_DMA_RX_FSM_RST); >> + readl_poll_timeout_atomic(se->base + SE_DMA_RX_IRQ_STAT, val, >> + val & RX_RESET_DONE, 10, 50000); >> + writel_relaxed(val, se->base + SE_DMA_RX_IRQ_CLR); >> + } >> + >> + return NOTIFY_OK; >> +} >> + >> +static void spi_geni_unregister_notifiers(void *data) >> +{ >> + struct spi_geni_master *mas = data; >> + >> + atomic_notifier_chain_unregister(&panic_notifier_list, >> &mas->panic_nb); >> +} >> + >> static int spi_geni_probe(struct platform_device *pdev) >> { >> int ret, irq; >> @@ -1161,6 +1220,15 @@ static int spi_geni_probe(struct >> platform_device *pdev) >> if (ret) >> return ret; >> + mas->panic_nb.notifier_call = spi_geni_panic_notifier; >> + ret = atomic_notifier_chain_register(&panic_notifier_list, >> &mas->panic_nb); >> + if (ret) >> + return ret; >> + >> + ret = devm_add_action_or_reset(dev, >> spi_geni_unregister_notifiers, mas); >> + if (ret) >> + return ret; >> + >> return devm_spi_register_controller(dev, spi); >> } >> >