From 9e5535adfd1cca2ed5e0c23d45fed139ed2f0ce6 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 1 May 2012 18:45:28 +1000 Subject: [PATCH] Reconnect: If we are logged in and we experience a session failure, then try to re-connect and redrive all I/O Signed-off-by: Ronnie Sahlberg --- include/iscsi-private.h | 6 +++ lib/connect.c | 109 ++++++++++++++++++++++++++++++++++++++++ lib/init.c | 22 ++++++-- lib/socket.c | 28 ++++++++++- lib/sync.c | 16 ++++-- 5 files changed, 172 insertions(+), 9 deletions(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 6229cfc..3cf771a 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -87,6 +87,7 @@ struct iscsi_context { int secneg_phase; int login_attempts; int is_loggedin; + int is_reconnecting; int chap_a; int chap_i; @@ -109,6 +110,9 @@ struct iscsi_context { enum iscsi_initial_r2t use_initial_r2t; enum iscsi_immediate_data want_immediate_data; enum iscsi_immediate_data use_immediate_data; + + int lun; + const char *portal; }; #define ISCSI_PDU_IMMEDIATE 0x40 @@ -261,5 +265,7 @@ 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); + #endif /* __iscsi_private_h__ */ diff --git a/lib/connect.c b/lib/connect.c index 854045e..c81be19 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -15,8 +15,11 @@ along with this program; if not, see . */ #include +#include #include +#include #include +#include "slist.h" #include "iscsi.h" #include "iscsi-private.h" #include "scsi-lowlevel.h" @@ -109,6 +112,9 @@ iscsi_full_connect_async(struct iscsi_context *iscsi, const char *portal, { struct connect_task *ct; + iscsi->lun = lun; + iscsi->portal = strdup(portal); + ct = malloc(sizeof(struct connect_task)); if (ct == NULL) { iscsi_set_error(iscsi, "Out-of-memory. Failed to allocate " @@ -124,3 +130,106 @@ iscsi_full_connect_async(struct iscsi_context *iscsi, const char *portal, } return 0; } + +int iscsi_reconnect(struct iscsi_context *old_iscsi) +{ + struct iscsi_context *iscsi; + +try_again: + + iscsi = iscsi_create_context(old_iscsi->initiator_name); + iscsi->is_reconnecting = 1; + + iscsi_set_targetname(iscsi, old_iscsi->target_name); + + iscsi_set_header_digest(iscsi, old_iscsi->want_header_digest); + + if (old_iscsi->user != NULL) { + iscsi_set_initiator_username_pwd(iscsi, old_iscsi->user, old_iscsi->passwd); + } + + iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); + + iscsi->lun = old_iscsi->lun; + + iscsi->portal = strdup(old_iscsi->portal); + + if (iscsi_full_connect_sync(iscsi, iscsi->portal, iscsi->lun) != 0) { + iscsi_destroy_context(iscsi); + sleep(1); + goto try_again; + } + + while (old_iscsi->waitpdu) { + struct iscsi_pdu *pdu = old_iscsi->waitpdu; + + SLIST_REMOVE(&old_iscsi->waitpdu, pdu); + if (pdu->itt == 0xffffffff) { + continue; + } + + pdu->itt = iscsi->itt++; + iscsi_pdu_set_itt(pdu, pdu->itt); + + pdu->cmdsn = iscsi->cmdsn++; + iscsi_pdu_set_cmdsn(pdu, pdu->cmdsn); + + iscsi_pdu_set_expstatsn(pdu, iscsi->statsn); + iscsi->statsn++; + + pdu->written = 0; + SLIST_ADD_END(&iscsi->outqueue, pdu); + } + + while (old_iscsi->outqueue) { + struct iscsi_pdu *pdu = old_iscsi->outqueue; + + SLIST_REMOVE(&old_iscsi->outqueue, pdu); + if (pdu->itt == 0xffffffff) { + continue; + } + + pdu->itt = iscsi->itt++; + iscsi_pdu_set_itt(pdu, pdu->itt); + + pdu->cmdsn = iscsi->cmdsn++; + iscsi_pdu_set_cmdsn(pdu, pdu->cmdsn); + + iscsi_pdu_set_expstatsn(pdu, iscsi->statsn); + iscsi->statsn++; + + pdu->written = 0; + SLIST_ADD_END(&iscsi->outqueue, pdu); + } + + if (dup2(iscsi->fd, old_iscsi->fd) == -1) { + iscsi_destroy_context(iscsi); + goto try_again; + } + + + free(discard_const(old_iscsi->initiator_name)); + free(discard_const(old_iscsi->target_name)); + free(discard_const(old_iscsi->target_address)); + free(discard_const(old_iscsi->alias)); + free(discard_const(old_iscsi->portal)); + if (old_iscsi->incoming != NULL) { + iscsi_free_iscsi_in_pdu(old_iscsi->incoming); + } + if (old_iscsi->inqueue != NULL) { + iscsi_free_iscsi_inqueue(old_iscsi->inqueue); + } + free(old_iscsi->error_string); + free(discard_const(old_iscsi->user)); + free(discard_const(old_iscsi->passwd)); + free(discard_const(old_iscsi->chap_c)); + + close(iscsi->fd); + iscsi->fd = old_iscsi->fd; + memcpy(old_iscsi, iscsi, sizeof(struct iscsi_context)); + free(iscsi); + + old_iscsi->is_reconnecting = 0; + + return 0; +} diff --git a/lib/init.c b/lib/init.c index 705e4c5..979d936 100644 --- a/lib/init.c +++ b/lib/init.c @@ -188,15 +188,25 @@ iscsi_destroy_context(struct iscsi_context *iscsi) while ((pdu = iscsi->outqueue)) { SLIST_REMOVE(&iscsi->outqueue, pdu); if ( !(pdu->flags & ISCSI_PDU_NO_CALLBACK)) { - pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL, - pdu->private_data); + /* If an error happened during connect/login, we dont want to + call any of the callbacks. + */ + if (iscsi->is_loggedin) { + pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL, + pdu->private_data); + } } iscsi_free_pdu(iscsi, pdu); } while ((pdu = iscsi->waitpdu)) { SLIST_REMOVE(&iscsi->waitpdu, pdu); - pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL, - pdu->private_data); + /* If an error happened during connect/login, we dont want to + call any of the callbacks. + */ + if (iscsi->is_loggedin) { + pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL, + pdu->private_data); + } iscsi_free_pdu(iscsi, pdu); } @@ -212,6 +222,9 @@ iscsi_destroy_context(struct iscsi_context *iscsi) free(discard_const(iscsi->alias)); iscsi->alias = NULL; + free(discard_const(iscsi->portal)); + iscsi->portal = NULL; + if (iscsi->incoming != NULL) { iscsi_free_iscsi_in_pdu(iscsi->incoming); } @@ -228,7 +241,6 @@ iscsi_destroy_context(struct iscsi_context *iscsi) free(discard_const(iscsi->passwd)); iscsi->passwd = NULL; - free(discard_const(iscsi->chap_c)); iscsi->chap_c = NULL; diff --git a/lib/socket.c b/lib/socket.c index f405d52..c88cdfb 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -410,6 +410,11 @@ 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; } if (revents & POLLHUP) { @@ -417,6 +422,11 @@ iscsi_service(struct iscsi_context *iscsi, int revents) "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; } @@ -434,6 +444,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; } @@ -448,12 +463,23 @@ 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; } } if (revents & POLLIN) { - if (iscsi_read_from_socket(iscsi) != 0) + if (iscsi_read_from_socket(iscsi) != 0) { + if (iscsi->is_loggedin) { + if (iscsi_reconnect(iscsi) == 0) { + return 0; + } + } return -1; + } } return 0; diff --git a/lib/sync.c b/lib/sync.c index 49284aa..027682c 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -38,19 +38,29 @@ static void event_loop(struct iscsi_context *iscsi, struct iscsi_sync_state *state) { struct pollfd pfd; + int ret; while (state->finished == 0) { pfd.fd = iscsi_get_fd(iscsi); pfd.events = iscsi_which_events(iscsi); - if (poll(&pfd, 1, -1) < 0) { + if ((ret = poll(&pfd, 1, 1000)) < 0) { iscsi_set_error(iscsi, "Poll failed"); return; } + if (ret == 0) { + /* poll timedout, try again */ + continue; + } if (iscsi_service(iscsi, pfd.revents) < 0) { + /* if we are reconnecting we just try and try again */ + if (iscsi->is_reconnecting) { + continue; + } + iscsi_set_error(iscsi, - "iscsi_service failed with : %s", - iscsi_get_error(iscsi)); + "iscsi_service failed with : %s", + iscsi_get_error(iscsi)); return; } }