From ded75ae18a4b70c068eb4bd69a346215901db11a Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 18 Aug 2014 08:37:30 +0200 Subject: [PATCH] Fix WRITE SAME data buffer length handling From the SPC-4 paragraph about WRITE SAME(10): "The WRITE SAME (10) command requests that the device server transfer a single logical block from the Data-Out Buffer [ ... ]". Hence always pass a data buffer when sending a WRITE SAME(10) command. Set the NDOB bit in the WRITE SAME(16) command if no data out buffer is present. Signed-off-by: Bart Van Assche --- include/scsi-lowlevel.h | 4 ++-- lib/iscsi-command.c | 6 ++++-- lib/scsi-lowlevel.c | 11 +++++++---- test-tool/test_writesame10_0blocks.c | 9 +++++---- test-tool/test_writesame10_unmap_unaligned.c | 3 ++- test-tool/test_writesame10_unmap_until_end.c | 4 ++-- 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index 897444f..343ff19 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -1072,8 +1072,8 @@ EXTERN struct scsi_task *scsi_cdb_verify16(uint64_t lba, uint32_t xferlen, int v EXTERN struct scsi_task *scsi_cdb_write10(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task *scsi_cdb_write12(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number); EXTERN struct scsi_task *scsi_cdb_write16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number); -EXTERN struct scsi_task *scsi_cdb_writesame10(int wrprotect, int anchor, int unmap, uint32_t lba, int group, uint16_t num_blocks); -EXTERN struct scsi_task *scsi_cdb_writesame16(int wrprotect, int anchor, int unmap, uint64_t lba, int group, uint32_t num_blocks); +EXTERN struct scsi_task *scsi_cdb_writesame10(int wrprotect, int anchor, int unmap, uint32_t lba, int group, uint16_t num_blocks, uint32_t datalen); +EXTERN struct scsi_task *scsi_cdb_writesame16(int wrprotect, int anchor, int unmap, uint64_t lba, int group, uint32_t num_blocks, uint32_t datalen); EXTERN struct scsi_task *scsi_cdb_writeverify10(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number); EXTERN struct scsi_task *scsi_cdb_writeverify12(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number); EXTERN struct scsi_task *scsi_cdb_writeverify16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number); diff --git a/lib/iscsi-command.c b/lib/iscsi-command.c index aed962b..1164d7b 100644 --- a/lib/iscsi-command.c +++ b/lib/iscsi-command.c @@ -1518,7 +1518,8 @@ iscsi_writesame10_task(struct iscsi_context *iscsi, int lun, uint32_t lba, struct scsi_task *task; struct iscsi_data d; - task = scsi_cdb_writesame10(wrprotect, anchor, unmap, lba, group, num_blocks); + task = scsi_cdb_writesame10(wrprotect, anchor, unmap, lba, group, + num_blocks, datalen); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "writesame10 cdb."); @@ -1551,7 +1552,8 @@ iscsi_writesame16_task(struct iscsi_context *iscsi, int lun, uint64_t lba, struct scsi_task *task; struct iscsi_data d; - task = scsi_cdb_writesame16(wrprotect, anchor, unmap, lba, group, num_blocks); + task = scsi_cdb_writesame16(wrprotect, anchor, unmap, lba, group, + num_blocks, datalen); if (task == NULL) { iscsi_set_error(iscsi, "Out-of-memory: Failed to create " "writesame16 cdb."); diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index a60ae73..696626b 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -2106,7 +2106,7 @@ err: * WRITE_SAME10 */ struct scsi_task * -scsi_cdb_writesame10(int wrprotect, int anchor, int unmap, uint32_t lba, int group, uint16_t num_blocks) +scsi_cdb_writesame10(int wrprotect, int anchor, int unmap, uint32_t lba, int group, uint16_t num_blocks, uint32_t datalen) { struct scsi_task *task; @@ -2135,7 +2135,7 @@ scsi_cdb_writesame10(int wrprotect, int anchor, int unmap, uint32_t lba, int gro task->cdb_size = 10; task->xfer_dir = SCSI_XFER_WRITE; - task->expxferlen = 512; + task->expxferlen = datalen; return task; } @@ -2144,7 +2144,7 @@ scsi_cdb_writesame10(int wrprotect, int anchor, int unmap, uint32_t lba, int gro * WRITE_SAME16 */ struct scsi_task * -scsi_cdb_writesame16(int wrprotect, int anchor, int unmap, uint64_t lba, int group, uint32_t num_blocks) +scsi_cdb_writesame16(int wrprotect, int anchor, int unmap, uint64_t lba, int group, uint32_t num_blocks, uint32_t datalen) { struct scsi_task *task; @@ -2165,6 +2165,9 @@ scsi_cdb_writesame16(int wrprotect, int anchor, int unmap, uint64_t lba, int gro if (unmap) { task->cdb[1] |= 0x08; } + if (datalen == 0) { + task->cdb[1] |= 0x01; + } scsi_set_uint32(&task->cdb[2], lba >> 32); scsi_set_uint32(&task->cdb[6], lba & 0xffffffff); scsi_set_uint32(&task->cdb[10], num_blocks); @@ -2174,7 +2177,7 @@ scsi_cdb_writesame16(int wrprotect, int anchor, int unmap, uint64_t lba, int gro task->cdb_size = 16; task->xfer_dir = SCSI_XFER_WRITE; - task->expxferlen = 512; + task->expxferlen = datalen; return task; } diff --git a/test-tool/test_writesame10_0blocks.c b/test-tool/test_writesame10_0blocks.c index a9b0c0b..11ac599 100644 --- a/test-tool/test_writesame10_0blocks.c +++ b/test-tool/test_writesame10_0blocks.c @@ -27,6 +27,7 @@ void test_writesame10_0blocks(void) { int ret; + unsigned char *buf = alloca(block_size); CHECK_FOR_DATALOSS; CHECK_FOR_SBC; @@ -41,7 +42,7 @@ test_writesame10_0blocks(void) inq_bl->wsnz); ret = writesame10(iscsic, tgt_lun, 0, block_size, 0, - 0, 0, 0, 0, NULL); + 0, 0, 0, 0, buf); if (ret == -2) { CU_PASS("[SKIPPED] Target does not support WRITESAME10. Skipping test"); return; @@ -60,20 +61,20 @@ test_writesame10_0blocks(void) logging(LOG_VERBOSE, "Test WRITESAME10 0-blocks one block past end-of-LUN"); ret = writesame10_lbaoutofrange(iscsic, tgt_lun, num_blocks + 1, block_size, 0, - 0, 0, 0, 0, NULL); + 0, 0, 0, 0, buf); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Test WRITESAME10 0-blocks at LBA==2^31"); ret = writesame10_lbaoutofrange(iscsic, tgt_lun, 0x80000000, block_size, 0, - 0, 0, 0, 0, NULL); + 0, 0, 0, 0, buf); CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Test WRITESAME10 0-blocks at LBA==-1"); ret = writesame10_lbaoutofrange(iscsic, tgt_lun, -1, block_size, 0, - 0, 0, 0, 0, NULL); + 0, 0, 0, 0, buf); CU_ASSERT_EQUAL(ret, 0); } diff --git a/test-tool/test_writesame10_unmap_unaligned.c b/test-tool/test_writesame10_unmap_unaligned.c index cd370d9..35ce236 100644 --- a/test-tool/test_writesame10_unmap_unaligned.c +++ b/test-tool/test_writesame10_unmap_unaligned.c @@ -29,6 +29,7 @@ void test_writesame10_unmap_unaligned(void) { int i, ret; + unsigned char *buf = alloca(block_size); CHECK_FOR_DATALOSS; CHECK_FOR_THIN_PROVISIONING; @@ -42,7 +43,7 @@ test_writesame10_unmap_unaligned(void) logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME10 at LBA:%d", lbppb - i, i); ret = writesame10_invalidfieldincdb(iscsic, tgt_lun, i, block_size, lbppb - i, - 0, 1, 0, 0, NULL); + 0, 1, 0, 0, buf); CU_ASSERT_EQUAL(ret, 0); } } diff --git a/test-tool/test_writesame10_unmap_until_end.c b/test-tool/test_writesame10_unmap_until_end.c index a8a0f49..8cbc761 100644 --- a/test-tool/test_writesame10_unmap_until_end.c +++ b/test-tool/test_writesame10_unmap_until_end.c @@ -54,8 +54,8 @@ test_writesame10_unmap_until_end(void) logging(LOG_VERBOSE, "Unmap %d blocks using WRITESAME10", i); ret = writesame10(iscsic, tgt_lun, num_blocks - i, - 0, i, - 0, 1, 0, 0, NULL); + block_size, i, + 0, 1, 0, 0, buf); CU_ASSERT_EQUAL(ret, 0); if (rc16->lbprz) {