From: Sasha Levin <sashal@kernel.org>
To: patches@lists.linux.dev, stable@vger.kernel.org
Cc: Luke Wang <ziniu.wang_1@nxp.com>,
Ulf Hansson <ulf.hansson@linaro.org>,
Jens Axboe <axboe@kernel.dk>, Sasha Levin <sashal@kernel.org>,
linux-block@vger.kernel.org
Subject: [PATCH AUTOSEL 6.19-6.12] block: decouple secure erase size limit from discard size limit
Date: Wed, 11 Feb 2026 07:30:19 -0500 [thread overview]
Message-ID: <20260211123112.1330287-9-sashal@kernel.org> (raw)
In-Reply-To: <20260211123112.1330287-1-sashal@kernel.org>
From: Luke Wang <ziniu.wang_1@nxp.com>
[ Upstream commit ee81212f74a57c5d2b56cf504f40d528dac6faaf ]
Secure erase should use max_secure_erase_sectors instead of being limited
by max_discard_sectors. Separate the handling of REQ_OP_SECURE_ERASE from
REQ_OP_DISCARD to allow each operation to use its own size limit.
Signed-off-by: Luke Wang <ziniu.wang_1@nxp.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
This is an important caller. `blk_insert_cloned_request` is used by
device mapper (dm) to submit cloned requests. If
`blk_queue_get_max_sectors` returns the wrong value (e.g.,
`max_discard_sectors` instead of `max_secure_erase_sectors`), and the
cloned request's sector count exceeds the wrong limit but fits within
the correct one, the request will be **rejected with BLK_STS_IOERR** on
line 3291. This is a real I/O error path!
Here is my complete analysis:
---
## Detailed Analysis
### 1. Commit Message Analysis
The commit clearly describes a **bug fix**: secure erase operations were
incorrectly limited by `max_discard_sectors` instead of
`max_secure_erase_sectors`. This is a correctness issue, not a feature.
The commit was reviewed by Ulf Hansson (MMC maintainer) and merged by
Jens Axboe (block layer maintainer).
### 2. Code Change Analysis
The patch modifies two files with two distinct changes:
**Change A: `block/blk.h` - `blk_queue_get_max_sectors()`**
Before the fix:
```211:213:block/blk.h
if (unlikely(op == REQ_OP_DISCARD || op == REQ_OP_SECURE_ERASE))
return min(q->limits.max_discard_sectors,
UINT_MAX >> SECTOR_SHIFT);
```
Both `REQ_OP_DISCARD` and `REQ_OP_SECURE_ERASE` used
`max_discard_sectors`. The fix separates them so secure erase uses
`max_secure_erase_sectors`.
**Change B: `block/blk-merge.c` - `bio_split_discard()`**
The original `bio_split_discard()` always split using
`lim->max_discard_sectors`, even for `REQ_OP_SECURE_ERASE` (which
reaches this function via `__bio_split_to_limits` at line 407-409 of
`blk.h`). The fix refactors the function into a wrapper that selects the
correct limit and a helper `__bio_split_discard()` that does the actual
splitting.
### 3. Bug Mechanism and Impact
**The bug**: The kernel `queue_limits` structure has separate fields for
`max_discard_sectors` and `max_secure_erase_sectors`, but the block
layer core code in two critical paths ignored the secure erase field and
always used the discard field.
**Concrete impact scenarios**:
1. **virtio_blk**: This driver reads separate limits from the virtio
config for discard (`max_discard_sectors`) and secure erase
(`max_secure_erase_sectors`). The driver even documents the
workaround: *"The discard and secure erase limits are combined since
the Linux block layer uses the same limit for both commands."*
(virtio_blk.c lines 1336-1341). This means the driver had to
artificially reduce its limits to compensate for the block layer bug.
2. **xen-blkfront**: Sets `max_secure_erase_sectors = UINT_MAX` while
`max_discard_sectors = get_capacity(gd)` - different values.
3. **dm (device mapper)**: The `blk_insert_cloned_request()` function
calls `blk_queue_get_max_sectors()` and rejects requests that exceed
the limit with `BLK_STS_IOERR`. If `max_secure_erase_sectors` >
`max_discard_sectors` on the underlying device, valid secure erase
requests could be rejected with I/O errors. Conversely, if
`max_secure_erase_sectors` < `max_discard_sectors`, oversized
requests could be sent to hardware.
4. **The most dangerous case**: When `max_secure_erase_sectors <
max_discard_sectors`, the bio splitting code won't split the secure
erase bio when it should, sending a request larger than the device
can handle. This can cause **I/O errors, device failures, or data
integrity issues** with secure erase operations.
### 4. Scope and Risk Assessment
- **Lines changed**: ~25 lines of actual logic change across 2 files
- **Files touched**: `block/blk-merge.c` and `block/blk.h` (core block
layer, but well-contained)
- **Risk of regression**: **Low**. For any driver that sets
`max_secure_erase_sectors == max_discard_sectors` (like MMC), the
behavior is completely unchanged. For drivers with different values,
the behavior is corrected.
- **The change is obviously correct**: Secure erase operations should
use secure erase limits, not discard limits.
### 5. Affected Subsystems and Users
- MMC/eMMC (mobile, embedded) - same values, no visible change
- virtio_blk (VMs, cloud) - corrects a workaround-needing limitation
- xen-blkfront (Xen VMs) - corrects a real mismatch
- dm (device-mapper stacking) - fixes potential I/O errors
### 6. Backport Complexity
- For kernels 6.12+: The patch should apply nearly cleanly;
`bio_submit_split()` and the current code structure exist.
- For kernels 6.1-6.11: The `blk.h` change applies cleanly (same
pattern). The `blk-merge.c` change needs adaptation because the
splitting mechanism is different (returns split bio instead of using
`bio_submit_split`), but the conceptual fix is the same.
- For kernel 5.15: `max_secure_erase_sectors` field exists but
`blkdev_issue_secure_erase` was introduced in 6.1; older kernels have
different secure erase code paths.
### 7. Stability Indicators
- Reviewed by Ulf Hansson (MMC subsystem co-maintainer)
- Merged by Jens Axboe (block layer maintainer)
- Simple, obviously correct fix
### 8. Conclusion
This commit fixes a real bug where the block layer core was using the
wrong limit for secure erase operations, potentially causing:
- I/O errors when valid requests are rejected
- Oversized requests being sent to hardware
- Unnecessary performance limitations
The fix is small, contained, obviously correct, and was reviewed by
multiple maintainers. It affects core block layer code that is shared
across all block devices supporting secure erase. The risk of regression
is minimal since the change only affects the code path when
`max_secure_erase_sectors != max_discard_sectors`.
**YES**
block/blk-merge.c | 21 +++++++++++++++++----
block/blk.h | 6 +++++-
2 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/block/blk-merge.c b/block/blk-merge.c
index d3115d7469df0..bf8faadb0bd46 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -158,8 +158,9 @@ static struct bio *bio_submit_split(struct bio *bio, int split_sectors)
return bio;
}
-struct bio *bio_split_discard(struct bio *bio, const struct queue_limits *lim,
- unsigned *nsegs)
+static struct bio *__bio_split_discard(struct bio *bio,
+ const struct queue_limits *lim, unsigned *nsegs,
+ unsigned int max_sectors)
{
unsigned int max_discard_sectors, granularity;
sector_t tmp;
@@ -169,8 +170,7 @@ struct bio *bio_split_discard(struct bio *bio, const struct queue_limits *lim,
granularity = max(lim->discard_granularity >> 9, 1U);
- max_discard_sectors =
- min(lim->max_discard_sectors, bio_allowed_max_sectors(lim));
+ max_discard_sectors = min(max_sectors, bio_allowed_max_sectors(lim));
max_discard_sectors -= max_discard_sectors % granularity;
if (unlikely(!max_discard_sectors))
return bio;
@@ -194,6 +194,19 @@ struct bio *bio_split_discard(struct bio *bio, const struct queue_limits *lim,
return bio_submit_split(bio, split_sectors);
}
+struct bio *bio_split_discard(struct bio *bio, const struct queue_limits *lim,
+ unsigned *nsegs)
+{
+ unsigned int max_sectors;
+
+ if (bio_op(bio) == REQ_OP_SECURE_ERASE)
+ max_sectors = lim->max_secure_erase_sectors;
+ else
+ max_sectors = lim->max_discard_sectors;
+
+ return __bio_split_discard(bio, lim, nsegs, max_sectors);
+}
+
static inline unsigned int blk_boundary_sectors(const struct queue_limits *lim,
bool is_atomic)
{
diff --git a/block/blk.h b/block/blk.h
index e4c433f62dfc7..4cd5a91346d8a 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -208,10 +208,14 @@ static inline unsigned int blk_queue_get_max_sectors(struct request *rq)
struct request_queue *q = rq->q;
enum req_op op = req_op(rq);
- if (unlikely(op == REQ_OP_DISCARD || op == REQ_OP_SECURE_ERASE))
+ if (unlikely(op == REQ_OP_DISCARD))
return min(q->limits.max_discard_sectors,
UINT_MAX >> SECTOR_SHIFT);
+ if (unlikely(op == REQ_OP_SECURE_ERASE))
+ return min(q->limits.max_secure_erase_sectors,
+ UINT_MAX >> SECTOR_SHIFT);
+
if (unlikely(op == REQ_OP_WRITE_ZEROES))
return q->limits.max_write_zeroes_sectors;
--
2.51.0
next prev parent reply other threads:[~2026-02-11 12:31 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-11 12:30 [PATCH AUTOSEL 6.19-5.10] s390/perf: Disable register readout on sampling events Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.10] arm64: Add support for TSV110 Spectre-BHB mitigation Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.10] xenbus: Use .freeze/.thaw to handle xenbus devices Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.10] s390/purgatory: Add -Wno-default-const-init-unsafe to KBUILD_CFLAGS Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.18] s390/boot: " Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.1] perf/arm-cmn: Support CMN-600AE Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.18] ntfs: ->d_compare() must not block Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.12] ACPI: x86: s2idle: Invoke Microsoft _DSM Function 9 (Turn On Display) Sasha Levin
2026-02-11 12:30 ` Sasha Levin [this message]
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.10] sparc: don't reference obsolete termio struct for TC* constants Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.10] EFI/CPER: don't go past the ARM processor CPER record buffer Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19] ACPI: scan: Use async schedule function in acpi_scan_clear_dep_fn() Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.6] cpufreq: dt-platdev: Block the driver from probing on more QC platforms Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.10] EFI/CPER: don't dump the entire memory region Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.12] ACPI: battery: fix incorrect charging status when current is zero Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.18] rust: cpufreq: always inline functions using build_assert with arguments Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.18] blk-mq-sched: unify elevators checking for async requests Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.10] x86/xen/pvh: Enable PAE mode for 32-bit guest only when CONFIG_X86_PAE is set Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.12] APEI/GHES: ARM processor Error: don't go past allocated memory Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.18] md raid: fix hang when stopping arrays with metadata through dm-raid Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.10] tools/power cpupower: Reset errno before strtoull() Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.10] sparc: Synchronize user stack on fork and clone Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.10] blk-mq-debugfs: add missing debugfs_mutex in blk_mq_debugfs_register_hctxs() Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.10] rnbd-srv: Zero the rsp buffer before using it Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.12] alpha: fix user-space corruption during memory compaction Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.10] ACPICA: Abort AML bytecode execution when executing AML_FATAL_OP Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19] arm64: mte: Set TCMA1 whenever MTE is present in the kernel Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.18] tools/cpupower: Fix inverted APERF capability check Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.15] ACPI: processor: Fix NULL-pointer dereference in acpi_processor_errata_piix4() Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.12] ACPI: resource: Add JWIPC JVC9100 to irq1_level_low_skip_override[] Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.6] perf/cxlpmu: Replace IRQF_ONESHOT with IRQF_NO_THREAD Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.6] md-cluster: fix NULL pointer dereference in process_metadata_update Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-5.10] APEI/GHES: ensure that won't go past CPER allocated record Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.12] powercap: intel_rapl: Add PL4 support for Ice Lake Sasha Levin
2026-02-11 12:30 ` [PATCH AUTOSEL 6.19-6.18] io_uring/timeout: annotate data race in io_flush_timeouts() Sasha Levin
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=20260211123112.1330287-9-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=axboe@kernel.dk \
--cc=linux-block@vger.kernel.org \
--cc=patches@lists.linux.dev \
--cc=stable@vger.kernel.org \
--cc=ulf.hansson@linaro.org \
--cc=ziniu.wang_1@nxp.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