From 55c1f0d2ecb2bbd5993f009a59dbf8de53e1c727 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Fri, 19 Sep 2014 16:26:46 -0700 Subject: [PATCH] TESTS: Add support to test against a /dev/sg device on linux Signed-off-by: Ronnie Sahlberg --- test-tool/iscsi-support.c | 131 +++++++++++++++++++++++++++-- test-tool/iscsi-support.h | 1 + test-tool/iscsi-test-cu.c | 92 +++++++++++++++----- test-tool/test_read10_beyond_eol.c | 1 - test-tool/test_read16_beyond_eol.c | 1 - 5 files changed, 195 insertions(+), 31 deletions(-) diff --git a/test-tool/iscsi-support.c b/test-tool/iscsi-support.c index 2a891a7..eb30c25 100644 --- a/test-tool/iscsi-support.c +++ b/test-tool/iscsi-support.c @@ -1,3 +1,4 @@ + /* iscsi-test tool support @@ -18,6 +19,8 @@ along with this program; if not, see . */ +#include "config.h" + #define _GNU_SOURCE #include #include @@ -31,6 +34,13 @@ #include #include #include + +#ifdef HAVE_SG_IO +#include +#include +#include +#endif + #include "slist.h" #include "iscsi.h" #include "scsi-lowlevel.h" @@ -170,16 +180,119 @@ static int check_result(const char *opcode, struct scsi_device *sdev, static struct scsi_task *send_scsi_command(struct scsi_device *sdev, struct scsi_task *task, struct iscsi_data *d) { - if (sdev->error_str != NULL) { - free(discard_const(sdev->error_str)); - sdev->error_str = NULL; - } - task = iscsi_scsi_command_sync(sdev->iscsi_ctx, sdev->iscsi_lun, task, d); - if (task == NULL) { - sdev->error_str = strdup(iscsi_get_error(sdev->iscsi_ctx)); + if (sdev->iscsi_url) { + if (sdev->error_str != NULL) { + free(discard_const(sdev->error_str)); + sdev->error_str = NULL; + } + task = iscsi_scsi_command_sync(sdev->iscsi_ctx, sdev->iscsi_lun, task, d); + if (task == NULL) { + sdev->error_str = strdup(iscsi_get_error(sdev->iscsi_ctx)); + } + + return task; } - return task; +#ifdef HAVE_SG_IO + if (sdev->sgio_dev) { + sg_io_hdr_t io_hdr; + unsigned int sense_len=32; + unsigned char sense[sense_len]; + char buf[1024]; + + memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); + io_hdr.interface_id = 'S'; + + /* CDB */ + io_hdr.cmdp = task->cdb; + io_hdr.cmd_len = task->cdb_size; + + /* Where to store the sense_data, if there was an error */ + io_hdr.sbp = sense; + io_hdr.mx_sb_len = sense_len; + sense_len=0; + + /* Transfer direction, either in or out. Linux does not yet + support bidirectional SCSI transfers ? + */ + switch (task->xfer_dir) { + case SCSI_XFER_WRITE: + io_hdr.dxfer_direction = SG_DXFER_TO_DEV; + io_hdr.dxferp = d->data; + io_hdr.dxfer_len = d->size; + break; + case SCSI_XFER_READ: + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + task->datain.size = task->expxferlen; + task->datain.data = malloc(task->expxferlen); + io_hdr.dxferp = task->datain.data; + io_hdr.dxfer_len = task->datain.size; + break; + } + + /* SCSI timeout in ms */ + io_hdr.timeout = 5000; + + if(ioctl(sdev->sgio_fd, SG_IO, &io_hdr) < 0){ + sdev->error_str = strdup("SG_IO ioctl failed"); + return NULL; + } + + /* now for the error processing */ + if(io_hdr.sb_len_wr > 0){ + task->status = SCSI_STATUS_CHECK_CONDITION; + task->sense.error_type = sense[0] & 0x7f; + switch (task->sense.error_type) { + case 0x70: + case 0x71: + task->sense.key = sense[2] & 0x0f; + task->sense.ascq = scsi_get_uint16(&sense[12]); + break; + case 0x72: + case 0x73: + task->sense.key = sense[1] & 0x0f; + task->sense.ascq = scsi_get_uint16(&sense[2]); + break; + } + sense_len=io_hdr.sb_len_wr; + snprintf(buf, sizeof(buf), "SENSE KEY:%s(%d) ASCQ:%s(0x%04x)", + scsi_sense_key_str(task->sense.key), + task->sense.key, + scsi_sense_ascq_str(task->sense.ascq), + task->sense.ascq); + sdev->error_str = strdup(buf); + return task; + } + + if(io_hdr.masked_status){ + task->status = SCSI_STATUS_ERROR; + task->sense.key = 0x0f; + task->sense.ascq = 0xffff; + + sdev->error_str = strdup("SCSI masked error"); + return NULL; + } + if(io_hdr.host_status){ + task->status = SCSI_STATUS_ERROR; + task->sense.key = 0x0f; + task->sense.ascq = 0xffff; + + snprintf(buf, sizeof(buf), "SCSI host error. Status=0x%x", io_hdr.host_status); + sdev->error_str = strdup(buf); + return task; + } + if(io_hdr.driver_status){ + task->status = SCSI_STATUS_ERROR; + task->sense.key = 0x0f; + task->sense.ascq = 0xffff; + + sdev->error_str = strdup("SCSI driver error"); + return NULL; + } + return task; + } +#endif + return NULL; } void logging(int level, const char *format, ...) @@ -1714,7 +1827,7 @@ int report_supported_opcodes(struct scsi_device *sdev, struct scsi_task **out_ta task = send_scsi_command(sdev, task, NULL); - ret = check_result("INQUIRY", sdev, task, status, key, ascq, num_ascq); + ret = check_result("REPORT_SUPPORTED_OPCODES", sdev, task, status, key, ascq, num_ascq); if (out_task) { *out_task = task; } else if (task) { diff --git a/test-tool/iscsi-support.h b/test-tool/iscsi-support.h index e5093e0..0cadb28 100644 --- a/test-tool/iscsi-support.h +++ b/test-tool/iscsi-support.h @@ -195,6 +195,7 @@ struct scsi_device { const char *iscsi_url; const char *sgio_dev; + int sgio_fd; }; extern struct scsi_device *sd; diff --git a/test-tool/iscsi-test-cu.c b/test-tool/iscsi-test-cu.c index e7f9420..70ad3c4 100644 --- a/test-tool/iscsi-test-cu.c +++ b/test-tool/iscsi-test-cu.c @@ -17,6 +17,8 @@ along with this program; if not, see . */ +#include "config.h" + #define _GNU_SOURCE #include #include @@ -30,6 +32,12 @@ #include #include +#ifdef HAVE_SG_IO +#include +#include +#include +#endif + #include #include #include @@ -673,15 +681,17 @@ test_teardown(void) int suite_init(void) { - if (sd->iscsi_ctx) { - iscsi_logout_sync(sd->iscsi_ctx); - iscsi_destroy_context(sd->iscsi_ctx); - } - sd->iscsi_ctx = iscsi_context_login(initiatorname1, sd->iscsi_url, &sd->iscsi_lun); - if (sd->iscsi_ctx == NULL) { - fprintf(stderr, - "error: Failed to login to target for test set-up\n"); - return 1; + if (sd->iscsi_url) { + if (sd->iscsi_ctx) { + iscsi_logout_sync(sd->iscsi_ctx); + iscsi_destroy_context(sd->iscsi_ctx); + } + sd->iscsi_ctx = iscsi_context_login(initiatorname1, sd->iscsi_url, &sd->iscsi_lun); + if (sd->iscsi_ctx == NULL) { + fprintf(stderr, + "error: Failed to login to target for test set-up\n"); + return 1; + } } #ifndef HAVE_CU_SUITEINFO_PSETUPFUNC /* libcunit version 1 */ @@ -697,10 +707,12 @@ suite_cleanup(void) /* libcunit version 1 */ test_teardown(); #endif - if (sd->iscsi_ctx) { - iscsi_logout_sync(sd->iscsi_ctx); - iscsi_destroy_context(sd->iscsi_ctx); - sd->iscsi_ctx = NULL; + if (sd->iscsi_url) { + if (sd->iscsi_ctx) { + iscsi_logout_sync(sd->iscsi_ctx); + iscsi_destroy_context(sd->iscsi_ctx); + sd->iscsi_ctx = NULL; + } } return 0; } @@ -863,11 +875,31 @@ static void parse_and_add_tests(char *testname_re) static int connect_scsi_device(struct scsi_device *sdev, const char *initiatorname) { - sdev->iscsi_ctx = iscsi_context_login(initiatorname, sdev->iscsi_url, &sdev->iscsi_lun); - if (sdev->iscsi_ctx == NULL) { - return -1; + if (sdev->iscsi_url) { + sdev->iscsi_ctx = iscsi_context_login(initiatorname, sdev->iscsi_url, &sdev->iscsi_lun); + if (sdev->iscsi_ctx == NULL) { + return -1; + } + return 0; } - return 0; +#ifdef HAVE_SG_IO + if (sdev->sgio_dev) { + int version; + + if ((sdev->sgio_fd = open(sdev->sgio_dev, O_RDWR)) == -1) { + fprintf(stderr, "Failed to open SG_IO device %s. Error:%s\n", sdev->sgio_dev, + strerror(errno)); + return -1; + } + if ((ioctl(sdev->sgio_fd, SG_GET_VERSION_NUM, &version) < 0) || (version < 30000)) { + fprintf(stderr, "%s is not a SCSI device node\n", sdev->sgio_dev); + close(sdev->sgio_fd); + return -1; + } + return 0; + } +#endif + return -1; } static void free_scsi_device(struct scsi_device *sdev) @@ -885,6 +917,15 @@ static void free_scsi_device(struct scsi_device *sdev) iscsi_destroy_context(sdev->iscsi_ctx); sdev->iscsi_ctx = NULL; } + + if (sdev->sgio_dev) { + free(discard_const(sdev->sgio_dev)); + sdev->sgio_dev = NULL; + } + if (sdev->sgio_fd != -1) { + close(sdev->sgio_fd); + sdev->sgio_fd = -1; + } free(sd); } @@ -929,6 +970,7 @@ main(int argc, char *argv[]) sd = malloc(sizeof(struct scsi_device)); memset(sd, '\0', sizeof(struct scsi_device)); + sd->sgio_fd = -1; while ((c = getopt_long(argc, argv, "?hli:I:t:sdgfAsSnuvxV", long_opts, &opt_idx)) > 0) { @@ -991,7 +1033,13 @@ main(int argc, char *argv[]) } if (optind < argc) { - sd->iscsi_url = strdup(argv[optind++]); + if (!strncmp(argv[optind], "iscsi://", 8)) { + sd->iscsi_url = strdup(argv[optind++]); +#ifdef HAVE_SG_IO + } else if (!strncmp(argv[optind], "/dev/sg", 7)) { + sd->sgio_dev = strdup(argv[optind++]); +#endif + } } if (optind < argc) { fprintf(stderr, "error: too many arguments\n"); @@ -1002,8 +1050,12 @@ main(int argc, char *argv[]) /* XXX why is this done? */ real_iscsi_queue_pdu = dlsym(RTLD_NEXT, "iscsi_queue_pdu"); - if (sd->iscsi_url == NULL) { - fprintf(stderr, "You must specify the URL\n"); + if (sd->iscsi_url == NULL && sd->sgio_dev== NULL ) { +#ifdef HAVE_SG_IO + fprintf(stderr, "You must specify either an iSCSI URL or a /dev/sg device\n"); +#else + fprintf(stderr, "You must specify either an iSCSI URL\n"); +#endif print_usage(); if (testname_re) free(testname_re); diff --git a/test-tool/test_read10_beyond_eol.c b/test-tool/test_read10_beyond_eol.c index 236ebe4..ae9aa95 100644 --- a/test-tool/test_read10_beyond_eol.c +++ b/test-tool/test_read10_beyond_eol.c @@ -46,7 +46,6 @@ test_read10_beyond_eol(void) CU_ASSERT_EQUAL(ret, 0); } - logging(LOG_VERBOSE, "Test READ10 1-256 blocks at LBA==2^31"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) { diff --git a/test-tool/test_read16_beyond_eol.c b/test-tool/test_read16_beyond_eol.c index 6e6138e..badf6b3 100644 --- a/test-tool/test_read16_beyond_eol.c +++ b/test-tool/test_read16_beyond_eol.c @@ -60,7 +60,6 @@ test_read16_beyond_eol(void) CU_ASSERT_EQUAL(ret, 0); } - logging(LOG_VERBOSE, "Test READ16 1-256 blocks at LBA==2^63"); for (i = 1; i <= 256; i++) { if (maximum_transfer_length && maximum_transfer_length < i) {