From: Zongzhen Feng <1768315307@qq.com>
To: miquel.raynal@bootlin.com
Cc: linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org,
richard@nod.at, vigneshr@ti.com,
Zongzhen Feng <1768315307@qq.com>
Subject: [PATCH v3] mtd: spinand: cache the last read page to avoid redundant SPI operations
Date: Tue, 8 Sep 2026 18:19:58 +0800 [thread overview]
Message-ID: <tencent_E818586423F2F16D98DEF1EC2723EE4A6308@qq.com> (raw)
In-Reply-To: <tencent_4C47F17C98F6F164274CEE84638558D62308@qq.com>
When squashfs reads files through mtdblock, the mtdblock layer
splits I/O into 512-byte sectors. For a 4K-page SPI NAND, this
means reading the same page 8 times (4096 / 512), generating 7x
redundant SPI read-from-cache operations. Each such operation
involves a full SPI bus transaction, significantly slowing down
boot time and file access.
Cache the last successfully read page in spinand_device to avoid
these redundant operations. When the same {target, eraseblock,
page} is requested consecutively, the data is served directly
from the bounce buffer (databuf) via memcpy, skipping the SPI
transaction entirely.
The cache is invalidated on write, erase, ECC errors, and when
the bounce buffer is overwritten by non-cacheable reads (RAW,
continuous, or OOB-only).
Signed-off-by: Zongzhen Feng <1768315307@qq.com>
---
v3:
- Invalidate cache when databuf is overwritten by a non-cacheable
read (RAW, continuous, OOB-only) to prevent stale cache hits.
- Invalidate cache on uncorrectable ECC errors to prevent
subsequent reads from bypassing ECC checks.
v2:
- Fix compilation error: req->disable_ecc -> req->mode != MTD_OPS_RAW
- Remove spurious indentation change
- Use real name instead of pseudonym
---
drivers/mtd/nand/spi/core.c | 39 +++++++++++++++++++++++++++++++++++++
include/linux/mtd/spinand.h | 9 +++++++++
2 files changed, 48 insertions(+)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 8bf9301f25e7..f545dd948604 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -560,6 +560,15 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
req->ooblen);
}
+ if (req->datalen && !req->continuous && req->mode != MTD_OPS_RAW) {
+ spinand->cur_target_cache = req->pos.target;
+ spinand->cur_block_cache = req->pos.eraseblock;
+ spinand->cur_page_cache = req->pos.page;
+ spinand->cache_valid = true;
+ } else if (req->datalen) {
+ spinand->cache_valid = false;
+ }
+
return 0;
}
@@ -833,6 +842,20 @@ static int spinand_mtd_regular_page_read(struct mtd_info *mtd, loff_t from,
if (disable_ecc)
iter.req.mode = MTD_OPS_RAW;
+ if (spinand->cache_valid && !disable_ecc &&
+ !iter.req.ooblen &&
+ iter.req.pos.target == spinand->cur_target_cache &&
+ iter.req.pos.eraseblock == spinand->cur_block_cache &&
+ iter.req.pos.page == spinand->cur_page_cache) {
+ if (iter.req.datalen)
+ memcpy(iter.req.databuf.in,
+ spinand->databuf + iter.req.dataoffs,
+ iter.req.datalen);
+ ops->retlen += iter.req.datalen;
+ ops->oobretlen += iter.req.ooblen;
+ continue;
+ }
+
ret = spinand_select_target(spinand, iter.req.pos.target);
if (ret)
break;
@@ -842,6 +865,9 @@ static int spinand_mtd_regular_page_read(struct mtd_info *mtd, loff_t from,
if (ret < 0 && ret != -EBADMSG)
break;
+ if (ret == -EBADMSG)
+ spinand->cache_valid = false;
+
if (ret == -EBADMSG && spinand->set_read_retry) {
if (spinand->read_retries && (++retry_mode <= spinand->read_retries)) {
ret = spinand->set_read_retry(spinand, retry_mode);
@@ -1064,6 +1090,12 @@ static int spinand_mtd_write(struct mtd_info *mtd, loff_t to,
if (ret)
break;
+ if (spinand->cache_valid &&
+ iter.req.pos.target == spinand->cur_target_cache &&
+ iter.req.pos.eraseblock == spinand->cur_block_cache &&
+ iter.req.pos.page == spinand->cur_page_cache)
+ spinand->cache_valid = false;
+
ret = spinand_write_page(spinand, &iter.req);
if (ret)
break;
@@ -1188,6 +1220,11 @@ static int spinand_erase(struct nand_device *nand, const struct nand_pos *pos)
if (!ret && (status & STATUS_ERASE_FAILED))
ret = -EIO;
+ if (!ret && spinand->cache_valid &&
+ spinand->cur_target_cache == pos->target &&
+ spinand->cur_block_cache == pos->eraseblock)
+ spinand->cache_valid = false;
+
return ret;
}
@@ -2025,6 +2062,7 @@ static void spinand_cleanup(struct spinand_device *spinand)
nanddev_ecc_engine_cleanup(nand);
nanddev_cleanup(nand);
spinand_manufacturer_cleanup(spinand);
+ spinand->cache_valid = false;
kfree(spinand->databuf);
kfree(spinand->scratchbuf);
}
@@ -2044,6 +2082,7 @@ static int spinand_probe(struct spi_mem *mem)
spi_mem_set_drvdata(mem, spinand);
spinand_set_of_node(spinand, mem->spi->dev.of_node);
mutex_init(&spinand->lock);
+ spinand->cache_valid = false;
mtd = spinand_to_mtd(spinand);
mtd->dev.parent = &mem->spi->dev;
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 5f4c00ae72a7..887ef6d34a41 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -757,6 +757,10 @@ struct spinand_mem_ops {
* a command addressing a page or an eraseblock embedded in
* this die. Only required if your chip exposes several dies
* @cur_target: currently selected target/die
+ * @cur_target_cache: target of the cached page
+ * @cur_block_cache: eraseblock of the cached page
+ * @cur_page_cache: page number of the cached page
+ * @cache_valid: whether the cached page is valid
* @eccinfo: on-die ECC information
* @cfg_cache: config register cache. One entry per die
* @databuf: bounce buffer for data
@@ -798,6 +802,11 @@ struct spinand_device {
unsigned int target);
unsigned int cur_target;
+ unsigned int cur_target_cache;
+ unsigned int cur_block_cache;
+ unsigned int cur_page_cache;
+ bool cache_valid;
+
struct spinand_ecc_info eccinfo;
u8 *cfg_cache;
--
2.25.1
next prev parent reply other threads:[~2026-09-08 10:20 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-08 3:25 [PATCH v2] " fzz
2026-09-08 9:39 ` Miquel Raynal
2026-09-08 10:28 ` Zongzhen Feng
2026-09-08 10:19 ` Zongzhen Feng [this message]
2026-09-08 14:22 ` [PATCH v3] " Miquel Raynal
2026-09-09 6:29 ` Zongzhen Feng
2026-09-10 4:08 ` [PATCH v4] " Zongzhen Feng
2026-09-10 4:26 ` sashiko-bot
2026-09-10 12:42 ` Miquel Raynal
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=tencent_E818586423F2F16D98DEF1EC2723EE4A6308@qq.com \
--to=1768315307@qq.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mtd@lists.infradead.org \
--cc=miquel.raynal@bootlin.com \
--cc=richard@nod.at \
--cc=vigneshr@ti.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®