From 85e427ea54eee2a6fb25e66796c67b111a8045a9 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 25 Sep 2012 06:54:01 -0700 Subject: [PATCH 1/9] SCSI: READTOC change alloclen to a uint32 and encode it in the CDB --- include/scsi-lowlevel.h | 2 +- lib/scsi-lowlevel.c | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index 0483ee1..7c9f431 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -110,7 +110,7 @@ enum scsi_xfer_dir { /* * READTOC */ -EXTERN struct scsi_task *scsi_cdb_readtoc(int msf, int format, int track_session, uint32_t xferlen); +EXTERN struct scsi_task *scsi_cdb_readtoc(int msf, int format, int track_session, uint16_t alloc_len); enum scsi_readtoc_fmt { SCSI_READ_TOC = 0, diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index a536177..11c01c3 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -308,7 +308,7 @@ scsi_cdb_readcapacity10(int lba, int pmi) * READTOC */ struct scsi_task * -scsi_cdb_readtoc(int msf, int format, int track_session, uint32_t xferlen) +scsi_cdb_readtoc(int msf, int format, int track_session, uint16_t alloc_len) { struct scsi_task *task; @@ -335,13 +335,15 @@ scsi_cdb_readtoc(int msf, int format, int track_session, uint32_t xferlen) task->cdb[6] = 0xff & track_session; } + *(uint16_t *)&task->cdb[7] = htons(alloc_len); + task->cdb_size = 10; - if (xferlen != 0) { + if (alloc_len != 0) { task->xfer_dir = SCSI_XFER_READ; } else { task->xfer_dir = SCSI_XFER_NONE; } - task->expxferlen = xferlen; + task->expxferlen = alloc_len; task->params.readtoc.msf = msf; task->params.readtoc.format = format; From ce2db039a52bfd00c978a86a30fce1cfbac13f07 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 27 Sep 2012 18:29:57 -0700 Subject: [PATCH 2/9] TEST: try to make 0410 nicer 0410 is the first test for the MMC commandset. This commandset is in hindsight a bit more complex than other commandsets in that we have to handle quite a lot of cases : 1, if it is not a MMC device, we should check we get INVALID_OPCODE and then bail. Then if it is a MMC device, there are at least three different cases we have to handle: 2.a If there is no medium in the device 2.b there is media, but the medium is blank 2.c there is readable medium this makes MMC tests a lot more complex than other commandsets so we have to try to get this one as nice and simple as possible so we can reuse this test as the framework when adding other MMC tests. (even the 2.b test is not 100% right, since if the medium is BR, then a blank disk will fail with sense, but blank pre-BD mediums will not fail the READTOC command) --- test-tool/0410_readtoc_basic.c | 179 +++++++++++++++++++-------------- 1 file changed, 106 insertions(+), 73 deletions(-) diff --git a/test-tool/0410_readtoc_basic.c b/test-tool/0410_readtoc_basic.c index 3ba9a4f..e7bb4d4 100644 --- a/test-tool/0410_readtoc_basic.c +++ b/test-tool/0410_readtoc_basic.c @@ -29,16 +29,20 @@ int T0410_readtoc_basic(const char *initiator, const char *url, int data_loss, i struct scsi_inquiry_standard *inq; struct scsi_readcapacity10 *rc10; struct scsi_readtoc_list *list, *list1; - int ret, lun, i, toc_device; - int media, full_size; + int ret, lun, i, toc_device, full_size; + int is_blank = 0; + int no_medium = 0; printf("0410_readtoc_basic:\n"); printf("===================\n"); if (show_info) { printf("Test Read TOC command.\n"); printf(" If device does not support, just verify appropriate error returned\n"); - printf("1, Check we can read the TOC: track 0, non-MSF.\n"); + printf("1, Verify we can read the TOC: track 0, non-MSF. (non-MMC devices should return sense)\n"); printf("2, Make sure at least 4 bytes returned as header.\n"); + printf("3, Verify we can read the TOC: track 1, non-MSF.\n"); + printf("4, Make sure at least 4 bytes returned as header.\n"); + printf("5, Verify track 0 and 1 both returned the same data.\n"); printf("\n"); return 0; } @@ -52,6 +56,7 @@ int T0410_readtoc_basic(const char *initiator, const char *url, int data_loss, i ret = 0; + printf("Read standard INQUIRY data ... "); /* Submit INQUIRY so we can find out the device type */ task = iscsi_inquiry_sync(iscsi, lun, 0, 0, 255); @@ -92,22 +97,25 @@ int T0410_readtoc_basic(const char *initiator, const char *url, int data_loss, i printf("Check device-type is either of DISK, TAPE or CD/DVD ... "); switch (inq->device_type) { + case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC: + toc_device = 1; + break; case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS: case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS: toc_device = 0; break; - case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC: - toc_device = 1; - break; default: - printf("[FAILED]\n"); - printf("Device-type is not DISK, TAPE or CD/DVD. Device reported:%s\n", scsi_devtype_to_str(inq->device_type)); - ret = -1; + /* Unknown device type */ + printf("[SKIPPED]\n"); + printf("This test is only available on SBC/SBC/SSC devices\n"); + ret = -2; goto finished; } + scsi_free_scsi_task(task); printf("[OK]\n"); - printf("CD/DVD Device. Check if media present.\n"); + + printf("CD/DVD Device. Check if medium present ... "); task = iscsi_readcapacity10_sync(iscsi, lun, 0, 0); if (task == NULL) { printf("[FAILED]\n"); @@ -115,108 +123,127 @@ int T0410_readtoc_basic(const char *initiator, const char *url, int data_loss, i ret = -1; goto finished; } - if (task->status != SCSI_STATUS_GOOD) { + if (task->status == SCSI_STATUS_GOOD) { + rc10 = scsi_datain_unmarshall(task); + if (rc10 == NULL) { + printf("[FAILED]\n"); + printf("failed to unmarshall readcapacity10 data. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + /* LBA will return 0, if the medium is blank. */ + is_blank = rc10->lba ? 0 : 1; + } + /* If we get 'medium not present' there is no medium in the drive */ + if (task->status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_NOT_READY + && (task->sense.ascq == SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT + || task->sense.ascq == SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN + || task->sense.ascq == SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED)) { + no_medium = 1; + printf("[OK]\n"); + printf("No medium in drive. Medium access commands should fail\n"); + goto test1; + } else if (task->status != SCSI_STATUS_GOOD) { printf("[FAILED]\n"); printf("Readcapacity command: failed with sense. %s\n", iscsi_get_error(iscsi)); ret = -1; scsi_free_scsi_task(task); goto finished; } - rc10 = scsi_datain_unmarshall(task); - if (rc10 == NULL) { - printf("[FAILED]\n"); - printf("failed to unmarshall readcapacity10 data. %s\n", iscsi_get_error(iscsi)); - ret = -1; - scsi_free_scsi_task(task); - goto finished; + + scsi_free_scsi_task(task); + + printf("[OK]\n"); + if (is_blank) { + printf("Blank disk loaded. ReadTOC should fail.\n"); + } else { + printf("There is a disk in the drive. ReadTOC should work.\n"); } - /* LBA will return 0, if there is no media. */ - media = rc10->lba ? 1 : 0; test1: - - printf("Read TOC format 0000b (TOC)\n"); - if (toc_device) { - printf(" On MMC Device\n"); - } else { - printf(" On non-MMC Device\n"); - } + printf("Verify we can READTOC format 0000b (TOC) track 0 (%s) ... ", + toc_device ? "On MMC Device" : "On non-MMC Device" + ); task = iscsi_readtoc_sync(iscsi, lun, 0, 0, 0, 255); if (task == NULL) { printf("[FAILED]\n"); - printf("Failed to send READ TOC command : %s\n", iscsi_get_error(iscsi)); + printf("Failed to send READTOC command : %s\n", iscsi_get_error(iscsi)); ret = -1; goto finished; } + + /* If no medium, just check if we have appropriate error and bail. */ + if (no_medium) { + if (task->status == SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("READTOC Should have failed since no medium is loaded.\n"); + scsi_free_scsi_task(task); + ret = -1; + goto finished; + } + + if (task->status != SCSI_STATUS_CHECK_CONDITION + || task->sense.key != SCSI_SENSE_NOT_READY + || (task->sense.ascq != SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT && task->sense.ascq != SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN + && task->sense.ascq != SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED)) { + printf("[FAILED]\n"); + printf("READTOC failed but ascq was wrong. Should " + "have failed with MEDIUM_NOT_PRESENT. " + "Sense:%s\n", iscsi_get_error(iscsi)); + scsi_free_scsi_task(task); + ret = -1; + goto finished; + } + + printf("[OK]\n"); + printf("No disk, we got the correct sense code that medium is not present. Skipping the remainder of the test\n"); + scsi_free_scsi_task(task); + goto finished; + } + + /* If this is a non-MMC device, just verify that that comand failed as expected and then bail */ if (!toc_device) { if (task->status == SCSI_STATUS_GOOD) { printf("[FAILED]\n"); - printf("READ TOC Should have failed\n"); + printf("READTOC Should have failed\n"); ret = -1; } else if (task->status != SCSI_STATUS_CHECK_CONDITION || task->sense.key != SCSI_SENSE_ILLEGAL_REQUEST || task->sense.ascq != SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { printf("[FAILED]\n"); - printf("READ TOC failed but ascq was wrong. Should have failed with ILLEGAL_REQUEST/INVALID OPERATOR. Sense:%s\n", iscsi_get_error(iscsi)); - ret = -1; - } else { - printf("[OK]\n"); - ret = 0; - } - - scsi_free_scsi_task(task); - goto finished; - } - - - /* If no media, just check if we have appropriate error and bail. */ - if (!media) { - printf("No media, check we get appropriate error.\n"); - if (task->status == SCSI_STATUS_GOOD) { - printf("[FAILED]\n"); - printf("READ TOC Should have failed\n"); + printf("READTOC failed but ascq was wrong. Should have failed with ILLEGAL_REQUEST/INVALID OPERATION_CODE. Sense:%s\n", iscsi_get_error(iscsi)); ret = -1; - } else if (task->status != SCSI_STATUS_CHECK_CONDITION - || task->sense.key != SCSI_SENSE_NOT_READY - || (task->sense.ascq - != SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT - && task->sense.ascq - != SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN - && task->sense.ascq - != SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED)) { - printf("[FAILED]\n"); - printf("READ TOC failed but ascq was wrong. Should " - "have failed with MEDIUM_NOT_PRESENT. " - "Sense:%s\n", iscsi_get_error(iscsi)); - ret = -1; } else { printf("[OK]\n"); - ret = 0; + printf("Not an MMC device so READTOC failed as it should. Skipping rest of test\n"); } - scsi_free_scsi_task(task); goto finished; } - - /* We should only still be here if we are an MMC device and - have media. */ if (task->status != SCSI_STATUS_GOOD) { printf("[FAILED]\n"); - printf("READ TOC command failed : %s\n", iscsi_get_error(iscsi)); + printf("READTOC command failed : %s\n", iscsi_get_error(iscsi)); scsi_free_scsi_task(task); ret = -1; goto finished; } + printf("[OK]\n"); + + test2: + /* If we get here, there is a disk loaded and it contains data */ + printf("Verify we got at least 4 bytes of data for track 0 ... "); full_size = scsi_datain_getfullsize(task); if (full_size < 4) { printf("[FAILED]\n"); @@ -235,12 +262,13 @@ test2: } printf("[OK]\n"); + test3: - printf("Check lba of 1 is same as lba 0\n"); + printf("Verify we can READTOC format 0000b (TOC) track 1 ... "); task1 = iscsi_readtoc_sync(iscsi, lun, 0, 1, 0, 255); if (task1 == NULL) { printf("[FAILED]\n"); - printf("Failed to send READ TOC command : %s\n", iscsi_get_error(iscsi)); + printf("Failed to send READTOC command : %s\n", iscsi_get_error(iscsi)); scsi_free_scsi_task(task); ret = -1; goto finished; @@ -248,14 +276,17 @@ test3: if (task1->status != SCSI_STATUS_GOOD) { printf("[FAILED]\n"); - printf("READ TOC command failed : %s\n", iscsi_get_error(iscsi)); + printf("READTOC command failed : %s\n", iscsi_get_error(iscsi)); scsi_free_scsi_task(task); scsi_free_scsi_task(task1); ret = -1; goto finished; } + printf("[OK]\n"); +test4: + printf("Verify we got at least 4 bytes of data for track 1 ... "); full_size = scsi_datain_getfullsize(task1); if (full_size < 4) { printf("[FAILED]\n"); @@ -274,8 +305,10 @@ test3: ret = -1; goto finished; } - scsi_free_scsi_task(task1); + printf("[OK]\n"); +test5: + printf("Verify track 0 and 1 both returned the same data ... "); if (list->num != list1->num || list->first != list1->first || list->last != list1->last) { @@ -283,11 +316,11 @@ test3: printf("Read TOC header of lba 0 != TOC of lba 1.\n"); ret = -1; scsi_free_scsi_task(task); + scsi_free_scsi_task(task1); goto finished; } for (i=0; inum; i++) { - printf(" TOC descriptor %i\n", i); if (list->desc[i].desc.toc.adr != list1->desc[i].desc.toc.adr || list->desc[i].desc.toc.control != list1->desc[i].desc.toc.control || list->desc[i].desc.toc.track != list1->desc[i].desc.toc.track || @@ -296,13 +329,13 @@ test3: printf("Read TOC descriptors of lba 0 != TOC of lba 1.\n"); ret = -1; scsi_free_scsi_task(task); + scsi_free_scsi_task(task1); goto finished; } } - - printf("[OK]\n"); scsi_free_scsi_task(task); + scsi_free_scsi_task(task1); finished: From 0aa14bbe3984624f15fb9f6ee6c796830b4a046e Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 27 Sep 2012 18:47:15 -0700 Subject: [PATCH 3/9] TEST: 0420 some cosmetic printf changes --- test-tool/0420_reserve6_simple.c | 33 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/test-tool/0420_reserve6_simple.c b/test-tool/0420_reserve6_simple.c index 306f961..b3adcc7 100644 --- a/test-tool/0420_reserve6_simple.c +++ b/test-tool/0420_reserve6_simple.c @@ -58,7 +58,7 @@ int T0420_reserve6_simple(const char *initiator, const char *url, int data_loss, ret = 0; - printf("Send RESERVE6...\n"); + printf("Send RESERVE6 ... "); task = iscsi_reserve6_sync(iscsi, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -85,9 +85,9 @@ int T0420_reserve6_simple(const char *initiator, const char *url, int data_loss, scsi_free_scsi_task(task); goto finished; } - printf("[OK]\n"); - printf("Send RELEASE6...\n"); + + printf("Send RELEASE6 ... "); task = iscsi_release6_sync(iscsi, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -107,9 +107,10 @@ int T0420_reserve6_simple(const char *initiator, const char *url, int data_loss, printf("[OK]\n"); + test2: printf("Test that reservation works.\n"); - printf("Send RESERVE6 from Initiator 1...\n"); + printf("Send RESERVE6 from Initiator 1 ... "); task = iscsi_reserve6_sync(iscsi, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -129,7 +130,7 @@ test2: printf("[OK]\n"); - printf("Send RESERVE6 from Initiator 1...\n"); + printf("Send RESERVE6 from Initiator 1 ... "); task = iscsi_reserve6_sync(iscsi, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -148,8 +149,11 @@ test2: } printf("[OK]\n"); + + + test3: - printf("Send RESERVE6 from Initiator 2...Expect conflict.\n"); + printf("Send RESERVE6 from Initiator 2. Expect conflict. ... "); task = iscsi_reserve6_sync(iscsi2, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -168,8 +172,11 @@ test3: } printf("[OK]\n"); + + + test4: - printf("Send TESTUNITREADY from Initiator 1...\n"); + printf("Send TESTUNITREADY from Initiator 1 ... "); task = iscsi_testunitready_sync(iscsi, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -188,9 +195,9 @@ test4: } printf("[OK]\n"); -test5: - printf("Send TESTUNITREADY from Initiator 2...Expect conflict.\n"); +test5: + printf("Send TESTUNITREADY from Initiator 2. Expect conflict. ... "); task = iscsi_testunitready_sync(iscsi2, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -208,9 +215,11 @@ test5: } printf("[OK]\n"); + + test6: printf("Test that release actually works\n"); - printf("Send RELEASE6 from Initiator 1...\n"); + printf("Send RELEASE6 from Initiator 1 ... "); task = iscsi_release6_sync(iscsi, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -230,7 +239,7 @@ test6: printf("[OK]\n"); - printf("Send RESERVE6 Initiator 2...\n"); + printf("Send RESERVE6 Initiator 2 ... "); task = iscsi_reserve6_sync(iscsi2, lun); if (task == NULL) { printf("[FAILED]\n"); @@ -249,7 +258,7 @@ test6: } printf("[OK]\n"); - printf("Send RELEASE6 Initiator 2...\n"); + printf("Send RELEASE6 Initiator 2 ... "); task = iscsi_reserve6_sync(iscsi2, lun); if (task == NULL) { printf("[FAILED]\n"); From da893b0cf92c2a351994ce05510943c264895a72 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 27 Sep 2012 18:49:38 -0700 Subject: [PATCH 4/9] TEST 0420 cosmetic printf change --- test-tool/0420_reserve6_simple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-tool/0420_reserve6_simple.c b/test-tool/0420_reserve6_simple.c index b3adcc7..994fc47 100644 --- a/test-tool/0420_reserve6_simple.c +++ b/test-tool/0420_reserve6_simple.c @@ -130,7 +130,7 @@ test2: printf("[OK]\n"); - printf("Send RESERVE6 from Initiator 1 ... "); + printf("Send another RESERVE6 from Initiator 1 ... "); task = iscsi_reserve6_sync(iscsi, lun); if (task == NULL) { printf("[FAILED]\n"); From 2b6bc6c6875629749c69744deddebde6e04c60dd Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Fri, 28 Sep 2012 10:09:14 -0500 Subject: [PATCH 5/9] test: Add test to release from second initiator when already reserved by first initiator. --- test-tool/0420_reserve6_simple.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test-tool/0420_reserve6_simple.c b/test-tool/0420_reserve6_simple.c index 994fc47..3f8a2ba 100644 --- a/test-tool/0420_reserve6_simple.c +++ b/test-tool/0420_reserve6_simple.c @@ -36,6 +36,7 @@ int T0420_reserve6_simple(const char *initiator, const char *url, int data_loss, printf("1. Test simple RESERVE6 followed by RELEASE6\n"); printf("2. Test Initator 1 can reserve if already reserved by Intiator 1.\n"); printf("3. Test Initiator 2 can't reserve if already reserved by Initiator 1.\n"); + printf("3a. Test Initiator 2 release when reserved by Initiator 1 returns success, but without releasing.\n"); printf("4. Test Initiator 1 can testunitready if reserved by Initiator 1.\n"); printf("5. Test Initiator 2 can't testunitready if reserved by Initiator 1.\n"); printf("6. Test Initiator 2 can get reservation once Intiator 1 releases reservation.\n"); @@ -174,6 +175,27 @@ test3: printf("[OK]\n"); +test3a: + printf("Send RELEASE6 from Initiator 2. Expect NO-OP ..."); + task = iscsi_release6_sync(iscsi2, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send RELEASE6 command : %s\n", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + /* We expect this command to pass. */ + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("RELEASE6 command: failed with sense %s\n", + iscsi_get_error(iscsi)); + scsi_free_scsi_task(task); + ret = -1; + goto test4; + } + printf("[OK]\n"); + test4: printf("Send TESTUNITREADY from Initiator 1 ... "); From b160b950600490b0f894f0ee3a8d72103ce87e18 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sun, 30 Sep 2012 08:25:23 -0700 Subject: [PATCH 6/9] TESTS: Add 'initiator2' variable so we can run tests with two different initiatir names Update the 0420 test to use different initiator names for the two sessions to the target. --- test-tool/0420_reserve6_simple.c | 2 +- test-tool/iscsi-test.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/test-tool/0420_reserve6_simple.c b/test-tool/0420_reserve6_simple.c index 3f8a2ba..5213adc 100644 --- a/test-tool/0420_reserve6_simple.c +++ b/test-tool/0420_reserve6_simple.c @@ -51,7 +51,7 @@ int T0420_reserve6_simple(const char *initiator, const char *url, int data_loss, return -1; } - iscsi2 = iscsi_context_login(initiator, url, &lun); + iscsi2 = iscsi_context_login(initiator2, url, &lun); if (iscsi2 == NULL) { printf("Failed to login to target\n"); return -1; diff --git a/test-tool/iscsi-test.c b/test-tool/iscsi-test.c index b70e440..fad2af0 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -35,6 +35,8 @@ #include "iscsi-test.h" const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test"; +const char *initiator2 = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test-2"; + static int data_loss = 0; static int show_info = 0; @@ -242,6 +244,7 @@ void print_help(void) { fprintf(stderr, "Usage: iscsi-test [OPTION...] \n"); fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n"); + fprintf(stderr, " -I, --initiator-name-2=iqn-name Second initiatorname to use\n"); fprintf(stderr, " -t, --test=test-name Which test to run. Default is to run all tests.\n"); fprintf(stderr, " -l, --list List all tests.\n"); fprintf(stderr, " --info, Print extra info about a test.\n"); @@ -372,6 +375,7 @@ int main(int argc, const char *argv[]) { "usage", 0, POPT_ARG_NONE, &show_usage, 0, "Display brief usage message", NULL }, { "list", 'l', POPT_ARG_NONE, &list_tests, 0, "List all tests", NULL }, { "initiator-name", 'i', POPT_ARG_STRING, &initiator, 0, "Initiatorname to use", "iqn-name" }, + { "initiator-name-2", 'I', POPT_ARG_STRING, &initiator, 0, "Second initiatorname to use for tests using more two sessions", "iqn-name" }, { "test", 't', POPT_ARG_STRING, &testname, 0, "Which test to run", "testname" }, { "info", 0, POPT_ARG_NONE, &show_info, 0, "Show information about the test", "testname" }, { "dataloss", 0, POPT_ARG_NONE, &data_loss, 0, "Allow destructuve tests", NULL }, From c57827fef4b39a868bad1f032afe9b72b46aebfe Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sun, 30 Sep 2012 08:31:15 -0700 Subject: [PATCH 7/9] TESTS: Add externs for the two initiator names --- test-tool/iscsi-test.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index db25234..2c9e72d 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -17,6 +17,9 @@ along with this program; if not, see . */ +extern const char *initiator; +extern const char *initiator2; + struct iscsi_context *iscsi_context_login(const char *initiatorname, const char *url, int *lun); struct iscsi_async_state { From add9607244ed830ad3616190f46c484ef69b6c03 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sun, 30 Sep 2012 08:43:56 -0700 Subject: [PATCH 8/9] TEST: Add readme for the test tool --- test-tool/README | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 test-tool/README diff --git a/test-tool/README b/test-tool/README new file mode 100644 index 0000000..8b147d4 --- /dev/null +++ b/test-tool/README @@ -0,0 +1,45 @@ +This directory contains a libiscsi based test tool. +The purpose of this test tool is to validate iscsi and scsi protocol compliance of a target. + + +Initiatornames and LUN-masking +============================== +If the target uses lun-masking or ACLs you need to set the target up to allow +iscsi-test access to the LUNs you want to test. +By default iscsi-test uses the following two initiator names : + iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test + iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-test-2 + +Most tests only use the first name but some tests, such as the RESERVE/RELEASE +tests, will use both names. + +Optionally you can use different initiatir names for your testing using the +command line flags : + --initiator-name=iqn. + --initiator-name-2=iqn. + + +Listing all tests and test details: +=================================== +The --list argument is used to show a lost of all major tests that are available + iscsi-test --list + +To list all tests and a description of each test and subtests, use + iscsi-test --list --info + +Running tests: +============== +Running tests you need to specify which test to run using the --test argument. +This can either be the name of one specific test or a set of tests using * as +the wildcard character. + +Example to run one specific test : + iscsi-test --test="T1020_bufferoffset_invalid" iscsi://127.0.0.1/iqn.ronnie.test/1 + +Example to run all READ10 tests : + iscsi-test --test="*read10*" iscsi://127.0.0.1/iqn.ronnie.test/1 + +Or to run every test : + iscsi-test --test="*" iscsi://127.0.0.1/iqn.ronnie.test/1 + + From 30a32b4335a0cf4b7cdc04a01c05efe9b233a251 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sun, 30 Sep 2012 09:25:39 -0700 Subject: [PATCH 9/9] TESTS: Add two tests that RESERVE6 is dropped on LUN-reset as well as session logout --- Makefile.am | 2 + test-tool/0421_reserve6_lun_reset.c | 212 ++++++++++++++++++++++++++++ test-tool/0422_reserve6_logout.c | 166 ++++++++++++++++++++++ test-tool/iscsi-test.c | 2 + test-tool/iscsi-test.h | 2 + 5 files changed, 384 insertions(+) create mode 100644 test-tool/0421_reserve6_lun_reset.c create mode 100644 test-tool/0422_reserve6_logout.c diff --git a/Makefile.am b/Makefile.am index 623a948..2306eac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -135,6 +135,8 @@ bin_iscsi_test_SOURCES = test-tool/iscsi-test.c \ test-tool/0404_inquiry_all_reported_vpd.c \ test-tool/0410_readtoc_basic.c \ test-tool/0420_reserve6_simple.c \ + test-tool/0421_reserve6_lun_reset.c \ + test-tool/0422_reserve6_logout.c \ \ test-tool/1000_cmdsn_invalid.c \ test-tool/1010_datasn_invalid.c \ diff --git a/test-tool/0421_reserve6_lun_reset.c b/test-tool/0421_reserve6_lun_reset.c new file mode 100644 index 0000000..e956d0e --- /dev/null +++ b/test-tool/0421_reserve6_lun_reset.c @@ -0,0 +1,212 @@ +/* + Copyright (C) 2012 by Ronnie Sahlberg + + 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 "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test.h" + +struct mgmt_task { + uint32_t status; + uint32_t finished; +}; + +static void mgmt_cb(struct iscsi_context *iscsi _U_, int status _U_, + void *command_data, void *private_data) +{ + struct mgmt_task *mgmt_task = (struct mgmt_task *)private_data; + + mgmt_task->status = *(uint32_t *)command_data; + mgmt_task->finished = 1; +} + +int T0421_reserve6_lun_reset(const char *initiator, const char *url, int data_loss, int show_info) +{ + struct iscsi_context *iscsi, *iscsi2; + struct scsi_task *task; + int ret, lun; + struct mgmt_task mgmt_task = {0, 0}; + struct pollfd pfd; + + printf("0421_reserve6_lun_reset:\n"); + printf("========================\n"); + if (show_info) { + printf("Test that a RESERVE6 is dropped by a LUN-reset\n"); + printf(" If device does not support RESERVE6, just skip the test.\n"); + printf("1, Reserve the device from the first initiator.\n"); + printf("2, Verify we can access the LUN from the first initiator\n"); + printf("3, Verify we can NOT access the LUN from the second initiator\n"); + printf("4, Send a LUN-reset to the target\n"); + printf("5, Verify we can access the LUN from the second initiator\n"); + printf("\n"); + return 0; + } + + iscsi = iscsi_context_login(initiator, url, &lun); + if (iscsi == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + iscsi2 = iscsi_context_login(initiator2, url, &lun); + if (iscsi2 == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + ret = 0; + + + + + printf("Send RESERVE6 from the first initiator ... "); + task = iscsi_reserve6_sync(iscsi, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send RESERVE6 command : %s\n", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST + && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { + printf("[SKIPPED]\n"); + printf("RESERVE6 Not Supported\n"); + ret = -2; + scsi_free_scsi_task(task); + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("RESERVE6 failed with sense:%s\n", + iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto test2; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + + +test2: + printf("Verify we can access the LUN from the first initiator ... "); + task = iscsi_testunitready_sync(iscsi, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send TEST UNIT READY command: %s\n", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("TEST UNIT READY command: failed with sense %s\n", + iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto test3; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + + +test3: + printf("Verify we can NOT access the LUN from the second initiator ... "); + task = iscsi_testunitready_sync(iscsi2, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send TEST UNIT READY command: %s\n", + iscsi_get_error(iscsi2)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_RESERVATION_CONFLICT) { + printf("[FAILED]\n"); + printf("Expected RESERVATION CONFLICT\n"); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + +test4: + printf("Send a LUN Reset to the target ... "); + iscsi_task_mgmt_lun_reset_async(iscsi, lun, mgmt_cb, &mgmt_task); + while (mgmt_task.finished == 0) { + pfd.fd = iscsi_get_fd(iscsi); + pfd.events = iscsi_which_events(iscsi); + + if (poll(&pfd, 1, -1) < 0) { + printf("Poll failed"); + goto finished; + } + if (iscsi_service(iscsi, pfd.revents) < 0) { + printf("iscsi_service failed with : %s\n", iscsi_get_error(iscsi)); + break; + } + } + if (mgmt_task.status != 0) { + printf("[FAILED]\n"); + printf("Failed to reset the LUN\n"); + goto finished; + } + printf("[OK]\n"); + +test5: + /* We might be getting UNIT_ATTENTION/BUS_RESET after the lun-reset above. + If so just loop and try the TESTUNITREADY again until it clears + */ + printf("Verify we can access the LUN from the second initiator ... "); + task = iscsi_testunitready_sync(iscsi2, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send TEST UNIT READY command: %s\n", + iscsi_get_error(iscsi2)); + ret = -1; + goto finished; + } + if (task->status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_UNIT_ATTENTION + && task->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET) { + printf("Got BUS RESET. Retry accessing the LUN\n"); + goto test5; + + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("TEST UNIT READY command: failed with sense %s\n", + iscsi_get_error(iscsi2)); + ret = -1; + scsi_free_scsi_task(task); + goto test3; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + + +finished: + iscsi_logout_sync(iscsi); + iscsi_destroy_context(iscsi); + iscsi_logout_sync(iscsi2); + iscsi_destroy_context(iscsi2); + return ret; +} diff --git a/test-tool/0422_reserve6_logout.c b/test-tool/0422_reserve6_logout.c new file mode 100644 index 0000000..e30d597 --- /dev/null +++ b/test-tool/0422_reserve6_logout.c @@ -0,0 +1,166 @@ +/* + Copyright (C) 2012 by Ronnie Sahlberg + + 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 T0422_reserve6_logout(const char *initiator, const char *url, int data_loss, int show_info) +{ + struct iscsi_context *iscsi, *iscsi2; + struct scsi_task *task; + int ret, lun; + + printf("0422_reserve6_logout:\n"); + printf("=====================\n"); + if (show_info) { + printf("Test that a RESERVE6 is dropped when the session is logged out\n"); + printf(" If device does not support RESERVE6, just skip the test.\n"); + printf("1, Reserve the device from the first initiator.\n"); + printf("2, Verify we can access the LUN from the first initiator.\n"); + printf("3, Verify we can NOT access the LUN from the second initiator.\n"); + printf("4, Logout the first initiator.\n"); + printf("5, Verify we can access the LUN from the second initiator.\n"); + printf("\n"); + return 0; + } + + iscsi = iscsi_context_login(initiator, url, &lun); + if (iscsi == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + iscsi2 = iscsi_context_login(initiator2, url, &lun); + if (iscsi2 == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + ret = 0; + + + + + printf("Send RESERVE6 from the first initiator ... "); + task = iscsi_reserve6_sync(iscsi, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send RESERVE6 command : %s\n", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST + && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { + printf("[SKIPPED]\n"); + printf("RESERVE6 Not Supported\n"); + ret = -2; + scsi_free_scsi_task(task); + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("RESERVE6 failed with sense:%s\n", + iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto test2; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + + +test2: + printf("Verify we can access the LUN from the first initiator ... "); + task = iscsi_testunitready_sync(iscsi, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send TEST UNIT READY command: %s\n", + iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("TEST UNIT READY command: failed with sense %s\n", + iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto test3; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + + +test3: + printf("Verify we can NOT access the LUN from the second initiator ... "); + task = iscsi_testunitready_sync(iscsi2, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send TEST UNIT READY command: %s\n", + iscsi_get_error(iscsi2)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_RESERVATION_CONFLICT) { + printf("[FAILED]\n"); + printf("Expected RESERVATION CONFLICT\n"); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + +test4: + printf("Logout the first initiator ... "); + iscsi_logout_sync(iscsi); + iscsi_destroy_context(iscsi); + printf("[OK]\n"); + +test5: + printf("Verify we can access the LUN from the second initiator ... "); + task = iscsi_testunitready_sync(iscsi2, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send TEST UNIT READY command: %s\n", + iscsi_get_error(iscsi2)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("TEST UNIT READY command: failed with sense %s\n", + iscsi_get_error(iscsi2)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + + +finished: + iscsi_logout_sync(iscsi2); + iscsi_destroy_context(iscsi2); + return ret; +} diff --git a/test-tool/iscsi-test.c b/test-tool/iscsi-test.c index fad2af0..1c7c7ea 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -218,6 +218,8 @@ struct scsi_test tests[] = { /* reserve6/release6 */ { "T0420_reserve6_simple", T0420_reserve6_simple }, +{ "T0421_reserve6_lun_reset", T0421_reserve6_lun_reset }, +{ "T0422_reserve6_logout", T0422_reserve6_logout }, /* iSCSI protocol tests */ diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index 2c9e72d..3bd8e1c 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -170,6 +170,8 @@ int T0404_inquiry_all_reported_vpd(const char *initiator, const char *url, int d int T0410_readtoc_basic(const char *initiator, const char *url, int data_loss, int show_info); int T0420_reserve6_simple(const char *initiator, const char *url, int data_loss, int show_info); +int T0421_reserve6_lun_reset(const char *initiator, const char *url, int data_loss, int show_info); +int T0422_reserve6_logout(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);