diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index bedefd8..a70dcf7 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -1167,6 +1167,7 @@ enum ec_descr_type_code { STRM_TO_BLK_SEG_DESCR = 0x01, BLK_TO_BLK_SEG_DESCR = 0x02, STRM_TO_STRM_SEG_DESCR = 0x03, + BLK_TO_BLK_OFF_SEG_DESCR = 0x0A, /* Target descriptors : 0xEO to 0xFE */ IDENT_DESCR_TGT_DESCR = 0xE4, diff --git a/test-tool/iscsi-support.c b/test-tool/iscsi-support.c index 0e224bb..498e06e 100644 --- a/test-tool/iscsi-support.c +++ b/test-tool/iscsi-support.c @@ -2883,6 +2883,9 @@ int get_desc_len(enum ec_descr_type_code desc_type) case STRM_TO_STRM_SEG_DESCR: desc_len = 0x10 + SEG_DESC_SRC_INDEX_OFFSET; break; + case BLK_TO_BLK_OFF_SEG_DESCR: + desc_len = 0x1C + SEG_DESC_SRC_INDEX_OFFSET; + break; /* Target Descriptors */ case IPV6_TGT_DESCR: @@ -2992,6 +2995,23 @@ int populate_seg_desc_b2b(unsigned char *desc, int dc, int cat, int src_index, i return desc_len; } +int populate_seg_desc_b2b_off(unsigned char *desc, int cat, int src_index, + int dst_index, uint32_t num_bytes, + uint64_t src_lba, uint64_t dst_lba, + uint16_t src_byte_off, uint16_t dst_byte_off) +{ + int desc_len = populate_seg_desc_hdr(desc, BLK_TO_BLK_OFF_SEG_DESCR, 0, + cat, src_index, dst_index); + + scsi_set_uint32(&desc[8], num_bytes); + scsi_set_uint64(&desc[12], src_lba); + scsi_set_uint64(&desc[20], dst_lba); + scsi_set_uint16(&desc[28], src_byte_off); + scsi_set_uint16(&desc[30], dst_byte_off); + + return desc_len; +} + void populate_param_header(unsigned char *buf, int list_id, int str, int list_id_usage, int prio, int tgt_desc_len, int seg_desc_len, int inline_data_len) { buf[0] = list_id; diff --git a/test-tool/iscsi-support.h b/test-tool/iscsi-support.h index 4444c3c..78a20cc 100644 --- a/test-tool/iscsi-support.h +++ b/test-tool/iscsi-support.h @@ -917,6 +917,10 @@ int get_desc_len(enum ec_descr_type_code desc_type); int populate_tgt_desc(unsigned char *desc, enum ec_descr_type_code desc_type, int luid_type, int nul, int peripheral_type, uint16_t rel_init_port_id, int pad, struct scsi_device *dev); int populate_seg_desc_hdr(unsigned char *hdr, enum ec_descr_type_code desc_type, int dc, int cat, uint16_t src_index, uint16_t dst_index); int populate_seg_desc_b2b(unsigned char *desc, int dc, int cat, int src_index, int dst_index, uint16_t num_blks, uint64_t src_lba, uint64_t dst_lba); +int populate_seg_desc_b2b_off(unsigned char *desc, int cat, int src_index, + int dst_index, uint32_t num_bytes, + uint64_t src_lba, uint64_t dst_lba, + uint16_t src_byte_off, uint16_t dst_byte_off); void populate_param_header(unsigned char *buf, int list_id, int str, int list_id_usage, int prio, int tgt_desc_len, int seg_desc_len, int inline_data_len); int receive_copy_results(struct scsi_task **task, struct scsi_device *sdev, enum scsi_copy_results_sa sa, int list_id, diff --git a/test-tool/iscsi-test-cu.c b/test-tool/iscsi-test-cu.c index 0591459..31ab549 100644 --- a/test-tool/iscsi-test-cu.c +++ b/test-tool/iscsi-test-cu.c @@ -296,6 +296,7 @@ static CU_TestInfo tests_extended_copy[] = { { "DescrType", test_extendedcopy_descr_type }, { "ValidTgtDescr", test_extendedcopy_validate_tgt_descr }, { "ValidSegDescr", test_extendedcopy_validate_seg_descr }, + { "Large", test_extendedcopy_large }, CU_TEST_INFO_NULL }; diff --git a/test-tool/iscsi-test-cu.h b/test-tool/iscsi-test-cu.h index 4951080..601297d 100644 --- a/test-tool/iscsi-test-cu.h +++ b/test-tool/iscsi-test-cu.h @@ -63,6 +63,7 @@ void test_extendedcopy_descr_limits(void); void test_extendedcopy_descr_type(void); void test_extendedcopy_validate_tgt_descr(void); void test_extendedcopy_validate_seg_descr(void); +void test_extendedcopy_large(void); void test_get_lba_status_simple(void); void test_get_lba_status_beyond_eol(void); diff --git a/test-tool/test_extendedcopy_simple.c b/test-tool/test_extendedcopy_simple.c index 85adf1c..0f75945 100644 --- a/test-tool/test_extendedcopy_simple.c +++ b/test-tool/test_extendedcopy_simple.c @@ -35,7 +35,7 @@ test_extendedcopy_simple(void) unsigned int copied_blocks; unsigned char *buf1; unsigned char *buf2; - + uint64_t cp_dst_lba; copied_blocks = num_blocks / 2; if (copied_blocks > 2048) @@ -50,6 +50,12 @@ test_extendedcopy_simple(void) CHECK_FOR_DATALOSS; + cp_dst_lba = num_blocks - copied_blocks; + logging(LOG_VERBOSE, "Zero %u blocks at the end of the LUN (LBA:%llu)", + copied_blocks, (unsigned long long)cp_dst_lba); + memset(buf1, '\0', copied_blocks * block_size); + WRITE16(sd, cp_dst_lba, copied_blocks * block_size, + block_size, 0, 0, 0, 0, 0, buf1, EXPECT_STATUS_GOOD); logging(LOG_VERBOSE, "Write %u blocks of 'A' at LBA:0", copied_blocks); memset(buf1, 'A', copied_blocks * block_size); WRITE16(sd, 0, copied_blocks * block_size, block_size, 0, 0, 0, 0, 0, @@ -67,9 +73,9 @@ test_extendedcopy_simple(void) LU_ID_TYPE_LUN, 0, 0, 0, 0, sd); tgt_desc_len = offset - XCOPY_DESC_OFFSET; - /* Iniitialize segment descriptor list with one segment descriptor */ + /* Initialize segment descriptor list with one segment descriptor */ offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0, - copied_blocks, 0, num_blocks - copied_blocks); + copied_blocks, 0, cp_dst_lba); seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; /* Initialize the parameter list header */ @@ -80,14 +86,113 @@ test_extendedcopy_simple(void) logging(LOG_VERBOSE, "Read %u blocks from end of the LUN", copied_blocks); - READ16(sd, NULL, num_blocks - copied_blocks, copied_blocks * block_size, + READ16(sd, NULL, cp_dst_lba, copied_blocks * block_size, block_size, 0, 0, 0, 0, 0, buf2, EXPECT_STATUS_GOOD); if (memcmp(buf1, buf2, copied_blocks * block_size)) { CU_FAIL("Blocks were not copied correctly"); } - + + free(buf1); + free(buf2); +} + +/* to save time, avoid copying more than 256M */ +#define TEST_XCOPY_LARGE_CP_LEN_MAX (256 * 1024 * 1024) +void +test_extendedcopy_large(void) +{ + struct scsi_task *edl_task = NULL; + struct scsi_copy_results_op_params *opp = NULL; + uint32_t cp_len_bytes = 0; + int i, tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET; + struct iscsi_data data; + unsigned char *xcopybuf; + unsigned int write_blocks; + unsigned char *buf1; + unsigned char *buf2; + uint64_t cp_dst_lba; + + logging(LOG_VERBOSE, LOG_BLANK_LINE); + logging(LOG_VERBOSE, + "Test large EXTENDED COPY with type 0x0A segment descriptor"); + + CHECK_FOR_DATALOSS; + + /* check for 0x0A type SD support, which offers 32-bit lengths */ + RECEIVE_COPY_RESULTS(&edl_task, sd, SCSI_COPY_RESULTS_OP_PARAMS, 0, + (void **)&opp, EXPECT_STATUS_GOOD); + CU_ASSERT_NOT_EQUAL(opp, NULL); + + for (i = 0; i < opp->impl_desc_list_length; i++) { + if (opp->imp_desc_type_codes[i] == BLK_TO_BLK_OFF_SEG_DESCR) { + cp_len_bytes = opp->max_segment_length; + break; + } + } + scsi_free_scsi_task(edl_task); + + if (cp_len_bytes == 0) { + CU_PASS("[SKIPPED] BLK_TO_BLK_OFF_SEG_DESCR not supported"); + } + if (cp_len_bytes > TEST_XCOPY_LARGE_CP_LEN_MAX) { + cp_len_bytes = TEST_XCOPY_LARGE_CP_LEN_MAX; + } + if (num_blocks < (cp_len_bytes / block_size) * 2) { + CU_PASS("[SKIPPED] device too small to handle maxlen XCOPY"); + } + + write_blocks = num_blocks / 2; + if (write_blocks > 2048) + write_blocks = 2048; + buf1 = malloc(write_blocks * block_size); + buf2 = malloc(write_blocks * block_size); + + cp_dst_lba = num_blocks - (cp_len_bytes / block_size); + logging(LOG_VERBOSE, "Zero %u blocks at the end of the LUN (LBA:%llu)", + write_blocks, (unsigned long long)cp_dst_lba); + memset(buf1, '\0', write_blocks * block_size); + WRITE16(sd, cp_dst_lba, write_blocks * block_size, + block_size, 0, 0, 0, 0, 0, buf1, EXPECT_STATUS_GOOD); + logging(LOG_VERBOSE, "Write %u blocks of 'B' at LBA:0", write_blocks); + memset(buf1, 'B', write_blocks * block_size); + WRITE16(sd, 0, write_blocks * block_size, block_size, 0, 0, 0, 0, 0, + buf1, EXPECT_STATUS_GOOD); + + data.size = XCOPY_DESC_OFFSET + + get_desc_len(IDENT_DESCR_TGT_DESCR) + + get_desc_len(BLK_TO_BLK_OFF_SEG_DESCR); + data.data = alloca(data.size); + xcopybuf = data.data; + memset(xcopybuf, 0, data.size); + + /* Initialize target descriptor list with one target descriptor */ + offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR, + LU_ID_TYPE_LUN, 0, 0, 0, 0, sd); + tgt_desc_len = offset - XCOPY_DESC_OFFSET; + + /* Initialize segment descriptor list with one segment descriptor */ + offset += populate_seg_desc_b2b_off(xcopybuf+offset, 0, 0, 0, + cp_len_bytes, 0, cp_dst_lba, 0, 0); + seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; + + /* Initialize the parameter list header */ + populate_param_header(xcopybuf, 1, 0, LIST_ID_USAGE_DISCARD, 0, + tgt_desc_len, seg_desc_len, 0); + + EXTENDEDCOPY(sd, &data, EXPECT_STATUS_GOOD); + + logging(LOG_VERBOSE, "Read %u blocks from end of the LUN", + write_blocks); + READ16(sd, NULL, cp_dst_lba, write_blocks * block_size, + block_size, 0, 0, 0, 0, 0, buf2, + EXPECT_STATUS_GOOD); + + if (memcmp(buf1, buf2, write_blocks * block_size)) { + CU_FAIL("Blocks were not copied correctly"); + } + free(buf1); free(buf2); }