Commit 749c226c authored by David Disseldorp's avatar David Disseldorp Committed by Martin K. Petersen

scsi: target: Return COMPARE AND WRITE miscompare offsets

SBC-4 r15 5.3 COMPARE AND WRITE command states:
  if the compare operation does not indicate a match, then terminate the
  command with CHECK CONDITION status with the sense key set to
  MISCOMPARE and the additional sense code set to MISCOMPARE DURING
  VERIFY OPERATION. In the sense data (see 4.18 and SPC-5) the offset
  from the start of the Data-Out Buffer to the first byte of data that
  was not equal shall be reported in the INFORMATION field.

This change implements the missing logic to report the miscompare offset in
the sense data INFORMATION field. As an optimization, byte-by-byte
miscompare offset calculation is only performed after memcmp() mismatch.

Link: https://lore.kernel.org/r/20201031233211.5207-5-ddiss@suse.deSigned-off-by: default avatarDavid Disseldorp <ddiss@suse.de>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent ab628b9f
...@@ -435,13 +435,13 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success, ...@@ -435,13 +435,13 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
} }
/* /*
* compare @cmp_len bytes of @read_sgl with @cmp_sgl. On miscompare return * compare @cmp_len bytes of @read_sgl with @cmp_sgl. On miscompare, fill
* TCM_MISCOMPARE_VERIFY. * @miscmp_off and return TCM_MISCOMPARE_VERIFY.
*/ */
static sense_reason_t static sense_reason_t
compare_and_write_do_cmp(struct scatterlist *read_sgl, unsigned int read_nents, compare_and_write_do_cmp(struct scatterlist *read_sgl, unsigned int read_nents,
struct scatterlist *cmp_sgl, unsigned int cmp_nents, struct scatterlist *cmp_sgl, unsigned int cmp_nents,
unsigned int cmp_len) unsigned int cmp_len, unsigned int *miscmp_off)
{ {
unsigned char *buf = NULL; unsigned char *buf = NULL;
struct scatterlist *sg; struct scatterlist *sg;
...@@ -466,18 +466,24 @@ compare_and_write_do_cmp(struct scatterlist *read_sgl, unsigned int read_nents, ...@@ -466,18 +466,24 @@ compare_and_write_do_cmp(struct scatterlist *read_sgl, unsigned int read_nents,
* Compare SCSI READ payload against verify payload * Compare SCSI READ payload against verify payload
*/ */
offset = 0; offset = 0;
ret = TCM_NO_SENSE;
for_each_sg(read_sgl, sg, read_nents, i) { for_each_sg(read_sgl, sg, read_nents, i) {
unsigned int len = min(sg->length, cmp_len); unsigned int len = min(sg->length, cmp_len);
unsigned char *addr = kmap_atomic(sg_page(sg)); unsigned char *addr = kmap_atomic(sg_page(sg));
if (memcmp(addr, buf + offset, len)) { if (memcmp(addr, buf + offset, len)) {
pr_warn("Detected MISCOMPARE for addr: %p buf: %p\n", unsigned int i;
addr, buf + offset);
kunmap_atomic(addr); for (i = 0; i < len && addr[i] == buf[offset + i]; i++)
;
*miscmp_off = offset + i;
pr_warn("Detected MISCOMPARE at offset %u\n",
*miscmp_off);
ret = TCM_MISCOMPARE_VERIFY; ret = TCM_MISCOMPARE_VERIFY;
goto out;
} }
kunmap_atomic(addr); kunmap_atomic(addr);
if (ret != TCM_NO_SENSE)
goto out;
offset += len; offset += len;
cmp_len -= len; cmp_len -= len;
...@@ -485,7 +491,6 @@ compare_and_write_do_cmp(struct scatterlist *read_sgl, unsigned int read_nents, ...@@ -485,7 +491,6 @@ compare_and_write_do_cmp(struct scatterlist *read_sgl, unsigned int read_nents,
break; break;
} }
pr_debug("COMPARE AND WRITE read data matches compare data\n"); pr_debug("COMPARE AND WRITE read data matches compare data\n");
ret = TCM_NO_SENSE;
out: out:
kfree(buf); kfree(buf);
return ret; return ret;
...@@ -501,6 +506,7 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes ...@@ -501,6 +506,7 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes
unsigned int len; unsigned int len;
unsigned int block_size = dev->dev_attrib.block_size; unsigned int block_size = dev->dev_attrib.block_size;
unsigned int compare_len = (cmd->t_task_nolb * block_size); unsigned int compare_len = (cmd->t_task_nolb * block_size);
unsigned int miscmp_off = 0;
sense_reason_t ret = TCM_NO_SENSE; sense_reason_t ret = TCM_NO_SENSE;
int i; int i;
...@@ -532,8 +538,18 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes ...@@ -532,8 +538,18 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes
cmd->t_bidi_data_nents, cmd->t_bidi_data_nents,
cmd->t_data_sg, cmd->t_data_sg,
cmd->t_data_nents, cmd->t_data_nents,
compare_len); compare_len,
if (ret) &miscmp_off);
if (ret == TCM_MISCOMPARE_VERIFY) {
/*
* SBC-4 r15: 5.3 COMPARE AND WRITE command
* In the sense data (see 4.18 and SPC-5) the offset from the
* start of the Data-Out Buffer to the first byte of data that
* was not equal shall be reported in the INFORMATION field.
*/
cmd->sense_info = miscmp_off;
goto out;
} else if (ret)
goto out; goto out;
if (sg_alloc_table(&write_tbl, cmd->t_data_nents, GFP_KERNEL) < 0) { if (sg_alloc_table(&write_tbl, cmd->t_data_nents, GFP_KERNEL) < 0) {
......
...@@ -3196,6 +3196,7 @@ static const struct sense_detail sense_detail_table[] = { ...@@ -3196,6 +3196,7 @@ static const struct sense_detail sense_detail_table[] = {
.key = MISCOMPARE, .key = MISCOMPARE,
.asc = 0x1d, /* MISCOMPARE DURING VERIFY OPERATION */ .asc = 0x1d, /* MISCOMPARE DURING VERIFY OPERATION */
.ascq = 0x00, .ascq = 0x00,
.add_sense_info = true,
}, },
[TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED] = { [TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED] = {
.key = ABORTED_COMMAND, .key = ABORTED_COMMAND,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment