From 676bf2372c7d47b0978b1bb78fbc0c368642e67a Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 4 Oct 2012 21:35:36 -0700 Subject: [PATCH 01/36] TEST: Add test that target reset will release a RESERVE6 --- Makefile.am | 1 + test-tool/0424_reserve6_target_reset.c | 212 +++++++++++++++++++++++++ test-tool/iscsi-test.c | 1 + test-tool/iscsi-test.h | 1 + 4 files changed, 215 insertions(+) create mode 100644 test-tool/0424_reserve6_target_reset.c diff --git a/Makefile.am b/Makefile.am index 3ef053c..735760a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -138,6 +138,7 @@ bin_iscsi_test_SOURCES = test-tool/iscsi-test.c \ test-tool/0421_reserve6_lun_reset.c \ test-tool/0422_reserve6_logout.c \ test-tool/0423_reserve6_sessionloss.c \ + test-tool/0424_reserve6_target_reset.c \ \ test-tool/1000_cmdsn_invalid.c \ test-tool/1010_datasn_invalid.c \ diff --git a/test-tool/0424_reserve6_target_reset.c b/test-tool/0424_reserve6_target_reset.c new file mode 100644 index 0000000..b9fd07d --- /dev/null +++ b/test-tool/0424_reserve6_target_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 T0424_reserve6_target_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("0424_reserve6_target_reset:\n"); + printf("========================\n"); + if (show_info) { + printf("Test that a RESERVE6 is dropped by a Target-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 Target-reset\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 Target Cold-Reset ... "); + iscsi_task_mgmt_target_cold_reset_async(iscsi, 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/iscsi-test.c b/test-tool/iscsi-test.c index 07bc9d6..884240c 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -221,6 +221,7 @@ struct scsi_test tests[] = { { "T0421_reserve6_lun_reset", T0421_reserve6_lun_reset }, { "T0422_reserve6_logout", T0422_reserve6_logout }, { "T0423_reserve6_sessionloss", T0423_reserve6_sessionloss }, +{ "T0424_reserve6_target_reset", T0424_reserve6_target_reset }, /* iSCSI protocol tests */ diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index 2518594..acc3817 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -173,6 +173,7 @@ int T0420_reserve6_simple(const char *initiator, const char *url, int data_loss, 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 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 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); From b673d8ed572a208b07cd8b09c4f29359b0f8ce8d Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 16 Oct 2012 21:30:39 -0700 Subject: [PATCH 02/36] CONNECT: export the iscsi_reconnect() function. There are situations where you may want to trigger an iscsi reconnect explicitely from an application so make this function exposed to applications. --- include/iscsi-private.h | 1 - include/iscsi.h | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 40a96e3..bbc0462 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -274,7 +274,6 @@ unsigned long crc32c(char *buf, int len); struct scsi_task *iscsi_scsi_get_task_from_pdu(struct iscsi_pdu *pdu); -int iscsi_reconnect(struct iscsi_context *iscsi); void iscsi_set_noautoreconnect(struct iscsi_context *iscsi, int state); #ifdef __cplusplus diff --git a/include/iscsi.h b/include/iscsi.h index 3aac789..ea00f31 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -332,6 +332,15 @@ EXTERN int iscsi_full_connect_sync(struct iscsi_context *iscsi, const char *port */ EXTERN int iscsi_disconnect(struct iscsi_context *iscsi); +/* + * Disconnect a connection to a target and try to reconnect. + * + * Returns: + * 0 reconnect was successful + * <0 error + */ +EXTERN int iscsi_reconnect(struct iscsi_context *iscsi); + /* * Asynchronous call to perform an ISCSI login. * From 91267f5aaaaf705d4bb3a690dcba4eb7fb1c91b9 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 18 Oct 2012 09:21:54 +0200 Subject: [PATCH 03/36] Limit immediate and unsolicited data to FirstBurstLength RFC3270 describes in section 12.14 that immediate and unsolicited data sent from the initiator to the target must not exceed FristBurstLength bytes in total. --- lib/scsi-command.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/scsi-command.c b/lib/scsi-command.c index 7c6770e..3122b52 100644 --- a/lib/scsi-command.c +++ b/lib/scsi-command.c @@ -242,8 +242,8 @@ iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun, if (iscsi->use_immediate_data == ISCSI_IMMEDIATE_DATA_YES) { uint32_t len = data.size; - if (len > iscsi->target_max_recv_data_segment_length) { - len = iscsi->target_max_recv_data_segment_length; + if (len > iscsi->first_burst_length) { + len = iscsi->first_burst_length; } if (iscsi_pdu_add_data(iscsi, pdu, data.data, len) @@ -302,7 +302,7 @@ iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun, } /* Can we send some unsolicited data ? */ - if (pdu->nidata.size != 0 && iscsi->use_initial_r2t == ISCSI_INITIAL_R2T_NO) { + if (pdu->nidata.size != 0 && iscsi->use_initial_r2t == ISCSI_INITIAL_R2T_NO && iscsi->use_immediate_data == ISCSI_IMMEDIATE_DATA_NO) { uint32_t len = pdu->nidata.size - offset; if (len > iscsi->first_burst_length) { From c4592550d645aa5d1fb5edd64a5cff6e91a4bcdd Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 18 Oct 2012 09:40:05 +0200 Subject: [PATCH 04/36] Move iscsi_set_tcp_keepalive after socket creation Socket options should be set directly after socket creation. --- lib/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/socket.c b/lib/socket.c index 8ec437a..806ce7b 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -157,6 +157,7 @@ iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, iscsi->connect_data = private_data; set_nonblocking(iscsi->fd); + iscsi_set_tcp_keepalive(iscsi, 30, 3, 30); if (connect(iscsi->fd, ai->ai_addr, socksize) != 0 && errno != EINPROGRESS) { @@ -437,7 +438,6 @@ iscsi_service(struct iscsi_context *iscsi, int revents) return -1; } - iscsi_set_tcp_keepalive(iscsi, 30, 3, 30); iscsi->is_connected = 1; iscsi->socket_status_cb(iscsi, SCSI_STATUS_GOOD, NULL, iscsi->connect_data); From 4cb845477d1b99ae100f1d2f638aa6412c214e45 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 18 Oct 2012 10:36:26 +0200 Subject: [PATCH 05/36] Add debugging framework This patch adds a user configurable debug level. For testing it includes connection info and reporting errors. --- include/iscsi-private.h | 2 ++ include/iscsi.h | 8 ++++++++ lib/connect.c | 4 ++++ lib/init.c | 16 ++++++++++++++-- lib/libiscsi.def | 1 + lib/libiscsi.syms | 1 + lib/socket.c | 9 +++++++++ src/iscsi-inq.c | 8 +++++++- src/iscsi-ls.c | 9 +++++++-- 9 files changed, 53 insertions(+), 5 deletions(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index bbc0462..eb54a84 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -65,6 +65,7 @@ struct iscsi_context { const char *initiator_name; const char *target_name; const char *target_address; /* If a redirect */ + const char *connected_portal; const char *alias; const char *user; @@ -120,6 +121,7 @@ struct iscsi_context { const char *portal; int no_auto_reconnect; int reconnect_deferred; + int debug; }; #define ISCSI_PDU_IMMEDIATE 0x40 diff --git a/include/iscsi.h b/include/iscsi.h index ea00f31..60df33d 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -958,6 +958,14 @@ iscsi_scsi_task_cancel(struct iscsi_context *iscsi, EXTERN void iscsi_scsi_cancel_all_tasks(struct iscsi_context *iscsi); +#define DPRINTF(iscsi,level,fmt,args...) do { if ((iscsi)->debug >= level) {fprintf(stderr,"libiscsi: ");fprintf(stderr, (fmt), ##args); fprintf(stderr,"\n");} } while (0); + +/* + * This function is to set the debugging level (0=disabled). + */ +EXTERN void +iscsi_set_debug(struct iscsi_context *iscsi, int level); + #ifdef __cplusplus } #endif diff --git a/lib/connect.c b/lib/connect.c index 931f571..db24a3a 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -158,6 +158,8 @@ int iscsi_reconnect(struct iscsi_context *old_iscsi) { struct iscsi_context *iscsi = old_iscsi; + DPRINTF(iscsi,2,"reconnect initiated"); + /* This is mainly for tests, where we do not want to automatically reconnect but rather want the commands to fail with an error if the target drops the session. @@ -213,6 +215,8 @@ try_again: iscsi->lun = old_iscsi->lun; iscsi->portal = strdup(old_iscsi->portal); + + iscsi->debug = old_iscsi->debug; if (iscsi_full_connect_sync(iscsi, iscsi->portal, iscsi->lun) != 0) { iscsi_destroy_context(iscsi); diff --git a/lib/init.c b/lib/init.c index 1c11bcf..9c6bcac 100644 --- a/lib/init.c +++ b/lib/init.c @@ -244,6 +244,11 @@ iscsi_destroy_context(struct iscsi_context *iscsi) free(discard_const(iscsi->chap_c)); iscsi->chap_c = NULL; + if (iscsi->connected_portal != NULL) { + free(discard_const(iscsi->connected_portal)); + iscsi->connected_portal = NULL; + } + iscsi->connect_data = NULL; free(iscsi); @@ -251,8 +256,6 @@ iscsi_destroy_context(struct iscsi_context *iscsi) return 0; } - - void iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...) { @@ -270,9 +273,18 @@ iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...) free(iscsi->error_string); iscsi->error_string = str; + va_end(ap); + + DPRINTF(iscsi,1,str); } +void +iscsi_set_debug(struct iscsi_context *iscsi, int level) +{ + iscsi->debug = level; + DPRINTF(iscsi,2,"set debug level to %d",level); +} const char * iscsi_get_error(struct iscsi_context *iscsi) diff --git a/lib/libiscsi.def b/lib/libiscsi.def index 865a62f..0b6529e 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -62,6 +62,7 @@ iscsi_scsi_command_sync iscsi_scsi_task_cancel iscsi_service iscsi_set_alias +iscsi_set_debug iscsi_set_header_digest iscsi_set_initiator_username_pwd iscsi_set_isid_en diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index 9c6bc3a..cee6081 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -60,6 +60,7 @@ iscsi_scsi_command_sync iscsi_scsi_task_cancel iscsi_service iscsi_set_alias +iscsi_set_debug iscsi_set_header_digest iscsi_set_initiator_username_pwd iscsi_set_isid_en diff --git a/lib/socket.c b/lib/socket.c index 806ce7b..b038c0b 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -68,6 +68,8 @@ iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, struct addrinfo *ai = NULL; int socksize; + DPRINTF(iscsi,2,"connecting to portal %s",portal); + if (iscsi->fd != -1) { iscsi_set_error(iscsi, "Trying to connect but already connected."); @@ -170,6 +172,10 @@ iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, } freeaddrinfo(ai); + + if (iscsi->connected_portal) free(iscsi->connected_portal); + iscsi->connected_portal=strdup(portal); + return 0; } @@ -183,6 +189,9 @@ iscsi_disconnect(struct iscsi_context *iscsi) } close(iscsi->fd); + + if (iscsi->connected_portal) + DPRINTF(iscsi,2,"disconnected from portal %s",iscsi->connected_portal); iscsi->fd = -1; iscsi->is_connected = 0; diff --git a/src/iscsi-inq.c b/src/iscsi-inq.c index e0fe471..87b6d35 100644 --- a/src/iscsi-inq.c +++ b/src/iscsi-inq.c @@ -196,6 +196,7 @@ void print_help(void) fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n"); fprintf(stderr, " -e, --evpd=integer evpd\n"); fprintf(stderr, " -c, --pagecode=integer page code\n"); + fprintf(stderr, " -d, --debug=integer debug level (0=disabled)\n"); fprintf(stderr, "\n"); fprintf(stderr, "Help options:\n"); fprintf(stderr, " -?, --help Show this help message\n"); @@ -218,7 +219,7 @@ int main(int argc, const char *argv[]) const char *url = NULL; struct iscsi_url *iscsi_url = NULL; int evpd = 0, pagecode = 0; - int show_help = 0, show_usage = 0; + int show_help = 0, show_usage = 0, debug = 0; int res; struct poptOption popt_options[] = { @@ -227,6 +228,7 @@ int main(int argc, const char *argv[]) { "initiator-name", 'i', POPT_ARG_STRING, &initiator, 0, "Initiatorname to use", "iqn-name" }, { "evpd", 'e', POPT_ARG_INT, &evpd, 0, "evpd", "integer" }, { "pagecode", 'c', POPT_ARG_INT, &pagecode, 0, "page code", "integer" }, + { "debug", 'd', POPT_ARG_INT, &debug, 0, "Debugging level", "integer" }, POPT_TABLEEND }; @@ -263,6 +265,10 @@ int main(int argc, const char *argv[]) exit(10); } + if (debug > 0) { + iscsi_set_debug(iscsi, debug); + } + if (url == NULL) { fprintf(stderr, "You must specify the URL\n"); print_usage(); diff --git a/src/iscsi-ls.c b/src/iscsi-ls.c index 1ce5b3b..6951a57 100644 --- a/src/iscsi-ls.c +++ b/src/iscsi-ls.c @@ -291,6 +291,7 @@ void print_help(void) fprintf(stderr, "Usage: iscsi-ls [OPTION...] \n"); fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n"); fprintf(stderr, " -s, --show-luns Show the luns for each target\n"); + fprintf(stderr, " -d, --debug=integer debug level (0=disabled)\n"); fprintf(stderr, "\n"); fprintf(stderr, "Help options:\n"); fprintf(stderr, " -?, --help Show this help message\n"); @@ -314,13 +315,14 @@ int main(int argc, const char *argv[]) const char *url = NULL; poptContext pc; int res; - int show_help = 0, show_usage = 0; + int show_help = 0, show_usage = 0, debug = 0; struct poptOption popt_options[] = { { "help", '?', POPT_ARG_NONE, &show_help, 0, "Show this help message", NULL }, { "usage", 0, POPT_ARG_NONE, &show_usage, 0, "Display brief usage message", NULL }, { "initiator-name", 'i', POPT_ARG_STRING, &initiator, 0, "Initiatorname to use", "iqn-name" }, { "show-luns", 's', POPT_ARG_NONE, &showluns, 0, "Show the luns for each target", NULL }, + { "debug", 'd', POPT_ARG_INT, &debug, 0, "Debugging level", "integer" }, POPT_TABLEEND }; @@ -353,7 +355,6 @@ int main(int argc, const char *argv[]) poptFreeContext(pc); - if (url == NULL) { fprintf(stderr, "You must specify iscsi target portal.\n"); print_usage(); @@ -366,6 +367,10 @@ int main(int argc, const char *argv[]) exit(10); } + if (debug > 0) { + iscsi_set_debug(iscsi, debug); + } + iscsi_url = iscsi_parse_portal_url(iscsi, url); if (iscsi_url == NULL) { fprintf(stderr, "Failed to parse URL: %s\n", From cb6f2ce252cb7009252f94aae8d1f774c800243c Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 18 Oct 2012 10:48:52 +0200 Subject: [PATCH 06/36] iSCSI Redirect support This patch adds support for persistant portals like they are common to storage arrays with different interfaces or physical members. --- include/iscsi.h | 1 + lib/connect.c | 12 ++++++++++++ lib/login.c | 7 +++++++ 3 files changed, 20 insertions(+) diff --git a/include/iscsi.h b/include/iscsi.h index 60df33d..309c081 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -231,6 +231,7 @@ enum scsi_status { SCSI_STATUS_GOOD = 0, SCSI_STATUS_CHECK_CONDITION = 2, SCSI_STATUS_RESERVATION_CONFLICT = 0x18, + SCSI_STATUS_REDIRECT = 0x101, SCSI_STATUS_CANCELLED = 0x0f000000, SCSI_STATUS_ERROR = 0x0f000001 }; diff --git a/lib/connect.c b/lib/connect.c index db24a3a..eb9b7c7 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -35,6 +35,10 @@ struct connect_task { int lun; }; +static void +iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, + void *private_data); + static void iscsi_testunitready_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) @@ -82,6 +86,14 @@ iscsi_login_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, { struct connect_task *ct = private_data; + if (status == 0x101 && iscsi->target_address) { + iscsi_disconnect(iscsi); + if (iscsi_connect_async(iscsi, iscsi->target_address, iscsi_connect_cb, iscsi->connect_data) != 0) { + return; + } + return; + } + if (status != 0) { ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data); return; diff --git a/lib/login.c b/lib/login.c index b685743..d841a4b 100644 --- a/lib/login.c +++ b/lib/login.c @@ -1088,6 +1088,13 @@ iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, size -= len + 1; } + if (status == 0x101 && iscsi->target_address) { + DPRINTF(iscsi,2,"target requests redirect to %s",iscsi->target_address); + pdu->callback(iscsi, SCSI_STATUS_REDIRECT, NULL, + pdu->private_data); + return 0; + } + if (status != 0) { iscsi_set_error(iscsi, "Failed to log in to target. Status: %s(%d)", login_error_str(status), status); From f973608578a73e526fc09f107f5f9ca28eb28dcb Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 18 Oct 2012 11:55:15 +0200 Subject: [PATCH 07/36] Add iscsi_tcp_set_user_timeout function This patch adds a user configurable option to set the TCP_USER_TIMEOUT socket option. With this timeout set a broken TCP session is shutdown after a given timeout even there are unacked packets. SO_KEEPALIVE seems not to work in this case. --- include/iscsi-private.h | 2 ++ include/iscsi.h | 7 ++++++ lib/connect.c | 2 ++ lib/libiscsi.def | 1 + lib/libiscsi.syms | 1 + lib/socket.c | 47 ++++++++++++++++++++++++++++++++++++++++- 6 files changed, 59 insertions(+), 1 deletion(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index eb54a84..010eeb8 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -84,6 +84,8 @@ struct iscsi_context { int fd; int is_connected; + + int tcp_user_timeout; int current_phase; int next_phase; diff --git a/include/iscsi.h b/include/iscsi.h index 309c081..70881f6 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -967,6 +967,13 @@ iscsi_scsi_cancel_all_tasks(struct iscsi_context *iscsi); EXTERN void iscsi_set_debug(struct iscsi_context *iscsi, int level); +/* + * This function is to set the TCP_USER_TIMEOUT option. It has to be called after iscsi + * context creation. The value given in ms is then applied each time a new socket is created. + */ +EXTERN void +iscsi_set_tcp_user_timeout(struct iscsi_context *iscsi, int timeout_ms); + #ifdef __cplusplus } #endif diff --git a/lib/connect.c b/lib/connect.c index eb9b7c7..23ed62f 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -229,6 +229,8 @@ try_again: iscsi->portal = strdup(old_iscsi->portal); iscsi->debug = old_iscsi->debug; + + iscsi->tcp_user_timeout = old_iscsi->tcp_user_timeout; if (iscsi_full_connect_sync(iscsi, iscsi->portal, iscsi->lun) != 0) { iscsi_destroy_context(iscsi); diff --git a/lib/libiscsi.def b/lib/libiscsi.def index 0b6529e..56ca61a 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -72,6 +72,7 @@ iscsi_set_isid_reserved iscsi_set_session_type iscsi_set_targetname iscsi_set_tcp_keepalive +iscsi_set_tcp_user_timeout iscsi_startstopunit_sync iscsi_startstopunit_task iscsi_synchronizecache10_sync diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index cee6081..dba697a 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -70,6 +70,7 @@ iscsi_set_isid_reserved iscsi_set_session_type iscsi_set_targetname iscsi_set_tcp_keepalive +iscsi_set_tcp_user_timeout iscsi_startstopunit_sync iscsi_startstopunit_task iscsi_synchronizecache10_sync diff --git a/lib/socket.c b/lib/socket.c index b038c0b..0da1d95 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -159,7 +159,14 @@ iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, iscsi->connect_data = private_data; set_nonblocking(iscsi->fd); - iscsi_set_tcp_keepalive(iscsi, 30, 3, 30); + + if (iscsi->tcp_user_timeout > 0) { + set_tcp_user_timeout(iscsi); + } + else + { + iscsi_set_tcp_keepalive(iscsi, 30, 3, 30); + } if (connect(iscsi->fd, ai->ai_addr, socksize) != 0 && errno != EINPROGRESS) { @@ -524,6 +531,40 @@ iscsi_free_iscsi_inqueue(struct iscsi_in_pdu *inqueue) } } +#ifndef TCP_USER_TIMEOUT +#define TCP_USER_TIMEOUT 18 +#endif + +int set_tcp_user_timeout(struct iscsi_context *iscsi) +{ + int level, value; + + #if defined(__FreeBSD__) || defined(__sun) + struct protoent *buf; + + if ((buf = getprotobyname("tcp")) != NULL) + level = buf->p_proto; + else + return -1; + #else + level = SOL_TCP; + #endif + + value = iscsi->tcp_user_timeout; + if (setsockopt(iscsi->fd, level, TCP_USER_TIMEOUT, &value, sizeof(value)) != 0) { + iscsi_set_error(iscsi, "TCP: Failed to set tcp user timeout. Error %s(%d)", strerror(errno), errno); + return -1; + } + DPRINTF(iscsi,3,"TCP_USER_TIMEOUT set to %d",value); + return 0; +} + +void iscsi_set_tcp_user_timeout(struct iscsi_context *iscsi, int timeout_ms) +{ + iscsi->tcp_user_timeout=timeout_ms; + DPRINTF(iscsi,2,"TCP_USER_TIMEOUT will be set to %dms on next socket creation",timeout_ms); +} + int iscsi_set_tcp_keepalive(struct iscsi_context *iscsi, int idle, int count, int interval) { int level, value; @@ -544,6 +585,7 @@ int iscsi_set_tcp_keepalive(struct iscsi_context *iscsi, int idle, int count, in iscsi_set_error(iscsi, "TCP: Failed to set socket option SO_KEEPALIVE. Error %s(%d)", strerror(errno), errno); return -1; } + DPRINTF(iscsi,3,"SO_KEEPALIVE set to %d",value); #endif #ifdef TCP_KEEPCNT value = count; @@ -551,6 +593,7 @@ int iscsi_set_tcp_keepalive(struct iscsi_context *iscsi, int idle, int count, in iscsi_set_error(iscsi, "TCP: Failed to set tcp keepalive count. Error %s(%d)", strerror(errno), errno); return -1; } + DPRINTF(iscsi,3,"TCP_KEEPCNT set to %d",value); #endif #ifdef TCP_KEEPINTVL value = interval; @@ -558,6 +601,7 @@ int iscsi_set_tcp_keepalive(struct iscsi_context *iscsi, int idle, int count, in iscsi_set_error(iscsi, "TCP: Failed to set tcp keepalive interval. Error %s(%d)", strerror(errno), errno); return -1; } + DPRINTF(iscsi,3,"TCP_KEEPINTVL set to %d",value); #endif #ifdef TCP_KEEPIDLE value = idle; @@ -565,6 +609,7 @@ int iscsi_set_tcp_keepalive(struct iscsi_context *iscsi, int idle, int count, in iscsi_set_error(iscsi, "TCP: Failed to set tcp keepalive idle. Error %s(%d)", strerror(errno), errno); return -1; } + DPRINTF(iscsi,3,"TCP_KEEPIDLE set to %d",value); #endif return 0; From 326b2ea49dab81ac5adb3f8e30d340943e13a602 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 18 Oct 2012 12:18:40 +0200 Subject: [PATCH 08/36] Add backoff mechanism to iscsi_reconnect This patch adds a linear backoff mechanishm + jitter in case a reconnect fails. If there is a longer outage this is to avoid a large amount of simultaneous connects to the storage. --- lib/connect.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/connect.c b/lib/connect.c index 23ed62f..f82df28 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -209,6 +209,9 @@ int iscsi_reconnect(struct iscsi_context *old_iscsi) return 0; } + int retry = 0; + srand (time(NULL)^getpid()); + try_again: iscsi = iscsi_create_context(old_iscsi->initiator_name); @@ -234,7 +237,17 @@ try_again: if (iscsi_full_connect_sync(iscsi, iscsi->portal, iscsi->lun) != 0) { iscsi_destroy_context(iscsi); - sleep(1); + int backoff=retry; + if (backoff > 10) { + backoff+=rand()%10; + backoff-=5; + } + if (backoff > 30) { + backoff=30; + } + DPRINTF(iscsi,1,"reconnect try %d failed, waiting %d seconds",retry,backoff); + sleep(backoff); + retry++; goto try_again; } From cc2b0c9b1dc90aa6fd5d3fe63ea832fdcc0ba66a Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 18 Oct 2012 14:31:09 +0200 Subject: [PATCH 09/36] Set SO_KEEPALIVE before TCP_USER_TIMEOUT For TCP_USER_TIMEOUT to work it seems to be necessary that SO_KEEPALIVE is enabled. RFC5482 section 4.2 also says that the TCP_USER_TIMEOUT has to be less than the keepalive timeout. This means less than 30000 ms in the current libiscsi default settings for TCP keepalives. --- lib/socket.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/socket.c b/lib/socket.c index 0da1d95..9912976 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -160,13 +160,11 @@ iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, set_nonblocking(iscsi->fd); + iscsi_set_tcp_keepalive(iscsi, 30, 3, 30); + if (iscsi->tcp_user_timeout > 0) { set_tcp_user_timeout(iscsi); } - else - { - iscsi_set_tcp_keepalive(iscsi, 30, 3, 30); - } if (connect(iscsi->fd, ai->ai_addr, socksize) != 0 && errno != EINPROGRESS) { From abd20f3587a68b325ae18a03ecb63aa6301a5143 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 18 Oct 2012 20:03:58 -0700 Subject: [PATCH 10/36] Use the literal ISCSI_STATUS_REDIRECT instead of the numeric value --- lib/connect.c | 2 +- lib/login.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/connect.c b/lib/connect.c index f82df28..492f65a 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -86,7 +86,7 @@ iscsi_login_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, { struct connect_task *ct = private_data; - if (status == 0x101 && iscsi->target_address) { + if (status == SCSI_STATUS_REDIRECT && iscsi->target_address) { iscsi_disconnect(iscsi); if (iscsi_connect_async(iscsi, iscsi->target_address, iscsi_connect_cb, iscsi->connect_data) != 0) { return; diff --git a/lib/login.c b/lib/login.c index d841a4b..28b9226 100644 --- a/lib/login.c +++ b/lib/login.c @@ -1088,7 +1088,7 @@ iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, size -= len + 1; } - if (status == 0x101 && iscsi->target_address) { + if (status == SCSI_STATUS_REDIRECT && iscsi->target_address) { DPRINTF(iscsi,2,"target requests redirect to %s",iscsi->target_address); pdu->callback(iscsi, SCSI_STATUS_REDIRECT, NULL, pdu->private_data); From ad9cd56b2d36075e8ba8b6c54c8993a6eee77157 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 19 Oct 2012 23:40:12 +0200 Subject: [PATCH 11/36] Add setters for TCP keepalive values This patch adds 3 functions to set the 3 keepalive values TCP_KEEPIDLE, TCP_KEEPCNT and TCP_KEEPINTVL. The values have to be set after iscsi context creation and are then configured on the socket on each new connection. --- include/iscsi-private.h | 3 +++ include/iscsi.h | 22 ++++++++++++++++++++++ lib/connect.c | 3 +++ lib/init.c | 4 ++++ lib/libiscsi.def | 3 +++ lib/libiscsi.syms | 3 +++ lib/socket.c | 20 +++++++++++++++++++- 7 files changed, 57 insertions(+), 1 deletion(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 010eeb8..d0a73f6 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -86,6 +86,9 @@ struct iscsi_context { int is_connected; int tcp_user_timeout; + int tcp_keepcnt; + int tcp_keepintvl; + int tcp_keepidle; int current_phase; int next_phase; diff --git a/include/iscsi.h b/include/iscsi.h index 70881f6..30aa5ea 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -974,6 +974,28 @@ iscsi_set_debug(struct iscsi_context *iscsi, int level); EXTERN void iscsi_set_tcp_user_timeout(struct iscsi_context *iscsi, int timeout_ms); +/* + * This function is to set the TCP_KEEPIDLE option. It has to be called after iscsi + * context creation. + */ +EXTERN void +iscsi_set_tcp_keepidle(struct iscsi_context *iscsi, int value); + +/* + * This function is to set the TCP_KEEPCNT option. It has to be called after iscsi + * context creation. + */ +EXTERN void +iscsi_set_tcp_keepcnt(struct iscsi_context *iscsi, int value); + +/* + * This function is to set the TCP_KEEPINTVL option. It has to be called after iscsi + * context creation. + */ +EXTERN void +iscsi_set_tcp_keepintvl(struct iscsi_context *iscsi, int value); + + #ifdef __cplusplus } #endif diff --git a/lib/connect.c b/lib/connect.c index 492f65a..2f5b003 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -234,6 +234,9 @@ try_again: iscsi->debug = old_iscsi->debug; iscsi->tcp_user_timeout = old_iscsi->tcp_user_timeout; + iscsi->tcp_keepidle = old_iscsi->tcp_keepidle; + iscsi->tcp_keepcnt = old_iscsi->tcp_keepcnt; + iscsi->tcp_keepintvl = old_iscsi->tcp_keepintvl; if (iscsi_full_connect_sync(iscsi, iscsi->portal, iscsi->lun) != 0) { iscsi_destroy_context(iscsi); diff --git a/lib/init.c b/lib/init.c index 9c6bcac..61852f2 100644 --- a/lib/init.c +++ b/lib/init.c @@ -71,6 +71,10 @@ iscsi_create_context(const char *initiator_name) iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_YES; iscsi->want_header_digest = ISCSI_HEADER_DIGEST_NONE_CRC32C; + iscsi->tcp_keepcnt=3; + iscsi->tcp_keepintvl=30; + iscsi->tcp_keepidle=30; + return iscsi; } diff --git a/lib/libiscsi.def b/lib/libiscsi.def index 56ca61a..00cef83 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -73,6 +73,9 @@ iscsi_set_session_type iscsi_set_targetname iscsi_set_tcp_keepalive iscsi_set_tcp_user_timeout +iscsi_set_tcp_keepidle +iscsi_set_tcp_keepcnt +iscsi_set_tcp_keepintvl iscsi_startstopunit_sync iscsi_startstopunit_task iscsi_synchronizecache10_sync diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index dba697a..47fb0dc 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -71,6 +71,9 @@ iscsi_set_session_type iscsi_set_targetname iscsi_set_tcp_keepalive iscsi_set_tcp_user_timeout +iscsi_set_tcp_keepidle +iscsi_set_tcp_keepcnt +iscsi_set_tcp_keepintvl iscsi_startstopunit_sync iscsi_startstopunit_task iscsi_synchronizecache10_sync diff --git a/lib/socket.c b/lib/socket.c index 9912976..930a1eb 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -160,7 +160,7 @@ iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, set_nonblocking(iscsi->fd); - iscsi_set_tcp_keepalive(iscsi, 30, 3, 30); + iscsi_set_tcp_keepalive(iscsi, iscsi->tcp_keepidle, iscsi->tcp_keepcnt, iscsi->tcp_keepintvl); if (iscsi->tcp_user_timeout > 0) { set_tcp_user_timeout(iscsi); @@ -563,6 +563,24 @@ void iscsi_set_tcp_user_timeout(struct iscsi_context *iscsi, int timeout_ms) DPRINTF(iscsi,2,"TCP_USER_TIMEOUT will be set to %dms on next socket creation",timeout_ms); } +void iscsi_set_tcp_keepidle(struct iscsi_context *iscsi, int value) +{ + iscsi->tcp_keepidle=value; + DPRINTF(iscsi,2,"TCP_KEEPIDLE will be set to %d on next socket creation",value); +} + +void iscsi_set_tcp_keepcnt(struct iscsi_context *iscsi, int value) +{ + iscsi->tcp_keepcnt=value; + DPRINTF(iscsi,2,"TCP_KEEPCNT will be set to %d on next socket creation",value); +} + +void iscsi_set_tcp_keepintvl(struct iscsi_context *iscsi, int value) +{ + iscsi->tcp_keepintvl=value; + DPRINTF(iscsi,2,"TCP_KEEPINTVL will be set to %d on next socket creation",value); +} + int iscsi_set_tcp_keepalive(struct iscsi_context *iscsi, int idle, int count, int interval) { int level, value; From 236aaa011fbff65908867357647ef04e66f32a00 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Fri, 19 Oct 2012 23:48:04 +0200 Subject: [PATCH 12/36] Fix compiler warnings These patch fixes 3 compiler warnings introduce by my recent patches. --- lib/connect.c | 1 + lib/init.c | 2 +- lib/socket.c | 58 +++++++++++++++++++++++++-------------------------- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/lib/connect.c b/lib/connect.c index 2f5b003..c20e4ed 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "slist.h" #include "iscsi.h" #include "iscsi-private.h" diff --git a/lib/init.c b/lib/init.c index 61852f2..0269e43 100644 --- a/lib/init.c +++ b/lib/init.c @@ -280,7 +280,7 @@ iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...) va_end(ap); - DPRINTF(iscsi,1,str); + DPRINTF(iscsi,1,"%s",str); } void diff --git a/lib/socket.c b/lib/socket.c index 930a1eb..1327ee0 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -58,6 +58,34 @@ static void set_nonblocking(int fd) #endif } +#ifndef TCP_USER_TIMEOUT +#define TCP_USER_TIMEOUT 18 +#endif + +int set_tcp_user_timeout(struct iscsi_context *iscsi) +{ + int level, value; + + #if defined(__FreeBSD__) || defined(__sun) + struct protoent *buf; + + if ((buf = getprotobyname("tcp")) != NULL) + level = buf->p_proto; + else + return -1; + #else + level = SOL_TCP; + #endif + + value = iscsi->tcp_user_timeout; + if (setsockopt(iscsi->fd, level, TCP_USER_TIMEOUT, &value, sizeof(value)) != 0) { + iscsi_set_error(iscsi, "TCP: Failed to set tcp user timeout. Error %s(%d)", strerror(errno), errno); + return -1; + } + DPRINTF(iscsi,3,"TCP_USER_TIMEOUT set to %d",value); + return 0; +} + int iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, iscsi_command_cb cb, void *private_data) @@ -178,7 +206,7 @@ iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, freeaddrinfo(ai); - if (iscsi->connected_portal) free(iscsi->connected_portal); + if (iscsi->connected_portal) free(discard_const(iscsi->connected_portal)); iscsi->connected_portal=strdup(portal); return 0; @@ -529,34 +557,6 @@ iscsi_free_iscsi_inqueue(struct iscsi_in_pdu *inqueue) } } -#ifndef TCP_USER_TIMEOUT -#define TCP_USER_TIMEOUT 18 -#endif - -int set_tcp_user_timeout(struct iscsi_context *iscsi) -{ - int level, value; - - #if defined(__FreeBSD__) || defined(__sun) - struct protoent *buf; - - if ((buf = getprotobyname("tcp")) != NULL) - level = buf->p_proto; - else - return -1; - #else - level = SOL_TCP; - #endif - - value = iscsi->tcp_user_timeout; - if (setsockopt(iscsi->fd, level, TCP_USER_TIMEOUT, &value, sizeof(value)) != 0) { - iscsi_set_error(iscsi, "TCP: Failed to set tcp user timeout. Error %s(%d)", strerror(errno), errno); - return -1; - } - DPRINTF(iscsi,3,"TCP_USER_TIMEOUT set to %d",value); - return 0; -} - void iscsi_set_tcp_user_timeout(struct iscsi_context *iscsi, int timeout_ms) { iscsi->tcp_user_timeout=timeout_ms; From d1110b7515f417aeb640a32e15c8ebd118c89d67 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sat, 20 Oct 2012 18:11:35 +0200 Subject: [PATCH 13/36] Add various environment variables This patch allows the following parameters inside libiscsi to be adjusted without code modification: LIBISCSI_DEBUG LIBISCSI_TCP_USER_TIMEOUT LIBISCSI_TCP_KEEPIDLE LIBISCSI_TCP_KEEPCNT LIBISCSI_TCP_KEEPINTVL You can now enable debugging of libiscsi inside e.g. qemu-kvm with LIBISCSI_DEBUG=3 qemu-kvm -hda iscsi://... --- lib/init.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/init.c b/lib/init.c index 0269e43..0cb5cf9 100644 --- a/lib/init.c +++ b/lib/init.c @@ -74,6 +74,26 @@ iscsi_create_context(const char *initiator_name) iscsi->tcp_keepcnt=3; iscsi->tcp_keepintvl=30; iscsi->tcp_keepidle=30; + + if (getenv("LIBISCSI_TCP_USER_TIMEOUT") != NULL) { + iscsi_set_tcp_user_timeout(iscsi,atoi(getenv("LIBISCSI_TCP_USER_TIMEOUT"))); + } + + if (getenv("LIBISCSI_TCP_KEEPCNT") != NULL) { + iscsi_set_tcp_keepcnt(iscsi,atoi(getenv("LIBISCSI_TCP_KEEPCNT"))); + } + + if (getenv("LIBISCSI_TCP_KEEPINTVL") != NULL) { + iscsi_set_tcp_keepintvl(iscsi,atoi(getenv("LIBISCSI_TCP_KEEPINTVL"))); + } + + if (getenv("LIBISCSI_TCP_KEEPIDLE") != NULL) { + iscsi_set_tcp_keepidle(iscsi,atoi(getenv("LIBISCSI_TCP_KEEPIDLE"))); + } + + if (getenv("LIBISCSI_DEBUG") != NULL) { + iscsi_set_debug(iscsi,atoi(getenv("LIBISCSI_DEBUG"))); + } return iscsi; } From 78a31ad4a1286ac192b4f3650242e35ddeb9de23 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sat, 20 Oct 2012 18:43:48 +0200 Subject: [PATCH 14/36] Add iscsi_set_tcp_syncnt function This patch adds support for setting TCP_SYNCNT to overwrite the system default values. This allows indirect support for a configurable connect timeout. Linux uses a exponential backoff for SYN retries starting with 1 second. This means for a value n for TCP_SYNCNT, the connect will effectively timeout after 2^(n+1)-1 seconds. --- include/iscsi-private.h | 1 + lib/connect.c | 1 + lib/init.c | 10 +++++++--- lib/libiscsi.def | 1 + lib/libiscsi.syms | 1 + lib/socket.c | 42 ++++++++++++++++++++++++++++++++++++++++- 6 files changed, 52 insertions(+), 4 deletions(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index d0a73f6..f523b6e 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -89,6 +89,7 @@ struct iscsi_context { int tcp_keepcnt; int tcp_keepintvl; int tcp_keepidle; + int tcp_syncnt; int current_phase; int next_phase; diff --git a/lib/connect.c b/lib/connect.c index c20e4ed..650b0a2 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -238,6 +238,7 @@ try_again: iscsi->tcp_keepidle = old_iscsi->tcp_keepidle; iscsi->tcp_keepcnt = old_iscsi->tcp_keepcnt; iscsi->tcp_keepintvl = old_iscsi->tcp_keepintvl; + iscsi->tcp_syncnt = old_iscsi->tcp_syncnt; if (iscsi_full_connect_sync(iscsi, iscsi->portal, iscsi->lun) != 0) { iscsi_destroy_context(iscsi); diff --git a/lib/init.c b/lib/init.c index 0cb5cf9..41e20f0 100644 --- a/lib/init.c +++ b/lib/init.c @@ -74,6 +74,10 @@ iscsi_create_context(const char *initiator_name) iscsi->tcp_keepcnt=3; iscsi->tcp_keepintvl=30; iscsi->tcp_keepidle=30; + + if (getenv("LIBISCSI_DEBUG") != NULL) { + iscsi_set_debug(iscsi,atoi(getenv("LIBISCSI_DEBUG"))); + } if (getenv("LIBISCSI_TCP_USER_TIMEOUT") != NULL) { iscsi_set_tcp_user_timeout(iscsi,atoi(getenv("LIBISCSI_TCP_USER_TIMEOUT"))); @@ -90,9 +94,9 @@ iscsi_create_context(const char *initiator_name) if (getenv("LIBISCSI_TCP_KEEPIDLE") != NULL) { iscsi_set_tcp_keepidle(iscsi,atoi(getenv("LIBISCSI_TCP_KEEPIDLE"))); } - - if (getenv("LIBISCSI_DEBUG") != NULL) { - iscsi_set_debug(iscsi,atoi(getenv("LIBISCSI_DEBUG"))); + + if (getenv("LIBISCSI_TCP_SYNCNT") != NULL) { + iscsi_set_tcp_syncnt(iscsi,atoi(getenv("LIBISCSI_TCP_SYNCNT"))); } return iscsi; diff --git a/lib/libiscsi.def b/lib/libiscsi.def index 00cef83..6c197f8 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -76,6 +76,7 @@ iscsi_set_tcp_user_timeout iscsi_set_tcp_keepidle iscsi_set_tcp_keepcnt iscsi_set_tcp_keepintvl +iscsi_set_tcp_syncnt iscsi_startstopunit_sync iscsi_startstopunit_task iscsi_synchronizecache10_sync diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index 47fb0dc..5a86af8 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -74,6 +74,7 @@ iscsi_set_tcp_user_timeout iscsi_set_tcp_keepidle iscsi_set_tcp_keepcnt iscsi_set_tcp_keepintvl +iscsi_set_tcp_syncnt iscsi_startstopunit_sync iscsi_startstopunit_task iscsi_synchronizecache10_sync diff --git a/lib/socket.c b/lib/socket.c index 1327ee0..f481f60 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -86,6 +86,34 @@ int set_tcp_user_timeout(struct iscsi_context *iscsi) return 0; } +#ifndef TCP_SYNCNT +#define TCP_SYNCNT 7 +#endif + +int set_tcp_syncnt(struct iscsi_context *iscsi) +{ + int level, value; + + #if defined(__FreeBSD__) || defined(__sun) + struct protoent *buf; + + if ((buf = getprotobyname("tcp")) != NULL) + level = buf->p_proto; + else + return -1; + #else + level = SOL_TCP; + #endif + + value = iscsi->tcp_syncnt; + if (setsockopt(iscsi->fd, level, TCP_SYNCNT, &value, sizeof(value)) != 0) { + iscsi_set_error(iscsi, "TCP: Failed to set tcp syn retries. Error %s(%d)", strerror(errno), errno); + return -1; + } + DPRINTF(iscsi,3,"TCP_SYNCNT set to %d",value); + return 0; +} + int iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, iscsi_command_cb cb, void *private_data) @@ -194,6 +222,10 @@ iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, set_tcp_user_timeout(iscsi); } + if (iscsi->tcp_syncnt > 0) { + set_tcp_syncnt(iscsi); + } + if (connect(iscsi->fd, ai->ai_addr, socksize) != 0 && errno != EINPROGRESS) { iscsi_set_error(iscsi, "Connect failed with errno : " @@ -461,7 +493,7 @@ iscsi_service(struct iscsi_context *iscsi, int revents) if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) { int err = 0; socklen_t err_size = sizeof(err); - + DPRINTF(iscsi,3,"inside iscsi service is_connected=0"); if (getsockopt(iscsi->fd, SOL_SOCKET, SO_ERROR, &err, &err_size) != 0 || err != 0) { if (err == 0) { @@ -480,6 +512,8 @@ iscsi_service(struct iscsi_context *iscsi, int revents) return -1; } + DPRINTF(iscsi,2,"connection to %s established",iscsi->connected_portal); + iscsi->is_connected = 1; iscsi->socket_status_cb(iscsi, SCSI_STATUS_GOOD, NULL, iscsi->connect_data); @@ -557,6 +591,12 @@ iscsi_free_iscsi_inqueue(struct iscsi_in_pdu *inqueue) } } +void iscsi_set_tcp_syncnt(struct iscsi_context *iscsi, int value) +{ + iscsi->tcp_syncnt=value; + DPRINTF(iscsi,2,"TCP_SYNCNT will be set to %d on next socket creation",value); +} + void iscsi_set_tcp_user_timeout(struct iscsi_context *iscsi, int timeout_ms) { iscsi->tcp_user_timeout=timeout_ms; From cbae6ae0581d486417c3d8caafc620b352496cbd Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sat, 20 Oct 2012 18:44:43 +0200 Subject: [PATCH 15/36] Remove accidently left debug message in iscsi_service --- lib/socket.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/socket.c b/lib/socket.c index f481f60..5033269 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -493,7 +493,6 @@ iscsi_service(struct iscsi_context *iscsi, int revents) if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) { int err = 0; socklen_t err_size = sizeof(err); - DPRINTF(iscsi,3,"inside iscsi service is_connected=0"); if (getsockopt(iscsi->fd, SOL_SOCKET, SO_ERROR, &err, &err_size) != 0 || err != 0) { if (err == 0) { From 881fc9743aae33bdad87b3dd18bf90904aa623ce Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sat, 20 Oct 2012 18:54:46 +0200 Subject: [PATCH 16/36] Use generic tcp_set_sockopt function for tcp setsockopt operations --- lib/socket.c | 73 +++++++++++++++++----------------------------------- 1 file changed, 24 insertions(+), 49 deletions(-) diff --git a/lib/socket.c b/lib/socket.c index 5033269..dbccbfb 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -58,13 +58,9 @@ static void set_nonblocking(int fd) #endif } -#ifndef TCP_USER_TIMEOUT -#define TCP_USER_TIMEOUT 18 -#endif - -int set_tcp_user_timeout(struct iscsi_context *iscsi) +int set_tcp_sockopt(int sockfd, int optname, int value) { - int level, value; + int level; #if defined(__FreeBSD__) || defined(__sun) struct protoent *buf; @@ -75,14 +71,22 @@ int set_tcp_user_timeout(struct iscsi_context *iscsi) return -1; #else level = SOL_TCP; - #endif + #endif - value = iscsi->tcp_user_timeout; - if (setsockopt(iscsi->fd, level, TCP_USER_TIMEOUT, &value, sizeof(value)) != 0) { + return setsockopt(sockfd, level, optname, &value, sizeof(value)); +} + +#ifndef TCP_USER_TIMEOUT +#define TCP_USER_TIMEOUT 18 +#endif + +int set_tcp_user_timeout(struct iscsi_context *iscsi) +{ + if (set_tcp_sockopt(iscsi->fd, TCP_USER_TIMEOUT, iscsi->tcp_user_timeout) != 0) { iscsi_set_error(iscsi, "TCP: Failed to set tcp user timeout. Error %s(%d)", strerror(errno), errno); return -1; } - DPRINTF(iscsi,3,"TCP_USER_TIMEOUT set to %d",value); + DPRINTF(iscsi,3,"TCP_USER_TIMEOUT set to %d",iscsi->tcp_user_timeout); return 0; } @@ -92,25 +96,11 @@ int set_tcp_user_timeout(struct iscsi_context *iscsi) int set_tcp_syncnt(struct iscsi_context *iscsi) { - int level, value; - - #if defined(__FreeBSD__) || defined(__sun) - struct protoent *buf; - - if ((buf = getprotobyname("tcp")) != NULL) - level = buf->p_proto; - else - return -1; - #else - level = SOL_TCP; - #endif - - value = iscsi->tcp_syncnt; - if (setsockopt(iscsi->fd, level, TCP_SYNCNT, &value, sizeof(value)) != 0) { + if (set_tcp_sockopt(iscsi->fd, TCP_SYNCNT, iscsi->tcp_syncnt) != 0) { iscsi_set_error(iscsi, "TCP: Failed to set tcp syn retries. Error %s(%d)", strerror(errno), errno); return -1; } - DPRINTF(iscsi,3,"TCP_SYNCNT set to %d",value); + DPRINTF(iscsi,3,"TCP_SYNCNT set to %d",iscsi->tcp_syncnt); return 0; } @@ -622,49 +612,34 @@ void iscsi_set_tcp_keepintvl(struct iscsi_context *iscsi, int value) int iscsi_set_tcp_keepalive(struct iscsi_context *iscsi, int idle, int count, int interval) { - int level, value; -#if defined(__FreeBSD__) || defined(__sun) - struct protoent *buf; - - if ((buf = getprotobyname("tcp")) != NULL) - level = buf->p_proto; - else - return -1; -#else - level = SOL_TCP; -#endif - #ifdef SO_KEEPALIVE - value =1; + int value = 1; if (setsockopt(iscsi->fd, SOL_SOCKET, SO_KEEPALIVE, &value, sizeof(value)) != 0) { iscsi_set_error(iscsi, "TCP: Failed to set socket option SO_KEEPALIVE. Error %s(%d)", strerror(errno), errno); return -1; } DPRINTF(iscsi,3,"SO_KEEPALIVE set to %d",value); -#endif #ifdef TCP_KEEPCNT - value = count; - if (setsockopt(iscsi->fd, level, TCP_KEEPCNT, &value, sizeof(value)) != 0) { + if (set_tcp_sockopt(iscsi->fd, TCP_KEEPCNT, count) != 0) { iscsi_set_error(iscsi, "TCP: Failed to set tcp keepalive count. Error %s(%d)", strerror(errno), errno); return -1; } - DPRINTF(iscsi,3,"TCP_KEEPCNT set to %d",value); + DPRINTF(iscsi,3,"TCP_KEEPCNT set to %d",count); #endif #ifdef TCP_KEEPINTVL - value = interval; - if (setsockopt(iscsi->fd, level, TCP_KEEPINTVL, &value, sizeof(value)) != 0) { + if (set_tcp_sockopt(iscsi->fd, TCP_KEEPINTVL, interval) != 0) { iscsi_set_error(iscsi, "TCP: Failed to set tcp keepalive interval. Error %s(%d)", strerror(errno), errno); return -1; } - DPRINTF(iscsi,3,"TCP_KEEPINTVL set to %d",value); + DPRINTF(iscsi,3,"TCP_KEEPINTVL set to %d",interval); #endif #ifdef TCP_KEEPIDLE - value = idle; - if (setsockopt(iscsi->fd, level, TCP_KEEPIDLE, &value, sizeof(value)) != 0) { + if (set_tcp_sockopt(iscsi->fd, TCP_KEEPIDLE, idle) != 0) { iscsi_set_error(iscsi, "TCP: Failed to set tcp keepalive idle. Error %s(%d)", strerror(errno), errno); return -1; } - DPRINTF(iscsi,3,"TCP_KEEPIDLE set to %d",value); + DPRINTF(iscsi,3,"TCP_KEEPIDLE set to %d",idle); +#endif #endif return 0; From 3f5a75ce5ecbff369b2283f20eb941fab6864d63 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sat, 20 Oct 2012 19:00:32 +0200 Subject: [PATCH 17/36] Remove redundant code in iscsi_service() --- lib/socket.c | 46 ++++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/lib/socket.c b/lib/socket.c index dbccbfb..83df934 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -439,6 +439,17 @@ iscsi_write_to_socket(struct iscsi_context *iscsi) return 0; } +int inline +iscsi_service_reconnect_if_loggedin(struct iscsi_context *iscsi) +{ + if (iscsi->is_loggedin) { + if (iscsi_reconnect(iscsi) == 0) { + return 0; + } + } + return -1; +} + int iscsi_service(struct iscsi_context *iscsi, int revents) { @@ -460,24 +471,14 @@ iscsi_service(struct iscsi_context *iscsi, int revents) } iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, iscsi->connect_data); - if (iscsi->is_loggedin) { - if (iscsi_reconnect(iscsi) == 0) { - return 0; - } - } - return -1; + return iscsi_service_reconnect_if_loggedin(iscsi); } if (revents & POLLHUP) { iscsi_set_error(iscsi, "iscsi_service: POLLHUP, " "socket error."); iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, iscsi->connect_data); - if (iscsi->is_loggedin) { - if (iscsi_reconnect(iscsi) == 0) { - return 0; - } - } - return -1; + return iscsi_service_reconnect_if_loggedin(iscsi); } if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) { @@ -493,12 +494,7 @@ iscsi_service(struct iscsi_context *iscsi, int revents) strerror(err), err); iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, iscsi->connect_data); - if (iscsi->is_loggedin) { - if (iscsi_reconnect(iscsi) == 0) { - return 0; - } - } - return -1; + return iscsi_service_reconnect_if_loggedin(iscsi); } DPRINTF(iscsi,2,"connection to %s established",iscsi->connected_portal); @@ -511,22 +507,12 @@ iscsi_service(struct iscsi_context *iscsi, int revents) if (revents & POLLOUT && iscsi->outqueue != NULL) { if (iscsi_write_to_socket(iscsi) != 0) { - if (iscsi->is_loggedin) { - if (iscsi_reconnect(iscsi) == 0) { - return 0; - } - } - return -1; + return iscsi_service_reconnect_if_loggedin(iscsi); } } if (revents & POLLIN) { if (iscsi_read_from_socket(iscsi) != 0) { - if (iscsi->is_loggedin) { - if (iscsi_reconnect(iscsi) == 0) { - return 0; - } - } - return -1; + return iscsi_service_reconnect_if_loggedin(iscsi); } } From 20cf2b279e0def7662c6fed38bb1c63262698804 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sat, 20 Oct 2012 19:08:57 +0200 Subject: [PATCH 18/36] Fix incorrect whitespaces At a few places there where spaces where tabulators where appropriate --- lib/connect.c | 10 +++++----- lib/init.c | 5 ++--- lib/login.c | 4 ++-- lib/scsi-lowlevel.c | 12 ++++++------ lib/socket.c | 26 +++++++++++++------------- lib/sync.c | 6 +++--- 6 files changed, 31 insertions(+), 32 deletions(-) diff --git a/lib/connect.c b/lib/connect.c index 650b0a2..8ad2afe 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -87,13 +87,13 @@ iscsi_login_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, { struct connect_task *ct = private_data; - if (status == SCSI_STATUS_REDIRECT && iscsi->target_address) { + if (status == SCSI_STATUS_REDIRECT && iscsi->target_address) { iscsi_disconnect(iscsi); if (iscsi_connect_async(iscsi, iscsi->target_address, iscsi_connect_cb, iscsi->connect_data) != 0) { return; } return; - } + } if (status != 0) { ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data); @@ -171,7 +171,7 @@ int iscsi_reconnect(struct iscsi_context *old_iscsi) { struct iscsi_context *iscsi = old_iscsi; - DPRINTF(iscsi,2,"reconnect initiated"); + DPRINTF(iscsi,2,"reconnect initiated"); /* This is mainly for tests, where we do not want to automatically reconnect but rather want the commands to fail with an error @@ -210,8 +210,8 @@ int iscsi_reconnect(struct iscsi_context *old_iscsi) return 0; } - int retry = 0; - srand (time(NULL)^getpid()); + int retry = 0; + srand (time(NULL)^getpid()); try_again: diff --git a/lib/init.c b/lib/init.c index 41e20f0..5bd6d0c 100644 --- a/lib/init.c +++ b/lib/init.c @@ -272,7 +272,7 @@ iscsi_destroy_context(struct iscsi_context *iscsi) free(discard_const(iscsi->chap_c)); iscsi->chap_c = NULL; - if (iscsi->connected_portal != NULL) { + if (iscsi->connected_portal != NULL) { free(discard_const(iscsi->connected_portal)); iscsi->connected_portal = NULL; } @@ -587,8 +587,7 @@ iscsi_destroy_url(struct iscsi_url *iscsi_url) int iscsi_set_initiator_username_pwd(struct iscsi_context *iscsi, - const char *user, - const char *passwd) + const char *user, const char *passwd) { free(discard_const(iscsi->user)); iscsi->user = strdup(user); diff --git a/lib/login.c b/lib/login.c index 28b9226..8e57839 100644 --- a/lib/login.c +++ b/lib/login.c @@ -1088,12 +1088,12 @@ iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, size -= len + 1; } - if (status == SCSI_STATUS_REDIRECT && iscsi->target_address) { + if (status == SCSI_STATUS_REDIRECT && iscsi->target_address) { DPRINTF(iscsi,2,"target requests redirect to %s",iscsi->target_address); pdu->callback(iscsi, SCSI_STATUS_REDIRECT, NULL, pdu->private_data); return 0; - } + } if (status != 0) { iscsi_set_error(iscsi, "Failed to log in to target. Status: %s(%d)", diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index 199b4d5..c784af2 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -76,8 +76,8 @@ scsi_malloc(struct scsi_task *task, size_t size) } struct value_string { - int value; - const char *string; + int value; + const char *string; }; static const char * @@ -1285,7 +1285,7 @@ scsi_cdb_compareandwrite(uint64_t lba, uint32_t xferlen, int blocksize, int wrpr task->xfer_dir = SCSI_XFER_WRITE; } else { task->xfer_dir = SCSI_XFER_NONE; - } + } task->expxferlen = xferlen; task->params.compareandwrite.lba = lba; @@ -2390,9 +2390,9 @@ scsi_get_task_private_ptr(struct scsi_task *task) struct scsi_data_buffer { - struct scsi_data_buffer *next; - uint32_t len; - unsigned char *data; + struct scsi_data_buffer *next; + uint32_t len; + unsigned char *data; }; int diff --git a/lib/socket.c b/lib/socket.c index 83df934..bb70083 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -114,7 +114,7 @@ iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, struct addrinfo *ai = NULL; int socksize; - DPRINTF(iscsi,2,"connecting to portal %s",portal); + DPRINTF(iscsi,2,"connecting to portal %s",portal); if (iscsi->fd != -1) { iscsi_set_error(iscsi, @@ -212,7 +212,7 @@ iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, set_tcp_user_timeout(iscsi); } - if (iscsi->tcp_syncnt > 0) { + if (iscsi->tcp_syncnt > 0) { set_tcp_syncnt(iscsi); } @@ -497,7 +497,7 @@ iscsi_service(struct iscsi_context *iscsi, int revents) return iscsi_service_reconnect_if_loggedin(iscsi); } - DPRINTF(iscsi,2,"connection to %s established",iscsi->connected_portal); + DPRINTF(iscsi,2,"connection to %s established",iscsi->connected_portal); iscsi->is_connected = 1; iscsi->socket_status_cb(iscsi, SCSI_STATUS_GOOD, NULL, @@ -568,32 +568,32 @@ iscsi_free_iscsi_inqueue(struct iscsi_in_pdu *inqueue) void iscsi_set_tcp_syncnt(struct iscsi_context *iscsi, int value) { - iscsi->tcp_syncnt=value; - DPRINTF(iscsi,2,"TCP_SYNCNT will be set to %d on next socket creation",value); + iscsi->tcp_syncnt=value; + DPRINTF(iscsi,2,"TCP_SYNCNT will be set to %d on next socket creation",value); } void iscsi_set_tcp_user_timeout(struct iscsi_context *iscsi, int timeout_ms) { - iscsi->tcp_user_timeout=timeout_ms; - DPRINTF(iscsi,2,"TCP_USER_TIMEOUT will be set to %dms on next socket creation",timeout_ms); + iscsi->tcp_user_timeout=timeout_ms; + DPRINTF(iscsi,2,"TCP_USER_TIMEOUT will be set to %dms on next socket creation",timeout_ms); } void iscsi_set_tcp_keepidle(struct iscsi_context *iscsi, int value) { - iscsi->tcp_keepidle=value; - DPRINTF(iscsi,2,"TCP_KEEPIDLE will be set to %d on next socket creation",value); + iscsi->tcp_keepidle=value; + DPRINTF(iscsi,2,"TCP_KEEPIDLE will be set to %d on next socket creation",value); } void iscsi_set_tcp_keepcnt(struct iscsi_context *iscsi, int value) { - iscsi->tcp_keepcnt=value; - DPRINTF(iscsi,2,"TCP_KEEPCNT will be set to %d on next socket creation",value); + iscsi->tcp_keepcnt=value; + DPRINTF(iscsi,2,"TCP_KEEPCNT will be set to %d on next socket creation",value); } void iscsi_set_tcp_keepintvl(struct iscsi_context *iscsi, int value) { - iscsi->tcp_keepintvl=value; - DPRINTF(iscsi,2,"TCP_KEEPINTVL will be set to %d on next socket creation",value); + iscsi->tcp_keepintvl=value; + DPRINTF(iscsi,2,"TCP_KEEPINTVL will be set to %d on next socket creation",value); } int iscsi_set_tcp_keepalive(struct iscsi_context *iscsi, int idle, int count, int interval) diff --git a/lib/sync.c b/lib/sync.c index f8903f5..e0b8a8d 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -30,9 +30,9 @@ #include "scsi-lowlevel.h" struct iscsi_sync_state { - int finished; - int status; - struct scsi_task *task; + int finished; + int status; + struct scsi_task *task; }; static void From d30b279474cb273a80e971e300019d6190616bc4 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sat, 20 Oct 2012 19:12:13 +0200 Subject: [PATCH 19/36] Unify paramters in tcp_set_user_timeout() Use the same function definition as in the other tcp setters. --- lib/socket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/socket.c b/lib/socket.c index bb70083..78d8b94 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -572,9 +572,9 @@ void iscsi_set_tcp_syncnt(struct iscsi_context *iscsi, int value) DPRINTF(iscsi,2,"TCP_SYNCNT will be set to %d on next socket creation",value); } -void iscsi_set_tcp_user_timeout(struct iscsi_context *iscsi, int timeout_ms) +void iscsi_set_tcp_user_timeout(struct iscsi_context *iscsi, int value) { - iscsi->tcp_user_timeout=timeout_ms; + iscsi->tcp_user_timeout=value; DPRINTF(iscsi,2,"TCP_USER_TIMEOUT will be set to %dms on next socket creation",timeout_ms); } From a26a6e12d51f2b54eb6a9d4cc8f67b47cffc497f Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sat, 20 Oct 2012 19:13:53 +0200 Subject: [PATCH 20/36] Fix paramter to DPRINTF in tcp_set_user_timeout() --- lib/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/socket.c b/lib/socket.c index 78d8b94..5e50954 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -575,7 +575,7 @@ void iscsi_set_tcp_syncnt(struct iscsi_context *iscsi, int value) void iscsi_set_tcp_user_timeout(struct iscsi_context *iscsi, int value) { iscsi->tcp_user_timeout=value; - DPRINTF(iscsi,2,"TCP_USER_TIMEOUT will be set to %dms on next socket creation",timeout_ms); + DPRINTF(iscsi,2,"TCP_USER_TIMEOUT will be set to %dms on next socket creation",value); } void iscsi_set_tcp_keepidle(struct iscsi_context *iscsi, int value) From 86fa62558e780f76e0221d2937088d22a3f834dd Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sun, 21 Oct 2012 08:13:16 +0200 Subject: [PATCH 21/36] LD_ISCSI: add debugging support --- src/ld_iscsi.c | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/ld_iscsi.c b/src/ld_iscsi.c index e0e7ad8..b9397a8 100644 --- a/src/ld_iscsi.c +++ b/src/ld_iscsi.c @@ -37,6 +37,10 @@ static const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:ld-iscs #define ISCSI_MAX_FD 255 +static int debug = 0; + +#define LD_ISCSI_DPRINTF(level,fmt,args...) do { if ((debug) >= level) {fprintf(stderr,"ld_iscsi: ");fprintf(stderr, (fmt), ##args); fprintf(stderr,"\n");} } while (0); + struct iscsi_fd_list { int is_iscsi; int dup2fd; @@ -64,15 +68,14 @@ int open(const char *path, int flags, mode_t mode) iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { - fprintf(stderr, "ld-iscsi: Failed to create context\n"); + LD_ISCSI_DPRINTF(0,"Failed to create context"); errno = ENOMEM; return -1; } iscsi_url = iscsi_parse_full_url(iscsi, path); if (iscsi_url == NULL) { - fprintf(stderr, "ld-iscsi: Failed to parse URL: %s\n", - iscsi_get_error(iscsi)); + LD_ISCSI_DPRINTF(0,"Failed to parse URL: %s\n", iscsi_get_error(iscsi)); iscsi_destroy_context(iscsi); errno = EINVAL; return -1; @@ -84,7 +87,7 @@ int open(const char *path, int flags, mode_t mode) if (iscsi_url->user != NULL) { if (iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, iscsi_url->passwd) != 0) { - fprintf(stderr, "Failed to set initiator username and password\n"); + LD_ISCSI_DPRINTF(0,"Failed to set initiator username and password"); iscsi_destroy_context(iscsi); errno = ENOMEM; return -1; @@ -92,7 +95,7 @@ int open(const char *path, int flags, mode_t mode) } if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { - fprintf(stderr, "ld-iscsi: Login Failed. %s\n", iscsi_get_error(iscsi)); + LD_ISCSI_DPRINTF(0,"Login Failed. %s\n", iscsi_get_error(iscsi)); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); errno = EIO; @@ -101,7 +104,7 @@ int open(const char *path, int flags, mode_t mode) task = iscsi_readcapacity10_sync(iscsi, iscsi_url->lun, 0, 0); if (task == NULL || task->status != SCSI_STATUS_GOOD) { - fprintf(stderr, "ld-iscsi: failed to send readcapacity command\n"); + LD_ISCSI_DPRINTF(0,"failed to send readcapacity command"); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); errno = EIO; @@ -110,7 +113,7 @@ int open(const char *path, int flags, mode_t mode) rc10 = scsi_datain_unmarshall(task); if (rc10 == NULL) { - fprintf(stderr, "ld-iscsi: failed to unmarshall readcapacity10 data\n"); + LD_ISCSI_DPRINTF(0,"failed to unmarshall readcapacity10 data"); scsi_free_scsi_task(task); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); @@ -120,7 +123,7 @@ int open(const char *path, int flags, mode_t mode) fd = iscsi_get_fd(iscsi); if (fd >= ISCSI_MAX_FD) { - fprintf(stderr, "ld-iscsi: Too many files open\n"); + LD_ISCSI_DPRINTF(0,"Too many files open"); iscsi_destroy_url(iscsi_url); iscsi_destroy_context(iscsi); errno = ENFILE; @@ -277,11 +280,13 @@ ssize_t read(int fd, void *buf, size_t count) count = num_blocks * iscsi_fd_list[fd].block_size; } + LD_ISCSI_DPRINTF(4,"read10_sync: lun %d, lba %u, num_blocks: %u, block_size: %d, offset: %lu count: %lu",iscsi_fd_list[fd].lun,lba,num_blocks,iscsi_fd_list[fd].block_size,offset,count); + iscsi_fd_list[fd].in_flight = 1; task = iscsi_read10_sync(iscsi_fd_list[fd].iscsi, iscsi_fd_list[fd].lun, lba, num_blocks * iscsi_fd_list[fd].block_size, iscsi_fd_list[fd].block_size, 0, 0, 0, 0, 0); iscsi_fd_list[fd].in_flight = 0; if (task == NULL || task->status != SCSI_STATUS_GOOD) { - fprintf(stderr, "ld-iscsi: failed to send read10 command\n"); + LD_ISCSI_DPRINTF(0,"failed to send read10 command"); errno = EIO; return -1; } @@ -381,59 +386,63 @@ static void __attribute__((constructor)) _init(void) iscsi_fd_list[i].dup2fd = -1; } + if (getenv("LD_ISCSI_DEBUG") != NULL) { + debug = atoi(getenv("LD_ISCSI_DEBUG")); + } + real_open = dlsym(RTLD_NEXT, "open"); if (real_open == NULL) { - fprintf(stderr, "ld_iscsi: Failed to dlsym(open)\n"); + LD_ISCSI_DPRINTF(0,"Failed to dlsym(open)"); exit(10); } real_close = dlsym(RTLD_NEXT, "close"); if (real_close == NULL) { - fprintf(stderr, "ld_iscsi: Failed to dlsym(close)\n"); + LD_ISCSI_DPRINTF(0,"Failed to dlsym(close)"); exit(10); } real_fxstat = dlsym(RTLD_NEXT, "__fxstat"); if (real_fxstat == NULL) { - fprintf(stderr, "ld_iscsi: Failed to dlsym(__fxstat)\n"); + LD_ISCSI_DPRINTF(0,"Failed to dlsym(__fxstat)"); exit(10); } real_lxstat = dlsym(RTLD_NEXT, "__lxstat"); if (real_lxstat == NULL) { - fprintf(stderr, "ld_iscsi: Failed to dlsym(__lxstat)\n"); + LD_ISCSI_DPRINTF(0,"Failed to dlsym(__lxstat)"); exit(10); } real_xstat = dlsym(RTLD_NEXT, "__xstat"); if (real_xstat == NULL) { - fprintf(stderr, "ld_iscsi: Failed to dlsym(__xstat)\n"); + LD_ISCSI_DPRINTF(0,"Failed to dlsym(__xstat)"); exit(10); } real_read = dlsym(RTLD_NEXT, "read"); if (real_read == NULL) { - fprintf(stderr, "ld_iscsi: Failed to dlsym(read)\n"); + LD_ISCSI_DPRINTF(0,"Failed to dlsym(read)"); exit(10); } real_dup2 = dlsym(RTLD_NEXT, "dup2"); if (real_dup2 == NULL) { - fprintf(stderr, "ld_iscsi: Failed to dlsym(dup2)\n"); + LD_ISCSI_DPRINTF(0,"Failed to dlsym(dup2)"); exit(10); } real_fxstat64 = dlsym(RTLD_NEXT, "__fxstat64"); if (real_fxstat64 == NULL) { - fprintf(stderr, "ld_iscsi: Failed to dlsym(__fxstat64)\n"); + LD_ISCSI_DPRINTF(0,"Failed to dlsym(__fxstat64)"); } real_lxstat64 = dlsym(RTLD_NEXT, "__lxstat64"); if (real_lxstat64 == NULL) { - fprintf(stderr, "ld_iscsi: Failed to dlsym(_lxstat64)\n"); + LD_ISCSI_DPRINTF(0,"Failed to dlsym(_lxstat64)"); } real_xstat64 = dlsym(RTLD_NEXT, "__xstat64"); if (real_xstat64 == NULL) { - fprintf(stderr, "ld_iscsi: Failed to dlsym(__xstat64)\n"); + LD_ISCSI_DPRINTF(0,"Failed to dlsym(__xstat64)"); } } From 54d8aa0ab6758de94e17432ec89ecd8fc0cc101d Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sun, 21 Oct 2012 08:17:40 +0200 Subject: [PATCH 22/36] LD_ISCSI: use read16 instead of read10 Use READ16 to support for large volumes. READ16 takes a 64bit LBA offset instead of 32bit. --- src/ld_iscsi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ld_iscsi.c b/src/ld_iscsi.c index b9397a8..118a5d0 100644 --- a/src/ld_iscsi.c +++ b/src/ld_iscsi.c @@ -260,7 +260,7 @@ ssize_t read(int fd, void *buf, size_t count) { if ((iscsi_fd_list[fd].is_iscsi == 1) && (iscsi_fd_list[fd].in_flight == 0)) { uint64_t offset; - uint32_t num_blocks, lba; + uint64_t num_blocks, lba; struct scsi_task *task; if (iscsi_fd_list[fd].dup2fd >= 0) { @@ -280,10 +280,10 @@ ssize_t read(int fd, void *buf, size_t count) count = num_blocks * iscsi_fd_list[fd].block_size; } - LD_ISCSI_DPRINTF(4,"read10_sync: lun %d, lba %u, num_blocks: %u, block_size: %d, offset: %lu count: %lu",iscsi_fd_list[fd].lun,lba,num_blocks,iscsi_fd_list[fd].block_size,offset,count); + LD_ISCSI_DPRINTF(4,"read16_sync: lun %d, lba %lu, num_blocks: %lu, block_size: %d, offset: %lu count: %lu",iscsi_fd_list[fd].lun,lba,num_blocks,iscsi_fd_list[fd].block_size,offset,count); iscsi_fd_list[fd].in_flight = 1; - task = iscsi_read10_sync(iscsi_fd_list[fd].iscsi, iscsi_fd_list[fd].lun, lba, num_blocks * iscsi_fd_list[fd].block_size, iscsi_fd_list[fd].block_size, 0, 0, 0, 0, 0); + task = iscsi_read16_sync(iscsi_fd_list[fd].iscsi, iscsi_fd_list[fd].lun, lba, num_blocks * iscsi_fd_list[fd].block_size, iscsi_fd_list[fd].block_size, 0, 0, 0, 0, 0); iscsi_fd_list[fd].in_flight = 0; if (task == NULL || task->status != SCSI_STATUS_GOOD) { LD_ISCSI_DPRINTF(0,"failed to send read10 command"); From bca635c75da6792c9952a70ccbdc1c7d37b19391 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sun, 21 Oct 2012 08:22:17 +0200 Subject: [PATCH 23/36] LD_ISCSI: storage file mode for further usage --- src/ld_iscsi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ld_iscsi.c b/src/ld_iscsi.c index 118a5d0..cba17c6 100644 --- a/src/ld_iscsi.c +++ b/src/ld_iscsi.c @@ -50,6 +50,7 @@ struct iscsi_fd_list { uint32_t block_size; uint64_t num_blocks; off_t offset; + mode_t mode; }; static struct iscsi_fd_list iscsi_fd_list[ISCSI_MAX_FD]; @@ -137,6 +138,7 @@ int open(const char *path, int flags, mode_t mode) iscsi_fd_list[fd].num_blocks = rc10->lba + 1; iscsi_fd_list[fd].offset = 0; iscsi_fd_list[fd].lun = iscsi_url->lun; + iscsi_fd_list[fd].mode = mode; scsi_free_scsi_task(task); iscsi_destroy_url(iscsi_url); From 8a3e312f06c4dcb6f97dfa1bf654d401705f89bc Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sun, 21 Oct 2012 08:24:24 +0200 Subject: [PATCH 24/36] LD_ISCSI: Fail open with O_NONBLOCK Non-Blocking I/O is supported nowhere in the code. We should fail until we have support for it. --- src/ld_iscsi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ld_iscsi.c b/src/ld_iscsi.c index cba17c6..0caf785 100644 --- a/src/ld_iscsi.c +++ b/src/ld_iscsi.c @@ -67,6 +67,12 @@ int open(const char *path, int flags, mode_t mode) struct scsi_task *task; struct scsi_readcapacity10 *rc10; + if (mode & O_NONBLOCK) { + LD_ISCSI_DPRINTF(0,"Non-blocking I/O is currently not supported"); + errno = EINVAL; + return -1; + } + iscsi = iscsi_create_context(initiator); if (iscsi == NULL) { LD_ISCSI_DPRINTF(0,"Failed to create context"); From 08bfabcd366d8745ad0cc0a32c1a3e541645bd28 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sun, 21 Oct 2012 08:35:55 +0200 Subject: [PATCH 25/36] LD_ISCSI: Use readcapacity16 instead of readcapacity10 This allows for handling volumes larger than 2TB --- src/ld_iscsi.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ld_iscsi.c b/src/ld_iscsi.c index 0caf785..8f87ea5 100644 --- a/src/ld_iscsi.c +++ b/src/ld_iscsi.c @@ -65,7 +65,7 @@ int open(const char *path, int flags, mode_t mode) struct iscsi_context *iscsi; struct iscsi_url *iscsi_url; struct scsi_task *task; - struct scsi_readcapacity10 *rc10; + struct scsi_readcapacity16 *rc16; if (mode & O_NONBLOCK) { LD_ISCSI_DPRINTF(0,"Non-blocking I/O is currently not supported"); @@ -109,7 +109,7 @@ int open(const char *path, int flags, mode_t mode) return -1; } - task = iscsi_readcapacity10_sync(iscsi, iscsi_url->lun, 0, 0); + task = iscsi_readcapacity16_sync(iscsi, iscsi_url->lun); if (task == NULL || task->status != SCSI_STATUS_GOOD) { LD_ISCSI_DPRINTF(0,"failed to send readcapacity command"); iscsi_destroy_url(iscsi_url); @@ -118,8 +118,8 @@ int open(const char *path, int flags, mode_t mode) return -1; } - rc10 = scsi_datain_unmarshall(task); - if (rc10 == NULL) { + rc16 = scsi_datain_unmarshall(task); + if (rc16 == NULL) { LD_ISCSI_DPRINTF(0,"failed to unmarshall readcapacity10 data"); scsi_free_scsi_task(task); iscsi_destroy_url(iscsi_url); @@ -127,6 +127,8 @@ int open(const char *path, int flags, mode_t mode) errno = EIO; return -1; } + + LD_ISCSI_DPRINTF(4,"readcapacity16_sync: block_size: %d, num_blocks: %lu",rc16->block_length,rc16->returned_lba + 1); fd = iscsi_get_fd(iscsi); if (fd >= ISCSI_MAX_FD) { @@ -140,8 +142,8 @@ int open(const char *path, int flags, mode_t mode) iscsi_fd_list[fd].is_iscsi = 1; iscsi_fd_list[fd].dup2fd = -1; iscsi_fd_list[fd].iscsi = iscsi; - iscsi_fd_list[fd].block_size = rc10->block_size; - iscsi_fd_list[fd].num_blocks = rc10->lba + 1; + iscsi_fd_list[fd].block_size = rc16->block_length; + iscsi_fd_list[fd].num_blocks = rc16->returned_lba + 1; iscsi_fd_list[fd].offset = 0; iscsi_fd_list[fd].lun = iscsi_url->lun; iscsi_fd_list[fd].mode = mode; From bfac1f85a33d36362e10701fe52ac80d4b7d48cc Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sun, 21 Oct 2012 09:24:19 +0200 Subject: [PATCH 26/36] LD_SCSI: Introduce get_lba_status in read() For large continous reads I may be benifical to check if the blocks that are going to be read are allocated. If they are not allocated they do not need to be read which massivly speeds up the read. This behaviour is optional and can be turned on with environment variable LD_ISCSI_GET_LBA_STATUS=1 --- src/ld_iscsi.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/src/ld_iscsi.c b/src/ld_iscsi.c index 8f87ea5..d3a377b 100644 --- a/src/ld_iscsi.c +++ b/src/ld_iscsi.c @@ -51,6 +51,7 @@ struct iscsi_fd_list { uint64_t num_blocks; off_t offset; mode_t mode; + int get_lba_status; }; static struct iscsi_fd_list iscsi_fd_list[ISCSI_MAX_FD]; @@ -148,6 +149,14 @@ int open(const char *path, int flags, mode_t mode) iscsi_fd_list[fd].lun = iscsi_url->lun; iscsi_fd_list[fd].mode = mode; + if (getenv("LD_ISCSI_GET_LBA_STATUS") != NULL) { + iscsi_fd_list[fd].get_lba_status = atoi(getenv("LD_ISCSI_GET_LBA_STATUS")); + if (rc16->lbpme == 0){ + LD_ISCSI_DPRINTF(1,"Logical unit is fully provisioned. Will skip get_lba_status tasks"); + iscsi_fd_list[fd].get_lba_status = 0; + } + } + scsi_free_scsi_task(task); iscsi_destroy_url(iscsi_url); @@ -272,6 +281,7 @@ ssize_t read(int fd, void *buf, size_t count) uint64_t offset; uint64_t num_blocks, lba; struct scsi_task *task; + struct scsi_get_lba_status *lbas; if (iscsi_fd_list[fd].dup2fd >= 0) { return read(iscsi_fd_list[fd].dup2fd, buf, count); @@ -290,13 +300,58 @@ ssize_t read(int fd, void *buf, size_t count) count = num_blocks * iscsi_fd_list[fd].block_size; } + iscsi_fd_list[fd].in_flight = 1; + if (iscsi_fd_list[fd].get_lba_status != 0) { + LD_ISCSI_DPRINTF(4,"get_lba_status_sync: lun %d, lba %lu, num_blocks: %lu",iscsi_fd_list[fd].lun,lba,num_blocks); + task = iscsi_get_lba_status_sync(iscsi_fd_list[fd].iscsi, iscsi_fd_list[fd].lun, lba, 8+16); + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + LD_ISCSI_DPRINTF(0,"failed to send get_lba_status command"); + iscsi_fd_list[fd].in_flight = 0; + errno = EIO; + return -1; + } + lbas = scsi_datain_unmarshall(task); + if (lbas == NULL) { + LD_ISCSI_DPRINTF(0,"failed to unmarshall get_lba_status data"); + scsi_free_scsi_task(task); + iscsi_fd_list[fd].in_flight = 0; + errno = EIO; + return -1; + } + + u_int32_t i; + LD_ISCSI_DPRINTF(5,"get_lba_status: num_descriptors: %d",lbas->num_descriptors); + u_int32_t _num_allocated=0; + u_int32_t _num_blocks=0; + for (i=0;inum_descriptors;i++) { + struct scsi_lba_status_descriptor *lbasd = &lbas->descriptors[i]; + LD_ISCSI_DPRINTF(5,"get_lba_status_descriptor %d, lba %lu, num_blocks %d, type %d",i,lbasd->lba,lbasd->num_blocks,lbasd->provisioning); + if (lbasd->lba != _num_blocks+lba) { + LD_ISCSI_DPRINTF(0,"get_lba_status response is non-continuous"); + scsi_free_scsi_task(task); + iscsi_fd_list[fd].in_flight = 0; + errno = EIO; + return -1; + } + _num_allocated+=(lbasd->provisioning==0x00)?lbasd->num_blocks:0; + _num_blocks+=lbasd->num_blocks; + } + scsi_free_scsi_task(task); + if (_num_allocated == 0 && _num_blocks >= num_blocks) { + LD_ISCSI_DPRINTF(4,"skipped read16_sync for non-allocated blocks: lun %d, lba %lu, num_blocks: %lu, block_size: %d, offset: %lu count: %lu",iscsi_fd_list[fd].lun,lba,num_blocks,iscsi_fd_list[fd].block_size,offset,count); + memset(buf, 0x00, count); + iscsi_fd_list[fd].offset += count; + iscsi_fd_list[fd].in_flight = 0; + return count; + } + } + LD_ISCSI_DPRINTF(4,"read16_sync: lun %d, lba %lu, num_blocks: %lu, block_size: %d, offset: %lu count: %lu",iscsi_fd_list[fd].lun,lba,num_blocks,iscsi_fd_list[fd].block_size,offset,count); - iscsi_fd_list[fd].in_flight = 1; task = iscsi_read16_sync(iscsi_fd_list[fd].iscsi, iscsi_fd_list[fd].lun, lba, num_blocks * iscsi_fd_list[fd].block_size, iscsi_fd_list[fd].block_size, 0, 0, 0, 0, 0); iscsi_fd_list[fd].in_flight = 0; if (task == NULL || task->status != SCSI_STATUS_GOOD) { - LD_ISCSI_DPRINTF(0,"failed to send read10 command"); + LD_ISCSI_DPRINTF(0,"failed to send read16 command"); errno = EIO; return -1; } From afc963c312acd1278ef1135e011655ab50e04290 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Sun, 21 Oct 2012 20:29:39 +0200 Subject: [PATCH 27/36] LD_ISCSI: cache last result of get_lba_status get_lba_status returns provisioning for a range of blocks starting from given lba. Especially for sequential reads its likely that the next block read is already covered by the last result. In case there is write support added to ld_iscsi there needs to be an invalidation code for the cache. --- src/ld_iscsi.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/ld_iscsi.c b/src/ld_iscsi.c index d3a377b..915ae26 100644 --- a/src/ld_iscsi.c +++ b/src/ld_iscsi.c @@ -52,6 +52,8 @@ struct iscsi_fd_list { off_t offset; mode_t mode; int get_lba_status; + struct scsi_lba_status_descriptor lbasd_cached; + int lbasd_cache_valid; }; static struct iscsi_fd_list iscsi_fd_list[ISCSI_MAX_FD]; @@ -302,6 +304,17 @@ ssize_t read(int fd, void *buf, size_t count) iscsi_fd_list[fd].in_flight = 1; if (iscsi_fd_list[fd].get_lba_status != 0) { + if (iscsi_fd_list[fd].lbasd_cache_valid==1) { + LD_ISCSI_DPRINTF(5,"cached get_lba_status_descriptor is lba %lu, num_blocks %d, provisioning %d",iscsi_fd_list[fd].lbasd_cached.lba,iscsi_fd_list[fd].lbasd_cached.num_blocks,iscsi_fd_list[fd].lbasd_cached.provisioning); + if (iscsi_fd_list[fd].lbasd_cached.provisioning != 0x00 && lba >= iscsi_fd_list[fd].lbasd_cached.lba && lba+num_blocks < iscsi_fd_list[fd].lbasd_cached.lba+iscsi_fd_list[fd].lbasd_cached.num_blocks) + { + LD_ISCSI_DPRINTF(4,"skipped read16_sync for non-allocated blocks: lun %d, lba %lu, num_blocks: %lu, block_size: %d, offset: %lu count: %lu",iscsi_fd_list[fd].lun,lba,num_blocks,iscsi_fd_list[fd].block_size,offset,count); + memset(buf, 0x00, count); + iscsi_fd_list[fd].offset += count; + iscsi_fd_list[fd].in_flight = 0; + return count; + } + } LD_ISCSI_DPRINTF(4,"get_lba_status_sync: lun %d, lba %lu, num_blocks: %lu",iscsi_fd_list[fd].lun,lba,num_blocks); task = iscsi_get_lba_status_sync(iscsi_fd_list[fd].iscsi, iscsi_fd_list[fd].lun, lba, 8+16); if (task == NULL || task->status != SCSI_STATUS_GOOD) { @@ -325,7 +338,7 @@ ssize_t read(int fd, void *buf, size_t count) u_int32_t _num_blocks=0; for (i=0;inum_descriptors;i++) { struct scsi_lba_status_descriptor *lbasd = &lbas->descriptors[i]; - LD_ISCSI_DPRINTF(5,"get_lba_status_descriptor %d, lba %lu, num_blocks %d, type %d",i,lbasd->lba,lbasd->num_blocks,lbasd->provisioning); + LD_ISCSI_DPRINTF(5,"get_lba_status_descriptor %d, lba %lu, num_blocks %d, provisioning %d",i,lbasd->lba,lbasd->num_blocks,lbasd->provisioning); if (lbasd->lba != _num_blocks+lba) { LD_ISCSI_DPRINTF(0,"get_lba_status response is non-continuous"); scsi_free_scsi_task(task); @@ -335,6 +348,8 @@ ssize_t read(int fd, void *buf, size_t count) } _num_allocated+=(lbasd->provisioning==0x00)?lbasd->num_blocks:0; _num_blocks+=lbasd->num_blocks; + iscsi_fd_list[fd].lbasd_cached=lbas->descriptors[i]; + iscsi_fd_list[fd].lbasd_cache_valid=1; } scsi_free_scsi_task(task); if (_num_allocated == 0 && _num_blocks >= num_blocks) { From c2836e2a9aea67ec14360260c2e031ba8f57f8a3 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 23 Oct 2012 10:53:14 +0200 Subject: [PATCH 28/36] DPRINTF add iscsi target_name if available This patch adds the iscsi target name to the debug output --- include/iscsi.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/include/iscsi.h b/include/iscsi.h index 30aa5ea..cae57f3 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -959,7 +959,17 @@ iscsi_scsi_task_cancel(struct iscsi_context *iscsi, EXTERN void iscsi_scsi_cancel_all_tasks(struct iscsi_context *iscsi); -#define DPRINTF(iscsi,level,fmt,args...) do { if ((iscsi)->debug >= level) {fprintf(stderr,"libiscsi: ");fprintf(stderr, (fmt), ##args); fprintf(stderr,"\n");} } while (0); +#define DPRINTF(iscsi,level,fmt,args...) \ + do { \ + if ((iscsi)->debug >= level) { \ + fprintf(stderr,"libiscsi: "); \ + fprintf(stderr, (fmt), ##args); \ + if (iscsi->target_name) { \ + fprintf(stderr," [%s]",iscsi->target_name); \ + } \ + fprintf(stderr,"\n"); \ + } \ + } while (0); /* * This function is to set the debugging level (0=disabled). From 6e82c481850af6f3d3ce70397afcfa76bf2baa11 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 23 Oct 2012 10:55:04 +0200 Subject: [PATCH 29/36] RECONNECT fix read from freed iscsi context --- lib/connect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/connect.c b/lib/connect.c index 8ad2afe..7907bed 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -241,7 +241,6 @@ try_again: iscsi->tcp_syncnt = old_iscsi->tcp_syncnt; if (iscsi_full_connect_sync(iscsi, iscsi->portal, iscsi->lun) != 0) { - iscsi_destroy_context(iscsi); int backoff=retry; if (backoff > 10) { backoff+=rand()%10; @@ -251,6 +250,7 @@ try_again: backoff=30; } DPRINTF(iscsi,1,"reconnect try %d failed, waiting %d seconds",retry,backoff); + iscsi_destroy_context(iscsi); sleep(backoff); retry++; goto try_again; From 56b1e27980cf72111aa6c313592344a1c7af9242 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 23 Oct 2012 10:59:07 +0200 Subject: [PATCH 30/36] LD_ISCSI fix whitespace --- src/ld_iscsi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ld_iscsi.c b/src/ld_iscsi.c index 915ae26..b6c4aa8 100644 --- a/src/ld_iscsi.c +++ b/src/ld_iscsi.c @@ -70,7 +70,7 @@ int open(const char *path, int flags, mode_t mode) struct scsi_task *task; struct scsi_readcapacity16 *rc16; - if (mode & O_NONBLOCK) { + if (mode & O_NONBLOCK) { LD_ISCSI_DPRINTF(0,"Non-blocking I/O is currently not supported"); errno = EINVAL; return -1; @@ -165,7 +165,7 @@ int open(const char *path, int flags, mode_t mode) return fd; } - return real_open(path, flags, mode); + return real_open(path, flags, mode); } int open64(const char *path, int flags, mode_t mode) @@ -361,7 +361,7 @@ ssize_t read(int fd, void *buf, size_t count) } } - LD_ISCSI_DPRINTF(4,"read16_sync: lun %d, lba %lu, num_blocks: %lu, block_size: %d, offset: %lu count: %lu",iscsi_fd_list[fd].lun,lba,num_blocks,iscsi_fd_list[fd].block_size,offset,count); + LD_ISCSI_DPRINTF(4,"read16_sync: lun %d, lba %lu, num_blocks: %lu, block_size: %d, offset: %lu count: %lu",iscsi_fd_list[fd].lun,lba,num_blocks,iscsi_fd_list[fd].block_size,offset,count); task = iscsi_read16_sync(iscsi_fd_list[fd].iscsi, iscsi_fd_list[fd].lun, lba, num_blocks * iscsi_fd_list[fd].block_size, iscsi_fd_list[fd].block_size, 0, 0, 0, 0, 0); iscsi_fd_list[fd].in_flight = 0; From fec061ac683d4b6f4f986b26c7cbe72932902e3c Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 23 Oct 2012 15:59:28 +0200 Subject: [PATCH 31/36] Add iscsi-readcapacity16 binary This patch adds a small binary to read the size of an iscsi target. The value is returned on stdout. --- Makefile.am | 3 +- include/iscsi.h | 2 +- src/iscsi-readcapacity16.c | 166 +++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 src/iscsi-readcapacity16.c diff --git a/Makefile.am b/Makefile.am index 735760a..6868df6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,9 +37,10 @@ lib_libiscsi_la_LDFLAGS = \ # libiscsi utilities if PROGRAMS -bin_PROGRAMS += bin/iscsi-inq bin/iscsi-ls +bin_PROGRAMS += bin/iscsi-inq bin/iscsi-ls bin/iscsi-readcapacity16 bin_iscsi_inq_SOURCES = src/iscsi-inq.c bin_iscsi_ls_SOURCES = src/iscsi-ls.c +bin_iscsi_readcapacity16_SOURCES = src/iscsi-readcapacity16.c # Other examples diff --git a/include/iscsi.h b/include/iscsi.h index cae57f3..aeaa121 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -963,7 +963,7 @@ iscsi_scsi_cancel_all_tasks(struct iscsi_context *iscsi); do { \ if ((iscsi)->debug >= level) { \ fprintf(stderr,"libiscsi: "); \ - fprintf(stderr, (fmt), ##args); \ + fprintf(stderr, (fmt), ##args); \ if (iscsi->target_name) { \ fprintf(stderr," [%s]",iscsi->target_name); \ } \ diff --git a/src/iscsi-readcapacity16.c b/src/iscsi-readcapacity16.c new file mode 100644 index 0000000..3954b42 --- /dev/null +++ b/src/iscsi-readcapacity16.c @@ -0,0 +1,166 @@ +/* + Copyright (C) 2010 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 +#include +#include "iscsi.h" +#include "scsi-lowlevel.h" + +const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-readcapacity16"; + +void print_usage(void) +{ + fprintf(stderr, "Usage: iscsi-readcapacity16 [-?] [-?|--help] [--usage] [-i|--initiator-name=iqn-name] \n"); +} + +void print_help(void) +{ + fprintf(stderr, "Usage: iscsi_readcapacity16 [OPTION...] \n"); + fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n"); + fprintf(stderr, " -d, --debug=integer debug level (0=disabled)\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Help options:\n"); + fprintf(stderr, " -?, --help Show this help message\n"); + fprintf(stderr, " --usage Display brief usage message\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "iSCSI URL format : %s\n", ISCSI_URL_SYNTAX); + fprintf(stderr, "\n"); + fprintf(stderr, " is either of:\n"); + fprintf(stderr, " \"hostname\" iscsi.example\n"); + fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n"); + fprintf(stderr, " \"ipv6-address\" [fce0::1]\n"); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + struct iscsi_context *iscsi; + const char **extra_argv; + int extra_argc = 0; + const char *url = NULL; + struct iscsi_url *iscsi_url = NULL; + int show_help = 0, show_usage = 0, debug = 0; + int res; + + struct poptOption popt_options[] = { + { "help", '?', POPT_ARG_NONE, &show_help, 0, "Show this help message", NULL }, + { "usage", 0, POPT_ARG_NONE, &show_usage, 0, "Display brief usage message", NULL }, + { "initiator-name", 'i', POPT_ARG_STRING, &initiator, 0, "Initiatorname to use", "iqn-name" }, + { "debug", 'd', POPT_ARG_INT, &debug, 0, "Debugging level", "integer" }, + POPT_TABLEEND + }; + + pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_POSIXMEHARDER); + if ((res = poptGetNextOpt(pc)) < -1) { + fprintf(stderr, "Failed to parse option : %s %s\n", + poptBadOption(pc, 0), poptStrerror(res)); + exit(10); + } + extra_argv = poptGetArgs(pc); + if (extra_argv) { + url = *extra_argv; + extra_argv++; + while (extra_argv[extra_argc]) { + extra_argc++; + } + } + + if (show_help != 0) { + print_help(); + exit(0); + } + + if (show_usage != 0) { + print_usage(); + exit(0); + } + + poptFreeContext(pc); + + iscsi = iscsi_create_context(initiator); + if (iscsi == NULL) { + fprintf(stderr, "Failed to create context\n"); + exit(10); + } + + if (debug > 0) { + iscsi_set_debug(iscsi, debug); + } + + if (url == NULL) { + fprintf(stderr, "You must specify the URL\n"); + print_usage(); + exit(10); + } + iscsi_url = iscsi_parse_full_url(iscsi, url); + if (iscsi_url == NULL) { + fprintf(stderr, "Failed to parse URL: %s\n", + iscsi_get_error(iscsi)); + exit(10); + } + + iscsi_set_targetname(iscsi, iscsi_url->target); + iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); + iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); + + if (iscsi_url->user != NULL) { + if (iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, iscsi_url->passwd) != 0) { + fprintf(stderr, "Failed to set initiator username and password\n"); + exit(10); + } + } + + if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { + fprintf(stderr, "Login Failed. %s\n", iscsi_get_error(iscsi)); + iscsi_destroy_url(iscsi_url); + iscsi_destroy_context(iscsi); + exit(10); + } + + struct scsi_task *task; + struct scsi_readcapacity16 *rc16; + + task = iscsi_readcapacity16_sync(iscsi, iscsi_url->lun); + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + fprintf(stderr,"failed to send readcapacity command\n"); + iscsi_destroy_url(iscsi_url); + iscsi_destroy_context(iscsi); + exit(10); + } + + rc16 = scsi_datain_unmarshall(task); + if (rc16 == NULL) { + fprintf(stderr,"failed to unmarshall readcapacity16 data\n"); + scsi_free_scsi_task(task); + iscsi_destroy_url(iscsi_url); + iscsi_destroy_context(iscsi); + exit(10); + } + + fprintf(stdout,"%lu",rc16->block_length*(rc16->returned_lba + 1)); + + iscsi_destroy_url(iscsi_url); + + iscsi_logout_sync(iscsi); + iscsi_destroy_context(iscsi); + return 0; +} + From 2427926327f36d3f920b85e4df169506ae3814e7 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 23 Oct 2012 17:17:53 +0200 Subject: [PATCH 32/36] ISCSI-READCAPACITY16 modified copyright notice --- src/iscsi-readcapacity16.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/iscsi-readcapacity16.c b/src/iscsi-readcapacity16.c index 3954b42..2de858a 100644 --- a/src/iscsi-readcapacity16.c +++ b/src/iscsi-readcapacity16.c @@ -1,4 +1,5 @@ /* + Copyright (C) 2012 by Peter Lieven Copyright (C) 2010 by Ronnie Sahlberg This program is free software; you can redistribute it and/or modify From 97dcf94d722642095e91c048f875e4e122d8fea2 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 24 Oct 2012 07:35:30 -0700 Subject: [PATCH 33/36] iSCSI: when reconnecting we should not automatically requeue any DATA-OUT PDUs Dont requeue data-out pdus, or other pdus with the DELETE_WHEN_SENT flag, such as nops. These, like the DATA-OUT pdu will instead be automatically re-sent when the original write command is sent again. Signed-off-by: Ronnie Sahlberg --- lib/connect.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/connect.c b/lib/connect.c index 7907bed..9d0d8f8 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -285,6 +285,15 @@ try_again: continue; } + if (pdu->flags & ISCSI_PDU_DELETE_WHEN_SENT) { + /* We dont want to requeue things like DATA-OUT since these guys + * will be reissued automatically anyway once the corresponding + * write command is replayed. + */ + iscsi_free_pdu(old_iscsi, pdu); + continue; + } + pdu->itt = iscsi->itt++; iscsi_pdu_set_itt(pdu, pdu->itt); From b700d44f03cb3ab9c72f9544ac8deb65b4c92f46 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 24 Oct 2012 21:19:27 -0700 Subject: [PATCH 34/36] TESTS: Add a simple test to check that a target survives huge imemdiate data Signed-off-by: Ronnie Sahlberg --- Makefile.am | 3 +- test-tool/1020_bufferoffset_invalid.c | 2 +- test-tool/1030_unsolicited_data_overflow.c | 150 +++++++++++++++++++++ test-tool/iscsi-test.c | 3 + test-tool/iscsi-test.h | 2 + 5 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 test-tool/1030_unsolicited_data_overflow.c diff --git a/Makefile.am b/Makefile.am index 6868df6..688f847 100644 --- a/Makefile.am +++ b/Makefile.am @@ -143,7 +143,8 @@ bin_iscsi_test_SOURCES = test-tool/iscsi-test.c \ \ test-tool/1000_cmdsn_invalid.c \ test-tool/1010_datasn_invalid.c \ - test-tool/1020_bufferoffset_invalid.c + test-tool/1020_bufferoffset_invalid.c \ + test-tool/1030_unsolicited_data_overflow.c endif diff --git a/test-tool/1020_bufferoffset_invalid.c b/test-tool/1020_bufferoffset_invalid.c index e920c5f..80b1025 100644 --- a/test-tool/1020_bufferoffset_invalid.c +++ b/test-tool/1020_bufferoffset_invalid.c @@ -65,7 +65,7 @@ int T1020_bufferoffset_invalid(const char *initiator, const char *url, int data_ struct scsi_task *task; struct scsi_readcapacity16 *rc16; int ret, lun; - unsigned char data[block_size * 256]; + unsigned char data[4096 * 256]; struct iscsi_async_state test_state; printf("1020_bufferoffset_invalid:\n"); diff --git a/test-tool/1030_unsolicited_data_overflow.c b/test-tool/1030_unsolicited_data_overflow.c new file mode 100644 index 0000000..b5dd350 --- /dev/null +++ b/test-tool/1030_unsolicited_data_overflow.c @@ -0,0 +1,150 @@ +/* + 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 "iscsi-private.h" +#include "scsi-lowlevel.h" +#include "iscsi-test.h" + +uint32_t block_size; + +static void test_cb(struct iscsi_context *iscsi _U_, int status, + void *command_data _U_, void *private_data) +{ + struct scsi_task *task = command_data; + struct iscsi_async_state *state = private_data; + + state->finished = 1; + state->status = status; + + if (status) { + task->status = status; + } +} + +int T1030_unsolicited_data_overflow(const char *initiator, const char *url, int data_loss, int show_info) +{ + struct iscsi_context *iscsi, *iscsi2; + struct scsi_task *task; + struct scsi_readcapacity16 *rc16; + int ret, lun; + unsigned char *buf = NULL; + struct iscsi_async_state test_state; + uint32_t old_first_burst_len; + + printf("1030_unsolicited_data_overflow:\n"); + printf("===============================\n"); + if (show_info) { + printf("Test sending command with way more unsolicited data than the target supports\n"); + printf("1, Send HUGE unsolicited data to the target.\n"); + printf("2, Verify the target is still alive\n"); + printf("\n"); + return 0; + } + + iscsi = iscsi_context_login(initiator, url, &lun); + if (iscsi == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + /* find the size of the LUN */ + task = iscsi_readcapacity16_sync(iscsi, lun); + if (task == NULL) { + printf("Failed to send READCAPACITY16 command: %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("READCAPACITY16 command: failed with sense. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + rc16 = scsi_datain_unmarshall(task); + if (rc16 == NULL) { + printf("failed to unmarshall READCAPACITY16 data. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + block_size = rc16->block_length; + scsi_free_scsi_task(task); + + + if (!data_loss) { + printf("--dataloss flag is not set. Skipping test\n"); + ret = -2; + goto finished; + } + + + ret = 0; + + iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_YES; + old_first_burst_len = iscsi->first_burst_length; + /* make first burst REAL big */ + iscsi->first_burst_length *= 16; + buf = malloc(iscsi->first_burst_length); + + printf("Write too much unsolicited data ... "); + /* we dont want autoreconnect since some targets will drop the session + * on this condition. + */ + iscsi_set_noautoreconnect(iscsi, 1); + + // 102400 -- 1024000 + task = iscsi_write16_task(iscsi, lun, 0, buf, + iscsi->first_burst_length, block_size, + 0, 0, 0, 0, 0, + test_cb, &test_state); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send WRITE16 command: %s\n", iscsi_get_error(iscsi)); + ret++; + goto test2; + } + + test_state.task = task; + test_state.finished = 0; + test_state.status = 0; + wait_until_test_finished(iscsi, &test_state); + printf("[OK]\n"); + + +test2: + printf("Verify the target is still alive ... "); + iscsi2 = iscsi_context_login(initiator, url, &lun); + if (iscsi2 == NULL) { + printf("[FAILED]\n"); + printf("Target is dead?\n"); + ret = -1; + goto finished; + } + printf("[OK]\n"); + +finished: + if (buf) { + free(buf); + } + iscsi_destroy_context(iscsi); + iscsi_destroy_context(iscsi2); + return ret; +} diff --git a/test-tool/iscsi-test.c b/test-tool/iscsi-test.c index 884240c..5259133 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -234,6 +234,9 @@ struct scsi_test tests[] = { /* invalid bufferoffset from initiator */ { "T1020_bufferoffset_invalid", T1020_bufferoffset_invalid }, +/* sending too much unsolicited data */ +{ "T1030_unsolicited_data_overflow", T1030_unsolicited_data_overflow }, + { NULL, NULL } }; diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index acc3817..e59d64d 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -178,3 +178,5 @@ int T0424_reserve6_target_reset(const char *initiator, const char *url, int data 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); +int T1030_unsolicited_data_overflow(const char *initiator, const char *url, int data_loss, int show_info); + From 49947fe5e28c999ab531a2d5d722a3af2b1c0d27 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 25 Oct 2012 09:41:28 +0200 Subject: [PATCH 35/36] TEST: fix unitialized ret variable in T0000 --- test-tool/0000_testunitready_simple.c | 1 + 1 file changed, 1 insertion(+) diff --git a/test-tool/0000_testunitready_simple.c b/test-tool/0000_testunitready_simple.c index 1cbbbc9..029fc44 100644 --- a/test-tool/0000_testunitready_simple.c +++ b/test-tool/0000_testunitready_simple.c @@ -41,6 +41,7 @@ int T0000_testunitready_simple(const char *initiator, const char *url, int data_ return -1; } + ret=0; printf("Test TESTUNITREADY ... "); task = iscsi_testunitready_sync(iscsi, lun); From 03528f3965e24a7e8b7dddbab5827dac6a947a81 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 25 Oct 2012 10:27:09 +0200 Subject: [PATCH 36/36] TEST: add skip command to skip individual tests This patch adds a command to skip individual tests. It additionally accepts more than one test separated by comma. --- test-tool/iscsi-test.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test-tool/iscsi-test.c b/test-tool/iscsi-test.c index 5259133..b719d26 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -242,7 +242,7 @@ struct scsi_test tests[] = { void print_usage(void) { - fprintf(stderr, "Usage: iscsi-test [-?] [-?|--help] [--usage] [-t|--test=]\n" + fprintf(stderr, "Usage: iscsi-test [-?] [-?|--help] [--usage] [-t|--test=] [-s|--skip=]\n" "\t\t[-l|--list] [--info] [-i|--initiator-name=]\n" "\t\t\n"); } @@ -253,6 +253,7 @@ void print_help(void) 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, " -s, --skip=test-name Which test to skip. 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"); fprintf(stderr, " --dataloss Allow destructive tests.\n"); @@ -376,6 +377,7 @@ int main(int argc, const char *argv[]) int res, num_failed, num_skipped; struct scsi_test *test; char *testname = NULL; + char *skipname = NULL; struct poptOption popt_options[] = { { "help", '?', POPT_ARG_NONE, &show_help, 0, "Show this help message", NULL }, @@ -384,6 +386,7 @@ int main(int argc, const char *argv[]) { "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" }, + { "skip", 's', POPT_ARG_STRING, &skipname, 0, "Which test to skip", "skipname" }, { "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 }, POPT_TABLEEND @@ -438,6 +441,21 @@ int main(int argc, const char *argv[]) if (testname != NULL && fnmatch(testname, test->name, 0)) { continue; } + + if (skipname != NULL) { + char * pchr = skipname; + char * pchr2 = NULL; + int skip = 0; + do { + pchr2 = strchr(pchr,','); + if (pchr2) pchr2[0]=0x00; + if (!fnmatch(pchr, test->name, 0)) { + skip = 1; + } + if (pchr2) {pchr2[0]=',';pchr=pchr2+1;} + } while (pchr2); + if (skip) continue; + } res = test->test(initiator, url, data_loss, show_info); if (res == 0) {