diff --git a/Makefile.am b/Makefile.am index 5c3927f..31827c4 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 @@ -138,11 +139,13 @@ 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/0430_report_all_supported_ops.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/include/iscsi-private.h b/include/iscsi-private.h index 40a96e3..f523b6e 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; @@ -83,6 +84,12 @@ struct iscsi_context { int fd; int is_connected; + + int tcp_user_timeout; + int tcp_keepcnt; + int tcp_keepintvl; + int tcp_keepidle; + int tcp_syncnt; int current_phase; int next_phase; @@ -120,6 +127,7 @@ struct iscsi_context { const char *portal; int no_auto_reconnect; int reconnect_deferred; + int debug; }; #define ISCSI_PDU_IMMEDIATE 0x40 @@ -274,7 +282,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 a1936cc..bbbca61 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 }; @@ -332,6 +333,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. * @@ -956,6 +966,53 @@ 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); \ + 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). + */ +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); + +/* + * 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 931f571..9d0d8f8 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" @@ -35,6 +36,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 +87,14 @@ 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) { + 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; @@ -158,6 +171,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. @@ -195,6 +210,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); @@ -213,10 +231,28 @@ try_again: iscsi->lun = old_iscsi->lun; iscsi->portal = strdup(old_iscsi->portal); + + 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; + iscsi->tcp_syncnt = old_iscsi->tcp_syncnt; if (iscsi_full_connect_sync(iscsi, iscsi->portal, iscsi->lun) != 0) { + 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); iscsi_destroy_context(iscsi); - sleep(1); + sleep(backoff); + retry++; goto try_again; } @@ -249,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); diff --git a/lib/init.c b/lib/init.c index 1c11bcf..5bd6d0c 100644 --- a/lib/init.c +++ b/lib/init.c @@ -71,6 +71,34 @@ 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; + + 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"))); + } + + 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_TCP_SYNCNT") != NULL) { + iscsi_set_tcp_syncnt(iscsi,atoi(getenv("LIBISCSI_TCP_SYNCNT"))); + } + return iscsi; } @@ -244,6 +272,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 +284,6 @@ iscsi_destroy_context(struct iscsi_context *iscsi) return 0; } - - void iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...) { @@ -270,9 +301,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,"%s",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) @@ -547,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/libiscsi.def b/lib/libiscsi.def index b24dbaa..c3a915c 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -64,6 +64,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 @@ -73,6 +74,11 @@ iscsi_set_isid_reserved 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_set_tcp_syncnt iscsi_startstopunit_sync iscsi_startstopunit_task iscsi_synchronizecache10_sync diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index 6a2a71e..ddf3e1a 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -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 @@ -71,6 +72,11 @@ iscsi_set_isid_reserved 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_set_tcp_syncnt iscsi_startstopunit_sync iscsi_startstopunit_task iscsi_synchronizecache10_sync diff --git a/lib/login.c b/lib/login.c index b685743..8e57839 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 == 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)", login_error_str(status), status); diff --git a/lib/scsi-command.c b/lib/scsi-command.c index 9682c12..0fe5291 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) { diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index 0b7f6a2..197a5c9 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 * @@ -1399,7 +1399,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; @@ -2508,9 +2508,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 8ec437a..5e50954 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -58,6 +58,52 @@ static void set_nonblocking(int fd) #endif } +int set_tcp_sockopt(int sockfd, int optname, int value) +{ + int level; + + #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 + + 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",iscsi->tcp_user_timeout); + return 0; +} + +#ifndef TCP_SYNCNT +#define TCP_SYNCNT 7 +#endif + +int set_tcp_syncnt(struct iscsi_context *iscsi) +{ + 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",iscsi->tcp_syncnt); + return 0; +} + int iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, iscsi_command_cb cb, void *private_data) @@ -68,6 +114,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."); @@ -157,6 +205,16 @@ iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, iscsi->connect_data = private_data; set_nonblocking(iscsi->fd); + + 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); + } + + if (iscsi->tcp_syncnt > 0) { + set_tcp_syncnt(iscsi); + } if (connect(iscsi->fd, ai->ai_addr, socksize) != 0 && errno != EINPROGRESS) { @@ -169,6 +227,10 @@ iscsi_connect_async(struct iscsi_context *iscsi, const char *portal, } freeaddrinfo(ai); + + if (iscsi->connected_portal) free(discard_const(iscsi->connected_portal)); + iscsi->connected_portal=strdup(portal); + return 0; } @@ -182,6 +244,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; @@ -374,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) { @@ -395,30 +471,19 @@ 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) { int err = 0; socklen_t err_size = sizeof(err); - if (getsockopt(iscsi->fd, SOL_SOCKET, SO_ERROR, &err, &err_size) != 0 || err != 0) { if (err == 0) { @@ -429,15 +494,11 @@ 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); } - iscsi_set_tcp_keepalive(iscsi, 30, 3, 30); + 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); @@ -446,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); } } @@ -515,47 +566,66 @@ 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 value) +{ + iscsi->tcp_user_timeout=value; + 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) +{ + 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; -#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; } -#endif + DPRINTF(iscsi,3,"SO_KEEPALIVE set to %d",value); #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",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",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",idle); +#endif #endif return 0; diff --git a/lib/sync.c b/lib/sync.c index 4e0294f..ef9d290 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 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", diff --git a/src/iscsi-readcapacity16.c b/src/iscsi-readcapacity16.c new file mode 100644 index 0000000..2de858a --- /dev/null +++ b/src/iscsi-readcapacity16.c @@ -0,0 +1,167 @@ +/* + Copyright (C) 2012 by Peter Lieven + 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; +} + diff --git a/src/ld_iscsi.c b/src/ld_iscsi.c index e0e7ad8..b6c4aa8 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; @@ -46,6 +50,10 @@ struct iscsi_fd_list { uint32_t block_size; uint64_t num_blocks; 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]; @@ -60,19 +68,24 @@ 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"); + errno = EINVAL; + return -1; + } 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 +97,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,35 +105,37 @@ 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; 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) { - 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; return -1; } - rc10 = scsi_datain_unmarshall(task); - if (rc10 == NULL) { - fprintf(stderr, "ld-iscsi: failed to unmarshall readcapacity10 data\n"); + 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); iscsi_destroy_context(iscsi); 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) { - 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; @@ -130,10 +145,19 @@ 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; + + 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); @@ -141,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) @@ -257,8 +281,9 @@ 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; + struct scsi_get_lba_status *lbas; if (iscsi_fd_list[fd].dup2fd >= 0) { return read(iscsi_fd_list[fd].dup2fd, buf, count); @@ -278,10 +303,70 @@ ssize_t read(int fd, void *buf, size_t 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); + 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) { + 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, 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); + 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; + 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) { + 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); + + 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) { - fprintf(stderr, "ld-iscsi: failed to send read10 command\n"); + LD_ISCSI_DPRINTF(0,"failed to send read16 command"); errno = EIO; return -1; } @@ -381,59 +466,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)"); } } 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); 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/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 7534523..cad27bd 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 }, /* Maintenance In - Report Supported Operations */ { "T0430_report_all_supported_ops", T0430_report_all_supported_ops }, @@ -236,12 +237,15 @@ 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 } }; 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"); } @@ -252,6 +256,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"); @@ -375,6 +380,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 }, @@ -383,6 +389,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 @@ -437,6 +444,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) { diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index 9c5b5da..604b4df 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -173,9 +173,12 @@ 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 T0430_report_all_supported_ops(const char *initiator, const char *url, int data_loss, int show_info); int T1000_cmdsn_invalid(const char *initiator, const char *url, int data_loss, int show_info); int T1010_datasn_invalid(const char *initiator, const char *url, int data_loss, int show_info); int T1020_bufferoffset_invalid(const char *initiator, const char *url, int data_loss, int show_info); +int T1030_unsolicited_data_overflow(const char *initiator, const char *url, int data_loss, int show_info); +