diff --git a/Makefile.am b/Makefile.am index 688f847..31827c4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -140,6 +140,7 @@ bin_iscsi_test_SOURCES = test-tool/iscsi-test.c \ test-tool/0422_reserve6_logout.c \ test-tool/0423_reserve6_sessionloss.c \ test-tool/0424_reserve6_target_reset.c \ + test-tool/0430_report_all_supported_ops.c \ \ test-tool/1000_cmdsn_invalid.c \ test-tool/1010_datasn_invalid.c \ diff --git a/include/iscsi.h b/include/iscsi.h index aeaa121..bbbca61 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -750,6 +750,10 @@ EXTERN struct scsi_task * iscsi_release6_task(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data); +EXTERN struct scsi_task * +iscsi_report_supported_opcodes_task(struct iscsi_context *iscsi, int lun, + int return_timeouts, int maxsize, + iscsi_command_cb cb, void *private_data); /* * Sync commands for SCSI @@ -914,6 +918,9 @@ iscsi_reserve6_sync(struct iscsi_context *iscsi, int lun); EXTERN struct scsi_task * iscsi_release6_sync(struct iscsi_context *iscsi, int lun); +EXTERN struct scsi_task * +iscsi_report_supported_opcodes_sync(struct iscsi_context *iscsi, int lun, + int return_timeouts, int maxsize); /* * This function is used when the application wants to specify its own buffers to read the data diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index 3381571..9d25b47 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -53,6 +53,7 @@ enum scsi_opcode { SCSI_OPCODE_WRITE_SAME16 = 0x93, SCSI_OPCODE_SERVICE_ACTION_IN = 0x9E, SCSI_OPCODE_REPORTLUNS = 0xA0, + SCSI_OPCODE_MAINTENANCE_IN = 0xA3, SCSI_OPCODE_READ12 = 0xA8, SCSI_OPCODE_WRITE12 = 0xAA, SCSI_OPCODE_WRITE_VERIFY12 = 0xAE, @@ -64,6 +65,16 @@ enum scsi_service_action_in { SCSI_GET_LBA_STATUS = 0x12 }; +enum scsi_maintenance_in { + SCSI_REPORT_SUPPORTED_OP_CODES = 0x0c +}; + +enum scsi_op_code_reporting_options { + SCSI_REPORT_SUPPORTING_OPS_ALL = 0x00, + SCSI_REPORT_SUPPORTING_OPCODE = 0x01, + SCSI_REPORT_SUPPORTING_SERVICEACTION = 0x02 +}; + /* sense keys */ enum scsi_sense_key { SCSI_SENSE_NO_SENSE = 0x00, @@ -265,6 +276,18 @@ struct scsi_readtoc_params { int track_session; }; + +struct scsi_report_supported_params { + int return_timeouts; +}; + +struct scsi_maintenancein_params { + enum scsi_maintenance_in sa; + union { + struct scsi_report_supported_params reportsupported; + } params; +}; + struct scsi_sense { unsigned char error_type; enum scsi_sense_key key; @@ -318,6 +341,7 @@ struct scsi_task { struct scsi_modesense6_params modesense6; struct scsi_serviceactionin_params serviceactionin; struct scsi_readtoc_params readtoc; + struct scsi_maintenancein_params maintenancein; } params; enum scsi_residual residual_status; @@ -748,6 +772,30 @@ struct scsi_get_lba_status { struct scsi_lba_status_descriptor *descriptors; }; + +struct scsi_op_timeout_descriptor { + uint16_t descriptor_length; + uint8_t reserved; + uint8_t command_specific; + uint32_t nominal_processing_timeout; + uint32_t recommended_timeout; + +}; +struct scsi_command_descriptor { + uint8_t op_code; + uint8_t reserved1; + uint16_t service_action; + uint8_t reserved2; + uint8_t reserved3; + uint16_t cdb_length; + struct scsi_op_timeout_descriptor to[0]; +}; + +struct scsi_report_supported_op_codes { + uint32_t num_descriptors; + struct scsi_command_descriptor descriptors[0]; +}; + EXTERN int scsi_datain_getfullsize(struct scsi_task *task); EXTERN void *scsi_datain_unmarshall(struct scsi_task *task); @@ -781,6 +829,7 @@ EXTERN struct scsi_task *scsi_cdb_writesame10(int wrprotect, int anchor, int unm EXTERN struct scsi_task *scsi_cdb_writesame16(int wrprotect, int anchor, int unmap, int pbdata, int lbdata, uint64_t lba, int group, uint32_t num_blocks); EXTERN struct scsi_task *scsi_cdb_prefetch10(uint32_t lba, int num_blocks, int immed, int group); EXTERN struct scsi_task *scsi_cdb_prefetch16(uint64_t lba, int num_blocks, int immed, int group); +EXTERN struct scsi_task *scsi_cdb_report_supported_opcodes(int report_timeouts, uint32_t alloc_len); void *scsi_malloc(struct scsi_task *task, size_t size); diff --git a/lib/libiscsi.def b/lib/libiscsi.def index 6c197f8..c3a915c 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -52,6 +52,8 @@ iscsi_reserve6_sync iscsi_reserve6_task iscsi_release6_sync iscsi_release6_task +iscsi_report_supported_opcodes_sync +iscsi_report_supported_opcodes_task iscsi_reconnect iscsi_set_noautoreconnect iscsi_reportluns_sync @@ -136,6 +138,7 @@ scsi_cdb_readcapacity16 scsi_cdb_readtoc scsi_cdb_reserve6 scsi_cdb_release6 +scsi_cdb_report_supported_opcodes scsi_cdb_serviceactionin16 scsi_cdb_startstopunit scsi_cdb_synchronizecache10 diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index 5a86af8..ddf3e1a 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -50,6 +50,8 @@ iscsi_reserve6_sync iscsi_reserve6_task iscsi_release6_sync iscsi_release6_task +iscsi_report_supported_opcodes_sync +iscsi_report_supported_opcodes_task iscsi_reconnect iscsi_set_noautoreconnect iscsi_reportluns_sync @@ -134,6 +136,7 @@ scsi_cdb_readcapacity16 scsi_cdb_readtoc scsi_cdb_reserve6 scsi_cdb_release6 +scsi_cdb_report_supported_opcodes scsi_cdb_serviceactionin16 scsi_cdb_startstopunit scsi_cdb_synchronizecache10 diff --git a/lib/scsi-command.c b/lib/scsi-command.c index 3122b52..0fe5291 100644 --- a/lib/scsi-command.c +++ b/lib/scsi-command.c @@ -1541,6 +1541,28 @@ iscsi_release6_task(struct iscsi_context *iscsi, int lun, return task; } +struct scsi_task * +iscsi_report_supported_opcodes_task(struct iscsi_context *iscsi, + int lun, int return_timeouts, int maxsize, + iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + + task = scsi_cdb_report_supported_opcodes(return_timeouts, maxsize); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "Maintenance In/Read Supported Op Codes cdb."); + return NULL; + } + if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, + private_data) != 0) { + scsi_free_scsi_task(task); + return NULL; + } + + return task; +} + struct scsi_task * iscsi_scsi_get_task_from_pdu(struct iscsi_pdu *pdu) { diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index c784af2..3787072 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -221,7 +221,7 @@ scsi_reportluns_cdb(int report_type, int alloc_len) } /* - * parse the data in blob and calcualte the size of a full report luns + * parse the data in blob and calculate the size of a full report luns * datain structure */ static int @@ -353,7 +353,7 @@ scsi_cdb_readtoc(int msf, int format, int track_session, uint16_t alloc_len) } /* - * parse the data in blob and calcualte the size of a full read TOC + * parse the data in blob and calculate the size of a full read TOC * datain structure */ static int @@ -563,8 +563,122 @@ scsi_serviceactionin_datain_unmarshall(struct scsi_task *task) return NULL; } + /* - * parse the data in blob and calcualte the size of a full + * parse the data in blob and calculate the size of a full maintenancein + * datain structure + */ +static int +scsi_maintenancein_datain_getfullsize(struct scsi_task *task) +{ + + switch (task->params.maintenancein.sa) { + case SCSI_REPORT_SUPPORTED_OP_CODES: + return ntohl(*(uint32_t *)&(task->datain.data[0])) + 4; + default: + return -1; + } + +} + + +/* + * maintenance_in unmarshall + */ +static void * +scsi_maintenancein_datain_unmarshall(struct scsi_task *task) +{ + struct scsi_report_supported_op_codes *rsoc; + struct scsi_command_descriptor *desc, *datain; + uint32_t len, i; + int return_timeouts, desc_size; + + switch (task->params.maintenancein.sa) { + case SCSI_REPORT_SUPPORTED_OP_CODES: + if (task->datain.size < 4) { + return NULL; + } + + len = ntohl(*(uint32_t *)&(task->datain.data[0])); + rsoc = scsi_malloc(task, sizeof(struct scsi_report_supported_op_codes) + len); + if (rsoc == NULL) { + return NULL; + } + /* Does the descriptor include command timeout info? */ + return_timeouts = task->params.maintenancein.params.reportsupported.return_timeouts; + + /* Size of descriptor depends on whether timeout included. */ + desc_size = sizeof (struct scsi_command_descriptor); + if (return_timeouts) { + desc_size += sizeof (struct scsi_op_timeout_descriptor); + } + rsoc->num_descriptors = len / desc_size; + + desc = &rsoc->descriptors[0]; + datain = (struct scsi_command_descriptor *)&task->datain.data[4]; + + for (i=0; i < rsoc->num_descriptors; i++) { + desc->op_code = datain->op_code; + desc->service_action = ntohs(datain->service_action); + desc->cdb_length = ntohs(datain->cdb_length); + if (return_timeouts) { + desc->to[0].descriptor_length = ntohs(datain->to[0].descriptor_length); + desc->to[0].command_specific = datain->to[0].command_specific; + desc->to[0].nominal_processing_timeout + = ntohl(datain->to[0].nominal_processing_timeout); + desc->to[0].recommended_timeout + = ntohl(datain->to[0].recommended_timeout); + } + desc = (struct scsi_command_descriptor *)((char *)desc + desc_size); + datain = (struct scsi_command_descriptor *)((char *)datain + desc_size); + } + + return rsoc; + }; + + return NULL; +} + +/* + * MAINTENANCE In / Read Supported Op Codes + */ +struct scsi_task * +scsi_cdb_report_supported_opcodes(int return_timeouts, uint32_t alloc_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_MAINTENANCE_IN; + task->cdb[1] = SCSI_REPORT_SUPPORTED_OP_CODES; + task->cdb[2] = SCSI_REPORT_SUPPORTING_OPS_ALL; + + if (return_timeouts) { + task->cdb[2] |= 0x80; + } + + *(uint32_t *)&task->cdb[6] = htonl(alloc_len); + + task->cdb_size = 12; + if (alloc_len != 0) { + task->xfer_dir = SCSI_XFER_READ; + } else { + task->xfer_dir = SCSI_XFER_NONE; + } + task->expxferlen = alloc_len; + + task->params.maintenancein.sa = SCSI_REPORT_SUPPORTED_OP_CODES; + task->params.maintenancein.params.reportsupported.return_timeouts = return_timeouts; + + return task; +} + +/* + * parse the data in blob and calculate the size of a full * readcapacity10 datain structure */ static int @@ -638,7 +752,7 @@ scsi_cdb_inquiry(int evpd, int page_code, int alloc_len) } /* - * parse the data in blob and calcualte the size of a full + * parse the data in blob and calculate the size of a full * inquiry datain structure */ static int @@ -1598,7 +1712,7 @@ scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc, } /* - * parse the data in blob and calcualte the size of a full + * parse the data in blob and calculate the size of a full * modesense6 datain structure */ static int @@ -2172,6 +2286,8 @@ scsi_datain_getfullsize(struct scsi_task *task) return scsi_readtoc_datain_getfullsize(task); case SCSI_OPCODE_REPORTLUNS: return scsi_reportluns_datain_getfullsize(task); + case SCSI_OPCODE_MAINTENANCE_IN: + return scsi_maintenancein_datain_getfullsize(task); } return -1; } @@ -2196,6 +2312,8 @@ scsi_datain_unmarshall(struct scsi_task *task) return scsi_reportluns_datain_unmarshall(task); case SCSI_OPCODE_SERVICE_ACTION_IN: return scsi_serviceactionin_datain_unmarshall(task); + case SCSI_OPCODE_MAINTENANCE_IN: + return scsi_maintenancein_datain_unmarshall(task); } return NULL; } diff --git a/lib/sync.c b/lib/sync.c index e0b8a8d..ef9d290 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -874,6 +874,24 @@ iscsi_release6_sync(struct iscsi_context *iscsi, int lun) return state.task; } +struct scsi_task * +iscsi_report_supported_opcodes_sync(struct iscsi_context *iscsi, int lun, int return_timeouts, int maxsize) +{ + struct iscsi_sync_state state; + + memset(&state, 0, sizeof(state)); + + if (iscsi_report_supported_opcodes_task(iscsi, lun, return_timeouts, maxsize, scsi_sync_cb, &state) == NULL) { + iscsi_set_error(iscsi, "Failed to send MaintenanceIn:" + "Report Supported Opcodes command"); + return NULL; + } + + event_loop(iscsi, &state); + + return state.task; +} + struct scsi_task * iscsi_scsi_command_sync(struct iscsi_context *iscsi, int lun, struct scsi_task *task, struct iscsi_data *data) diff --git a/test-tool/0430_report_all_supported_ops.c b/test-tool/0430_report_all_supported_ops.c new file mode 100644 index 0000000..70d59b0 --- /dev/null +++ b/test-tool/0430_report_all_supported_ops.c @@ -0,0 +1,172 @@ +/* + Copyright (C) 2012 by Ronnie Sahlberg + Copyright (C) 2012 by Jon Grimm + + 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 "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test.h" + +int T0430_report_all_supported_ops(const char *initiator, const char *url, int data_loss, int show_info) +{ + struct iscsi_context *iscsi; + struct scsi_task *task; + struct scsi_report_supported_op_codes *rsoc; + struct scsi_command_descriptor *desc; + int ret, lun, i; + int full_size, desc_size; + + printf("0430_report_all_supported_ops:\n"); + printf("===================\n"); + if (show_info) { + printf("Test MaintenanceIn: Report Supported Operations.\n"); + printf("1, Report Supported Ops (no timeout information).\n"); + printf("2, Report Supported Ops (with timeout information).\n"); + printf("\n"); + return 0; + } + + iscsi = iscsi_context_login(initiator, url, &lun); + if (iscsi == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + ret = 0; + + printf("See if Report Supported Opcodes is supported... "); + /* See how big data is */ + task = iscsi_report_supported_opcodes_sync(iscsi, lun, 0, 4); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send Report Supported Opcodes command : %s\n", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("REPORT SUPPORTED OPCODES command failed : %s\n", + iscsi_get_error(iscsi)); + scsi_free_scsi_task(task); + ret = -1; + goto finished; + } + full_size = scsi_datain_getfullsize(task); + + if (full_size > task->datain.size) { + scsi_free_scsi_task(task); + /* we need more data for the full list */ + if ((task = iscsi_report_supported_opcodes_sync(iscsi, lun, 0, full_size)) == NULL) { + printf("[FAILED]\n"); + printf("REPORT SUPPORTED OPCODES failed : %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + } + rsoc = scsi_datain_unmarshall(task); + if (rsoc == NULL) { + printf("[FAILED]\n"); + printf("failed to unmarshall inquiry datain blob\n"); + scsi_free_scsi_task(task); + ret = -1; + goto finished; + } + + printf("Supported Commands: %d\n", rsoc->num_descriptors); + printf("=======================\n"); + for (i=0; i < rsoc->num_descriptors; i++) { + printf("op:%x\tsa:%x\tcdb length:%d\n", + rsoc->descriptors[i].op_code, + rsoc->descriptors[i].service_action, + rsoc->descriptors[i].cdb_length); + } + + printf("\n[OK]\n"); + scsi_free_scsi_task(task); + + +test2: + /*Report All Supported Operations including timeout info.*/ + printf("See if Report Supported Opcodes with Timeouts is supported... "); + /* See how big data is */ + task = iscsi_report_supported_opcodes_sync(iscsi, lun, 1, 4); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send Report Supported Opcodes command : %s\n", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("REPORT SUPPORTED OPCODES command failed : %s\n", + iscsi_get_error(iscsi)); + scsi_free_scsi_task(task); + ret = -1; + goto finished; + } + full_size = scsi_datain_getfullsize(task); + + if (full_size > task->datain.size) { + scsi_free_scsi_task(task); + + /* we need more data for the full list */ + if ((task = iscsi_report_supported_opcodes_sync(iscsi, lun, 0, full_size)) == NULL) { + printf("[FAILED]\n"); + printf("REPORT SUPPORTED OPCODES failed : %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + } + rsoc = scsi_datain_unmarshall(task); + if (rsoc == NULL) { + printf("[FAILED]\n"); + printf("failed to unmarshall inquiry datain blob\n"); + scsi_free_scsi_task(task); + ret = -1; + goto finished; + } + + printf("Supported Commands (with timeout information): %d\n", rsoc->num_descriptors); + printf("=======================\n"); + desc_size = sizeof (struct scsi_command_descriptor) + + sizeof (struct scsi_op_timeout_descriptor); + desc = &rsoc->descriptors[0]; + for (i=0; i < rsoc->num_descriptors; i++) { + printf("op:%x\tsa:%x\tcdb_length:%d\ttimout info: length:%d\tcommand specific:%x\tnominal processing%d\trecommended%d\n", + desc->op_code, + desc->service_action, + desc->cdb_length, + desc->to[0].descriptor_length, + desc->to[0].command_specific, + desc->to[0].nominal_processing_timeout, + desc->to[0].recommended_timeout); + desc = (struct scsi_command_descriptor *)((char *)desc + + desc_size); + } + + printf("\n[OK]\n"); + scsi_free_scsi_task(task); + +finished: + iscsi_logout_sync(iscsi); + iscsi_destroy_context(iscsi); + return ret; +} diff --git a/test-tool/iscsi-test.c b/test-tool/iscsi-test.c index b719d26..cad27bd 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -223,6 +223,9 @@ struct scsi_test tests[] = { { "T0423_reserve6_sessionloss", T0423_reserve6_sessionloss }, { "T0424_reserve6_target_reset", T0424_reserve6_target_reset }, +/* Maintenance In - Report Supported Operations */ +{ "T0430_report_all_supported_ops", T0430_report_all_supported_ops }, + /* iSCSI protocol tests */ /* invalid cmdsn from initiator */ diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index e59d64d..604b4df 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -175,6 +175,8 @@ int T0422_reserve6_logout(const char *initiator, const char *url, int data_loss, int T0423_reserve6_sessionloss(const char *initiator, const char *url, int data_loss, int show_info); int T0424_reserve6_target_reset(const char *initiator, const char *url, int data_loss, int show_info); +int T0430_report_all_supported_ops(const char *initiator, const char *url, int data_loss, int show_info); + int T1000_cmdsn_invalid(const char *initiator, const char *url, int data_loss, int show_info); int T1010_datasn_invalid(const char *initiator, const char *url, int data_loss, int show_info); int T1020_bufferoffset_invalid(const char *initiator, const char *url, int data_loss, int show_info);