* [PATCH 0/5] spi: Add support for panic mem writes
@ 2026-10-05 8:11 Paul Cercueil
2026-10-05 8:11 ` [PATCH 1/5] spi: spi-mem: " Paul Cercueil
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Paul Cercueil @ 2026-10-05 8:11 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown,
Thomas Petazzoni
Cc: Kees Cook, Tony Luck, Guilherme G . Piccoli, linux-mtd,
linux-kernel, linux-spi, Paul Cercueil
Hi,
This patchset updates the MTD spi-nor code as well as the spi-mem code
to add support for panic mem writes to a memory partition configured as
pstore.
In the context of a kernel panic, preemption, SMP and IRQs are disabled.
Therefore the SPI driver must make sure that its code paths don't sleep,
or rely on interrupts.
A driver that will respect these conditions can say so by setting the
'panic_write' flag in its spi_controller_mem_caps. The MTD core won't
attempt a panic mem write without this flag set.
Only the .exec_op() path will be used for panic writes, given that the
fallback code relies on the generic SPI API, which explicitly states
that it mustn't be called from code that can't sleep.
In case of a kernel panic with a compatible SPI driver, and a mem write
is attempted, the 'spi_mem_op' struct will have its 'panic_write' flag
set, and the SPI driver can then handle it accordingly.
The spi-cadence-xspi driver gets updated in the process, adding support
for a irq-less mode where register polling is used, so that it can
support panic mem writes.
Cheers,
-Paul
Tanmay Jagdale (5):
spi: spi-mem: Add support for panic mem writes
mtd: spi-nor: Add support for panic writes
spi: cadence-xspi: Add irq-less support
spi: cadence-xspi: Don't use infinite timeout in register poll
spi: cadence-xspi: Add support for panic writes
drivers/mtd/spi-nor/core.c | 67 ++++++++++++++++++++-
drivers/spi/spi-cadence-xspi.c | 103 ++++++++++++++++++++-------------
drivers/spi/spi-mem.c | 25 ++++++++
include/linux/spi/spi-mem.h | 5 ++
4 files changed, 156 insertions(+), 44 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/5] spi: spi-mem: Add support for panic mem writes
2026-10-05 8:11 [PATCH 0/5] spi: Add support for panic mem writes Paul Cercueil
@ 2026-10-05 8:11 ` Paul Cercueil
2026-10-05 8:24 ` sashiko-bot
2026-10-05 8:11 ` [PATCH 2/5] mtd: spi-nor: Add support for panic writes Paul Cercueil
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Paul Cercueil @ 2026-10-05 8:11 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown,
Thomas Petazzoni
Cc: Kees Cook, Tony Luck, Guilherme G . Piccoli, linux-mtd,
linux-kernel, linux-spi, Tanmay Jagdale, Paul Cercueil
From: Tanmay Jagdale <tanmay@marvell.com>
Add a 'panic_write' flag to the controller's capabilities struct.
When set, the SPI controller indicates that it will be able to send
commands to a connected NOR or NAND memory in the context of a kernel
panic, which means without sleeping and without relying on IRQs.
Whether or not a given memory operation is a panic mem write is given by
the spi_mem_op.panic_write flag.
Signed-off-by: Tanmay Jagdale <tanmay@marvell.com>
Co-developed-by: Paul Cercueil <paul.cercueil@bootlin.com>
Signed-off-by: Paul Cercueil <paul.cercueil@bootlin.com>
---
drivers/spi/spi-mem.c | 25 +++++++++++++++++++++++++
include/linux/spi/spi-mem.h | 5 +++++
2 files changed, 30 insertions(+)
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index b94680b60af9..89710f9f555e 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -398,6 +398,31 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
u8 *tmpbuf;
int ret;
+ if (op->panic_write) {
+ if (!spi_mem_controller_is_capable(ctlr, panic_write))
+ return -EOPNOTSUPP;
+
+ /*
+ * Only support panic writes if we'll go through .exec_op().
+ * The regular SPI path (spi_sync(), etc.) explicitly mentions
+ * that it can only be used from a context that may sleep, which
+ * is not the case on a kernel panic.
+ * If this changes in the future, this could be reworked, but
+ * for now this will have to do.
+ */
+ if (WARN_ON_ONCE(!ctlr->mem_ops || !ctlr->mem_ops->exec_op))
+ return -EOPNOTSUPP;
+
+ if (spi_get_csgpiod(mem->spi, 0))
+ return -EPERM;
+
+ /* On panic IRQs are disabled so we cannot wait on locks */
+ if (mutex_is_locked(&ctlr->bus_lock_mutex) ||
+ mutex_is_locked(&ctlr->io_mutex)) {
+ return -EPERM;
+ }
+ }
+
/* Make sure the operation frequency is correct before going futher */
spi_mem_adjust_op_freq(mem, (struct spi_mem_op *)op);
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index 0cce6b57242a..2633307bfad5 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -169,6 +169,7 @@ enum spi_mem_data_dir {
* operation does not involve transferring data
* @data.buf.in: input buffer (must be DMA-able)
* @data.buf.out: output buffer (must be DMA-able)
+ * @panic_write: The command is executed in the context of a kernel panic
* @max_freq: frequency limitation wrt this operation. 0 means there is no
* specific constraint and the highest achievable frequency can be
* attempted.
@@ -211,6 +212,7 @@ struct spi_mem_op {
} buf;
} data;
+ bool panic_write;
unsigned int max_freq;
};
@@ -392,6 +394,8 @@ struct spi_controller_mem_ops {
* is a pause in the transfer (eg. internal bus contention or
* DMA arbitration on an interconnect). Features such as NAND
* continuous reads shall not be leveraged.
+ * @panic_write: Set to true if the controller driver supports writing to a
+ * connected NAND/NOR memory in a kernel panic context
*/
struct spi_controller_mem_caps {
bool dtr;
@@ -400,6 +404,7 @@ struct spi_controller_mem_caps {
bool per_op_freq;
bool secondary_op_tmpl;
bool no_cs_assertion;
+ bool panic_write;
};
#define spi_mem_controller_is_capable(ctlr, cap) \
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/5] mtd: spi-nor: Add support for panic writes
2026-10-05 8:11 [PATCH 0/5] spi: Add support for panic mem writes Paul Cercueil
2026-10-05 8:11 ` [PATCH 1/5] spi: spi-mem: " Paul Cercueil
@ 2026-10-05 8:11 ` Paul Cercueil
2026-10-05 8:23 ` sashiko-bot
2026-10-05 8:11 ` [PATCH 3/5] spi: cadence-xspi: Add irq-less support Paul Cercueil
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Paul Cercueil @ 2026-10-05 8:11 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown,
Thomas Petazzoni
Cc: Kees Cook, Tony Luck, Guilherme G . Piccoli, linux-mtd,
linux-kernel, linux-spi, Tanmay Jagdale, Paul Cercueil
From: Tanmay Jagdale <tanmay@marvell.com>
If the SPI controller supports it, provide the functionality of doing a
panic write.
Update the spi-nor core code and controllers to make sure they won't try
to grab locked mutexes or sleep when doing a panic write, as preemption
and IRQs are disabled.
The information is carried down to the SPI controller driver through the
spi_mem_op structure. If the corresponding flag is set there, and the
SPI driver does support panic writes, it then has to respect the same
rules.
Signed-off-by: Tanmay Jagdale <tanmay@marvell.com>
Co-developed-by: Paul Cercueil <paul.cercueil@bootlin.com>
Signed-off-by: Paul Cercueil <paul.cercueil@bootlin.com>
---
drivers/mtd/spi-nor/core.c | 67 ++++++++++++++++++++++++++++++++++++--
1 file changed, 64 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 8bc117b46e02..2cdeebf3e25b 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -96,6 +96,9 @@ void spi_nor_spimem_setup_op(const struct spi_nor *nor,
if (op->data.nbytes)
op->data.buswidth = spi_nor_get_protocol_data_nbits(proto);
+ if (nor->mtd.oops_panic_write)
+ op->panic_write = true;
+
if (spi_nor_protocol_is_dtr(proto)) {
/*
* SPIMEM supports mixed DTR modes, but right now we can only
@@ -281,7 +284,7 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to,
if (spi_nor_spimem_bounce(nor, &op))
memcpy(nor->bouncebuf, buf, op.data.nbytes);
- if (nor->dirmap.wdesc) {
+ if (nor->dirmap.wdesc && !nor->mtd.oops_panic_write) {
nbytes = spi_mem_dirmap_write(nor->dirmap.wdesc, op.addr.val,
op.data.nbytes, op.data.buf.out);
} else {
@@ -664,6 +667,9 @@ static void spi_nor_rww_end_rdst(struct spi_nor *nor)
static int spi_nor_lock_rdst(struct spi_nor *nor)
{
+ if (nor->mtd.oops_panic_write)
+ return 0;
+
if (spi_nor_use_parallel_locking(nor))
return spi_nor_rww_start_rdst(nor);
@@ -672,6 +678,9 @@ static int spi_nor_lock_rdst(struct spi_nor *nor)
static void spi_nor_unlock_rdst(struct spi_nor *nor)
{
+ if (nor->mtd.oops_panic_write)
+ return;
+
if (spi_nor_use_parallel_locking(nor)) {
spi_nor_rww_end_rdst(nor);
wake_up(&nor->rww.wait);
@@ -729,7 +738,10 @@ static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor,
if (ret)
return 0;
- cond_resched();
+ if (nor->mtd.oops_panic_write)
+ cpu_relax();
+ else
+ cond_resched();
}
dev_dbg(nor->dev, "flash operation timed out\n");
@@ -1291,6 +1303,9 @@ static void spi_nor_rww_end_io(struct spi_nor *nor)
static int spi_nor_lock_device(struct spi_nor *nor)
{
+ if (nor->mtd.oops_panic_write)
+ return 0;
+
if (!spi_nor_use_parallel_locking(nor))
return 0;
@@ -1299,6 +1314,9 @@ static int spi_nor_lock_device(struct spi_nor *nor)
static void spi_nor_unlock_device(struct spi_nor *nor)
{
+ if (nor->mtd.oops_panic_write)
+ return;
+
if (spi_nor_use_parallel_locking(nor)) {
spi_nor_rww_end_io(nor);
wake_up(&nor->rww.wait);
@@ -1336,6 +1354,9 @@ int spi_nor_prep_and_lock(struct spi_nor *nor)
{
int ret;
+ if (nor->mtd.oops_panic_write)
+ return 0;
+
ret = spi_nor_prep(nor);
if (ret)
return ret;
@@ -1351,6 +1372,9 @@ int spi_nor_prep_and_lock(struct spi_nor *nor)
void spi_nor_unlock_and_unprep(struct spi_nor *nor)
{
+ if (nor->mtd.oops_panic_write)
+ return;
+
if (!spi_nor_use_parallel_locking(nor)) {
mutex_unlock(&nor->lock);
} else {
@@ -1407,6 +1431,9 @@ static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t le
{
int ret;
+ if (nor->mtd.oops_panic_write)
+ return 0;
+
ret = spi_nor_prep(nor);
if (ret)
return ret;
@@ -1422,6 +1449,9 @@ static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t le
static void spi_nor_unlock_and_unprep_pe(struct spi_nor *nor, loff_t start, size_t len)
{
+ if (nor->mtd.oops_panic_write)
+ return;
+
if (!spi_nor_use_parallel_locking(nor)) {
mutex_unlock(&nor->lock);
} else {
@@ -1480,6 +1510,9 @@ static int spi_nor_prep_and_lock_rd(struct spi_nor *nor, loff_t start, size_t le
{
int ret;
+ if (nor->mtd.oops_panic_write)
+ return 0;
+
ret = spi_nor_prep(nor);
if (ret)
return ret;
@@ -1495,6 +1528,9 @@ static int spi_nor_prep_and_lock_rd(struct spi_nor *nor, loff_t start, size_t le
static void spi_nor_unlock_and_unprep_rd(struct spi_nor *nor, loff_t start, size_t len)
{
+ if (nor->mtd.oops_panic_write)
+ return;
+
if (!spi_nor_use_parallel_locking(nor)) {
mutex_unlock(&nor->lock);
} else {
@@ -3393,6 +3429,26 @@ static void spi_nor_soft_reset(struct spi_nor *nor)
usleep_range(SPI_NOR_SRST_SLEEP_MIN, SPI_NOR_SRST_SLEEP_MAX);
}
+static int spi_nor_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+
+ /*
+ * At this point preemption and local interrupts are disabled, so we
+ * can't get the lock if it's taken.
+ */
+ if (mutex_is_locked(&nor->lock))
+ return -EPERM;
+
+ if (spi_nor_use_parallel_locking(nor) &&
+ (nor->rww.ongoing_io || nor->rww.ongoing_rd)) {
+ return -EPERM;
+ }
+
+ return spi_nor_write(mtd, to, len, retlen, buf);
+}
+
/* mtd suspend handler */
static int spi_nor_suspend(struct mtd_info *mtd)
{
@@ -3596,8 +3652,13 @@ static int spi_nor_set_mtd_info(struct spi_nor *nor)
mtd->size = nor->params->size;
mtd->_read = spi_nor_read;
/* Might be already set by some SST flashes. */
- if (!mtd->_write)
+ if (!mtd->_write) {
mtd->_write = spi_nor_write;
+ if (nor->spimem &&
+ spi_mem_controller_is_capable(nor->spimem->spi->controller, panic_write)) {
+ mtd->_panic_write = spi_nor_panic_write;
+ }
+ }
mtd->_suspend = spi_nor_suspend;
mtd->_resume = spi_nor_resume;
mtd->_get_device = spi_nor_get_device;
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 3/5] spi: cadence-xspi: Add irq-less support
2026-10-05 8:11 [PATCH 0/5] spi: Add support for panic mem writes Paul Cercueil
2026-10-05 8:11 ` [PATCH 1/5] spi: spi-mem: " Paul Cercueil
2026-10-05 8:11 ` [PATCH 2/5] mtd: spi-nor: Add support for panic writes Paul Cercueil
@ 2026-10-05 8:11 ` Paul Cercueil
2026-10-05 8:24 ` sashiko-bot
2026-10-05 8:11 ` [PATCH 4/5] spi: cadence-xspi: Don't use infinite timeout in register poll Paul Cercueil
2026-10-05 8:11 ` [PATCH 5/5] spi: cadence-xspi: Add support for panic writes Paul Cercueil
4 siblings, 1 reply; 11+ messages in thread
From: Paul Cercueil @ 2026-10-05 8:11 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown,
Thomas Petazzoni
Cc: Kees Cook, Tony Luck, Guilherme G . Piccoli, linux-mtd,
linux-kernel, linux-spi, Tanmay Jagdale, Paul Cercueil
From: Tanmay Jagdale <tanmay@marvell.com>
Make the interrupt line optional. If not provided, the driver will
instead resort to polling registers.
This can be useful in itself, but will be used later to support write on
kernel panic.
Note the cdns_xspi_is_{sdma,stig}_ready() seem to have moved for no
reason; they were actually moved out of a conditional compilation block
(#ifdef CONFIG_64BIT), as they are needed in conf-independent paths now.
Signed-off-by: Tanmay Jagdale <tanmay@marvell.com>
Co-developed-by: Paul Cercueil <paul.cercueil@bootlin.com>
Signed-off-by: Paul Cercueil <paul.cercueil@bootlin.com>
---
drivers/spi/spi-cadence-xspi.c | 85 +++++++++++++++++++---------------
1 file changed, 48 insertions(+), 37 deletions(-)
diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index 39c868a5b171..7cf52bcb05df 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -365,6 +365,30 @@ static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi)
100, 1000);
}
+static bool cdns_xspi_is_stig_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
+{
+ u32 ctrl_stat;
+
+ return !readl_relaxed_poll_timeout
+ (cdns_xspi->iobase + CDNS_XSPI_CTRL_STATUS_REG,
+ ctrl_stat,
+ ((ctrl_stat & BIT(3)) == 0),
+ sleep ? MRVL_XSPI_POLL_DELAY_US : 0,
+ sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0);
+}
+
+static bool cdns_xspi_is_sdma_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
+{
+ u32 ctrl_stat;
+
+ return !readl_relaxed_poll_timeout
+ (cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG,
+ ctrl_stat,
+ (ctrl_stat & CDNS_XSPI_SDMA_TRIGGER),
+ sleep ? MRVL_XSPI_POLL_DELAY_US : 0,
+ sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0);
+}
+
static void cdns_xspi_trigger_command(struct cdns_xspi_dev *cdns_xspi,
u32 cmd_regs[6])
{
@@ -562,16 +586,25 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
- wait_for_completion(&cdns_xspi->sdma_complete);
- if (cdns_xspi->sdma_error) {
- cdns_xspi->set_interrupts_handler(cdns_xspi, false);
+ if (cdns_xspi->irq >= 0) {
+ wait_for_completion(&cdns_xspi->sdma_complete);
+ if (cdns_xspi->sdma_error) {
+ cdns_xspi->set_interrupts_handler(cdns_xspi, false);
+ return -EIO;
+ }
+ } else if (!cdns_xspi_is_sdma_ready(cdns_xspi, true)) {
return -EIO;
}
+
cdns_xspi->sdma_handler(cdns_xspi);
}
- wait_for_completion(&cdns_xspi->cmd_complete);
- cdns_xspi->set_interrupts_handler(cdns_xspi, false);
+ if (cdns_xspi->irq >= 0) {
+ wait_for_completion(&cdns_xspi->cmd_complete);
+ cdns_xspi->set_interrupts_handler(cdns_xspi, false);
+ } else if (!cdns_xspi_is_stig_ready(cdns_xspi, true)) {
+ return -EIO;
+ }
cmd_status = cdns_xspi_check_command_status(cdns_xspi);
if (cmd_status)
@@ -1052,30 +1085,6 @@ static int cdns_xspi_prepare_transfer(int cs, int dir, int len, u32 *cmd_regs)
return 0;
}
-static bool cdns_xspi_is_stig_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
-{
- u32 ctrl_stat;
-
- return !readl_relaxed_poll_timeout
- (cdns_xspi->iobase + CDNS_XSPI_CTRL_STATUS_REG,
- ctrl_stat,
- ((ctrl_stat & BIT(3)) == 0),
- sleep ? MRVL_XSPI_POLL_DELAY_US : 0,
- sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0);
-}
-
-static bool cdns_xspi_is_sdma_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
-{
- u32 ctrl_stat;
-
- return !readl_relaxed_poll_timeout
- (cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG,
- ctrl_stat,
- (ctrl_stat & CDNS_XSPI_SDMA_TRIGGER),
- sleep ? MRVL_XSPI_POLL_DELAY_US : 0,
- sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0);
-}
-
static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
struct spi_message *m)
{
@@ -1268,15 +1277,17 @@ static int cdns_xspi_probe(struct platform_device *pdev)
}
#endif
- cdns_xspi->irq = platform_get_irq(pdev, 0);
- if (cdns_xspi->irq < 0)
- return -ENXIO;
+ cdns_xspi->irq = platform_get_irq_optional(pdev, 0);
+ if (cdns_xspi->irq < 0 && cdns_xspi->irq != -ENXIO)
+ return cdns_xspi->irq;
- ret = devm_request_irq(dev, cdns_xspi->irq, cdns_xspi_irq_handler,
- IRQF_SHARED, pdev->name, cdns_xspi);
- if (ret) {
- dev_err(dev, "Failed to request IRQ: %d\n", cdns_xspi->irq);
- return ret;
+ if (cdns_xspi->irq >= 0) {
+ ret = devm_request_irq(dev, cdns_xspi->irq, cdns_xspi_irq_handler,
+ IRQF_SHARED, pdev->name, cdns_xspi);
+ if (ret) {
+ dev_err(dev, "Failed to request IRQ: %d\n", cdns_xspi->irq);
+ return ret;
+ }
}
#ifdef CONFIG_64BIT
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 4/5] spi: cadence-xspi: Don't use infinite timeout in register poll
2026-10-05 8:11 [PATCH 0/5] spi: Add support for panic mem writes Paul Cercueil
` (2 preceding siblings ...)
2026-10-05 8:11 ` [PATCH 3/5] spi: cadence-xspi: Add irq-less support Paul Cercueil
@ 2026-10-05 8:11 ` Paul Cercueil
2026-10-05 8:21 ` sashiko-bot
2026-10-05 8:11 ` [PATCH 5/5] spi: cadence-xspi: Add support for panic writes Paul Cercueil
4 siblings, 1 reply; 11+ messages in thread
From: Paul Cercueil @ 2026-10-05 8:11 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown,
Thomas Petazzoni
Cc: Kees Cook, Tony Luck, Guilherme G . Piccoli, linux-mtd,
linux-kernel, linux-spi, Tanmay Jagdale, Paul Cercueil
From: Tanmay Jagdale <tanmay@marvell.com>
The functions cdns_xspi_is_{sdma,stig}_ready() take a 'sleep' argument
which, if false, would result in the polling being a busy-wait.
But it would also switch to infinite timeouts, which we don't want.
Note that this code is dead right now ('sleep' is always true) but this
will change when panic write handling is implemented.
Signed-off-by: Tanmay Jagdale <tanmay@marvell.com>
Co-developed-by: Paul Cercueil <paul.cercueil@bootlin.com>
Signed-off-by: Paul Cercueil <paul.cercueil@bootlin.com>
---
drivers/spi/spi-cadence-xspi.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index 7cf52bcb05df..09ed2afbd4af 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -374,7 +374,7 @@ static bool cdns_xspi_is_stig_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
ctrl_stat,
((ctrl_stat & BIT(3)) == 0),
sleep ? MRVL_XSPI_POLL_DELAY_US : 0,
- sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0);
+ MRVL_XSPI_POLL_TIMEOUT_US);
}
static bool cdns_xspi_is_sdma_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
@@ -386,7 +386,7 @@ static bool cdns_xspi_is_sdma_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
ctrl_stat,
(ctrl_stat & CDNS_XSPI_SDMA_TRIGGER),
sleep ? MRVL_XSPI_POLL_DELAY_US : 0,
- sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0);
+ MRVL_XSPI_POLL_TIMEOUT_US);
}
static void cdns_xspi_trigger_command(struct cdns_xspi_dev *cdns_xspi,
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 5/5] spi: cadence-xspi: Add support for panic writes
2026-10-05 8:11 [PATCH 0/5] spi: Add support for panic mem writes Paul Cercueil
` (3 preceding siblings ...)
2026-10-05 8:11 ` [PATCH 4/5] spi: cadence-xspi: Don't use infinite timeout in register poll Paul Cercueil
@ 2026-10-05 8:11 ` Paul Cercueil
2026-10-05 8:26 ` sashiko-bot
4 siblings, 1 reply; 11+ messages in thread
From: Paul Cercueil @ 2026-10-05 8:11 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown,
Thomas Petazzoni
Cc: Kees Cook, Tony Luck, Guilherme G . Piccoli, linux-mtd,
linux-kernel, linux-spi, Tanmay Jagdale, Paul Cercueil
From: Tanmay Jagdale <tanmay@marvell.com>
Advertise support for panic writes. When a memory op is received with
the 'panic' flag set, the driver will then work without relying on
interrupts and without sleeping.
Signed-off-by: Tanmay Jagdale <tanmay@marvell.com>
Co-developed-by: Paul Cercueil <paul.cercueil@bootlin.com>
Signed-off-by: Paul Cercueil <paul.cercueil@bootlin.com>
---
drivers/spi/spi-cadence-xspi.c | 26 ++++++++++++++++++--------
1 file changed, 18 insertions(+), 8 deletions(-)
diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index 09ed2afbd4af..0b7162740940 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -350,10 +350,11 @@ struct cdns_xspi_dev {
void (*set_interrupts_handler)(struct cdns_xspi_dev *cdns_xspi, bool enabled);
bool xfer_in_progress;
+ bool panic_write;
int current_xfer_qword;
};
-static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi)
+static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi, bool sleep)
{
u32 ctrl_stat;
@@ -362,7 +363,7 @@ static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi)
ctrl_stat,
((ctrl_stat &
CDNS_XSPI_CTRL_BUSY) == 0),
- 100, 1000);
+ sleep ? 100 : 0, 1000);
}
static bool cdns_xspi_is_stig_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
@@ -549,7 +550,7 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
int ret;
int dummybytes = op->dummy.nbytes;
- ret = cdns_xspi_wait_for_controller_idle(cdns_xspi);
+ ret = cdns_xspi_wait_for_controller_idle(cdns_xspi, !op->panic_write);
if (ret < 0)
return -EIO;
@@ -586,23 +587,23 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
- if (cdns_xspi->irq >= 0) {
+ if (cdns_xspi->irq >= 0 && !op->panic_write) {
wait_for_completion(&cdns_xspi->sdma_complete);
if (cdns_xspi->sdma_error) {
cdns_xspi->set_interrupts_handler(cdns_xspi, false);
return -EIO;
}
- } else if (!cdns_xspi_is_sdma_ready(cdns_xspi, true)) {
+ } else if (!cdns_xspi_is_sdma_ready(cdns_xspi, !op->panic_write)) {
return -EIO;
}
cdns_xspi->sdma_handler(cdns_xspi);
}
- if (cdns_xspi->irq >= 0) {
+ if (cdns_xspi->irq >= 0 && !op->panic_write) {
wait_for_completion(&cdns_xspi->cmd_complete);
cdns_xspi->set_interrupts_handler(cdns_xspi, false);
- } else if (!cdns_xspi_is_stig_ready(cdns_xspi, true)) {
+ } else if (!cdns_xspi_is_stig_ready(cdns_xspi, !op->panic_write)) {
return -EIO;
}
@@ -622,6 +623,8 @@ static int cdns_xspi_mem_op(struct cdns_xspi_dev *cdns_xspi,
if (cdns_xspi->cur_cs != spi_get_chipselect(mem->spi, 0))
cdns_xspi->cur_cs = spi_get_chipselect(mem->spi, 0);
+ cdns_xspi->panic_write = op->panic_write;
+
return cdns_xspi_send_stig_command(cdns_xspi, op,
(dir != SPI_MEM_NO_DATA));
}
@@ -831,9 +834,11 @@ static bool cdns_xspi_is_dll_locked(struct cdns_xspi_dev *cdns_xspi)
{
u32 dll_lock;
+ /* Busy-wait in case we wake up the PHY from suspend to do a panic write */
return !readl_relaxed_poll_timeout(cdns_xspi->iobase +
CDNS_XSPI_INTR_STATUS_REG,
- dll_lock, ((dll_lock & CDNS_XSPI_DLL_LOCK) == 1), 10, 10000);
+ dll_lock, ((dll_lock & CDNS_XSPI_DLL_LOCK) == 1),
+ cdns_xspi->panic_write ? 0 : 10, 10000);
}
/* Static configuration of PHY */
@@ -1186,6 +1191,10 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
}
#endif
+static const struct spi_controller_mem_caps cdns_xspi_mem_caps = {
+ .panic_write = true,
+};
+
static int cdns_xspi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1306,6 +1315,7 @@ static int cdns_xspi_probe(struct platform_device *pdev)
}
host->num_chipselect = 1 << cdns_xspi->hw_num_banks;
+ host->mem_caps = &cdns_xspi_mem_caps;
ret = devm_spi_register_controller(dev, host);
if (ret) {
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/5] spi: cadence-xspi: Don't use infinite timeout in register poll
2026-10-05 8:11 ` [PATCH 4/5] spi: cadence-xspi: Don't use infinite timeout in register poll Paul Cercueil
@ 2026-10-05 8:21 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-10-05 8:21 UTC (permalink / raw)
To: Paul Cercueil, Thomas Petazzoni, Mark Brown, Takahiro Kuwano,
Richard Weinberger, Vignesh Raghavendra, Miquel Raynal,
Michael Walle, Pratyush Yadav
Cc: Kees Cook, linux-kernel, linux-spi, Tanmay Jagdale, linux-mtd,
Guilherme G . Piccoli, Tony Luck
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] spi: cadence-xspi: deadlock in panic writes due to non-atomic poll macro
--
commit 3f62b2245460c33dcb6fb6615d7fdf03de827776
Author: Tanmay Jagdale <tanmay@marvell.com>
spi: cadence-xspi: Don't use infinite timeout in register poll
The commit modifies the register polling functions to enforce a non-zero
timeout even when the sleep argument is false. This prevents busy-waits from
switching to infinite timeouts. The change anticipates future panic write
handling support.
> diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
> index 7cf52bcb05df6..09ed2afbd4af3 100644
> --- a/drivers/spi/spi-cadence-xspi.c
> +++ b/drivers/spi/spi-cadence-xspi.c
> @@ -374,7 +374,7 @@ static bool cdns_xspi_is_stig_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
> ctrl_stat,
> ((ctrl_stat & BIT(3)) == 0),
> sleep ? MRVL_XSPI_POLL_DELAY_US : 0,
> - sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0);
> + MRVL_XSPI_POLL_TIMEOUT_US);
[Severity: High]
Does this unconditionally trigger timekeeping functions in panic contexts?
When sleep is false, the panic write handler can deadlock.
The readl_relaxed_poll_timeout() macro expands to poll_timeout_us(), which
unconditionally calls ktime_get() when a non-zero timeout is provided.
In a panic context, timekeeping is suspended.
If a system panic occurs while another CPU holds the timekeeping seqlock
(tk_core.seq) and is stopped, ktime_get() will spin endlessly in
read_seqcount_begin(), deadlocking the system.
This prevents oops/kmsg crash logs from being written to flash, which is why
the <linux/iopoll.h> API contract explicitly forbids using it in atomic
contexts with a non-zero timeout.
To safely poll without sleeping in panic contexts, should the driver
conditionally use readl_relaxed_poll_timeout_atomic(), which relies on
udelay() and cycle counting instead of ktime_get()?
> }
>
> static bool cdns_xspi_is_sdma_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
> @@ -386,7 +386,7 @@ static bool cdns_xspi_is_sdma_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
> ctrl_stat,
> (ctrl_stat & CDNS_XSPI_SDMA_TRIGGER),
> sleep ? MRVL_XSPI_POLL_DELAY_US : 0,
> - sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0);
> + MRVL_XSPI_POLL_TIMEOUT_US);
[Severity: High]
Can the same deadlock occur in the SDMA polling loop since this also calls
readl_relaxed_poll_timeout() with a non-zero timeout when sleep is false?
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261005081141.33688-1-paul.cercueil@bootlin.com?part=4
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/5] mtd: spi-nor: Add support for panic writes
2026-10-05 8:11 ` [PATCH 2/5] mtd: spi-nor: Add support for panic writes Paul Cercueil
@ 2026-10-05 8:23 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-10-05 8:23 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Vignesh Raghavendra,
Miquel Raynal, Richard Weinberger, Thomas Petazzoni,
Takahiro Kuwano, Paul Cercueil, Mark Brown
Cc: linux-kernel, linux-spi, Kees Cook, Tanmay Jagdale, Tony Luck,
Guilherme G . Piccoli, linux-mtd
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] mtd: spi-nor: infinite loop in panic write timeout via jiffies
- [High] mtd: spi-nor: panic write ignores ongoing_pe causing hardware conflict
- [High] mtd: spi-nor: sleep in atomic context during panic write
--
commit 6866cee3049043ab91dbf3b45f823a4010bb97cc
Author: Tanmay Jagdale <tanmay@marvell.com>
mtd: spi-nor: Add support for panic writes
This commit adds support for panic writes to the SPI NOR framework. It
plumbs the oops_panic_write flag through to the SPI controllers,
avoiding mutexes and blocking calls when preemption and IRQs are
disabled.
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
[ ... ]
> @@ -729,7 +738,10 @@ static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor,
> if (ret)
> return 0;
>
> - cond_resched();
> + if (nor->mtd.oops_panic_write)
> + cpu_relax();
[Severity: High]
In spi_nor_wait_till_ready_with_timeout(), the loop condition relies on:
if (time_after_eq(jiffies, deadline))
Since jiffies does not advance in a panic context because local IRQs are
disabled, does this mean the system will hang indefinitely during a panic
if the flash chip takes time to complete or becomes unresponsive?
> + else
> + cond_resched();
> }
[ ... ]
> @@ -3393,6 +3429,26 @@ static void spi_nor_soft_reset(struct spi_nor *nor)
> usleep_range(SPI_NOR_SRST_SLEEP_MIN, SPI_NOR_SRST_SLEEP_MAX);
> }
>
> +static int spi_nor_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
> + size_t *retlen, const u_char *buf)
> +{
> + struct spi_nor *nor = mtd_to_spi_nor(mtd);
> +
> + /*
> + * At this point preemption and local interrupts are disabled, so we
> + * can't get the lock if it's taken.
> + */
> + if (mutex_is_locked(&nor->lock))
> + return -EPERM;
> +
> + if (spi_nor_use_parallel_locking(nor) &&
> + (nor->rww.ongoing_io || nor->rww.ongoing_rd)) {
> + return -EPERM;
> + }
[Severity: High]
Does this omit checking nor->rww.ongoing_pe?
If a panic occurs while a long-running erase or program operation is in
progress, ongoing_pe will be true. If the lock was released during the
parallel hardware operation, the panic write might proceed and send write
commands while the hardware write in progress bit is set.
The hardware will ignore the commands, but the software will loop in
spi_nor_wait_till_ready() until the original operation finishes, incorrectly
assuming the panic dump was successfully written.
> +
> + return spi_nor_write(mtd, to, len, retlen, buf);
[Severity: High]
Can this sequence lead to a sleep-in-atomic bug?
spi_nor_write() can call down to spi_nor_octal_dtr_write() which does a
sleepable memory allocation when unaligned addresses are passed:
tmp_buf = kmalloc(nor->params->page_size, GFP_KERNEL);
Additionally, the write eventually reaches spi_mem_exec_op() and then
spi_mem_access_start(), which unconditionally takes mutexes:
mutex_lock(&ctlr->bus_lock_mutex);
mutex_lock(&ctlr->io_mutex);
Since mtd->_panic_write operates with local IRQs and preemption disabled,
would these paths cause a recursive panic and completely halt the crash
dump process?
> +}
> +
> /* mtd suspend handler */
> static int spi_nor_suspend(struct mtd_info *mtd)
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261005081141.33688-1-paul.cercueil@bootlin.com?part=2
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 3/5] spi: cadence-xspi: Add irq-less support
2026-10-05 8:11 ` [PATCH 3/5] spi: cadence-xspi: Add irq-less support Paul Cercueil
@ 2026-10-05 8:24 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-10-05 8:24 UTC (permalink / raw)
To: Pratyush Yadav, Vignesh Raghavendra, Richard Weinberger,
Takahiro Kuwano, Paul Cercueil, Miquel Raynal, Mark Brown,
Michael Walle, Thomas Petazzoni
Cc: Tanmay Jagdale, Tony Luck, Kees Cook, linux-spi,
Guilherme G . Piccoli, linux-mtd, linux-kernel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] spi: cadence-xspi: unhandled interrupt storm in polling mode
- [High] spi: cadence-xspi: SDMA W1C trigger bit not cleared in polling mode
--
commit 85002136114ad45055d6ac972dbcbdf8c8e3d6b2
Author: Tanmay Jagdale <tanmay@marvell.com>
spi: cadence-xspi: Add irq-less support
This commit makes the interrupt line optional. If an interrupt is not
provided, the driver resorts to polling registers. It modifies
cdns_xspi_send_stig_command() and probe functions to handle environments
without a dedicated IRQ.
> diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
> index 39c868a5b171c..7cf52bcb05df6 100644
> --- a/drivers/spi/spi-cadence-xspi.c
> +++ b/drivers/spi/spi-cadence-xspi.c
> @@ -365,6 +365,30 @@ static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi)
[ ... ]
> +static bool cdns_xspi_is_sdma_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
> +{
> + u32 ctrl_stat;
> +
> + return !readl_relaxed_poll_timeout
> + (cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG,
> + ctrl_stat,
> + (ctrl_stat & CDNS_XSPI_SDMA_TRIGGER),
> + sleep ? MRVL_XSPI_POLL_DELAY_US : 0,
> + sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0);
> +}
[Severity: High]
Does this leave the W1C (Write-1-to-Clear) trigger bit set?
It appears CDNS_XSPI_INTR_STATUS_REG is a latching W1C register (as seen in
cdns_xspi_irq_handler() which reads and writes back to clear latched events).
In polling mode (when irq < 0), this function only reads the register. For
base Cadence hardware, cdns_xspi_set_interrupts() doesn't clear this either.
Could this cause the CDNS_XSPI_SDMA_TRIGGER bit to remain permanently set
after the first SDMA transfer, leading subsequent transfers to falsely see a
ready state and potentially access memory prematurely?
[ ... ]
> @@ -562,16 +586,25 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
>
> cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
>
> - wait_for_completion(&cdns_xspi->sdma_complete);
> - if (cdns_xspi->sdma_error) {
> - cdns_xspi->set_interrupts_handler(cdns_xspi, false);
> + if (cdns_xspi->irq >= 0) {
> + wait_for_completion(&cdns_xspi->sdma_complete);
> + if (cdns_xspi->sdma_error) {
> + cdns_xspi->set_interrupts_handler(cdns_xspi, false);
> + return -EIO;
> + }
> + } else if (!cdns_xspi_is_sdma_ready(cdns_xspi, true)) {
> return -EIO;
> }
> +
> cdns_xspi->sdma_handler(cdns_xspi);
> }
>
> - wait_for_completion(&cdns_xspi->cmd_complete);
> - cdns_xspi->set_interrupts_handler(cdns_xspi, false);
> + if (cdns_xspi->irq >= 0) {
> + wait_for_completion(&cdns_xspi->cmd_complete);
> + cdns_xspi->set_interrupts_handler(cdns_xspi, false);
> + } else if (!cdns_xspi_is_stig_ready(cdns_xspi, true)) {
> + return -EIO;
> + }
[Severity: High]
Does this leave hardware interrupts permanently unmasked in polling mode?
At the start of cdns_xspi_send_stig_command(), interrupts are unmasked
unconditionally:
cdns_xspi->set_interrupts_handler(cdns_xspi, true);
However, when irq < 0, the corresponding teardown call:
cdns_xspi->set_interrupts_handler(cdns_xspi, false);
is skipped because it is guarded by if (cdns_xspi->irq >= 0).
If the physical IRQ line is connected or shared, could this cause the
controller to assert it indefinitely, triggering an unhandled interrupt storm
that forces the kernel to disable the IRQ line?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261005081141.33688-1-paul.cercueil@bootlin.com?part=3
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/5] spi: spi-mem: Add support for panic mem writes
2026-10-05 8:11 ` [PATCH 1/5] spi: spi-mem: " Paul Cercueil
@ 2026-10-05 8:24 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-10-05 8:24 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Richard Weinberger, Paul Cercueil,
Takahiro Kuwano, Vignesh Raghavendra, Mark Brown,
Thomas Petazzoni, Miquel Raynal
Cc: Tanmay Jagdale, Tony Luck, Guilherme G . Piccoli, Kees Cook,
linux-kernel, linux-mtd, linux-spi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] spi: spi-mem: panic write deadlocks system by calling spi_mem_access_start
--
commit be9ac542fea508a1fa056661e5ddb8e23cf4a7c4
Author: Tanmay Jagdale <tanmay@marvell.com>
spi: spi-mem: Add support for panic mem writes
Add a panic_write flag to the controller capabilities. When set, this
indicates the SPI controller can send commands to a connected NOR or NAND
memory in the context of a kernel panic, without sleeping or relying on IRQs.
> diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
> index b94680b60af95..89710f9f555ed 100644
> --- a/drivers/spi/spi-mem.c
> +++ b/drivers/spi/spi-mem.c
> @@ -398,6 +398,31 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> u8 *tmpbuf;
> int ret;
>
> + if (op->panic_write) {
> + if (!spi_mem_controller_is_capable(ctlr, panic_write))
> + return -EOPNOTSUPP;
> +
> + /*
> + * Only support panic writes if we'll go through .exec_op().
> + * The regular SPI path (spi_sync(), etc.) explicitly mentions
> + * that it can only be used from a context that may sleep, which
> + * is not the case on a kernel panic.
> + * If this changes in the future, this could be reworked, but
> + * for now this will have to do.
> + */
> + if (WARN_ON_ONCE(!ctlr->mem_ops || !ctlr->mem_ops->exec_op))
> + return -EOPNOTSUPP;
> +
> + if (spi_get_csgpiod(mem->spi, 0))
> + return -EPERM;
> +
> + /* On panic IRQs are disabled so we cannot wait on locks */
> + if (mutex_is_locked(&ctlr->bus_lock_mutex) ||
> + mutex_is_locked(&ctlr->io_mutex)) {
> + return -EPERM;
> + }
> + }
[Severity: High]
Does this unconditionally invoke sleepable operations during a panic write?
If the mutexes are currently unlocked when op->panic_write is true,
spi_mem_exec_op() proceeds and calls spi_mem_access_start():
drivers/spi/spi-mem.c:spi_mem_exec_op() {
...
if (ctlr->mem_ops && ctlr->mem_ops->exec_op && !spi_get_csgpiod(mem->spi, 0)) {
ret = spi_mem_access_start(mem);
...
}
spi_mem_access_start() explicitly calls mutex_lock(), which violates atomic
constraints and triggers might_sleep() warnings, and calls
pm_runtime_resume_and_get(), which may sleep.
Additionally, spi_mem_access_start() unconditionally calls spi_flush_queue().
If the SPI message queue is not empty (for example, from an async transfer
queued via spi_async() right before the panic), spi_flush_queue() dequeues
and executes pending regular SPI transfers using the driver standard
callback:
spi_mem_access_start() ->
spi_flush_queue() ->
__spi_pump_messages() ->
.transfer_one_message()
For cadence-xspi (the driver supporting panic writes here), the callback
invokes usleep_range() for polling delays.
Executing usleep_range() or other sleepable wait mechanisms in a panic
context where interrupts and preemption are disabled will result in a
system deadlock, preventing the crash dump or panic logging from
completing.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261005081141.33688-1-paul.cercueil@bootlin.com?part=1
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 5/5] spi: cadence-xspi: Add support for panic writes
2026-10-05 8:11 ` [PATCH 5/5] spi: cadence-xspi: Add support for panic writes Paul Cercueil
@ 2026-10-05 8:26 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-10-05 8:26 UTC (permalink / raw)
To: Miquel Raynal, Vignesh Raghavendra, Thomas Petazzoni,
Michael Walle, Takahiro Kuwano, Richard Weinberger,
Pratyush Yadav, Mark Brown, Paul Cercueil
Cc: linux-kernel, linux-spi, Guilherme G . Piccoli, Tony Luck,
Tanmay Jagdale, linux-mtd, Kees Cook
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] spi: cadence-xspi: missing PHY wakeup in panic write path
- [High] spi: cadence-xspi: deadlock in panic write via ktime_get()
- [High] spi: cadence-xspi: unmasked interrupts leak in polling mode
--
commit 61aacaab4ca9e95d9f29e2f99754ac08cf18616e
Author: Tanmay Jagdale <tanmay@marvell.com>
spi: cadence-xspi: Add support for panic writes
This commit adds support for panic writes to the Cadence XSPI driver by
checking the panic flag on memory operations. When a panic write is requested,
the driver operates without relying on interrupts or sleeping.
> diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
> index 09ed2afbd4af3..0b7162740940f 100644
> --- a/drivers/spi/spi-cadence-xspi.c
> +++ b/drivers/spi/spi-cadence-xspi.c
> @@ -350,10 +350,11 @@ struct cdns_xspi_dev {
> void (*set_interrupts_handler)(struct cdns_xspi_dev *cdns_xspi, bool enabled);
>
> bool xfer_in_progress;
> + bool panic_write;
> int current_xfer_qword;
> };
>
> -static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi)
> +static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi, bool sleep)
> {
> u32 ctrl_stat;
>
> @@ -362,7 +363,7 @@ static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi)
> ctrl_stat,
> ((ctrl_stat &
> CDNS_XSPI_CTRL_BUSY) == 0),
> - 100, 1000);
> + sleep ? 100 : 0, 1000);
[Severity: High]
Does this code cause a deadlock during a kernel panic?
readl_relaxed_poll_timeout() expands to read_poll_timeout(), which evaluates
ktime_get() to enforce the timeout, even when the delay is 0. If another CPU
was halted by an NMI or IPI while holding the write lock for the timekeeping
sequence lock (tk_core.seq) during the panic, calling ktime_get() will hang
the system.
Should this use readl_relaxed_poll_timeout_atomic() instead to avoid
timekeeping functions during a panic?
[ ... ]
> @@ -586,23 +587,23 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
>
> cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
>
> - if (cdns_xspi->irq >= 0) {
> + if (cdns_xspi->irq >= 0 && !op->panic_write) {
> wait_for_completion(&cdns_xspi->sdma_complete);
> if (cdns_xspi->sdma_error) {
> cdns_xspi->set_interrupts_handler(cdns_xspi, false);
> return -EIO;
> }
> - } else if (!cdns_xspi_is_sdma_ready(cdns_xspi, true)) {
> + } else if (!cdns_xspi_is_sdma_ready(cdns_xspi, !op->panic_write)) {
> return -EIO;
> }
>
> cdns_xspi->sdma_handler(cdns_xspi);
> }
>
> - if (cdns_xspi->irq >= 0) {
> + if (cdns_xspi->irq >= 0 && !op->panic_write) {
> wait_for_completion(&cdns_xspi->cmd_complete);
> cdns_xspi->set_interrupts_handler(cdns_xspi, false);
> - } else if (!cdns_xspi_is_stig_ready(cdns_xspi, true)) {
> + } else if (!cdns_xspi_is_stig_ready(cdns_xspi, !op->panic_write)) {
> return -EIO;
> }
[Severity: High]
Does this leak unmasked interrupts in polling mode?
Earlier in cdns_xspi_send_stig_command(), interrupts are unconditionally
enabled:
cdns_xspi->set_interrupts_handler(cdns_xspi, true);
If the controller is in polling mode (irq < 0), this code takes the else-if
branch. Whether cdns_xspi_is_stig_ready() succeeds or fails, the function
returns without calling cdns_xspi->set_interrupts_handler(cdns_xspi, false).
Will this cause an unhandled interrupt storm if the hardware IRQ line
is physically shared with other devices?
[ ... ]
> @@ -622,6 +623,8 @@ static int cdns_xspi_mem_op(struct cdns_xspi_dev *cdns_xspi,
> if (cdns_xspi->cur_cs != spi_get_chipselect(mem->spi, 0))
> cdns_xspi->cur_cs = spi_get_chipselect(mem->spi, 0);
>
> + cdns_xspi->panic_write = op->panic_write;
> +
> return cdns_xspi_send_stig_command(cdns_xspi, op,
> (dir != SPI_MEM_NO_DATA));
> }
[Severity: High]
Does this entry point fail to wake up the PHY from suspend during a panic
write?
If the system panics while the device is suspended, the controller will
attempt to execute STIG commands while the PHY is still suspended, which
could lead to bus faults or hardware lockups.
Should this path call cdns_xspi_configure_phy() to wake up the PHY before
sending commands during a panic write?
[ ... ]
> @@ -831,9 +834,11 @@ static bool cdns_xspi_is_dll_locked(struct cdns_xspi_dev *cdns_xspi)
> {
> u32 dll_lock;
>
> + /* Busy-wait in case we wake up the PHY from suspend to do a panic write */
> return !readl_relaxed_poll_timeout(cdns_xspi->iobase +
> CDNS_XSPI_INTR_STATUS_REG,
> - dll_lock, ((dll_lock & CDNS_XSPI_DLL_LOCK) == 1), 10, 10000);
> + dll_lock, ((dll_lock & CDNS_XSPI_DLL_LOCK) == 1),
> + cdns_xspi->panic_write ? 0 : 10, 10000);
> }
[Severity: High]
Is this code reachable during a panic write?
The comment indicates this handles waking up the PHY during a panic write.
However, this function is only called from cdns_xspi_configure_phy(), which
is entirely omitted from the cdns_xspi_mem_op() panic write execution path.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261005081141.33688-1-paul.cercueil@bootlin.com?part=5
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-10-05 8:26 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-05 8:11 [PATCH 0/5] spi: Add support for panic mem writes Paul Cercueil
2026-10-05 8:11 ` [PATCH 1/5] spi: spi-mem: " Paul Cercueil
2026-10-05 8:24 ` sashiko-bot
2026-10-05 8:11 ` [PATCH 2/5] mtd: spi-nor: Add support for panic writes Paul Cercueil
2026-10-05 8:23 ` sashiko-bot
2026-10-05 8:11 ` [PATCH 3/5] spi: cadence-xspi: Add irq-less support Paul Cercueil
2026-10-05 8:24 ` sashiko-bot
2026-10-05 8:11 ` [PATCH 4/5] spi: cadence-xspi: Don't use infinite timeout in register poll Paul Cercueil
2026-10-05 8:21 ` sashiko-bot
2026-10-05 8:11 ` [PATCH 5/5] spi: cadence-xspi: Add support for panic writes Paul Cercueil
2026-10-05 8:26 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®