* [PATCH v7 00/11] scsi: scsi_debug: fix zoned write validation
@ 2026-09-25 7:17 Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 01/11] scsi: scsi_debug: Refuse a zoned device with a non-zero lowest aligned LBA Niklas Cassel
` (10 more replies)
0 siblings, 11 replies; 16+ messages in thread
From: Niklas Cassel @ 2026-09-25 7:17 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
This series fixes scsi_debug with regards to writes to a ZBC drive, and
to WRITE ATOMIC (16). Each patch describes the problem that it fixes.
Two configurations are refused rather than emulated, and those two
patches come first, as the rest of the series relies on what they
exclude: a zoned device whose lowest aligned LBA is not zero, which no
host writing in units of the reported zone write granularity could ever
write, and atomic writes on a zoned device, which no drive supports.
Little of this is reachable in a default configuration. Most of it needs
physblk_exp, atomic_wr or logical block provisioning to be set, or an
initiator that does not provide a data buffer matching the transfer
length of the command.
The series is based on 7.4/scsi-staging rather than 7.3/scsi-fixes (as
Damien suggested on v1) to avoid a build failure that would have
happened if this series was simply merged with linux-next:
error: too many arguments to function 'mk_sense_buffer'
Tested on a zoned scsi_debug device with 512 byte logical blocks and a
4096 byte physical block, and on a device that is not zoned for the
WRITE ATOMIC (16) patches. The notes below each patch describe what was
observed before and after it.
Changes since v6:
- Patch 5 now also reports the residual when WRITE SCATTERED (16) transfers
nothing, because it has no LBA range descriptors or a buffer transfer
length of zero, and when an injected error ends it after a range has
been written, as Sashiko pointed out.
- Patch 9 rewords the comment above the new check, as suggested by Damien.
Picked up his Reviewed-by.
Niklas Cassel (11):
scsi: scsi_debug: Refuse a zoned device with a non-zero lowest aligned
LBA
scsi: scsi_debug: Make atomic writes and ZBC emulation mutually
exclusive
scsi: scsi_debug: Take the zone metadata lock before the data lock
scsi: scsi_debug: Evaluate scsi_debug_lbp() only once
scsi: scsi_debug: Report the residual of a write
scsi: scsi_debug: Enforce physical block alignment of zoned writes
scsi: scsi_debug: Do not write a partial physical block to a zoned
device
scsi: scsi_debug: Advance the write pointer over the data written
scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it
scsi: scsi_debug: Map the region written by WRITE ATOMIC (16)
scsi: scsi_debug: Validate the access parameters of WRITE ATOMIC (16)
drivers/scsi/scsi_debug.c | 116 +++++++++++++++++++++++++++++++-------
1 file changed, 95 insertions(+), 21 deletions(-)
base-commit: f09d2c7485b32adb82336d0d748935c8237a649e
--
2.55.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v7 01/11] scsi: scsi_debug: Refuse a zoned device with a non-zero lowest aligned LBA
2026-09-25 7:17 [PATCH v7 00/11] scsi: scsi_debug: fix zoned write validation Niklas Cassel
@ 2026-09-25 7:17 ` Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 02/11] scsi: scsi_debug: Make atomic writes and ZBC emulation mutually exclusive Niklas Cassel
` (9 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Niklas Cassel @ 2026-09-25 7:17 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
scsi_debug reports the lowest_aligned module parameter in the LOWEST
ALIGNED LOGICAL BLOCK ADDRESS field, see SBC-6 r02 (T10/BSR INCITS 587),
5.20.2, but nothing else in the driver acts on it.
On a zoned device a non-zero value puts the start of every zone in the
middle of a physical block. ZBC requires a write to a sequential write
required zone to end on a physical block boundary, so a host writing in
units of the reported zone write granularity could never write such a
zone at all.
Refuse the combination rather than emulate a device that cannot be used.
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested by loading the module with each combination:
zbc=managed lowest_aligned=8 refused, -EINVAL
zbc=aware lowest_aligned=8 refused, -EINVAL
zbc=managed lowest_aligned=0 loads
lowest_aligned=8 loads, as the device is not zoned
---
drivers/scsi/scsi_debug.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 6941809dfdb7..c664966e95cb 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -8734,6 +8734,11 @@ static int __init scsi_debug_init(void)
sdebug_dev_size_mb = DEF_ZBC_DEV_SIZE_MB;
}
+ if (sdeb_zbc_in_use && sdebug_lowest_aligned) {
+ pr_err("lowest_aligned is not supported by a zoned device\n");
+ return -EINVAL;
+ }
+
if (sdebug_dev_size_mb == DEF_DEV_SIZE_PRE_INIT)
sdebug_dev_size_mb = DEF_DEV_SIZE_MB;
if (sdebug_dev_size_mb < 1)
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 02/11] scsi: scsi_debug: Make atomic writes and ZBC emulation mutually exclusive
2026-09-25 7:17 [PATCH v7 00/11] scsi: scsi_debug: fix zoned write validation Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 01/11] scsi: scsi_debug: Refuse a zoned device with a non-zero lowest aligned LBA Niklas Cassel
@ 2026-09-25 7:17 ` Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 03/11] scsi: scsi_debug: Take the zone metadata lock before the data lock Niklas Cassel
` (8 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Niklas Cassel @ 2026-09-25 7:17 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
There are no SMR drives that support atomic writes, so emulating a
device with both emulates something a user cannot encounter, and means
maintaining the zone handling of a command that no zoned device
implements.
Nothing in the standards forbids the combination, and for a host managed
drive whose logical block size equals its physical block size there is
no problem in it either: the vendor only has to give atomic writes an
alignment that makes sense with regards to ZBC. This is a choice not to
complicate scsi_debug.
Refuse the combination at initialisation. It can be changed back if such
drives ever appear.
Suggested-by: Damien Le Moal <dlemoal@kernel.org>
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: John Garry <john.garry@linux.dev>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested by loading the module with each combination:
zbc=managed atomic_wr=1 refused, -EINVAL
zbc=aware atomic_wr=1 refused, -EINVAL
zbc=managed loads
atomic_wr=1 loads
---
drivers/scsi/scsi_debug.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index c664966e95cb..8e45a57ae406 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -8739,6 +8739,11 @@ static int __init scsi_debug_init(void)
return -EINVAL;
}
+ if (sdeb_zbc_in_use && sdebug_atomic_wr) {
+ pr_err("atomic_wr is not supported by a zoned device\n");
+ return -EINVAL;
+ }
+
if (sdebug_dev_size_mb == DEF_DEV_SIZE_PRE_INIT)
sdebug_dev_size_mb = DEF_DEV_SIZE_MB;
if (sdebug_dev_size_mb < 1)
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 03/11] scsi: scsi_debug: Take the zone metadata lock before the data lock
2026-09-25 7:17 [PATCH v7 00/11] scsi: scsi_debug: fix zoned write validation Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 01/11] scsi: scsi_debug: Refuse a zoned device with a non-zero lowest aligned LBA Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 02/11] scsi: scsi_debug: Make atomic writes and ZBC emulation mutually exclusive Niklas Cassel
@ 2026-09-25 7:17 ` Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 04/11] scsi: scsi_debug: Evaluate scsi_debug_lbp() only once Niklas Cassel
` (7 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Niklas Cassel @ 2026-09-25 7:17 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
resp_comp_write() takes the data lock and then the zone metadata lock,
while every other command that takes both takes the metadata lock first
and reaches the data lock through do_device_access(). A COMPARE AND
WRITE and any other write to the same store can therefore deadlock each
other.
Take the locks in the same order as everywhere else.
Correct the comment above map_region() while here: the provisioning map
is covered by the metadata lock rather than the data lock, which is why
resp_unmap() takes only the metadata lock.
Assisted-by: LLM
Fixes: 84f3a3c01d70 ("scsi: scsi_debug: Atomic write support")
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug sector_size=512 dev_size_mb=128 lbpu=1 lbpws=1
COMPARE AND WRITE completes, and a READ(16) of the block that it wrote
returns, so the reordered locks are still taken and released correctly.
The deadlock itself was not reproduced. This kernel is built without
lockdep, and the window needs a COMPARE AND WRITE and another write to
the same store to interleave between the two acquisitions. Found by
review.
---
drivers/scsi/scsi_debug.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 8e45a57ae406..18aefe83b7b6 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -5575,8 +5575,8 @@ static int resp_comp_write(struct scsi_cmnd *scp,
"indicated=%u, IO sent=%d bytes\n", my_name,
dnum * lb_size, ret);
- sdeb_data_write_lock(sip);
sdeb_meta_write_lock(sip);
+ sdeb_data_write_lock(sip);
if (!comp_write_worker(sip, lba, num, arr, false)) {
mk_sense_buffer(scp, MISCOMPARE,
MISCOMPARE_DURING_VERIFY_OPERATION);
@@ -5584,12 +5584,12 @@ static int resp_comp_write(struct scsi_cmnd *scp,
goto cleanup_unlock;
}
- /* Cover sip->map_storep (which map_region()) sets with data lock */
+ /* Cover sip->map_storep (which map_region() sets) with the meta lock */
if (scsi_debug_lbp())
map_region(sip, lba, num);
cleanup_unlock:
- sdeb_meta_write_unlock(sip);
sdeb_data_write_unlock(sip);
+ sdeb_meta_write_unlock(sip);
cleanup_free:
kfree(arr);
return retval;
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 04/11] scsi: scsi_debug: Evaluate scsi_debug_lbp() only once
2026-09-25 7:17 [PATCH v7 00/11] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (2 preceding siblings ...)
2026-09-25 7:17 ` [PATCH v7 03/11] scsi: scsi_debug: Take the zone metadata lock before the data lock Niklas Cassel
@ 2026-09-25 7:17 ` Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 05/11] scsi: scsi_debug: Report the residual of a write Niklas Cassel
` (6 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Niklas Cassel @ 2026-09-25 7:17 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
corrupt_lbas(), resp_write_dt0() and resp_write_same() call
scsi_debug_lbp() to decide whether to take the zone metadata lock, and
call it again to decide whether to touch the provisioning map that the
lock protects.
The result is not constant: scsi_debug_lbp() is false while fake_rw is
set, and fake_rw is writable at runtime, both as a module parameter and
through its driver attribute. The second call can therefore touch the
map after the first decided not to take the lock.
Call it once and use the result throughout.
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: John Garry <john.garry@linux.dev>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug sector_size=512 dev_size_mb=128 lbpu=1 lbpws=1
A WRITE(16) completes and GET LBA STATUS then reports the region as
mapped, which covers resp_write_dt0(). A WRITE SAME and a WRITE SAME
with the UNMAP bit set complete, and GET LBA STATUS reports the first
region as mapped and the second as deallocated, which covers all three
uses of the result in resp_write_same().
corrupt_lbas() is not covered. It is reached by writing to the corrupt
file in debugfs, and that interface rejected the requests that were
tried, for a reason that has nothing to do with this patch.
The window that the change closes was not reproduced. It needs fake_rw
to be written between two calls, and was found by review.
---
drivers/scsi/scsi_debug.c | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 18aefe83b7b6..b64ae3ad300d 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -4953,12 +4953,11 @@ static int corrupt_lbas(struct sdebug_dev_info *devip, u64 lba, u32 num,
{
struct sdeb_store_info *sip = devip2sip(devip, false);
bool meta_data_locked = false;
+ bool lbp = scsi_debug_lbp();
u32 block, num_mapped, b, i;
int error = 0;
- if (sdebug_dev_is_zoned(devip) ||
- sdebug_dix ||
- scsi_debug_lbp()) {
+ if (sdebug_dev_is_zoned(devip) || sdebug_dix || lbp) {
sdeb_meta_write_lock(sip);
meta_data_locked = true;
}
@@ -4975,8 +4974,7 @@ static int corrupt_lbas(struct sdebug_dev_info *devip, u64 lba, u32 num,
goto out_unlock;
}
- if (scsi_debug_lbp() &&
- (!map_state(sip, lba, &num_mapped) || num > num_mapped)) {
+ if (lbp && (!map_state(sip, lba, &num_mapped) || num > num_mapped)) {
pr_err("can't modify unmapped logical blocks: %llu:%u",
lba, num);
error = -EINVAL;
@@ -5035,6 +5033,7 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
struct sdeb_store_info *sip = devip2sip(devip, true);
u8 *cmd = scp->cmnd;
bool meta_data_locked = false;
+ bool lbp = scsi_debug_lbp();
if (unlikely(sdebug_opts & SDEBUG_OPT_UNALIGNED_WRITE &&
atomic_read(&sdeb_inject_pending))) {
@@ -5101,8 +5100,7 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
}
if (sdebug_dev_is_zoned(devip) ||
- (sdebug_dix && scsi_prot_sg_count(scp)) ||
- scsi_debug_lbp()) {
+ (sdebug_dix && scsi_prot_sg_count(scp)) || lbp) {
sdeb_meta_write_lock(sip);
meta_data_locked = true;
}
@@ -5147,7 +5145,7 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
}
ret = do_device_access(sip, scp, 0, lba, num, group, true, false);
- if (unlikely(scsi_debug_lbp()))
+ if (unlikely(lbp))
map_region(sip, lba, num);
/* If ZBC zone then bump its write pointer */
@@ -5374,8 +5372,9 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
u8 *fs1p;
u8 *fsp;
bool meta_data_locked = false;
+ bool lbp = scsi_debug_lbp();
- if (sdebug_dev_is_zoned(devip) || scsi_debug_lbp()) {
+ if (sdebug_dev_is_zoned(devip) || lbp) {
sdeb_meta_write_lock(sip);
meta_data_locked = true;
}
@@ -5384,7 +5383,7 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
if (ret)
goto out;
- if (unmap && scsi_debug_lbp()) {
+ if (unmap && lbp) {
unmap_region(sip, lba, num);
goto out;
}
@@ -5414,7 +5413,7 @@ static int resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num,
block = do_div(lbaa, sdebug_store_sectors);
memmove(fsp + (block * lb_size), fs1p, lb_size);
}
- if (scsi_debug_lbp())
+ if (lbp)
map_region(sip, lba, num);
/* If ZBC zone then bump its write pointer */
if (sdebug_dev_is_zoned(devip))
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 05/11] scsi: scsi_debug: Report the residual of a write
2026-09-25 7:17 [PATCH v7 00/11] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (3 preceding siblings ...)
2026-09-25 7:17 ` [PATCH v7 04/11] scsi: scsi_debug: Evaluate scsi_debug_lbp() only once Niklas Cassel
@ 2026-09-25 7:17 ` Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 06/11] scsi: scsi_debug: Enforce physical block alignment of zoned writes Niklas Cassel
` (5 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Niklas Cassel @ 2026-09-25 7:17 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
A write whose data buffer is larger than its transfer length leaves a
residual that the initiator is entitled to be told about. resp_read_dt0()
and resp_write_tape() report it, but the write paths for a disk do not,
so such a write completes with GOOD status and a residual of zero while
a READ of the same length into the same buffer reports it correctly.
Report it in resp_write_dt0(), resp_write_scat() and resp_atomic_write().
resp_write_scat() differs twice over. Its data-out buffer also holds the
parameter list header and the LBA range descriptors, so what the command
consumed is sg_off rather than the bytes that the last range
transferred; and sg_off counts what was asked for rather than what was
transferred, so it can exceed the buffer and needs a guard. Two of its
exits also bypass the end of the loop: a command with no LBA range
descriptors or a buffer transfer length of zero transfers nothing, so
the whole buffer is residual, and an injected error ends the command
after a range has been written, where resp_write_dt0() reports the
residual before injecting it.
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug zbc=managed sector_size=512 physblk_exp=3 \
zone_size_mb=8 dev_size_mb=128 zone_nr_conv=2
An eight logical block WRITE(16) with a 5000 byte buffer reports
resid=904, having transferred 4096. A READ(16) of the same length into
the same buffer reported that before this patch and the WRITE reported
0. A buffer of exactly 4096 bytes reports 0, as does a 512 byte buffer,
which is consumed in its entirety. WRITE ATOMIC (16) behaves like the
WRITE, on a device that is not zoned.
WRITE SCATTERED (16) with one LBA range descriptor of eight blocks and a
5632 byte buffer reports resid=1024, having consumed 512 bytes of
parameter list and 4096 bytes of data. The same command with a 1024 byte
buffer completes and reports resid=0: sg_off reaches 4608, past the end
of the buffer, and without the guard the residual would have been
reported as 4294963712.
Changes since v6: a WRITE SCATTERED (16) with no LBA range descriptors,
or with a buffer transfer length of zero, and a 1024 byte buffer
reports resid=1024, as does the command above with a RECOVERED ERROR
injected (opts=8, every_nth=1), on a device that is not zoned. All
three reported 0 in v6.
---
drivers/scsi/scsi_debug.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index b64ae3ad300d..9220758bb801 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -5162,6 +5162,8 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
"%s: write: cdb indicated=%u, IO sent=%d bytes\n",
my_name, num * sdebug_sector_size, ret);
+ scsi_set_resid(scp, scsi_bufflen(scp) - ret);
+
if (unlikely((sdebug_opts & SDEBUG_OPT_RECOV_DIF_DIX) &&
atomic_read(&sdeb_inject_pending))) {
if (sdebug_opts & SDEBUG_OPT_RECOVERED_ERR) {
@@ -5233,8 +5235,10 @@ static int resp_write_scat(struct scsi_cmnd *scp,
"Unprotected WR to DIF device\n");
}
}
- if ((num_lrd == 0) || (bt_len == 0))
+ if (num_lrd == 0 || bt_len == 0) {
+ scsi_set_resid(scp, scsi_bufflen(scp));
return 0; /* T10 says these do-nothings are not errors */
+ }
if (lbdof == 0) {
if (sdebug_verbose)
sdev_printk(KERN_INFO, scp->device,
@@ -5327,6 +5331,9 @@ static int resp_write_scat(struct scsi_cmnd *scp,
if (unlikely((sdebug_opts & SDEBUG_OPT_RECOV_DIF_DIX) &&
atomic_read(&sdeb_inject_pending))) {
+ if (scsi_bufflen(scp) > sg_off + num_by)
+ scsi_set_resid(scp, scsi_bufflen(scp) -
+ (sg_off + num_by));
if (sdebug_opts & SDEBUG_OPT_RECOVERED_ERR) {
mk_sense_buffer(scp, RECOVERED_ERROR,
FAILURE_PREDICTION_THRESHOLD_EXCEEDED);
@@ -5350,6 +5357,13 @@ static int resp_write_scat(struct scsi_cmnd *scp,
sg_off += num_by;
cum_lb += num;
}
+ /*
+ * sg_off counts what the command asked for, which can exceed the
+ * buffer: lbdof is not validated against it, and a range is counted
+ * in full even if do_device_access() copied less.
+ */
+ if (scsi_bufflen(scp) > sg_off)
+ scsi_set_resid(scp, scsi_bufflen(scp) - sg_off);
ret = 0;
err_out_unlock:
sdeb_meta_write_unlock(sip);
@@ -6206,6 +6220,8 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
return DID_ERROR << 16;
if (unlikely(ret != len * sdebug_sector_size))
return DID_ERROR << 16;
+
+ scsi_set_resid(scp, scsi_bufflen(scp) - ret);
return 0;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 06/11] scsi: scsi_debug: Enforce physical block alignment of zoned writes
2026-09-25 7:17 [PATCH v7 00/11] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (4 preceding siblings ...)
2026-09-25 7:17 ` [PATCH v7 05/11] scsi: scsi_debug: Report the residual of a write Niklas Cassel
@ 2026-09-25 7:17 ` Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 07/11] scsi: scsi_debug: Do not write a partial physical block to a zoned device Niklas Cassel
` (4 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Niklas Cassel @ 2026-09-25 7:17 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
ZBC-3 r06 (T10/BSR INCITS 579), 4.5.3.3.2, requires a write to a
sequential write required zone to be terminated with ILLEGAL REQUEST /
UNALIGNED WRITE COMMAND both when it does not start at the write pointer
and when it does not end on a physical block boundary. That is why
sd_zbc_read_zones() sets the zone_write_granularity queue limit to the
physical block size of a host-managed device.
check_zbc_access_params() implements the first condition but not the
second. So with zbc=managed sector_size=512 physblk_exp=3, a write of a
single logical block at the write pointer is accepted and advances the
write pointer by one logical block, leaving it off the granularity that
was advertised: nothing can write at it any more, and the zone can only
be used again after being reset.
Check the ending LBA as well, with the same sense data, as the standard
gives both conditions the same. An entire medium write same command,
which the standard excludes, needs no special case: the preceding check
already terminates it with WRITE BOUNDARY VIOLATION. Sequential write
preferred zones are left alone, as writes to them need not be
sequential, and with the default physblk_exp=0 the check is a no-op.
Assisted-by: LLM
Fixes: f0d1cf9378bd ("scsi: scsi_debug: Add ZBC zone commands")
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug zbc=managed sector_size=512 physblk_exp=3 \
zone_size_mb=8 dev_size_mb=128 zone_nr_conv=2
Before this patch, a one logical block WRITE(16) at the write pointer of
an empty sequential write required zone completes with GOOD status and
leaves the write pointer at LBA 0x18001, which is not a multiple of the
4096 byte zone_write_granularity reported for the device. After it, the
same command is terminated with ILLEGAL REQUEST / UNALIGNED WRITE
COMMAND and the zone is left EMPTY, while an aligned eight block write
is still accepted.
---
drivers/scsi/scsi_debug.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 9220758bb801..3ed599a606ba 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -3980,6 +3980,16 @@ static int check_zbc_access_params(struct scsi_cmnd *scp,
UNALIGNED_WRITE_COMMAND);
return check_condition_result;
}
+ /*
+ * Writes must end on a physical block boundary, that is, the
+ * transfer length must be a multiple of the physical block
+ * size.
+ */
+ if (!IS_ALIGNED(lba + num, 1U << sdebug_physblk_exp)) {
+ mk_sense_buffer(scp, ILLEGAL_REQUEST,
+ UNALIGNED_WRITE_COMMAND);
+ return check_condition_result;
+ }
}
/* Handle implicit open of closed and empty zones */
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 07/11] scsi: scsi_debug: Do not write a partial physical block to a zoned device
2026-09-25 7:17 [PATCH v7 00/11] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (5 preceding siblings ...)
2026-09-25 7:17 ` [PATCH v7 06/11] scsi: scsi_debug: Enforce physical block alignment of zoned writes Niklas Cassel
@ 2026-09-25 7:17 ` Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 08/11] scsi: scsi_debug: Advance the write pointer over the data written Niklas Cassel
` (3 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Niklas Cassel @ 2026-09-25 7:17 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
do_device_access() copies one logical block at a time and stops at the
first short copy, so a data-out buffer smaller than the transfer length
of the command can leave part of a physical block written. An initiator
can arrange that with SG_IO.
That is acceptable everywhere but a sequential write required zone,
where ZBC-3 r06 (T10/BSR INCITS 579), 4.5.3.3.2, requires a write to end
on a physical block boundary.
For such a zone, stop at the last whole physical block that the buffer
holds. The bytes left over are not written, and are reported to the
initiator as part of the residual.
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug zbc=managed sector_size=512 physblk_exp=3 \
zone_size_mb=8 dev_size_mb=128 zone_nr_conv=2
issuing a sixteen logical block WRITE(16) through SG_IO with a buffer of
twelve blocks. To a sequential write required zone it now transfers
eight blocks, one whole physical block, and reports the remaining 2048
bytes as the residual; before this patch it transferred twelve. To a
conventional zone it still transfers all twelve and reports no residual.
With physblk_exp=0 an eight block write with a buffer of one block still
transfers one block, wherever it is addressed.
---
drivers/scsi/scsi_debug.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 3ed599a606ba..2c6a9e17e4a8 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -4273,6 +4273,8 @@ static int do_device_access(struct sdeb_store_info *sip, struct scsi_cmnd *scp,
u64 block;
enum dma_data_direction dir;
struct scsi_data_buffer *sdb = &scp->sdb;
+ struct scsi_device *sdp = scp->device;
+ struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdp->hostdata;
u8 *fsp;
int i, total = 0;
@@ -4300,6 +4302,25 @@ static int do_device_access(struct sdeb_store_info *sip, struct scsi_cmnd *scp,
fsp = sip->storep;
+ /*
+ * A write to a sequential write required zone has to end on a physical
+ * block boundary, so if the data-out buffer does not hold all of the
+ * data that the command asks for, write up to the last whole physical
+ * block that it does hold. What is left over is reported as part of
+ * the residual.
+ */
+ if (do_write && sdebug_dev_is_zoned(devip)) {
+ struct sdeb_zone_state *zsp = zbc_zone(devip, lba);
+
+ if (zsp->z_type == ZBC_ZTYPE_SWR) {
+ u32 avail = (sdb->length - sg_skip)
+ >> ilog2(sdebug_sector_size);
+
+ if (avail < num)
+ num = round_down(avail, 1U << sdebug_physblk_exp);
+ }
+ }
+
block = do_div(lba, sdebug_store_sectors);
/* Only allow 1x atomic write or multiple non-atomic writes at any given time */
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 08/11] scsi: scsi_debug: Advance the write pointer over the data written
2026-09-25 7:17 [PATCH v7 00/11] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (6 preceding siblings ...)
2026-09-25 7:17 ` [PATCH v7 07/11] scsi: scsi_debug: Do not write a partial physical block to a zoned device Niklas Cassel
@ 2026-09-25 7:17 ` Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 09/11] scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it Niklas Cassel
` (2 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Niklas Cassel @ 2026-09-25 7:17 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
resp_write_dt0() and resp_write_scat() advance the write pointer of a
sequential write required zone by the transfer length of the command
without looking at what do_device_access() returned, which is -1 when
the data direction of the command does not match the operation, and a
short byte count when the data-out buffer is smaller than the transfer
length. An initiator can produce both with SG_IO.
The zone then describes more data than is on the medium, and a write at
the position where the data really ends is terminated with UNALIGNED
WRITE COMMAND, so the zone has to be reset before it can be written to
again.
Advance the write pointer over the data that was written instead. As
do_device_access() does not write a partial physical block to such a
zone, that leaves the write pointer where a write can end.
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Fixes: f0d1cf9378bd ("scsi: scsi_debug: Add ZBC zone commands")
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug zbc=managed sector_size=512 physblk_exp=3 \
zone_size_mb=8 dev_size_mb=128 zone_nr_conv=2
issuing WRITE(16) through SG_IO with a data-out buffer shorter than the
transfer length of the command. A sixteen block write with a buffer of
twelve blocks leaves the write pointer of an empty sequential write
required zone eight blocks on, which is where the data it wrote ends,
and a following eight block write continues from there. An eight block
write with a buffer of one block writes nothing and leaves the write
pointer where it was.
Before this patch both advanced the write pointer by the full transfer
length, and a write at the end of the data that had actually been
written was then terminated with UNALIGNED WRITE COMMAND.
---
drivers/scsi/scsi_debug.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 2c6a9e17e4a8..f59c4f11c851 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -5179,9 +5179,9 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
if (unlikely(lbp))
map_region(sip, lba, num);
- /* If ZBC zone then bump its write pointer */
- if (sdebug_dev_is_zoned(devip))
- zbc_inc_wp(devip, lba, num);
+ /* If ZBC zone then bump its write pointer over the data written */
+ if (sdebug_dev_is_zoned(devip) && ret > 0)
+ zbc_inc_wp(devip, lba, ret >> ilog2(sdebug_sector_size));
if (meta_data_locked)
sdeb_meta_write_unlock(sip);
@@ -5347,9 +5347,10 @@ static int resp_write_scat(struct scsi_cmnd *scp,
* writes behaviour as possible.
*/
ret = do_device_access(sip, scp, sg_off, lba, num, group, true, true);
- /* If ZBC zone then bump its write pointer */
- if (sdebug_dev_is_zoned(devip))
- zbc_inc_wp(devip, lba, num);
+ /* If ZBC zone then bump its write pointer over the data written */
+ if (sdebug_dev_is_zoned(devip) && ret > 0)
+ zbc_inc_wp(devip, lba,
+ ret >> ilog2(sdebug_sector_size));
if (unlikely(scsi_debug_lbp()))
map_region(sip, lba, num);
if (unlikely(-1 == ret)) {
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 09/11] scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it
2026-09-25 7:17 [PATCH v7 00/11] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (7 preceding siblings ...)
2026-09-25 7:17 ` [PATCH v7 08/11] scsi: scsi_debug: Advance the write pointer over the data written Niklas Cassel
@ 2026-09-25 7:17 ` Niklas Cassel
2026-09-25 9:30 ` John Garry
2026-09-25 7:17 ` [PATCH v7 10/11] scsi: scsi_debug: Map the region written by WRITE ATOMIC (16) Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 11/11] scsi: scsi_debug: Validate the access parameters of " Niklas Cassel
10 siblings, 1 reply; 16+ messages in thread
From: Niklas Cassel @ 2026-09-25 7:17 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
A write whose data-out buffer is shorter than its transfer length is
short. An ordinary write transfers what the buffer holds and reports the
rest as a residual, but an atomic write cannot be short: SBC-6 r02
(T10/BSR INCITS 587), 4.28.1, requires each atomic write operation to
write either all of its data or none of it, and 4.28.2 requires one that
cannot complete to leave the LBAs that it specifies unaltered.
resp_atomic_write() does fail a short WRITE ATOMIC (16) with DID_ERROR,
but only after do_device_access() has returned, and do_device_access()
copies one logical block at a time, so by then the blocks that the
buffer did hold have been written. An eight block WRITE ATOMIC (16) with
a buffer of four fails having overwritten the first four.
Check the length of the buffer before writing anything, and fail the
command with the same DID_ERROR as before.
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Fixes: 84f3a3c01d70 ("scsi: scsi_debug: Atomic write support")
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug sector_size=512 physblk_exp=3 dev_size_mb=128 \
atomic_wr=1 lbpu=1
issuing a WRITE ATOMIC (16) of eight blocks through SG_IO with a buffer
of four blocks of 0xaa. It fails with DID_ERROR before and after this
patch, but before it the first four blocks read back as 0xaa afterwards,
and after it all eight read back as they were. A WRITE ATOMIC (16) with
a full buffer writes all eight blocks, as before.
Changes since v6: the comment is reworded, as Damien suggested.
---
drivers/scsi/scsi_debug.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index f59c4f11c851..c0bcf8155fc8 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -6247,6 +6247,10 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
}
}
+ /* Short atomic writes are not allowed. */
+ if (scsi_bufflen(scp) < len * sdebug_sector_size)
+ return DID_ERROR << 16;
+
ret = do_device_access(sip, scp, 0, lba, len, 0, true, true);
if (unlikely(ret == -1))
return DID_ERROR << 16;
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 10/11] scsi: scsi_debug: Map the region written by WRITE ATOMIC (16)
2026-09-25 7:17 [PATCH v7 00/11] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (8 preceding siblings ...)
2026-09-25 7:17 ` [PATCH v7 09/11] scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it Niklas Cassel
@ 2026-09-25 7:17 ` Niklas Cassel
2026-09-25 9:33 ` John Garry
2026-09-25 7:17 ` [PATCH v7 11/11] scsi: scsi_debug: Validate the access parameters of " Niklas Cassel
10 siblings, 1 reply; 16+ messages in thread
From: Niklas Cassel @ 2026-09-25 7:17 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
When logical block provisioning is enabled, every command that writes
user data calls map_region() so that GET LBA STATUS reports the region
as mapped. resp_atomic_write() does not, so GET LBA STATUS keeps
reporting a region as deallocated after a WRITE ATOMIC (16) has written
it.
Call map_region() the way resp_write_dt0() does, holding the zone
metadata write lock across the access. That lock is what serialises the
map against resp_unmap(), which does nothing unless logical block
provisioning is enabled.
Assisted-by: LLM
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Fixes: 84f3a3c01d70 ("scsi: scsi_debug: Atomic write support")
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug sector_size=512 physblk_exp=3 dev_size_mb=128 \
atomic_wr=1 lbpu=1
GET LBA STATUS reports a region that has never been written as
deallocated, and reports it as mapped after a WRITE ATOMIC (16) of eight
blocks. Only an ordinary WRITE did so before this patch.
---
drivers/scsi/scsi_debug.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index c0bcf8155fc8..c407f4c5ac47 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -6203,6 +6203,7 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
u8 *cmd = scp->cmnd;
u16 boundary, len;
u64 lba, lba_tmp;
+ bool lbp = scsi_debug_lbp();
int ret;
if (!scsi_debug_atomic_write()) {
@@ -6251,7 +6252,15 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
if (scsi_bufflen(scp) < len * sdebug_sector_size)
return DID_ERROR << 16;
+ if (lbp)
+ sdeb_meta_write_lock(sip);
+
ret = do_device_access(sip, scp, 0, lba, len, 0, true, true);
+ if (lbp) {
+ map_region(sip, lba, len);
+ sdeb_meta_write_unlock(sip);
+ }
+
if (unlikely(ret == -1))
return DID_ERROR << 16;
if (unlikely(ret != len * sdebug_sector_size))
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v7 11/11] scsi: scsi_debug: Validate the access parameters of WRITE ATOMIC (16)
2026-09-25 7:17 [PATCH v7 00/11] scsi: scsi_debug: fix zoned write validation Niklas Cassel
` (9 preceding siblings ...)
2026-09-25 7:17 ` [PATCH v7 10/11] scsi: scsi_debug: Map the region written by WRITE ATOMIC (16) Niklas Cassel
@ 2026-09-25 7:17 ` Niklas Cassel
10 siblings, 0 replies; 16+ messages in thread
From: Niklas Cassel @ 2026-09-25 7:17 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal, John Garry, Niklas Cassel
resp_atomic_write() validates the fields that are specific to an atomic
write, but never the range that the command addresses. Every other
command that writes user data calls check_device_access_params() first,
which rejects a transfer that ends beyond the capacity of the device,
one whose length exceeds the size of the store, and any write to a write
protected device.
Call check_device_access_params(). The zone checks that it ends with are
unreachable, as atomic writes and ZBC emulation are mutually exclusive.
Assisted-by: LLM
Fixes: 84f3a3c01d70 ("scsi: scsi_debug: Atomic write support")
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: John Garry <john.garry@linux.dev>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
Tested with:
modprobe scsi_debug sector_size=512 physblk_exp=3 dev_size_mb=128 \
atomic_wr=1
The device has a capacity of 262144 logical blocks. A WRITE ATOMIC (16)
of eight blocks at LBA 262140 is now terminated with ILLEGAL REQUEST /
LOGICAL BLOCK ADDRESS OUT OF RANGE; before this patch it completed with
GOOD status, having written eight blocks at LBA 0 instead.
With the wp module parameter set to 1, a WRITE ATOMIC (16) is now
terminated with DATA PROTECT / LOGICAL UNIT SOFTWARE WRITE PROTECTED,
which is what an ordinary WRITE(16) has always returned; before this
patch it completed with GOOD status and wrote the data.
---
drivers/scsi/scsi_debug.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index c407f4c5ac47..c4ed9ebb930c 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -6248,6 +6248,10 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
}
}
+ ret = check_device_access_params(scp, lba, len, true);
+ if (ret)
+ return ret;
+
/* Short atomic writes are not allowed. */
if (scsi_bufflen(scp) < len * sdebug_sector_size)
return DID_ERROR << 16;
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v7 09/11] scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it
2026-09-25 7:17 ` [PATCH v7 09/11] scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it Niklas Cassel
@ 2026-09-25 9:30 ` John Garry
2026-09-26 18:03 ` Niklas Cassel
0 siblings, 1 reply; 16+ messages in thread
From: John Garry @ 2026-09-25 9:30 UTC (permalink / raw)
To: Niklas Cassel, James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal
On 9/25/26 08:17, Niklas Cassel wrote:
> A write whose data-out buffer is shorter than its transfer length is
> short. An ordinary write transfers what the buffer holds and reports the
> rest as a residual, but an atomic write cannot be short: SBC-6 r02
> (T10/BSR INCITS 587), 4.28.1, requires each atomic write operation to
> write either all of its data or none of it, and 4.28.2 requires one that
> cannot complete to leave the LBAs that it specifies unaltered.
>
> resp_atomic_write() does fail a short WRITE ATOMIC (16) with DID_ERROR,
> but only after do_device_access() has returned, and do_device_access()
> copies one logical block at a time, so by then the blocks that the
> buffer did hold have been written. An eight block WRITE ATOMIC (16) with
> a buffer of four fails having overwritten the first four.
>
> Check the length of the buffer before writing anything, and fail the
> command with the same DID_ERROR as before.
>
> Assisted-by: LLM
> Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
> Fixes: 84f3a3c01d70 ("scsi: scsi_debug: Atomic write support")
> Signed-off-by: Niklas Cassel <cassel@kernel.org>
> ---
> Tested with:
>
> modprobe scsi_debug sector_size=512 physblk_exp=3 dev_size_mb=128 \
> atomic_wr=1 lbpu=1
>
> issuing a WRITE ATOMIC (16) of eight blocks through SG_IO with a buffer
> of four blocks of 0xaa. It fails with DID_ERROR before and after this
> patch, but before it the first four blocks read back as 0xaa afterwards,
> and after it all eight read back as they were. A WRITE ATOMIC (16) with
> a full buffer writes all eight blocks, as before.
>
What is the command which you use?
I am just wondering if this check should go into sd_setup_atomic_cmnd()
or somewhere else higher up. I mean, this check is not really specific
to scsi_debug, right?
> Changes since v6: the comment is reworded, as Damien suggested.
> ---
> drivers/scsi/scsi_debug.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index f59c4f11c851..c0bcf8155fc8 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
> @@ -6247,6 +6247,10 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
> }
> }
>
> + /* Short atomic writes are not allowed. */
> + if (scsi_bufflen(scp) < len * sdebug_sector_size)
> + return DID_ERROR << 16;
> +
> ret = do_device_access(sip, scp, 0, lba, len, 0, true, true);
> if (unlikely(ret == -1))
> return DID_ERROR << 16;
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v7 10/11] scsi: scsi_debug: Map the region written by WRITE ATOMIC (16)
2026-09-25 7:17 ` [PATCH v7 10/11] scsi: scsi_debug: Map the region written by WRITE ATOMIC (16) Niklas Cassel
@ 2026-09-25 9:33 ` John Garry
0 siblings, 0 replies; 16+ messages in thread
From: John Garry @ 2026-09-25 9:33 UTC (permalink / raw)
To: Niklas Cassel, James E.J. Bottomley, Martin K. Petersen
Cc: linux-scsi, Damien Le Moal
On 9/25/26 08:17, Niklas Cassel wrote:
> When logical block provisioning is enabled, every command that writes
> user data calls map_region() so that GET LBA STATUS reports the region
> as mapped. resp_atomic_write() does not, so GET LBA STATUS keeps
> reporting a region as deallocated after a WRITE ATOMIC (16) has written
> it.
>
> Call map_region() the way resp_write_dt0() does, holding the zone
> metadata write lock across the access. That lock is what serialises the
> map against resp_unmap(), which does nothing unless logical block
> provisioning is enabled.
>
> Assisted-by: LLM
> Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
> Fixes: 84f3a3c01d70 ("scsi: scsi_debug: Atomic write support")
> Signed-off-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: John Garry <john.garry@linux.dev>
> ---
> Tested with:
>
> modprobe scsi_debug sector_size=512 physblk_exp=3 dev_size_mb=128 \
> atomic_wr=1 lbpu=1
>
> GET LBA STATUS reports a region that has never been written as
> deallocated, and reports it as mapped after a WRITE ATOMIC (16) of eight
> blocks. Only an ordinary WRITE did so before this patch.
> ---
> drivers/scsi/scsi_debug.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
> index c0bcf8155fc8..c407f4c5ac47 100644
> --- a/drivers/scsi/scsi_debug.c
> +++ b/drivers/scsi/scsi_debug.c
> @@ -6203,6 +6203,7 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
> u8 *cmd = scp->cmnd;
> u16 boundary, len;
> u64 lba, lba_tmp;
> + bool lbp = scsi_debug_lbp();
> int ret;
>
> if (!scsi_debug_atomic_write()) {
> @@ -6251,7 +6252,15 @@ static int resp_atomic_write(struct scsi_cmnd *scp,
> if (scsi_bufflen(scp) < len * sdebug_sector_size)
> return DID_ERROR << 16;
>
> + if (lbp)
> + sdeb_meta_write_lock(sip);
> +
> ret = do_device_access(sip, scp, 0, lba, len, 0, true, true);
> + if (lbp) {
> + map_region(sip, lba, len);
> + sdeb_meta_write_unlock(sip);
> + }
> +
> if (unlikely(ret == -1))
> return DID_ERROR << 16;
> if (unlikely(ret != len * sdebug_sector_size))
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v7 09/11] scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it
2026-09-25 9:30 ` John Garry
@ 2026-09-26 18:03 ` Niklas Cassel
2026-09-28 8:52 ` John Garry
0 siblings, 1 reply; 16+ messages in thread
From: Niklas Cassel @ 2026-09-26 18:03 UTC (permalink / raw)
To: John Garry
Cc: James E.J. Bottomley, Martin K. Petersen, linux-scsi,
Damien Le Moal
Hello John,
On Fri, Sep 25, 2026 at 10:30:11AM +0100, John Garry wrote:
> > ---
> > Tested with:
> >
> > modprobe scsi_debug sector_size=512 physblk_exp=3 dev_size_mb=128 \
> > atomic_wr=1 lbpu=1
> >
> > issuing a WRITE ATOMIC (16) of eight blocks through SG_IO with a buffer
> > of four blocks of 0xaa. It fails with DID_ERROR before and after this
> > patch, but before it the first four blocks read back as 0xaa afterwards,
> > and after it all eight read back as they were. A WRITE ATOMIC (16) with
> > a full buffer writes all eight blocks, as before.
> >
>
> What is the command which you use?
SG_IO, with a CDB for eight blocks and a buffer of four:
sg_raw -s 2048 -i aa4 /dev/sdX 9c 00 00 00 00 00 00 00 00 40 00 00 00 08 00 00
>
> I am just wondering if this check should go into sd_setup_atomic_cmnd() or
> somewhere else higher up. I mean, this check is not really specific to
> scsi_debug, right?
sd can't produce a short atomic write: sd_setup_read_write_cmnd() derives
nr_blocks from blk_rq_sectors(), and the data buffer is the same request,
so the transfer length and the buffer always match.
A mismatch is only possible through SG_IO, where the caller supplies both.
SBC-6 4.28.2 puts the requirement on the device server: a failed atomic
write must leave the LBAs unaltered. scsi_debug is the device server here,
so I think this is the right place for it.
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v7 09/11] scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it
2026-09-26 18:03 ` Niklas Cassel
@ 2026-09-28 8:52 ` John Garry
0 siblings, 0 replies; 16+ messages in thread
From: John Garry @ 2026-09-28 8:52 UTC (permalink / raw)
To: Niklas Cassel
Cc: James E.J. Bottomley, Martin K. Petersen, linux-scsi,
Damien Le Moal
On 9/26/26 19:03, Niklas Cassel wrote:
> Hello John,
>
> On Fri, Sep 25, 2026 at 10:30:11AM +0100, John Garry wrote:
>>> ---
>>> Tested with:
>>>
>>> modprobe scsi_debug sector_size=512 physblk_exp=3 dev_size_mb=128 \
>>> atomic_wr=1 lbpu=1
>>>
>>> issuing a WRITE ATOMIC (16) of eight blocks through SG_IO with a buffer
>>> of four blocks of 0xaa. It fails with DID_ERROR before and after this
>>> patch, but before it the first four blocks read back as 0xaa afterwards,
>>> and after it all eight read back as they were. A WRITE ATOMIC (16) with
>>> a full buffer writes all eight blocks, as before.
>>>
>>
>> What is the command which you use?
>
> SG_IO, with a CDB for eight blocks and a buffer of four:
> sg_raw -s 2048 -i aa4 /dev/sdX 9c 00 00 00 00 00 00 00 00 40 00 00 00 08 00 00
>
>
>>
>> I am just wondering if this check should go into sd_setup_atomic_cmnd() or
>> somewhere else higher up. I mean, this check is not really specific to
>> scsi_debug, right?
>
> sd can't produce a short atomic write: sd_setup_read_write_cmnd() derives
> nr_blocks from blk_rq_sectors(), and the data buffer is the same request,
> so the transfer length and the buffer always match.
>
> A mismatch is only possible through SG_IO, where the caller supplies both.
>
> SBC-6 4.28.2 puts the requirement on the device server: a failed atomic
> write must leave the LBAs unaltered. scsi_debug is the device server here,
> so I think this is the right place for it.
>
OK, I think that the important information is "4.28.2 requires one that
cannot complete to leave the LBAs that it specifies unaltered", so maybe
add a small comment for that.
Reviewed-by: John Garry <john.garry@linux.dev>
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-09-28 8:52 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-25 7:17 [PATCH v7 00/11] scsi: scsi_debug: fix zoned write validation Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 01/11] scsi: scsi_debug: Refuse a zoned device with a non-zero lowest aligned LBA Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 02/11] scsi: scsi_debug: Make atomic writes and ZBC emulation mutually exclusive Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 03/11] scsi: scsi_debug: Take the zone metadata lock before the data lock Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 04/11] scsi: scsi_debug: Evaluate scsi_debug_lbp() only once Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 05/11] scsi: scsi_debug: Report the residual of a write Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 06/11] scsi: scsi_debug: Enforce physical block alignment of zoned writes Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 07/11] scsi: scsi_debug: Do not write a partial physical block to a zoned device Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 08/11] scsi: scsi_debug: Advance the write pointer over the data written Niklas Cassel
2026-09-25 7:17 ` [PATCH v7 09/11] scsi: scsi_debug: Refuse a short WRITE ATOMIC (16) before writing it Niklas Cassel
2026-09-25 9:30 ` John Garry
2026-09-26 18:03 ` Niklas Cassel
2026-09-28 8:52 ` John Garry
2026-09-25 7:17 ` [PATCH v7 10/11] scsi: scsi_debug: Map the region written by WRITE ATOMIC (16) Niklas Cassel
2026-09-25 9:33 ` John Garry
2026-09-25 7:17 ` [PATCH v7 11/11] scsi: scsi_debug: Validate the access parameters of " Niklas Cassel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox