From: sashiko-bot@kernel.org
To: "James Hilliard" <james.hilliard1@gmail.com>,
"Miquel Raynal" <miquel.raynal@bootlin.com>,
"Richard Weinberger" <richard@nod.at>,
"Vignesh Raghavendra" <vigneshr@ti.com>,
"Michał Kępień" <kernel@kempniu.pl>
Cc: linux-mtd@lists.infradead.org
Subject: Re: [PATCH 3/3] mtdchar: keep OOB chunks aligned with their data pages
Date: Sat, 07 Nov 2026 05:40:49 +0000 [thread overview]
Message-ID: <sashiko-outbox-202629@kernel.org> (raw)
In-Reply-To: <20261106-submit-mtdchar-page-chunking-v1-v1-3-2973a03531bc@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] mtdchar: incomplete OOB transfer when combined request
exhausts req.len
--
commit 148431ec8c48f9426f60a2b52dc5e2f9aab56347
Author: James Hilliard <james.hilliard1@gmail.com>
mtdchar: keep OOB chunks aligned with their data pages
This commit rounds non-final OOB chunks down to whole pages, caps the data
chunk to the corresponding page count when OOB is the limiting buffer, and
advances req.start by the number of OOB pages transferred on OOB-only
operations.
> diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
> index 189e462a9df0f..85e98905f7320 100644
> --- a/drivers/mtd/mtdchar.c
> +++ b/drivers/mtd/mtdchar.c
> @@ -589,6 +589,30 @@ static void adjust_oob_length(struct mtd_info *mtd, uint64_t start,
> ops->ooblen = min_t(u64, ops->ooblen, pages * oob_per_page);
> }
>
> +static int mtdchar_adjust_io_lengths(struct mtd_info *mtd, uint64_t start,
> + struct mtd_oob_ops *ops, uint64_t remaining_oob)
> +{
> + u32 oob_per_page = mtd_oobavail(mtd, ops);
> + u64 len;
> +
> + if (!ops->ooblen)
> + return 0;
> + if (!oob_per_page)
> + return -EINVAL;
> +
> + if (ops->ooblen < remaining_oob) {
> + /* Keep a page's OOB together when another chunk follows. */
> + ops->ooblen = rounddown(ops->ooblen, oob_per_page);
> + if (ops->len) {
> + len = (u64)(ops->ooblen / oob_per_page) * mtd->writesize;
> + len -= mtd_mod_by_ws(start, mtd);
> + ops->len = min_t(u64, ops->len, len);
> + }
> + }
> +
> + return 0;
> +}
> +
> static noinline_for_stack int
> mtdchar_write_ioctl(struct mtd_info *mtd, struct mtd_write_req __user *argp)
> {
> @@ -628,7 +652,8 @@ mtdchar_write_ioctl(struct mtd_info *mtd, struct mtd_write_req __user *argp)
> return -ENOMEM;
> }
>
> - oobbuf_len = min_t(size_t, req.ooblen, mtd->erasesize);
> + oobbuf_len = min_t(size_t, req.ooblen,
> + max(mtd->erasesize, mtd->oobsize));
> if (oobbuf_len > 0) {
> oobbuf = kvmalloc(oobbuf_len, GFP_KERNEL);
> if (!oobbuf) {
> @@ -648,13 +673,17 @@ mtdchar_write_ioctl(struct mtd_info *mtd, struct mtd_write_req __user *argp)
>
> /*
> * Shorten non-page-aligned, eraseblock-sized writes so that
> - * the write ends on an eraseblock boundary. This is necessary
> + * the write ends on a page boundary. This is necessary
> * for adjust_oob_length() to properly handle non-page-aligned
> * writes.
> */
> if (ops.len == mtd->erasesize)
> ops.len -= mtd_mod_by_ws(req.start + ops.len, mtd);
>
> + ret = mtdchar_adjust_io_lengths(mtd, req.start, &ops, req.ooblen);
> + if (ret)
> + break;
> +
> /*
> * For writes which are not OOB-only, adjust the amount of OOB
> * data written according to the number of data pages written.
> @@ -674,7 +703,11 @@ mtdchar_write_ioctl(struct mtd_info *mtd, struct mtd_write_req __user *argp)
> if (ret)
> break;
>
> - req.start += ops.retlen;
> + if (usr_data)
> + req.start += ops.retlen;
> + else
> + req.start += (u64)(ops.oobretlen / mtd_oobavail(mtd, &ops)) *
> + mtd->writesize;
> req.len -= ops.retlen;
> usr_data += ops.retlen;
>
> @@ -739,7 +772,8 @@ mtdchar_read_ioctl(struct mtd_info *mtd, struct mtd_read_req __user *argp)
> }
> }
>
> - oobbuf_len = min_t(size_t, req.ooblen, mtd->erasesize);
> + oobbuf_len = min_t(size_t, req.ooblen,
> + max(mtd->erasesize, mtd->oobsize));
> if (oobbuf_len > 0) {
> oobbuf = kvmalloc(oobbuf_len, GFP_KERNEL);
> if (!oobbuf) {
> @@ -761,7 +795,7 @@ mtdchar_read_ioctl(struct mtd_info *mtd, struct mtd_read_req __user *argp)
>
> /*
> * Shorten non-page-aligned, eraseblock-sized reads so that the
> - * read ends on an eraseblock boundary. This is necessary in
> + * read ends on a page boundary. This is necessary in
> * order to prevent OOB data for some pages from being
> * duplicated in the output of non-page-aligned reads requiring
> * multiple mtd_read_oob() calls to be completed.
> @@ -769,6 +803,9 @@ mtdchar_read_ioctl(struct mtd_info *mtd, struct mtd_read_req __user *argp)
> if (ops.len == mtd->erasesize)
> ops.len -= mtd_mod_by_ws(req.start + ops.len, mtd);
>
> + ret = mtdchar_adjust_io_lengths(mtd, req.start, &ops, req.ooblen);
> + if (ret)
> + break;
> if (ops.len < req.len)
> adjust_oob_length(mtd, req.start, &ops);
>
> @@ -789,7 +826,11 @@ mtdchar_read_ioctl(struct mtd_info *mtd, struct mtd_read_req __user *argp)
> break;
> }
>
> - req.start += ops.retlen;
> + if (usr_data)
> + req.start += ops.retlen;
> + else
> + req.start += (u64)(ops.oobretlen / mtd_oobavail(mtd, &ops)) *
> + mtd->writesize;
> req.len -= ops.retlen;
> usr_data += ops.retlen;
[Severity: Medium]
Can a combined data and OOB request still exhaust req.len and exit the I/O
loop before all of req.ooblen has been transferred?
When req.ooblen exceeds oobbuf_len (so ops->ooblen < remaining_oob) and the
remaining data req.len fits within the pages covered by ops->ooblen,
min_t(u64, ops->len, len) in mtdchar_adjust_io_lengths() leaves ops.len equal
to req.len. In mtdchar_read_ioctl(), adjust_oob_length() is then skipped
because ops.len == req.len, so mtd_read_oob() consumes all of req.len along
with ops.ooblen bytes of OOB across pages beyond ops.retlen.
Because usr_data is non-NULL, req.start only advances by ops.retlen instead
of the OOB pages visited, req.len drops to 0 while req.ooblen is still
non-zero, and the loop condition
while (req.len > 0 || (!usr_data && req.ooblen > 0))
terminates the ioctl without transferring the remaining OOB chunks.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261106-submit-mtdchar-page-chunking-v1-v1-0-2973a03531bc@gmail.com?part=3
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
prev parent reply other threads:[~2026-11-07 5:40 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-07 5:20 [PATCH 0/3] mtdchar: keep data and OOB aligned across ioctl chunks James Hilliard
2026-11-07 5:20 ` [PATCH 1/3] mtdchar: calculate OOB capacity from the starting column James Hilliard
2026-11-07 5:20 ` [PATCH 2/3] mtdchar: bound OOB reads to the data in intermediate chunks James Hilliard
2026-11-07 5:20 ` [PATCH 3/3] mtdchar: keep OOB chunks aligned with their data pages James Hilliard
2026-11-07 5:40 ` sashiko-bot [this message]
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=sashiko-outbox-202629@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=james.hilliard1@gmail.com \
--cc=kernel@kempniu.pl \
--cc=linux-mtd@lists.infradead.org \
--cc=miquel.raynal@bootlin.com \
--cc=richard@nod.at \
--cc=sashiko-reviews@lists.linux.dev \
--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