diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index 307f12f..79c020f 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -54,6 +54,8 @@ enum scsi_opcode { SCSI_OPCODE_MODESENSE10 = 0x5A, SCSI_OPCODE_PERSISTENT_RESERVE_IN = 0x5E, SCSI_OPCODE_PERSISTENT_RESERVE_OUT = 0x5F, + SCSI_OPCODE_EXTENDED_COPY = 0x83, + SCSI_OPCODE_RECEIVE_COPY_RESULTS = 0x84, SCSI_OPCODE_READ16 = 0x88, SCSI_OPCODE_COMPARE_AND_WRITE = 0x89, SCSI_OPCODE_WRITE16 = 0x8A, @@ -148,7 +150,10 @@ enum scsi_sense_key { EXTERN const char *scsi_sense_key_str(int key); /* ascq */ +#define SCSI_SENSE_ASCQ_NO_ADDL_SENSE 0x0000 #define SCSI_SENSE_ASCQ_SANITIZE_IN_PROGRESS 0x041b +#define SCSI_SENSE_ASCQ_UNREACHABLE_COPY_TARGET 0x0804 +#define SCSI_SENSE_ASCQ_COPY_TARGET_DEVICE_NOT_REACHABLE 0x0d02 #define SCSI_SENSE_ASCQ_WRITE_AFTER_SANITIZE_REQUIRED 0x1115 #define SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR 0x1a00 #define SCSI_SENSE_ASCQ_MISCOMPARE_DURING_VERIFY 0x1d00 @@ -158,6 +163,10 @@ EXTERN const char *scsi_sense_key_str(int key); #define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB 0x2400 #define SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED 0x2500 #define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST 0x2600 +#define SCSI_SENSE_ASCQ_TOO_MANY_TARGET_DESCRIPTORS 0x2606 +#define SCSI_SENSE_ASCQ_UNSUPPORTED_TARGET_DESCRIPTOR_TYPE_CODE 0x2607 +#define SCSI_SENSE_ASCQ_TOO_MANY_SEGMENT_DESCRIPTORS 0x2608 +#define SCSI_SENSE_ASCQ_UNSUPPORTED_SEGMENT_DESCRIPTOR_TYPE_CODE 0x2609 #define SCSI_SENSE_ASCQ_WRITE_PROTECTED 0x2700 #define SCSI_SENSE_ASCQ_HARDWARE_WRITE_PROTECTED 0x2701 #define SCSI_SENSE_ASCQ_SOFTWARE_WRITE_PROTECTED 0x2702 @@ -348,7 +357,6 @@ EXTERN struct scsi_task *scsi_cdb_testunitready(void); EXTERN struct scsi_task *scsi_cdb_sanitize(int immed, int ause, int sa, int param_len); - /* * REPORTLUNS */ @@ -1095,6 +1103,78 @@ EXTERN struct scsi_task *scsi_cdb_writeverify12(uint32_t lba, uint32_t xferlen, EXTERN struct scsi_task *scsi_cdb_writeverify16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number); +/* + * EXTENDED COPY + */ +#define XCOPY_DESC_OFFSET 16 +#define SEG_DESC_SRC_INDEX_OFFSET 4 +enum list_id_usage { + LIST_ID_USAGE_HOLD = 0, + LIST_ID_USAGE_DISCARD = 2, + LIST_ID_USAGE_DISABLE = 3 +}; + +enum ec_descr_type_code { + /* Segment descriptors : 0x00 to 0xBF */ + BLK_TO_STRM_SEG_DESCR = 0x00, + STRM_TO_BLK_SEG_DESCR = 0x01, + BLK_TO_BLK_SEG_DESCR = 0x02, + STRM_TO_STRM_SEG_DESCR = 0x03, + + /* Target descriptors : 0xEO to 0xFE */ + IDENT_DESCR_TGT_DESCR = 0xE4, + IPV4_TGT_DESCR = 0xE5, + IPV6_TGT_DESCR = 0xEA, + IP_COPY_SVC_TGT_DESCR = 0xEB +}; + +enum lu_id_type { + LU_ID_TYPE_LUN = 0x00, + LU_ID_TYPE_PROXY_TOKEN = 0x01, + LU_ID_TYPE_RSVD = 0x02, + LU_ID_TYPE_RSVD1 = 0x03 +}; + +EXTERN struct scsi_task *scsi_cdb_extended_copy(int immed); + +/* + * RECEIVE COPY RESULTS + */ +enum scsi_copy_results_sa { + SCSI_COPY_RESULTS_COPY_STATUS = 0, + SCSI_COPY_RESULTS_RECEIVE_DATA = 1, + SCSI_COPY_RESULTS_OP_PARAMS = 3, + SCSI_COPY_RESULTS_FAILED_SEGMENT = 4, +}; + +EXTERN struct scsi_task *scsi_cdb_receive_copy_results(enum scsi_copy_results_sa sa, int list_id, int xferlen); + +struct scsi_copy_results_copy_status { + uint32_t available_data; + uint8_t copy_manager_status; + uint8_t hdd; + uint16_t segments_processed; + uint8_t transfer_count_units; + uint32_t transfer_count; +}; + +struct scsi_copy_results_op_params { + uint32_t available_data; + uint16_t max_target_desc_count; + uint16_t max_segment_desc_count; + uint32_t max_desc_list_length; + uint32_t max_segment_length; + uint32_t max_inline_data_length; + uint32_t held_data_limit; + uint32_t max_stream_device_transfer_size; + uint16_t total_concurrent_copies; + uint8_t max_concurrent_copies; + uint8_t data_segment_granularity; + uint8_t inline_data_granularity; + uint8_t held_data_granularity; + uint8_t impl_desc_list_length; + uint8_t imp_desc_type_codes[0]; +}; void *scsi_malloc(struct scsi_task *task, size_t size); uint64_t scsi_get_uint64(const unsigned char *c); diff --git a/lib/libiscsi.def b/lib/libiscsi.def index 1491666..63dd6bc 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -159,6 +159,7 @@ iscsi_writesame16_sync iscsi_writesame16_task scsi_association_to_str scsi_cdb_compareandwrite +scsi_cdb_extended_copy scsi_cdb_inquiry scsi_cdb_get_lba_status scsi_cdb_modeselect6 @@ -177,6 +178,7 @@ scsi_cdb_read6 scsi_cdb_readcapacity10 scsi_cdb_readcapacity16 scsi_cdb_readtoc +scsi_cdb_receive_copy_results scsi_cdb_reserve6 scsi_cdb_release6 scsi_cdb_report_supported_opcodes diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index 7b8a051..a8db966 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -157,6 +157,7 @@ iscsi_writesame16_sync iscsi_writesame16_task scsi_association_to_str scsi_cdb_compareandwrite +scsi_cdb_extended_copy scsi_cdb_inquiry scsi_cdb_get_lba_status scsi_cdb_modeselect6 @@ -175,6 +176,7 @@ scsi_cdb_read6 scsi_cdb_readcapacity10 scsi_cdb_readcapacity16 scsi_cdb_readtoc +scsi_cdb_receive_copy_results scsi_cdb_reserve6 scsi_cdb_release6 scsi_cdb_report_supported_opcodes diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index e1ebb20..3d6e412 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -834,6 +834,61 @@ scsi_persistentreservein_datain_getfullsize(struct scsi_task *task) } } +static void * +scsi_receivecopyresults_datain_unmarshall(struct scsi_task *task) +{ + int sa = task->cdb[1] & 0x1f; + int len, i; + struct scsi_copy_results_copy_status *cs; + struct scsi_copy_results_op_params *op; + + switch (sa) { + case SCSI_COPY_RESULTS_COPY_STATUS: + len = task_get_uint32(task, 0); + + cs = scsi_malloc(task, len+4); + if (cs == NULL) { + return NULL; + } + cs->available_data = len; + cs->copy_manager_status = task_get_uint8(task, 4) & 0x7F; + cs->hdd = (task_get_uint8(task, 4) & 0x80) >> 7; + cs->segments_processed = task_get_uint16(task, 5); + cs->transfer_count_units = task_get_uint8(task, 7); + cs->transfer_count = task_get_uint32(task, 8); + return cs; + + case SCSI_COPY_RESULTS_OP_PARAMS: + len = task_get_uint32(task, 0); + + op = scsi_malloc(task, len+4); + if (op == NULL) { + return NULL; + } + op->available_data = len; + op->max_target_desc_count = task_get_uint16(task, 8); + op->max_segment_desc_count = task_get_uint16(task, 10); + op->max_desc_list_length = task_get_uint32(task, 12); + op->max_segment_length = task_get_uint32(task, 16); + op->max_inline_data_length = task_get_uint32(task, 20); + op->held_data_limit = task_get_uint32(task, 24); + op->max_stream_device_transfer_size = task_get_uint32(task, 28); + op->total_concurrent_copies = task_get_uint16(task, 34); + op->max_concurrent_copies = task_get_uint8(task, 36); + op->data_segment_granularity = task_get_uint8(task, 37); + op->inline_data_granularity = task_get_uint8(task, 38); + op->held_data_granularity = task_get_uint8(task, 39); + op->impl_desc_list_length = task_get_uint8(task, 43); + for (i = 0; i < (int)op->impl_desc_list_length; i++) { + op->imp_desc_type_codes[i] = task_get_uint8(task, 44+i); + } + return op; + default: + return NULL; + } +} + + static void * scsi_persistentreservein_datain_unmarshall(struct scsi_task *task) { @@ -3230,6 +3285,63 @@ scsi_cdb_writeverify16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprot return task; } +/* + * EXTENDED COPY + */ +struct scsi_task * +scsi_cdb_extended_copy(int param_len) +{ + struct scsi_task *task; + + task = malloc(sizeof(struct scsi_task)); + if (task == NULL) + return NULL; + + memset(task, 0, sizeof(struct scsi_task)); + task->cdb[0] = SCSI_OPCODE_EXTENDED_COPY; + task->cdb[10] = (param_len >> 24) & 0xFF; + task->cdb[11] = (param_len >> 16) & 0xFF; + task->cdb[12] = (param_len >> 8) & 0xFF; + task->cdb[13] = param_len & 0xFF; + /* Inititalize other fields in CDB */ + task->cdb_size = 16; + task->xfer_dir = SCSI_XFER_WRITE; + task->expxferlen = param_len; + + return task; +} + +/* + * RECEIVE COPY RESULTS + */ +struct scsi_task * +scsi_cdb_receive_copy_results(enum scsi_copy_results_sa sa, int list_id, int xferlen) +{ + struct scsi_task *task; + + task = malloc(sizeof(struct scsi_task)); + if (task == NULL) { + return NULL; + } + + memset(task, 0, sizeof(struct scsi_task)); + task->cdb[0] = SCSI_OPCODE_RECEIVE_COPY_RESULTS; + task->cdb[1] |= sa & 0x1f; + task->cdb[2] = list_id & 0xFF; + + scsi_set_uint32(&task->cdb[10], xferlen); + + task->cdb_size = 16; + if (xferlen != 0) { + task->xfer_dir = SCSI_XFER_READ; + } else { + task->xfer_dir = SCSI_XFER_NONE; + } + task->expxferlen = xferlen; + + return task; +} + int scsi_datain_getfullsize(struct scsi_task *task) { @@ -3281,6 +3393,8 @@ scsi_datain_unmarshall(struct scsi_task *task) return scsi_persistentreservein_datain_unmarshall(task); case SCSI_OPCODE_MAINTENANCE_IN: return scsi_maintenancein_datain_unmarshall(task); + case SCSI_OPCODE_RECEIVE_COPY_RESULTS: + return scsi_receivecopyresults_datain_unmarshall(task); } return NULL; } diff --git a/test-tool/Makefile.am b/test-tool/Makefile.am index d177f60..ec03a09 100644 --- a/test-tool/Makefile.am +++ b/test-tool/Makefile.am @@ -19,6 +19,12 @@ iscsi_test_cu_SOURCES = iscsi-test-cu.c \ test_compareandwrite_simple.c \ test_compareandwrite_dpofua.c \ test_compareandwrite_miscompare.c \ + test_extendedcopy_simple.c \ + test_extendedcopy_param.c \ + test_extendedcopy_descr_limits.c \ + test_extendedcopy_descr_type.c \ + test_extendedcopy_validate_tgt_descr.c \ + test_extendedcopy_validate_seg_descr.c \ test_get_lba_status_simple.c \ test_get_lba_status_beyond_eol.c \ test_get_lba_status_unmap_single.c \ @@ -93,6 +99,8 @@ iscsi_test_cu_SOURCES = iscsi-test-cu.c \ test_readcapacity16_protection.c \ test_readcapacity16_simple.c \ test_readonly_sbc.c \ + test_receive_copy_results_copy_status.c \ + test_receive_copy_results_op_params.c \ test_report_supported_opcodes_one_command.c \ test_report_supported_opcodes_rctd.c \ test_report_supported_opcodes_servactv.c \ diff --git a/test-tool/iscsi-support.c b/test-tool/iscsi-support.c index 5983a69..4dd271d 100644 --- a/test-tool/iscsi-support.c +++ b/test-tool/iscsi-support.c @@ -63,8 +63,12 @@ int no_medium_ascqs[3] = { int lba_oob_ascqs[1] = { SCSI_SENSE_ASCQ_LBA_OUT_OF_RANGE }; -int invalid_cdb_ascqs[1] = { - SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB +int invalid_cdb_ascqs[2] = { + SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB, + SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST +}; +int param_list_len_err_ascqs[1] = { + SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR }; int write_protect_ascqs[3] = { SCSI_SENSE_ASCQ_WRITE_PROTECTED, @@ -80,6 +84,19 @@ int removal_ascqs[1] = { int miscompare_ascqs[1] = { SCSI_SENSE_ASCQ_MISCOMPARE_DURING_VERIFY }; +int too_many_desc_ascqs[2] = { + SCSI_SENSE_ASCQ_TOO_MANY_TARGET_DESCRIPTORS, + SCSI_SENSE_ASCQ_TOO_MANY_SEGMENT_DESCRIPTORS, +}; +int unsupp_desc_code_ascqs[2] = { + SCSI_SENSE_ASCQ_UNSUPPORTED_TARGET_DESCRIPTOR_TYPE_CODE, + SCSI_SENSE_ASCQ_UNSUPPORTED_SEGMENT_DESCRIPTOR_TYPE_CODE +}; +int copy_aborted_ascqs[3] = { + SCSI_SENSE_ASCQ_NO_ADDL_SENSE, + SCSI_SENSE_ASCQ_UNREACHABLE_COPY_TARGET, + SCSI_SENSE_ASCQ_COPY_TARGET_DEVICE_NOT_REACHABLE +}; struct scsi_inquiry_standard *inq; struct scsi_inquiry_logical_block_provisioning *inq_lbp; @@ -2528,3 +2545,217 @@ finished: } return ret; } + +/* Extended Copy */ +int extendedcopy(struct scsi_device *sdev, struct iscsi_data *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq) +{ + struct scsi_task *task; + int ret; + + logging(LOG_VERBOSE, "Send EXTENDED COPY (Expecting %s)", + scsi_status_str(status)); + + if (!data_loss) { + logging(LOG_NORMAL, "--dataloss flag is not set in. Skipping extendedcopy\n"); + return -1; + } + + task = scsi_cdb_extended_copy(data->size); + + assert(task != NULL); + + send_scsi_command(sdev, task, data); + + ret = check_result("EXTENDEDCOPY", sdev, task, status, key, ascq, num_ascq); + scsi_free_scsi_task(task); + + return ret; +} + +int get_desc_len(enum ec_descr_type_code desc_type) +{ + int desc_len = 0; + switch (desc_type) { + /* Segment Descriptors */ + case BLK_TO_STRM_SEG_DESCR: + case STRM_TO_BLK_SEG_DESCR: + desc_len = 0x18; + break; + case BLK_TO_BLK_SEG_DESCR: + desc_len = 0x1C; + break; + case STRM_TO_STRM_SEG_DESCR: + desc_len = 0x14; + break; + + /* Target Descriptors */ + case IPV6_TGT_DESCR: + case IP_COPY_SVC_TGT_DESCR: + desc_len = 64; + break; + case IDENT_DESCR_TGT_DESCR: + default: + if (desc_type >= 0xE0 && desc_type <= 0xE9) + desc_len = 32; + } + + return desc_len; +} + +void populate_ident_tgt_desc(unsigned char *buf, struct scsi_device *dev) +{ + int ret; + struct scsi_task *inq_di_task = NULL; + struct scsi_inquiry_device_identification *inq_di = NULL; + struct scsi_inquiry_device_designator *desig, *tgt_desig = NULL; + enum scsi_designator_type prev_type = 0; + + ret = inquiry(dev, &inq_di_task, 1, SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, 255, EXPECT_STATUS_GOOD); + if (ret < 0 || inq_di_task == NULL) { + logging(LOG_NORMAL, "Failed to read Device Identification page"); + goto finished; + } else { + inq_di = scsi_datain_unmarshall(inq_di_task); + if (inq_di == NULL) { + logging(LOG_NORMAL, "Failed to unmarshall inquiry datain blob"); + goto finished; + } + } + + for (desig = inq_di->designators; desig; desig = desig->next) { + switch (desig->designator_type) { + case SCSI_DESIGNATOR_TYPE_VENDOR_SPECIFIC: + case SCSI_DESIGNATOR_TYPE_T10_VENDORT_ID: + case SCSI_DESIGNATOR_TYPE_EUI_64: + case SCSI_DESIGNATOR_TYPE_NAA: + if (prev_type <= desig->designator_type) { + tgt_desig = desig; + prev_type = desig->designator_type; + } + default: + continue; + } + } + if (tgt_desig == NULL) { + logging(LOG_NORMAL, "No suitalble target descriptor format found"); + goto finished; + } + + buf[0] = tgt_desig->code_set; + buf[1] = (tgt_desig->designator_type & 0xF) | ((tgt_desig->association & 3) << 4); + buf[3] = tgt_desig->designator_length; + memcpy(buf + 4, tgt_desig->designator, tgt_desig->designator_length); + + finished: + scsi_free_scsi_task(inq_di_task); +} + +int populate_tgt_desc(unsigned char *desc, enum ec_descr_type_code desc_type, int luid_type, int nul, int peripheral_type, int rel_init_port_id, int pad, struct scsi_device *dev) +{ + desc[0] = desc_type; + desc[1] = (luid_type << 6) | (nul << 5) | peripheral_type; + desc[2] = (rel_init_port_id >> 8) & 0xFF; + desc[3] = rel_init_port_id & 0xFF; + + if (desc_type == IDENT_DESCR_TGT_DESCR) + populate_ident_tgt_desc(desc+4, dev); + + if (peripheral_type == 0) { + // Issue readcapacity for each sd if testing with different LUs + // If single LU, use block_size from prior readcapacity involcation + desc[28] = pad << 2; + desc[29] = (block_size >> 16) & 0xFF; + desc[30] = (block_size >> 8) & 0xFF; + desc[31] = block_size & 0xFF; + } + return get_desc_len(desc_type); +} + +int populate_seg_desc_hdr(unsigned char *hdr, enum ec_descr_type_code desc_type, int dc, int cat, int src_index, int dst_index) +{ + int desc_len = get_desc_len(desc_type); + + hdr[0] = desc_type; + hdr[1] = ((dc << 1) | cat) & 0xFF; + hdr[2] = (desc_len >> 8) & 0xFF; + hdr[3] = (desc_len - SEG_DESC_SRC_INDEX_OFFSET) & 0xFF; /* don't account for the first 4 bytes in descriptor header*/ + hdr[4] = (src_index >> 8) & 0xFF; + hdr[5] = src_index & 0xFF; + hdr[6] = (dst_index >> 8) & 0xFF; + hdr[7] = dst_index & 0xFF; + + return desc_len; +} + +int populate_seg_desc_b2b(unsigned char *desc, int dc, int cat, int src_index, int dst_index, int num_blks, uint64_t src_lba, uint64_t dst_lba) +{ + int desc_len = populate_seg_desc_hdr(desc, BLK_TO_BLK_SEG_DESCR, dc, cat, src_index, dst_index); + + desc[10] = (num_blks >> 8) & 0xFF; + desc[11] = num_blks & 0xFF; + desc[12] = (src_lba >> 56) & 0xFF; + desc[13] = (src_lba >> 48) & 0xFF; + desc[14] = (src_lba >> 40) & 0xFF; + desc[15] = (src_lba >> 32) & 0xFF; + desc[16] = (src_lba >> 24) & 0xFF; + desc[17] = (src_lba >> 16) & 0xFF; + desc[18] = (src_lba >> 8) & 0xFF; + desc[19] = src_lba & 0xFF; + desc[20] = (dst_lba >> 56) & 0xFF; + desc[21] = (dst_lba >> 48) & 0xFF; + desc[22] = (dst_lba >> 40) & 0xFF; + desc[23] = (dst_lba >> 32) & 0xFF; + desc[24] = (dst_lba >> 24) & 0xFF; + desc[25] = (dst_lba >> 16) & 0xFF; + desc[26] = (dst_lba >> 8) & 0xFF; + desc[27] = dst_lba & 0xFF; + + 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; + buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7); + buf[2] = (tgt_desc_len >> 8) & 0xFF; + buf[3] = tgt_desc_len & 0xFF; + buf[8] = (seg_desc_len >> 24) & 0xFF; + buf[9] = (seg_desc_len >> 16) & 0xFF; + buf[10] = (seg_desc_len >> 8) & 0xFF; + buf[11] = seg_desc_len & 0xFF; + buf[12] = (inline_data_len >> 24) & 0xFF; + buf[13] = (inline_data_len >> 16) & 0xFF; + buf[14] = (inline_data_len >> 8) & 0xFF; + buf[15] = inline_data_len & 0xFF; +} + +int receive_copy_results(struct scsi_device *sdev, enum scsi_copy_results_sa sa, int list_id, void **datap, int status, enum scsi_sense_key key, int *ascq, int num_ascq) +{ + int ret; + struct scsi_task *task; + + logging(LOG_VERBOSE, "Send RECEIVE COPY RESULTS"); + + task = scsi_cdb_receive_copy_results(sa, list_id, 1024); + assert(task != NULL); + + task = send_scsi_command(sdev, task, NULL); + + ret = check_result("RECEIVECOPYRESULT", sdev, task, status, key, ascq, num_ascq); + + if (task->status == SCSI_STATUS_GOOD && datap != NULL) { + *datap = scsi_datain_unmarshall(task); + if (*datap == NULL) { + logging(LOG_NORMAL, + "[FAIL] failed to unmarshall RECEIVE COPY RESULTS data. %s", + iscsi_get_error(sdev->iscsi_ctx)); + return -1; + } + } + + ret = check_result("RECEIVECOPYRESULT", sdev, task, status, key, ascq, num_ascq); + if (task) + scsi_free_scsi_task(task); + + return ret; +} diff --git a/test-tool/iscsi-support.h b/test-tool/iscsi-support.h index 498122b..6c4ba87 100644 --- a/test-tool/iscsi-support.h +++ b/test-tool/iscsi-support.h @@ -37,20 +37,28 @@ extern const char *initiatorname2; #define EXPECT_STATUS_GOOD SCSI_STATUS_GOOD, SCSI_SENSE_NO_SENSE, NULL, 0 #define EXPECT_NO_MEDIUM SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_NOT_READY, no_medium_ascqs, 3 #define EXPECT_LBA_OOB SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, lba_oob_ascqs, 1 -#define EXPECT_INVALID_FIELD_IN_CDB SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, invalid_cdb_ascqs, 1 +#define EXPECT_INVALID_FIELD_IN_CDB SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, invalid_cdb_ascqs,2 +#define EXPECT_PARAM_LIST_LEN_ERR SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, param_list_len_err_ascqs, 1 +#define EXPECT_TOO_MANY_DESCR SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, too_many_desc_ascqs, 2 +#define EXPECT_UNSUPP_DESCR_CODE SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, unsupp_desc_code_ascqs, 2 #define EXPECT_MISCOMPARE SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_MISCOMPARE, miscompare_ascqs, 1 #define EXPECT_WRITE_PROTECTED SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_DATA_PROTECTION, write_protect_ascqs, 3 #define EXPECT_SANITIZE SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_NOT_READY, sanitize_ascqs, 1 #define EXPECT_REMOVAL_PREVENTED SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, removal_ascqs, 1 #define EXPECT_RESERVATION_CONFLICT SCSI_STATUS_RESERVATION_CONFLICT, 0, NULL, 0 +#define EXPECT_COPY_ABORTED SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_COPY_ABORTED, copy_aborted_ascqs, 3 int no_medium_ascqs[3]; int lba_oob_ascqs[1]; -int invalid_cdb_ascqs[1]; +int invalid_cdb_ascqs[2]; +int param_list_len_err_ascqs[1]; +int too_many_desc_ascqs[2]; +int unsupp_desc_code_ascqs[2]; int write_protect_ascqs[3]; int sanitize_ascqs[1]; int removal_ascqs[1]; int miscompare_ascqs[1]; +int copy_aborted_ascqs[3]; extern int loglevel; #define LOG_SILENT 0 @@ -314,5 +322,11 @@ int writeverify16(struct scsi_device *sdev, uint64_t lba, uint32_t datalen, int int set_swp(struct scsi_device *sdev); int clear_swp(struct scsi_device *sdev); - +int extendedcopy(struct scsi_device *sdev, struct iscsi_data *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq); +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, int 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, int src_index, int dst_index); +int populate_seg_desc_b2b(unsigned char *desc, int dc, int cat, int src_index, int dst_index, int num_blks, uint64_t src_lba, uint64_t dst_lba); +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_device *sdev, enum scsi_copy_results_sa sa, int list_id, void **datap, int status, enum scsi_sense_key key, int *ascq, int num_ascq); #endif /* _ISCSI_SUPPORT_H_ */ diff --git a/test-tool/iscsi-test-cu.c b/test-tool/iscsi-test-cu.c index 13df041..8cf7cb1 100644 --- a/test-tool/iscsi-test-cu.c +++ b/test-tool/iscsi-test-cu.c @@ -262,6 +262,22 @@ static CU_TestInfo tests_sanitize[] = { CU_TEST_INFO_NULL }; +static CU_TestInfo tests_extended_copy[] = { + { (char *)"Simple", test_extendedcopy_simple }, + { (char *)"ParamHdr", test_extendedcopy_param }, + { (char *)"DescrLimits", test_extendedcopy_descr_limits }, + { (char *)"DescrType", test_extendedcopy_descr_type }, + { (char *)"ValidTgtDescr", test_extendedcopy_validate_tgt_descr }, + { (char *)"ValidSegDescr", test_extendedcopy_validate_seg_descr }, + CU_TEST_INFO_NULL +}; + +static CU_TestInfo tests_receive_copy_results[] = { + { (char *)"CopyStatus", test_receive_copy_results_copy_status }, + { (char *)"OpParams", test_receive_copy_results_op_params }, + CU_TEST_INFO_NULL +}; + static CU_TestInfo tests_report_supported_opcodes[] = { { (char *)"Simple", test_report_supported_opcodes_simple }, { (char *)"OneCommand", test_report_supported_opcodes_one_command }, @@ -436,6 +452,7 @@ typedef struct libiscsi_suite_info { /* SCSI protocol tests */ static libiscsi_suite_info scsi_suites[] = { { "CompareAndWrite", NON_PGR_FUNCS, tests_compareandwrite }, + { "ExtendedCopy", NON_PGR_FUNCS, tests_extended_copy }, { "GetLBAStatus", NON_PGR_FUNCS, tests_get_lba_status }, { "Inquiry", NON_PGR_FUNCS, tests_inquiry }, { "Mandatory", NON_PGR_FUNCS, tests_mandatory }, @@ -456,6 +473,7 @@ static libiscsi_suite_info scsi_suites[] = { { "ReadCapacity10", NON_PGR_FUNCS, tests_readcapacity10 }, { "ReadCapacity16", NON_PGR_FUNCS, tests_readcapacity16 }, { "ReadOnly", NON_PGR_FUNCS, tests_readonly }, + { "ReceiveCopyResults", NON_PGR_FUNCS, tests_receive_copy_results }, { "ReportSupportedOpcodes", NON_PGR_FUNCS, tests_report_supported_opcodes }, { "Reserve6", NON_PGR_FUNCS, tests_reserve6 }, @@ -517,6 +535,7 @@ static libiscsi_suite_info iscsi_suites[] = { /* All tests */ static libiscsi_suite_info all_suites[] = { { "CompareAndWrite", NON_PGR_FUNCS, tests_compareandwrite }, + { "ExtendedCopy", NON_PGR_FUNCS, tests_extended_copy }, { "GetLBAStatus", NON_PGR_FUNCS, tests_get_lba_status }, { "Inquiry", NON_PGR_FUNCS, tests_inquiry }, { "Mandatory", NON_PGR_FUNCS, tests_mandatory }, @@ -538,6 +557,7 @@ static libiscsi_suite_info all_suites[] = { { "ReadCapacity10", NON_PGR_FUNCS, tests_readcapacity10 }, { "ReadCapacity16", NON_PGR_FUNCS, tests_readcapacity16 }, { "ReadOnly", NON_PGR_FUNCS, tests_readonly }, + { "ReceiveCopyResults", NON_PGR_FUNCS, tests_receive_copy_results }, { "ReportSupportedOpcodes", NON_PGR_FUNCS, tests_report_supported_opcodes }, { "Reserve6", NON_PGR_FUNCS, tests_reserve6 }, diff --git a/test-tool/iscsi-test-cu.h b/test-tool/iscsi-test-cu.h index c82a6e9..1aeb978 100644 --- a/test-tool/iscsi-test-cu.h +++ b/test-tool/iscsi-test-cu.h @@ -52,6 +52,13 @@ void test_compareandwrite_simple(void); void test_compareandwrite_dpofua(void); void test_compareandwrite_miscompare(void); +void test_extendedcopy_simple(void); +void test_extendedcopy_param(void); +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_get_lba_status_simple(void); void test_get_lba_status_beyond_eol(void); void test_get_lba_status_unmap_single(void); @@ -158,6 +165,9 @@ void test_readcapacity16_simple(void); void test_readonly_sbc(void); +void test_receive_copy_results_copy_status(void); +void test_receive_copy_results_op_params(void); + void test_report_supported_opcodes_one_command(void); void test_report_supported_opcodes_rctd(void); void test_report_supported_opcodes_servactv(void); diff --git a/test-tool/test_extendedcopy_descr_limits.c b/test-tool/test_extendedcopy_descr_limits.c new file mode 100644 index 0000000..53e96d2 --- /dev/null +++ b/test-tool/test_extendedcopy_descr_limits.c @@ -0,0 +1,120 @@ +/* + Copyright (c) 2015 SanDisk Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include + +#include + +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test-cu.h" + +int init_xcopy_descr(unsigned char *buf, int offset, int num_tgt_desc, + int num_seg_desc, int *tgt_desc_len, int *seg_desc_len) +{ + int i; + + /* Initialize target descriptor list with num_tgt_desc + * target descriptor */ + for (i = 0; i < num_tgt_desc; i++) + offset += populate_tgt_desc(buf+offset, IDENT_DESCR_TGT_DESCR, + LU_ID_TYPE_LUN, 0, 0, 0, 0, sd); + *tgt_desc_len = offset - XCOPY_DESC_OFFSET; + + /* Iniitialize segment descriptor list with num_seg_desc + * segment descriptor */ + for (i = 0; i < num_seg_desc; i++) + offset += populate_seg_desc_b2b(buf+offset, 0, 0, 0, 0, + 2048, 0, num_blocks - 2048); + *seg_desc_len = offset - XCOPY_DESC_OFFSET - *tgt_desc_len; + + return offset; +} + +void +test_extendedcopy_descr_limits(void) +{ + int ret; + struct iscsi_data data; + unsigned char *xcopybuf; + struct scsi_copy_results_op_params *opp; + int tgt_desc_len = 0, seg_desc_len = 0; + unsigned int alloc_len; + + logging(LOG_VERBOSE, LOG_BLANK_LINE); + logging(LOG_VERBOSE, "Test EXTENDED COPY descriptor limits"); + + CHECK_FOR_DATALOSS; + + logging(LOG_VERBOSE, "Issue RECEIVE COPY RESULTS (OPERATING PARAMS)"); + ret = receive_copy_results(sd, SCSI_COPY_RESULTS_OP_PARAMS, 0, + (void **)&opp, EXPECT_STATUS_GOOD); + CU_ASSERT_EQUAL(ret, 0); + + /* Allocate buffer to accommodate (MAX+1) target and + * segment descriptors */ + alloc_len = XCOPY_DESC_OFFSET + + (opp->max_target_desc_count+1) * + get_desc_len(IDENT_DESCR_TGT_DESCR) + + (opp->max_segment_desc_count+1) * + get_desc_len(BLK_TO_BLK_SEG_DESCR); + data.data = alloca(alloc_len); + xcopybuf = data.data; + + logging(LOG_VERBOSE, + "Test sending more than supported target descriptors"); + data.size = init_xcopy_descr(xcopybuf, XCOPY_DESC_OFFSET, + (opp->max_target_desc_count+1), 1, + &tgt_desc_len, &seg_desc_len); + populate_param_header(xcopybuf, 1, 0, 0, 0, + tgt_desc_len, seg_desc_len, 0); + ret = extendedcopy(sd, &data, EXPECT_TOO_MANY_DESCR); + if (ret == -2) { + CU_PASS("[SKIPPED] Target does not support " + "EXTENDED_COPY. Skipping test"); + return; + } + CU_ASSERT_EQUAL(ret, 0); + + logging(LOG_VERBOSE, + "Test sending more than supported segment descriptors"); + memset(xcopybuf, 0, alloc_len); + data.size = init_xcopy_descr(xcopybuf, XCOPY_DESC_OFFSET, 1, + (opp->max_segment_desc_count+1), + &tgt_desc_len, &seg_desc_len); + populate_param_header(xcopybuf, 2, 0, 0, 0, + tgt_desc_len, seg_desc_len, 0); + ret = extendedcopy(sd, &data, EXPECT_TOO_MANY_DESCR); + CU_ASSERT_EQUAL(ret, 0); + + logging(LOG_VERBOSE, + "Test sending descriptors > Maximum Descriptor List Length"); + memset(xcopybuf, 0, alloc_len); + if (opp->max_desc_list_length < alloc_len) { + data.size = init_xcopy_descr(xcopybuf, XCOPY_DESC_OFFSET, + (opp->max_target_desc_count+1), + (opp->max_segment_desc_count+1), + &tgt_desc_len, &seg_desc_len); + populate_param_header(xcopybuf, 3, 0, 0, 0, + tgt_desc_len, seg_desc_len, 0); + ret = extendedcopy(sd, &data, EXPECT_PARAM_LIST_LEN_ERR); + CU_ASSERT_EQUAL(ret, 0); + } +} diff --git a/test-tool/test_extendedcopy_descr_type.c b/test-tool/test_extendedcopy_descr_type.c new file mode 100644 index 0000000..28c0572 --- /dev/null +++ b/test-tool/test_extendedcopy_descr_type.c @@ -0,0 +1,93 @@ +/* + Copyright (c) 2015 SanDisk Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include + +#include + +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test-cu.h" + +int init_xcopybuf(unsigned char *buf, int tgt_desc_type, int seg_desc_type, + int *tgt_desc_len, int *seg_desc_len) +{ + int offset = XCOPY_DESC_OFFSET; + + offset += populate_tgt_desc(buf+offset, tgt_desc_type, LU_ID_TYPE_LUN, + 0, 0, 0, 0, sd); + *tgt_desc_len = offset - XCOPY_DESC_OFFSET; + if (seg_desc_type == BLK_TO_BLK_SEG_DESCR) + offset += populate_seg_desc_b2b(buf+offset, 0, 0, 0, 0, 2048, 0, + num_blocks - 2048); + else + offset += populate_seg_desc_hdr(buf+offset, seg_desc_type, + 0, 0, 0, 0); + *seg_desc_len = offset - XCOPY_DESC_OFFSET - *tgt_desc_len; + + return offset; +} + +void +test_extendedcopy_descr_type(void) +{ + int ret; + int tgt_desc_len = 0, seg_desc_len = 0, alloc_len; + struct iscsi_data data; + unsigned char *xcopybuf; + + logging(LOG_VERBOSE, LOG_BLANK_LINE); + logging(LOG_VERBOSE, + "Test EXTENDED COPY unsupported descriptor types"); + + CHECK_FOR_DATALOSS; + + alloc_len = XCOPY_DESC_OFFSET + + get_desc_len(IDENT_DESCR_TGT_DESCR) + + get_desc_len(BLK_TO_BLK_SEG_DESCR); + data.data = alloca(alloc_len); + xcopybuf = data.data; + + logging(LOG_VERBOSE, + "Send Fibre Channel N_Port_Name target descriptor"); + data.size = init_xcopybuf(xcopybuf, 0xE0, BLK_TO_BLK_SEG_DESCR, + &tgt_desc_len, &seg_desc_len); + populate_param_header(xcopybuf, 1, 0, 0, 0, + tgt_desc_len, seg_desc_len, 0); + + ret = extendedcopy(sd, &data, EXPECT_UNSUPP_DESCR_CODE); + if (ret == -2) { + CU_PASS("[SKIPPED] Target does not support " + "EXTENDED_COPY. Skipping test"); + return; + } + CU_ASSERT_EQUAL(ret, 0); + + logging(LOG_VERBOSE, "Send Stream-to-Stream Copy segment descriptor"); + memset(xcopybuf, 0, alloc_len); + data.size = init_xcopybuf(xcopybuf, IDENT_DESCR_TGT_DESCR, + STRM_TO_STRM_SEG_DESCR, + &tgt_desc_len, &seg_desc_len); + populate_param_header(xcopybuf, 1, 0, 0, 0, + tgt_desc_len, seg_desc_len, 0); + + ret = extendedcopy(sd, &data, EXPECT_UNSUPP_DESCR_CODE); + CU_ASSERT_EQUAL(ret, 0); +} diff --git a/test-tool/test_extendedcopy_param.c b/test-tool/test_extendedcopy_param.c new file mode 100644 index 0000000..ee5d4db --- /dev/null +++ b/test-tool/test_extendedcopy_param.c @@ -0,0 +1,83 @@ +/* + Copyright (c) 2015 SanDisk Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include + +#include + +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test-cu.h" + +void +test_extendedcopy_param(void) +{ + int ret; + int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET; + struct iscsi_data data; + unsigned char *xcopybuf; + + logging(LOG_VERBOSE, LOG_BLANK_LINE); + logging(LOG_VERBOSE, "Test EXTENDED COPY parameter list length"); + + CHECK_FOR_DATALOSS; + + data.size = XCOPY_DESC_OFFSET + + get_desc_len(IDENT_DESCR_TGT_DESCR) + + get_desc_len(BLK_TO_BLK_SEG_DESCR); + data.data = alloca(data.size); + xcopybuf = data.data; + + 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; + + offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0, + 2048, 0, num_blocks - 2048); + seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; + + populate_param_header(xcopybuf, 1, 0, 0, 0, + tgt_desc_len, seg_desc_len, 0); + + logging(LOG_VERBOSE, + "Test parameter list length truncating target descriptor"); + data.size = XCOPY_DESC_OFFSET + + get_desc_len(IDENT_DESCR_TGT_DESCR) - 1; + ret = extendedcopy(sd, &data, EXPECT_PARAM_LIST_LEN_ERR); + if (ret == -2) { + CU_PASS("[SKIPPED] Target does not support " + "EXTENDED_COPY. Skipping test"); + return; + } + CU_ASSERT_EQUAL(ret, 0); + + logging(LOG_VERBOSE, + "Test parameter list length truncating segment descriptor"); + data.size = XCOPY_DESC_OFFSET + + get_desc_len(IDENT_DESCR_TGT_DESCR) + + get_desc_len(BLK_TO_BLK_SEG_DESCR) - 1; + ret = extendedcopy(sd, &data, EXPECT_PARAM_LIST_LEN_ERR); + CU_ASSERT_EQUAL(ret, 0); + + logging(LOG_VERBOSE, "Test parameter list length = 0"); + data.size = 0; + ret = extendedcopy(sd, &data, EXPECT_STATUS_GOOD); + CU_ASSERT_EQUAL(ret, 0); +} diff --git a/test-tool/test_extendedcopy_simple.c b/test-tool/test_extendedcopy_simple.c new file mode 100644 index 0000000..1bbc4f3 --- /dev/null +++ b/test-tool/test_extendedcopy_simple.c @@ -0,0 +1,93 @@ +/* + Copyright (c) 2015 SanDisk Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include + +#include + +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test-cu.h" + +void +test_extendedcopy_simple(void) +{ + int ret; + int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET; + struct iscsi_data data; + unsigned char *xcopybuf; + unsigned char *buf1 = alloca(2048*block_size); + unsigned char *buf2 = alloca(2048*block_size); + + logging(LOG_VERBOSE, LOG_BLANK_LINE); + logging(LOG_VERBOSE, + "Test EXTENDED COPY of 2048 blocks from start of LUN to end of LUN"); + + CHECK_FOR_DATALOSS; + + logging(LOG_VERBOSE, "Write 2048 blocks of 'A' at LBA:0"); + memset(buf1, 'A', 2048*block_size); + ret = write16(sd, 0, 2048*block_size, block_size, 0, 0, 0, 0, 0, + buf1, EXPECT_STATUS_GOOD); + if (ret == -2) { + logging(LOG_NORMAL, "[SKIPPED] WRITE16 is not implemented."); + CU_PASS("WRITE16 is not implemented."); + return; + } + CU_ASSERT_EQUAL(ret, 0); + + data.size = XCOPY_DESC_OFFSET + + get_desc_len(IDENT_DESCR_TGT_DESCR) + + get_desc_len(BLK_TO_BLK_SEG_DESCR); + data.data = alloca(data.size); + xcopybuf = data.data; + + /* 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; + + /* Iniitialize segment descriptor list with one segment descriptor */ + offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0, + 2048, 0, num_blocks - 2048); + 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); + + ret = extendedcopy(sd, &data, EXPECT_STATUS_GOOD); + if (ret == -2) { + CU_PASS("[SKIPPED] Target does not support " + "EXTENDED_COPY. Skipping test"); + return; + } + CU_ASSERT_EQUAL(ret, 0); + + logging(LOG_VERBOSE, "Read 2048 blocks from end of the LUN"); + ret = read16(sd, NULL, num_blocks - 2048, 2048*block_size, + block_size, 0, 0, 0, 0, 0, buf2, + EXPECT_STATUS_GOOD); + CU_ASSERT_EQUAL(ret, 0); + + ret = memcmp(buf1, buf2, 2048); + if (ret != 0) + CU_FAIL("Blocks were not copied correctly"); +} diff --git a/test-tool/test_extendedcopy_validate_seg_descr.c b/test-tool/test_extendedcopy_validate_seg_descr.c new file mode 100644 index 0000000..36208f7 --- /dev/null +++ b/test-tool/test_extendedcopy_validate_seg_descr.c @@ -0,0 +1,83 @@ +/* + Copyright (c) 2015 SanDisk Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include + +#include + +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test-cu.h" + +void +test_extendedcopy_validate_seg_descr(void) +{ + int ret; + int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET; + struct iscsi_data data; + unsigned char *xcopybuf; + + logging(LOG_VERBOSE, LOG_BLANK_LINE); + logging(LOG_VERBOSE, "Test EXTENDED COPY segment descriptor fields"); + + CHECK_FOR_DATALOSS; + + data.size = XCOPY_DESC_OFFSET + + get_desc_len(IDENT_DESCR_TGT_DESCR) + + get_desc_len(BLK_TO_BLK_SEG_DESCR); + data.data = alloca(data.size); + xcopybuf = data.data; + + logging(LOG_VERBOSE, "Send invalid target descriptor index"); + 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; + /* Inaccessible DESTINATION TARGET DESCRIPTOR INDEX */ + offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 1, + 2048, 0, num_blocks - 2048); + seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; + populate_param_header(xcopybuf, 1, 0, 0, 0, + tgt_desc_len, seg_desc_len, 0); + + ret = extendedcopy(sd, &data, EXPECT_COPY_ABORTED); + if (ret == -2) { + CU_PASS("[SKIPPED] Target does not support " + "EXTENDED_COPY. Skipping test"); + return; + } + CU_ASSERT_EQUAL(ret, 0); + + logging(LOG_VERBOSE, + "Number of copy blocks beyond destination block device capacity"); + memset(xcopybuf, 0, data.size); + offset = XCOPY_DESC_OFFSET; + 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; + /* Beyond EOL */ + offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0, + 2048, 0, num_blocks - 1); + seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; + populate_param_header(xcopybuf, 1, 0, 0, 0, + tgt_desc_len, seg_desc_len, 0); + + ret = extendedcopy(sd, &data, EXPECT_COPY_ABORTED); + CU_ASSERT_EQUAL(ret, 0); +} diff --git a/test-tool/test_extendedcopy_validate_tgt_descr.c b/test-tool/test_extendedcopy_validate_tgt_descr.c new file mode 100644 index 0000000..ec197c7 --- /dev/null +++ b/test-tool/test_extendedcopy_validate_tgt_descr.c @@ -0,0 +1,82 @@ +/* + Copyright (c) 2015 SanDisk Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include + +#include + +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test-cu.h" + +void +test_extendedcopy_validate_tgt_descr(void) +{ + int ret; + int tgt_desc_len = 0, seg_desc_len = 0, offset = XCOPY_DESC_OFFSET; + struct iscsi_data data; + unsigned char *xcopybuf; + + logging(LOG_VERBOSE, LOG_BLANK_LINE); + logging(LOG_VERBOSE, "Test EXTENDED COPY target descriptor fields"); + + CHECK_FOR_DATALOSS; + + data.size = XCOPY_DESC_OFFSET + + get_desc_len(IDENT_DESCR_TGT_DESCR) + + get_desc_len(BLK_TO_BLK_SEG_DESCR); + data.data = alloca(data.size); + xcopybuf = data.data; + + logging(LOG_VERBOSE, "Unsupported LU_ID TYPE"); + /* Unsupported LU ID TYPE */ + offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR, + LU_ID_TYPE_RSVD, 0, 0, 0, 0, sd); + tgt_desc_len = offset - XCOPY_DESC_OFFSET; + offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0, + 2048, 0, num_blocks - 2048); + seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; + populate_param_header(xcopybuf, 1, 0, 0, 0, + tgt_desc_len, seg_desc_len, 0); + + ret = extendedcopy(sd, &data, EXPECT_INVALID_FIELD_IN_CDB); + if (ret == -2) { + CU_PASS("[SKIPPED] Target does not support " + "EXTENDED_COPY. Skipping test"); + return; + } + CU_ASSERT_EQUAL(ret, 0); + + logging(LOG_VERBOSE, "Test NUL bit in target descriptor"); + /* NUL bit */ + memset(xcopybuf, 0, data.size); + offset = XCOPY_DESC_OFFSET; + offset += populate_tgt_desc(xcopybuf+offset, IDENT_DESCR_TGT_DESCR, + LU_ID_TYPE_LUN, 1, 0, 0, 0, sd); + tgt_desc_len = offset - XCOPY_DESC_OFFSET; + offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0, + 2048, 0, num_blocks - 2048); + seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; + populate_param_header(xcopybuf, 1, 0, 0, 0, + tgt_desc_len, seg_desc_len, 0); + + ret = extendedcopy(sd, &data, EXPECT_COPY_ABORTED); + CU_ASSERT_EQUAL(ret, 0); +} diff --git a/test-tool/test_receive_copy_results_copy_status.c b/test-tool/test_receive_copy_results_copy_status.c new file mode 100644 index 0000000..d8dd95c --- /dev/null +++ b/test-tool/test_receive_copy_results_copy_status.c @@ -0,0 +1,83 @@ +/* + Copyright (c) 2015 SanDisk Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include + +#include + +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test-cu.h" + +void +test_receive_copy_results_copy_status(void) +{ + int ret; + struct scsi_copy_results_copy_status *csp; + int tgt_desc_len = 0, seg_desc_len = 0; + int offset = XCOPY_DESC_OFFSET, list_id = 1; + struct iscsi_data data; + unsigned char *xcopybuf; + + logging(LOG_VERBOSE, LOG_BLANK_LINE); + logging(LOG_VERBOSE, "Test RECEIVE COPY RESULTS, COPY STATUS"); + + logging(LOG_VERBOSE, "No copy in progress"); + ret = receive_copy_results(sd, SCSI_COPY_RESULTS_COPY_STATUS, + list_id, NULL, EXPECT_INVALID_FIELD_IN_CDB); + CU_ASSERT_EQUAL(ret, 0); + + CHECK_FOR_DATALOSS; + + logging(LOG_VERBOSE, "Issue Extended Copy"); + data.size = XCOPY_DESC_OFFSET + + get_desc_len(IDENT_DESCR_TGT_DESCR) + + get_desc_len(BLK_TO_BLK_SEG_DESCR); + data.data = alloca(data.size); + xcopybuf = data.data; + + /* 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; + + /* Iniitialize segment descriptor list with one segment descriptor */ + offset += populate_seg_desc_b2b(xcopybuf+offset, 0, 0, 0, 0, + 2048, 0, num_blocks - 2048); + seg_desc_len = offset - XCOPY_DESC_OFFSET - tgt_desc_len; + + /* Initialize the parameter list header */ + populate_param_header(xcopybuf, list_id, 0, 0, 0, + tgt_desc_len, seg_desc_len, 0); + + ret = extendedcopy(sd, &data, EXPECT_STATUS_GOOD); + if (ret == -2) { + CU_PASS("[SKIPPED] Target does not support " + "EXTENDED_COPY. Skipping test"); + return; + } + CU_ASSERT_EQUAL(ret, 0); + + logging(LOG_VERBOSE, + "Copy Status for the above Extended Copy command"); + ret = receive_copy_results(sd, SCSI_COPY_RESULTS_COPY_STATUS, + list_id, (void **)&csp, EXPECT_STATUS_GOOD); + CU_ASSERT_EQUAL(ret, 0); +} diff --git a/test-tool/test_receive_copy_results_op_params.c b/test-tool/test_receive_copy_results_op_params.c new file mode 100644 index 0000000..4f8e151 --- /dev/null +++ b/test-tool/test_receive_copy_results_op_params.c @@ -0,0 +1,46 @@ +/* + Copyright (c) 2015 SanDisk Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include +#include + +#include + +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test-cu.h" + +void +test_receive_copy_results_op_params(void) +{ + int ret; + struct scsi_copy_results_op_params *opp; + + logging(LOG_VERBOSE, LOG_BLANK_LINE); + logging(LOG_VERBOSE, "Test RECEIVE COPY RESULTS, OPERATING PARAMS"); + + ret = receive_copy_results(sd, SCSI_COPY_RESULTS_OP_PARAMS, 0, + (void **)&opp, EXPECT_STATUS_GOOD); + CU_ASSERT_EQUAL(ret, 0); + + logging(LOG_NORMAL, + "max_target_desc=%d, max_seg_desc=%d", + opp->max_target_desc_count, + opp->max_segment_desc_count); +}