scst_lib: Select WRITE SAME flags from the input CDB format

WRITE SAME emulation chooses WRITE(16) or WRITE(32) from the required DIF
handling, independently of the input CDB length. The builder nevertheless
copies the flags from the output format's offset in the input command.
For WRITE SAME(16) converted to WRITE(32), that is byte 10 of the transfer
length; for a long command converted to WRITE(16), it is the control byte.
An unrelated value can therefore change WRPROTECT and other output flags.

Select the input flags once using the original CDB length and use that
value in both output formats. Preserve the existing WRPROTECT defaulting.
Add KUnit cases with different flag and unrelated-byte values for both
conversions and for commands that retain their format.

Fixes: 906f4dfefb ("Merge t10-pi branch back")
This commit is contained in:
Gleb Chesnokov
2026-10-20 15:48:45 +03:00
parent f12687b530
commit c5b905ae7e
2 changed files with 54 additions and 8 deletions
@@ -227,10 +227,59 @@ static void write_same_app_tag_mask(struct kunit *test)
}
}
struct write_same_flags_case {
const char *name;
int cdb_len;
bool needs_dif;
u8 flags;
u8 other_byte;
u8 expected;
};
static const struct write_same_flags_case write_same_flags_cases[] = {
{ "ws16_to_write32_default", 16, true, 0x00, 0xa5, 0x20 },
{ "ws16_to_write32_protected", 16, true, 0x40, 0xa5, 0x40 },
{ "ws32_to_write16", 32, false, 0x00, 0xa5, 0x00 },
{ "ws16_to_write16", 16, false, 0x00, 0xa5, 0x00 },
{ "ws32_to_write32", 32, true, 0x40, 0xa5, 0x40 },
};
static void write_same_flags_desc(const struct write_same_flags_case *p, char *desc)
{
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s", p->name);
}
KUNIT_ARRAY_PARAM(write_same_flags, write_same_flags_cases, write_same_flags_desc);
static void write_same_flags(struct kunit *test)
{
const struct write_same_flags_case *p = test->param_value;
struct write_same_fixture *f = test->priv;
u8 cdb[32];
int len;
f->cmd.cdb_len = p->cdb_len;
f->cdb[0] = p->cdb_len == 16 ? WRITE_SAME_16 : VARIABLE_LENGTH_CMD;
f->cdb[p->cdb_len == 32 ? 10 : 1] = p->flags;
/* A short command's transfer length and a long one's control are not flags. */
f->cdb[p->cdb_len == 32 ? 1 : 10] = p->other_byte;
if (p->cdb_len == 32) {
f->cdb[7] = 0x18;
put_unaligned_be16(SUBCODE_WRITE_SAME_32, &f->cdb[8]);
}
f->wsp.dif_valid = p->needs_dif;
len = scst_ws_build_write_cdb(&f->wsp, cdb, 1, 1);
KUNIT_ASSERT_EQ(test, len, p->needs_dif ? 32 : 16);
KUNIT_EXPECT_EQ(test, cdb[p->needs_dif ? 10 : 1], p->expected);
}
static struct kunit_case write_same_cases[] = {
KUNIT_CASE_PARAM(write_same_app_tag, write_same_tag_gen_params),
KUNIT_CASE_PARAM(write_same_cdb_image, write_same_cdb_gen_params),
KUNIT_CASE_PARAM(write_same_app_tag_mask, write_same_mask_gen_params),
KUNIT_CASE_PARAM(write_same_flags, write_same_flags_gen_params),
{}
};
+5 -8
View File
@@ -6413,15 +6413,16 @@ int scst_ws_build_write_cdb(const struct scst_write_same_priv *wsp,
const struct scst_cmd *ws_cmd = wsp->ws_orig_cmd;
const struct scst_device *dev = ws_cmd->dev;
bool needs_dif = wsp->dif_valid;
u8 flags = ws_cmd->cdb[ws_cmd->cdb_len == 32 ? 10 : 1];
int write_cdb_len;
memset(write_cdb, 0, 32);
if (!needs_dif || dev->dev_dif_type != 2) {
write_cdb[0] = WRITE_16;
write_cdb_len = 16;
write_cdb[1] = ws_cmd->cdb[1];
write_cdb[1] = flags;
if (needs_dif) {
u8 wrprotect = ws_cmd->cdb[1] & 0xE0;
u8 wrprotect = flags & 0xE0;
if (wrprotect == 0)
wrprotect = 0x20;
@@ -6431,19 +6432,15 @@ int scst_ws_build_write_cdb(const struct scst_write_same_priv *wsp,
put_unaligned_be32(blocks, &write_cdb[10]);
} else {
/* There might be WRITE SAME(16) with WRPROTECT 0 here */
u8 wrprotect;
u8 wrprotect = flags & 0xE0;
if (ws_cmd->cdb_len != 32)
wrprotect = ws_cmd->cdb[1] & 0xE0;
else
wrprotect = ws_cmd->cdb[10] & 0xE0;
if (wrprotect == 0)
wrprotect = 0x20;
write_cdb[0] = VARIABLE_LENGTH_CMD;
put_unaligned_be16(SUBCODE_WRITE_32, &write_cdb[8]);
write_cdb[7] = 0x18;
write_cdb_len = 32;
write_cdb[10] = ws_cmd->cdb[10];
write_cdb[10] = flags;
write_cdb[10] |= wrprotect;
put_unaligned_be64(lba, &write_cdb[12]);
put_unaligned_be32(blocks, &write_cdb[28]);