Re: [PATCH v3 2/2] spi: qcom-geni: Add panic notifier to cancel and reset DMA during panic
Jyothi Kumar Seerapu <[email protected]>
| Newsgroups | org.kernel.vger.linux-spi,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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 ?> + } > + } > + > + 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); > } > >