* [PATCH v2 0/2] spi: spi-qpic-snand: fix the stale ECC context pointer
@ 2026-09-11 18:44 Johan Alvarado
2026-09-11 18:44 ` [PATCH v2 1/2] spi: spi-qpic-snand: publish the ECC context to snandc->qspi Johan Alvarado
2026-09-11 18:44 ` [PATCH v2 2/2] spi: spi-qpic-snand: drop the redundant ECC context handling Johan Alvarado
0 siblings, 2 replies; 5+ messages in thread
From: Johan Alvarado @ 2026-09-11 18:44 UTC (permalink / raw)
To: broonie, md.alam
Cc: konradybcio, pengpeng, miquel.raynal, j4g8y7, quic_varada,
quic_srichara, linux-spi, linux-mtd, linux-arm-msm, linux-kernel,
Johan Alvarado
qcom_spi_ecc_init_ctx_pipelined() installs the ooblayout but never
publishes the ECC context it allocates, so qcom_spi_ooblayout_ecc() and
qcom_spi_ooblayout_free() run against a pointer that describes something
else - a zeroed struct on a first probe, the previous attempt's freed
context on a retry. On IPQ5018 the qcom,smem-part parser makes that retry
routine, and like half the boots on a Mercusys MR80X failed to mount the
rootfs.
Patch 1 is the fix and is unchanged from v1. Patch 2 removes what becomes
redundant once the context is published. It is a cleanup with no
functional change, so it carries no Fixes: tag and is not marked for
stable.
Patch 1 was applied to mtd/fixes as 93bc7c4d2f41 on 2026-09-04 and
dropped the same day, so nothing from this series is queued.
v1: https://lore.kernel.org/all/20260825013848.1056946-1-contact@c127.dev/
Changes in v2:
- Add Gabor Juhos' Tested-by to patch 1.
- Add patch 2, dropping the now redundant assignment in
qcom_spi_ecc_prepare_io_req_pipelined() and the unused zeroed
struct qpic_ecc allocated in qcom_spi_probe(). Suggested by Gabor
Juhos, kept separate so the stable backport stays minimal.
- Rebase onto spi/for-next, which carries commit 44d39535cd4f ("spi:
spi-qpic-snand: remove interim 'dev_data' variable from
qcom_spi_probe()") touching the same probe lines as patch 2.
Both patches build with W=1 and sparse without warnings, and each patch
builds on its own.
Johan Alvarado (2):
spi: spi-qpic-snand: publish the ECC context to snandc->qspi
spi: spi-qpic-snand: drop the redundant ECC context handling
drivers/spi/spi-qpic-snand.c | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
base-commit: 98100d83adc8e17606605d131e685b1fbedfe092
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v2 1/2] spi: spi-qpic-snand: publish the ECC context to snandc->qspi 2026-09-11 18:44 [PATCH v2 0/2] spi: spi-qpic-snand: fix the stale ECC context pointer Johan Alvarado @ 2026-09-11 18:44 ` Johan Alvarado 2026-09-11 18:44 ` [PATCH v2 2/2] spi: spi-qpic-snand: drop the redundant ECC context handling Johan Alvarado 1 sibling, 0 replies; 5+ messages in thread From: Johan Alvarado @ 2026-09-11 18:44 UTC (permalink / raw) To: broonie, md.alam Cc: konradybcio, pengpeng, miquel.raynal, j4g8y7, quic_varada, quic_srichara, linux-spi, linux-mtd, linux-arm-msm, linux-kernel, Johan Alvarado qcom_spi_ooblayout_ecc() and qcom_spi_ooblayout_free() read the ECC configuration through snandc->qspi->ecc. qcom_spi_probe() points it at a zeroed scratch struct and only qcom_spi_ecc_prepare_io_req_pipelined(), which runs on page I/O, ever updates it. qcom_spi_ecc_init_ctx_pipelined() installs the ooblayout but does not publish the context it just allocated, and qcom_spi_ecc_cleanup_ctx_pipelined() frees that context without clearing the pointer. spinand_init() calls mtd_ooblayout_count_freebytes() right after the ECC context is created and before any page I/O, so the ooblayout always runs against a pointer that does not describe the current context: - On a first probe it reads the zeroed struct from qcom_spi_probe(), so steps, bytes and bbm_size are 0. The count then returns 0 rather than an error, so the probe continues with mtd->oobavail set to 0. - On a probe retry it reads the ecc_cfg the previous attempt freed. A retry is easy to hit. On IPQ5018 with the qcom,smem-part parser the partition parse returns -EPROBE_DEFER until SMEM has probed, so the first spi-nand probe defers. It defers inside mtd_device_parse_register(), after mtd_otp_nvmem_add() has already read the factory OTP - that read goes through prepare_io_req and leaves snandc->qspi->ecc pointing at the context that spinand_cleanup() then frees. The second probe allocates a new context, never publishes it, and computes the OOB layout from the freed one. Once the slab has been reused, qecc->steps holds garbage and oobregion->length = qecc->steps * 4; goes negative. qcom_spi_ooblayout_free() only reports -ERANGE for section 1 and later, so mtd_ooblayout_count_bytes() sums the regions and returns that negative length as the byte count. The -512 below is steps * 4 with steps == -128. It is a byte count that happens to collide with -ERESTARTSYS, not an error the driver returned. spinand_init() takes it as an error, and because it is not -EPROBE_DEFER the driver core never retries and the NAND never appears: spi-nand spi0.0: ESMT SPI NAND was found. spi-nand spi0.0: probe with driver spi-nand failed with error -512 UBI error: cannot open mtd rootfs, error -2 Waiting for root device /dev/ubiblock0_1... On a Mercusys MR80X (IPQ5018, ESMT F50D1G41LB) about half of the boots failed to mount the rootfs, the outcome depending on whether the freed memory had been overwritten yet. Publish the context when it is created and clear the pointer when it is destroyed. Clearing leaves snandc->qspi->ecc NULL after cleanup, which is safe: the mtd is unregistered before cleanup_ctx runs, so no ooblayout callback can follow. Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") Cc: stable@vger.kernel.org Tested-by: Gabor Juhos <j4g8y7@gmail.com> Signed-off-by: Johan Alvarado <contact@c127.dev> --- Unchanged from v1. Both hunks are untouched by 44d39535cd4f, so this applies to v7.3-rc2 and to spi/for-next alike. drivers/spi/spi-qpic-snand.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 9eafaea74f6b..20045d4a46fa 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -411,6 +411,8 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) dev_dbg(snandc->dev, "ECC strength: %u bits per %u bytes\n", ecc_cfg->strength, ecc_cfg->step_size); + snandc->qspi->ecc = ecc_cfg; + return 0; err_free_ecc_cfg: @@ -427,6 +429,7 @@ static void qcom_spi_ecc_cleanup_ctx_pipelined(struct nand_device *nand) kfree(snandc->qspi->oob_buf); snandc->qspi->oob_buf = NULL; + snandc->qspi->ecc = NULL; kfree(ecc_cfg); } -- 2.55.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] spi: spi-qpic-snand: drop the redundant ECC context handling 2026-09-11 18:44 [PATCH v2 0/2] spi: spi-qpic-snand: fix the stale ECC context pointer Johan Alvarado 2026-09-11 18:44 ` [PATCH v2 1/2] spi: spi-qpic-snand: publish the ECC context to snandc->qspi Johan Alvarado @ 2026-09-11 18:44 ` Johan Alvarado 2026-09-11 19:10 ` sashiko-bot 1 sibling, 1 reply; 5+ messages in thread From: Johan Alvarado @ 2026-09-11 18:44 UTC (permalink / raw) To: broonie, md.alam Cc: konradybcio, pengpeng, miquel.raynal, j4g8y7, quic_varada, quic_srichara, linux-spi, linux-mtd, linux-arm-msm, linux-kernel, Johan Alvarado qcom_spi_ecc_init_ctx_pipelined() now publishes the ECC context to snandc->qspi->ecc, so the assignment in qcom_spi_ecc_prepare_io_req_pipelined() repeats what the pointer already holds, and the zeroed struct qpic_ecc that qcom_spi_probe() allocates is never read. The pointer is non-NULL only between context creation and destruction, and every reader runs inside that window. The ooblayout callbacks are installed by init_ctx. The page read, write and program helpers run only when prepare_io_req has set page_rw or oob_rw. qcom_spi_block_erase() runs only while the mtd is registered, which happens after init_ctx and ends before cleanup_ctx. The controller drives a single chip select, so the per-controller pointer and the per-chip context cannot disagree. Remove both. No functional change. Suggested-by: Gabor Juhos <j4g8y7@gmail.com> Signed-off-by: Johan Alvarado <contact@c127.dev> --- Tested on a Mercusys MR80X (IPQ5018, ESMT F50D1G41LB) with both patches backported to 6.18.44. Five boots, rootfs mounted every time, mtd reported 0 ECC failures and 0 corrected bits, and a 4 MiB file on the UBIFS overlay kept its sha256 across the reboots. Overlay churn raised the UBI maximum erase counter from 13 to 15, so qcom_spi_block_erase() ran repeatedly. Build-tested on this base with W=1 and sparse, no warnings, patch 1 alone and both patches applied. drivers/spi/spi-qpic-snand.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 20045d4a46fa..760ee1c29402 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -437,9 +437,7 @@ static int qcom_spi_ecc_prepare_io_req_pipelined(struct nand_device *nand, struct nand_page_io_req *req) { struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); - struct qpic_ecc *ecc_cfg = nand_to_ecc_ctx(nand); - snandc->qspi->ecc = ecc_cfg; snandc->qspi->raw_rw = false; snandc->qspi->oob_rw = false; snandc->qspi->page_rw = false; @@ -1586,14 +1584,9 @@ static int qcom_spi_probe(struct platform_device *pdev) struct spi_controller *ctlr; struct qcom_nand_controller *snandc; struct qpic_spi_nand *qspi; - struct qpic_ecc *ecc; struct resource *res; int ret; - ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL); - if (!ecc) - return -ENOMEM; - qspi = devm_kzalloc(dev, sizeof(*qspi), GFP_KERNEL); if (!qspi) return -ENOMEM; @@ -1613,7 +1606,6 @@ static int qcom_spi_probe(struct platform_device *pdev) snandc->dev = dev; snandc->qspi = qspi; snandc->qspi->ctlr = ctlr; - snandc->qspi->ecc = ecc; snandc->props = of_device_get_match_data(dev); if (!snandc->props) { -- 2.55.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/2] spi: spi-qpic-snand: drop the redundant ECC context handling 2026-09-11 18:44 ` [PATCH v2 2/2] spi: spi-qpic-snand: drop the redundant ECC context handling Johan Alvarado @ 2026-09-11 19:10 ` sashiko-bot 2026-09-12 0:05 ` Johan Alvarado 0 siblings, 1 reply; 5+ messages in thread From: sashiko-bot @ 2026-09-11 19:10 UTC (permalink / raw) To: md.alam, broonie, Johan Alvarado Cc: j4g8y7, quic_varada, linux-arm-msm, linux-spi, miquel.raynal, pengpeng, linux-kernel, quic_srichara, linux-mtd, konradybcio Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] Deterministic kernel panic in qcom_spi_block_erase() when the host ECC engine is bypassed (e.g., via non-NAND SPI memory drivers like spi-nor, or ON-DIE/software ECC). -- commit a97a071d79125bb5db4d55c7d03a5f945fa92aef Author: Johan Alvarado <contact@c127.dev> spi: spi-qpic-snand: drop the redundant ECC context handling qcom_spi_ecc_init_ctx_pipelined() now publishes the ECC context to snandc->qspi->ecc, so the assignment in qcom_spi_ecc_prepare_io_req_pipelined() repeats what the pointer already holds, and the zeroed struct qpic_ecc that qcom_spi_probe() allocates is never read. > The pointer is non-NULL only between context creation and destruction, > and every reader runs inside that window. The ooblayout callbacks are > installed by init_ctx. The page read, write and program helpers run only > when prepare_io_req has set page_rw or oob_rw. qcom_spi_block_erase() > runs only while the mtd is registered, which happens after init_ctx and > ends before cleanup_ctx. Is this assumption accurate for all configurations? If the device tree specifies ON-DIE or software ECC (nand,ecc-engine = "on-die"), or if a non-NAND SPI memory device (like spi-nor) is attached to this controller, won't the host's init_ctx be bypassed? > diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c > index 20045d4a46fad..760ee1c29402f 100644 > --- a/drivers/spi/spi-qpic-snand.c > +++ b/drivers/spi/spi-qpic-snand.c [ ... ] > @@ -1586,14 +1584,9 @@ static int qcom_spi_probe(struct platform_device *pdev) > struct spi_controller *ctlr; > struct qcom_nand_controller *snandc; > struct qpic_spi_nand *qspi; > - struct qpic_ecc *ecc; > struct resource *res; > int ret; > > - ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL); > - if (!ecc) > - return -ENOMEM; > - [Severity: High] If init_ctx is bypassed as described above, will removing this allocation lead to a NULL pointer dereference in qcom_spi_block_erase()? If snandc->qspi->ecc remains NULL, a standard block erase operation delegated through qcom_spi_exec_op() to qcom_spi_block_erase() would appear to dereference it: drivers/spi/spi-qpic-snand.c:qcom_spi_block_erase() { struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; ... snandc->regs->cfg0 = cpu_to_le32((ecc_cfg->cfg0_raw & ~CW_PER_PAGE_MASK) | FIELD_PREP(CW_PER_PAGE_MASK, 0)); } > qspi = devm_kzalloc(dev, sizeof(*qspi), GFP_KERNEL); > if (!qspi) > return -ENOMEM; -- Sashiko AI review · https://sashiko.dev/#/patchset/20260911184416.109790-1-contact@c127.dev?part=2 ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/2] spi: spi-qpic-snand: drop the redundant ECC context handling 2026-09-11 19:10 ` sashiko-bot @ 2026-09-12 0:05 ` Johan Alvarado 0 siblings, 0 replies; 5+ messages in thread From: Johan Alvarado @ 2026-09-12 0:05 UTC (permalink / raw) To: broonie, md.alam Cc: Johan Alvarado, sashiko-reviews, j4g8y7, konradybcio, pengpeng, miquel.raynal, quic_varada, quic_srichara, linux-spi, linux-mtd, linux-arm-msm, linux-kernel On Fri, Sep 11, 2026 at 07:10:10PM +0000, sashiko-bot@kernel.org wrote: > Is this assumption accurate for all configurations? > If the device tree specifies ON-DIE or software ECC (nand,ecc-engine = > "on-die"), or if a non-NAND SPI memory device (like spi-nor) is attached to > this controller, won't the host's init_ctx be bypassed? The engine is selected by the nand-ecc-engine phandle, or by nand-no-ecc-engine and nand-use-soft-ecc-engine; the core does not read a nand,ecc-engine = "on-die" property. A chip node that phandles itself, or that omits the property and takes the SPI-NAND default, does land on the on-die engine, and qcom_spi_ecc_init_ctx_pipelined() is then never called. That configuration has never worked. The page helpers are gated on qspi->page_rw and qspi->oob_rw, which only qcom_spi_ecc_prepare_io_req_pipelined() sets, so qcom_spi_read_page() returns 0 with the buffer untouched while spi_mem_no_dirmap_read() reports the full length as read. Removing the phandle on an IPQ5018 board confirms it: the chip probes, then UBI reports "no valid UBI magic found inside mtd15" and "failed to attach mtd13, error -22". A block erase there took cfg0_raw and cfg1_raw from the zeroed scratch struct, which is a wrong erase configuration rather than a working one. So this patch changes no configuration that works today, and the three in-tree boards using this controller all set nand-ecc-engine = <&qpic_nand>. For spi-nor, the binding documents a spi-nand child only, and qcom_spi_cmd_mapping() returns -EOPNOTSUPP for opcodes outside the SPI-NAND set, RDSR 0x05 and SFDP 0x5A among them, so such a probe fails before an mtd is registered. A mismatched device tree should still not panic, and the silent read is the worse half of it. Refusing page access and block erase in qcom_spi_exec_op() while the context is NULL covers both. I have that patch ready and will send it on top once this series is applied, so it does not hold anything here up. It cannot go in qcom_spi_supports_op(): spinand_select_op_variant() uses that callback to choose the cache op templates during detection, long before any ECC context exists, so rejecting page operations there leaves no variant supported and the chip fails to probe with "unknown raw ID". Best regards, Johan ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-12 0:05 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-11 18:44 [PATCH v2 0/2] spi: spi-qpic-snand: fix the stale ECC context pointer Johan Alvarado 2026-09-11 18:44 ` [PATCH v2 1/2] spi: spi-qpic-snand: publish the ECC context to snandc->qspi Johan Alvarado 2026-09-11 18:44 ` [PATCH v2 2/2] spi: spi-qpic-snand: drop the redundant ECC context handling Johan Alvarado 2026-09-11 19:10 ` sashiko-bot 2026-09-12 0:05 ` Johan Alvarado
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®