scsi: target: Split out COMPARE AND WRITE memcmp into helper

In preparation for finding and returning the miscompare offset.

Link: https://lore.kernel.org/r/20201031233211.5207-4-ddiss@suse.de
Reviewed-by: Mike Christie <michael.christie@oracle.com>
Signed-off-by: David Disseldorp <ddiss@suse.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
David Disseldorp 2020-11-01 00:32:10 +01:00 committed by Martin K. Petersen
parent 8dd992fb67
commit ab628b9fc8
1 changed files with 68 additions and 53 deletions

View File

@ -434,20 +434,75 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
return ret;
}
/*
* compare @cmp_len bytes of @read_sgl with @cmp_sgl. On miscompare return
* TCM_MISCOMPARE_VERIFY.
*/
static sense_reason_t
compare_and_write_do_cmp(struct scatterlist *read_sgl, unsigned int read_nents,
struct scatterlist *cmp_sgl, unsigned int cmp_nents,
unsigned int cmp_len)
{
unsigned char *buf = NULL;
struct scatterlist *sg;
sense_reason_t ret;
unsigned int offset;
size_t rc;
int i;
buf = kzalloc(cmp_len, GFP_KERNEL);
if (!buf) {
ret = TCM_OUT_OF_RESOURCES;
goto out;
}
rc = sg_copy_to_buffer(cmp_sgl, cmp_nents, buf, cmp_len);
if (!rc) {
pr_err("sg_copy_to_buffer() failed for compare_and_write\n");
ret = TCM_OUT_OF_RESOURCES;
goto out;
}
/*
* Compare SCSI READ payload against verify payload
*/
offset = 0;
for_each_sg(read_sgl, sg, read_nents, i) {
unsigned int len = min(sg->length, cmp_len);
unsigned char *addr = kmap_atomic(sg_page(sg));
if (memcmp(addr, buf + offset, len)) {
pr_warn("Detected MISCOMPARE for addr: %p buf: %p\n",
addr, buf + offset);
kunmap_atomic(addr);
ret = TCM_MISCOMPARE_VERIFY;
goto out;
}
kunmap_atomic(addr);
offset += len;
cmp_len -= len;
if (!cmp_len)
break;
}
pr_debug("COMPARE AND WRITE read data matches compare data\n");
ret = TCM_NO_SENSE;
out:
kfree(buf);
return ret;
}
static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success,
int *post_ret)
{
struct se_device *dev = cmd->se_dev;
struct sg_table write_tbl = { };
struct scatterlist *write_sg, *sg;
unsigned char *buf = NULL, *addr;
struct scatterlist *write_sg;
struct sg_mapping_iter m;
unsigned int offset = 0, len;
unsigned int nlbas = cmd->t_task_nolb;
unsigned int len;
unsigned int block_size = dev->dev_attrib.block_size;
unsigned int compare_len = (nlbas * block_size);
unsigned int compare_len = (cmd->t_task_nolb * block_size);
sense_reason_t ret = TCM_NO_SENSE;
int rc, i;
int i;
/*
* Handle early failure in transport_generic_request_failure(),
@ -473,12 +528,13 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes
goto out;
}
buf = kzalloc(cmd->data_length, GFP_KERNEL);
if (!buf) {
pr_err("Unable to allocate compare_and_write buf\n");
ret = TCM_OUT_OF_RESOURCES;
ret = compare_and_write_do_cmp(cmd->t_bidi_data_sg,
cmd->t_bidi_data_nents,
cmd->t_data_sg,
cmd->t_data_nents,
compare_len);
if (ret)
goto out;
}
if (sg_alloc_table(&write_tbl, cmd->t_data_nents, GFP_KERNEL) < 0) {
pr_err("Unable to allocate compare_and_write sg\n");
@ -486,44 +542,9 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes
goto out;
}
write_sg = write_tbl.sgl;
/*
* Setup verify and write data payloads from total NumberLBAs.
*/
rc = sg_copy_to_buffer(cmd->t_data_sg, cmd->t_data_nents, buf,
cmd->data_length);
if (!rc) {
pr_err("sg_copy_to_buffer() failed for compare_and_write\n");
ret = TCM_OUT_OF_RESOURCES;
goto out;
}
/*
* Compare against SCSI READ payload against verify payload
*/
for_each_sg(cmd->t_bidi_data_sg, sg, cmd->t_bidi_data_nents, i) {
addr = (unsigned char *)kmap_atomic(sg_page(sg));
if (!addr) {
ret = TCM_OUT_OF_RESOURCES;
goto out;
}
len = min(sg->length, compare_len);
if (memcmp(addr, buf + offset, len)) {
pr_warn("Detected MISCOMPARE for addr: %p buf: %p\n",
addr, buf + offset);
kunmap_atomic(addr);
goto miscompare;
}
kunmap_atomic(addr);
offset += len;
compare_len -= len;
if (!compare_len)
break;
}
i = 0;
len = cmd->t_task_nolb * block_size;
len = compare_len;
sg_miter_start(&m, cmd->t_data_sg, cmd->t_data_nents, SG_MITER_TO_SG);
/*
* Currently assumes NoLB=1 and SGLs are PAGE_SIZE..
@ -568,13 +589,8 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes
__target_execute_cmd(cmd, false);
kfree(buf);
return ret;
miscompare:
pr_warn("Target/%s: Send MISCOMPARE check condition and sense\n",
dev->transport->name);
ret = TCM_MISCOMPARE_VERIFY;
out:
/*
* In the MISCOMPARE or failure case, unlock ->caw_sem obtained in
@ -582,7 +598,6 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes
*/
up(&dev->caw_sem);
sg_free_table(&write_tbl);
kfree(buf);
return ret;
}