summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/iscsi_if.h19
-rw-r--r--usr/discovery.c93
-rw-r--r--usr/initiator.c242
-rw-r--r--usr/initiator.h21
-rw-r--r--usr/initiator_common.c6
-rw-r--r--usr/io.c6
-rw-r--r--usr/iscsi_ipc.h2
-rw-r--r--usr/iscsi_sysfs.c14
-rw-r--r--usr/iscsi_sysfs.h1
-rw-r--r--usr/iscsiadm.c10
-rw-r--r--usr/netlink.c36
-rw-r--r--usr/session_info.c2
-rw-r--r--usr/session_mgmt.c1
-rw-r--r--usr/transport.c4
14 files changed, 361 insertions, 96 deletions
diff --git a/include/iscsi_if.h b/include/iscsi_if.h
index 72d4c41..0a96080 100644
--- a/include/iscsi_if.h
+++ b/include/iscsi_if.h
@@ -78,8 +78,9 @@ enum iscsi_uevent_e {
ISCSI_KEVENT_PATH_REQ = KEVENT_BASE + 7,
ISCSI_KEVENT_IF_DOWN = KEVENT_BASE + 8,
+ ISCSI_KEVENT_CONN_LOGIN_STATE = KEVENT_BASE + 9,
- ISCSI_KEVENT_MAX = ISCSI_KEVENT_IF_DOWN,
+ ISCSI_KEVENT_MAX = ISCSI_KEVENT_CONN_LOGIN_STATE,
};
enum iscsi_tgt_dscvr {
@@ -207,6 +208,11 @@ struct iscsi_uevent {
uint32_t cid;
uint64_t recv_handle;
} recv_req;
+ struct msg_conn_login {
+ uint32_t sid;
+ uint32_t cid;
+ uint32_t state; /* enum iscsi_conn_state */
+ } conn_login;
struct msg_conn_error {
uint32_t sid;
uint32_t cid;
@@ -320,6 +326,16 @@ enum iscsi_net_param {
ISCSI_NET_PARAM_PORT = 19,
};
+enum iscsi_conn_state {
+ ISCSI_CONN_STATE_FREE,
+ ISCSI_CONN_STATE_XPT_WAIT,
+ ISCSI_CONN_STATE_IN_LOGIN,
+ ISCSI_CONN_STATE_LOGGED_IN,
+ ISCSI_CONN_STATE_IN_LOGOUT,
+ ISCSI_CONN_STATE_LOGOUT_REQUESTED,
+ ISCSI_CONN_STATE_CLEANUP_WAIT,
+};
+
/*
* Common error codes
*/
@@ -475,6 +491,7 @@ enum iscsi_host_param {
#define CAP_DIGEST_OFFLOAD 0x1000 /* offload hdr and data digests */
#define CAP_PADDING_OFFLOAD 0x2000 /* offload padding insertion, removal,
and verification */
+#define CAP_LOGIN_OFFLOAD 0x4000 /* offload normal session login */
/*
* These flags describes reason of stop_conn() call
diff --git a/usr/discovery.c b/usr/discovery.c
index a0d073c..1f39002 100644
--- a/usr/discovery.c
+++ b/usr/discovery.c
@@ -1227,7 +1227,7 @@ static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context,
struct iscsi_conn *conn, unsigned long tmo,
int event)
{
- if (event == EV_CONN_RECV_PDU) {
+ if (event == EV_CONN_RECV_PDU || event == EV_CONN_LOGIN) {
conn->recv_context = ev_context;
return 0;
}
@@ -1241,6 +1241,89 @@ static struct iscsi_ipc_ev_clbk ipc_clbk = {
.sched_ev_context = iscsi_sched_ev_context,
};
+static int iscsi_wait_for_login(struct iscsi_conn *conn)
+{
+ struct iscsi_session *session = conn->session;
+ struct iscsi_transport *t = session->t;
+ struct pollfd pfd;
+ struct timeval connection_timer;
+ int timeout, rc;
+ uint32_t conn_state;
+ int status = 0;
+
+ if (!(t->caps & CAP_LOGIN_OFFLOAD))
+ return 0;
+
+ iscsi_timer_set(&connection_timer, conn->active_timeout);
+
+ /* prepare to poll */
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = conn->socket_fd;
+ pfd.events = POLLIN | POLLPRI;
+
+ timeout = iscsi_timer_msecs_until(&connection_timer);
+
+login_repoll:
+ log_debug(4, "discovery login process polling fd %d, "
+ "timeout in %f seconds", pfd.fd, timeout / 1000.0);
+
+ pfd.revents = 0;
+ rc = poll(&pfd, 1, timeout);
+
+ log_debug(7, "discovery login process returned from poll, rc %d", rc);
+
+ if (iscsi_timer_expired(&connection_timer)) {
+ log_warning("Discovery login session timed out.");
+ rc = ISCSI_ERR_INTERNAL;
+ goto done;
+ }
+
+ if (rc > 0) {
+ if (pfd.revents & (POLLIN | POLLPRI)) {
+ timeout = iscsi_timer_msecs_until(&connection_timer);
+ status = ipc->recv_conn_state(conn, &conn_state);
+ if (status == -EAGAIN)
+ goto login_repoll;
+ else if (status < 0) {
+ rc = ISCSI_ERR_TRANS;
+ goto done;
+ }
+
+ if (conn_state != ISCSI_CONN_STATE_LOGGED_IN)
+ rc = ISCSI_ERR_TRANS;
+ else
+ rc = 0;
+ goto done;
+ }
+
+ if (pfd.revents & POLLHUP) {
+ log_warning("discovery session"
+ "terminating after hangup");
+ rc = ISCSI_ERR_TRANS;
+ goto done;
+ }
+
+ if (pfd.revents & POLLNVAL) {
+ log_warning("discovery POLLNVAL");
+ rc = ISCSI_ERR_INTERNAL;
+ goto done;
+ }
+
+ if (pfd.revents & POLLERR) {
+ log_warning("discovery POLLERR");
+ rc = ISCSI_ERR_INTERNAL;
+ goto done;
+ }
+ } else if (rc < 0) {
+ log_error("Login poll error");
+ rc = ISCSI_ERR_INTERNAL;
+ goto done;
+ }
+
+done:
+ return rc;
+}
+
static int iscsi_create_session(struct iscsi_session *session,
struct iscsi_sendtargets_config *config,
char *data, unsigned int data_len)
@@ -1320,6 +1403,9 @@ redirect_reconnect:
iscsi_copy_operational_params(&session->conn[0], &config->session_conf,
&config->conn_conf);
+ if ((session->t->caps & CAP_LOGIN_OFFLOAD))
+ goto start_conn;
+
status_class = 0;
status_detail = 0;
rc = ISCSI_ERR_LOGIN;
@@ -1422,6 +1508,7 @@ redirect_reconnect:
if (!(t->caps & CAP_TEXT_NEGO))
return 0;
+start_conn:
log_debug(2, "%s discovery set params\n", __FUNCTION__);
rc = iscsi_session_set_params(conn);
if (rc) {
@@ -1439,7 +1526,9 @@ redirect_reconnect:
goto login_failed;
}
- return 0;
+ rc = iscsi_wait_for_login(conn);
+ if (!rc)
+ return 0;
login_failed:
iscsi_destroy_session(session);
diff --git a/usr/initiator.c b/usr/initiator.c
index 823c3ce..021d585 100644
--- a/usr/initiator.c
+++ b/usr/initiator.c
@@ -249,7 +249,7 @@ __session_conn_create(iscsi_session_t *session, int cid)
return ISCSI_ERR_NOMEM;
}
- conn->state = STATE_FREE;
+ conn->state = ISCSI_CONN_STATE_FREE;
conn->session = session;
/*
* TODO: we must export the socket_fd/transport_eph from sysfs
@@ -451,7 +451,8 @@ session_conn_shutdown(iscsi_conn_t *conn, queue_task_t *qtask,
log_debug(2, "disconnect conn");
/* this will check for a valid interconnect connection */
- conn->session->t->template->ep_disconnect(conn);
+ if (session->t->template->ep_disconnect)
+ session->t->template->ep_disconnect(conn);
if (session->id == -1)
goto cleanup;
@@ -459,9 +460,9 @@ session_conn_shutdown(iscsi_conn_t *conn, queue_task_t *qtask,
if (!iscsi_sysfs_session_has_leadconn(session->id))
goto cleanup;
- if (conn->state == STATE_IN_LOGIN ||
- conn->state == STATE_IN_LOGOUT ||
- conn->state == STATE_LOGGED_IN) {
+ if (conn->state == ISCSI_CONN_STATE_IN_LOGIN ||
+ conn->state == ISCSI_CONN_STATE_IN_LOGOUT ||
+ conn->state == ISCSI_CONN_STATE_LOGGED_IN) {
log_debug(2, "stop conn (conn state %d)", conn->state);
if (ipc->stop_conn(session->t->handle, session->id,
conn->id, STOP_CONN_TERM)) {
@@ -481,7 +482,9 @@ session_conn_shutdown(iscsi_conn_t *conn, queue_task_t *qtask,
cleanup:
if (session->id != -1) {
log_debug(2, "kdestroy session %u", session->id);
- if (ipc->destroy_session(session->t->handle, session->id)) {
+ session->r_stage = R_STAGE_SESSION_DESTOYED;
+ err = ipc->destroy_session(session->t->handle, session->id);
+ if (err) {
log_error("can not safely destroy session %d",
session->id);
return ISCSI_ERR_INTERNAL;
@@ -569,11 +572,11 @@ __session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop,
/* flush stale polls or errors queued */
iscsi_flush_context_pool(session);
conn_delete_timers(conn);
- conn->state = STATE_XPT_WAIT;
+ conn->state = ISCSI_CONN_STATE_XPT_WAIT;
conn->session->t->template->ep_disconnect(conn);
if (do_stop) {
- /* state: STATE_CLEANUP_WAIT */
+ /* state: ISCSI_CONN_STATE_CLEANUP_WAIT */
if (ipc->stop_conn(session->t->handle, session->id,
conn->id, do_stop)) {
log_error("can't stop connection %d:%d (%d)",
@@ -674,10 +677,10 @@ static void iscsi_login_eh(struct iscsi_conn *conn, struct queue_task *qtask,
iscsi_flush_context_pool(conn->session);
switch (conn->state) {
- case STATE_XPT_WAIT:
+ case ISCSI_CONN_STATE_XPT_WAIT:
switch (session->r_stage) {
case R_STAGE_NO_CHANGE:
- log_debug(6, "login failed STATE_XPT_WAIT/"
+ log_debug(6, "login failed ISCSI_CONN_STATE_XPT_WAIT/"
"R_STAGE_NO_CHANGE");
/* timeout during initial connect.
* clean connection. write ipc rsp or retry */
@@ -693,7 +696,7 @@ static void iscsi_login_eh(struct iscsi_conn *conn, struct queue_task *qtask,
}
break;
case R_STAGE_SESSION_REDIRECT:
- log_debug(6, "login failed STATE_XPT_WAIT/"
+ log_debug(6, "login failed ISCSI_CONN_STATE_XPT_WAIT/"
"R_STAGE_SESSION_REDIRECT");
/* timeout during initial redirect connect
* clean connection. write ipc rsp or retry */
@@ -704,7 +707,7 @@ static void iscsi_login_eh(struct iscsi_conn *conn, struct queue_task *qtask,
session_conn_reopen(conn, qtask, 0);
break;
case R_STAGE_SESSION_REOPEN:
- log_debug(6, "login failed STATE_XPT_WAIT/"
+ log_debug(6, "login failed ISCSI_CONN_STATE_XPT_WAIT/"
"R_STAGE_SESSION_REOPEN %d",
session->reopen_cnt);
/* timeout during reopen connect. try again */
@@ -718,11 +721,11 @@ static void iscsi_login_eh(struct iscsi_conn *conn, struct queue_task *qtask,
}
break;
- case STATE_IN_LOGIN:
+ case ISCSI_CONN_STATE_IN_LOGIN:
switch (session->r_stage) {
case R_STAGE_NO_CHANGE:
case R_STAGE_SESSION_REDIRECT:
- log_debug(6, "login failed STATE_IN_LOGIN/"
+ log_debug(6, "login failed ISCSI_CONN_STATE_IN_LOGIN/"
"R_STAGE_NO_CHANGE %d",
session->reopen_cnt);
/*
@@ -738,7 +741,7 @@ static void iscsi_login_eh(struct iscsi_conn *conn, struct queue_task *qtask,
STOP_CONN_RECOVER);
break;
case R_STAGE_SESSION_REOPEN:
- log_debug(6, "login failed STATE_IN_LOGIN/"
+ log_debug(6, "login failed ISCSI_CONN_STATE_IN_LOGIN/"
"R_STAGE_SESSION_REOPEN %d",
session->reopen_cnt);
session_conn_reopen(conn, qtask, STOP_CONN_RECOVER);
@@ -774,18 +777,18 @@ __conn_error_handle(iscsi_session_t *session, iscsi_conn_t *conn)
}
switch (conn->state) {
- case STATE_IN_LOGOUT:
+ case ISCSI_CONN_STATE_IN_LOGOUT:
/* logout was from eh - fall down to cleanup */
- case STATE_LOGGED_IN:
+ case ISCSI_CONN_STATE_LOGGED_IN:
/* mark failed connection */
- conn->state = STATE_CLEANUP_WAIT;
+ conn->state = ISCSI_CONN_STATE_CLEANUP_WAIT;
if (session->erl > 0) {
/* check if we still have some logged in connections */
for (i=0; i<ISCSI_CONN_MAX; i++) {
- if (session->conn[i].state == STATE_LOGGED_IN) {
+ if (session->conn[i].state ==
+ ISCSI_CONN_STATE_LOGGED_IN)
break;
- }
}
if (i != ISCSI_CONN_MAX) {
/* FIXME: re-assign leading connection
@@ -797,17 +800,19 @@ __conn_error_handle(iscsi_session_t *session, iscsi_conn_t *conn)
/* mark all connections as failed */
for (i=0; i<ISCSI_CONN_MAX; i++) {
- if (session->conn[i].state == STATE_LOGGED_IN)
- session->conn[i].state = STATE_CLEANUP_WAIT;
+ if (session->conn[i].state ==
+ ISCSI_CONN_STATE_LOGGED_IN)
+ session->conn[i].state =
+ ISCSI_CONN_STATE_CLEANUP_WAIT;
}
session->r_stage = R_STAGE_SESSION_REOPEN;
break;
- case STATE_IN_LOGIN:
+ case ISCSI_CONN_STATE_IN_LOGIN:
if (session->r_stage == R_STAGE_SESSION_REOPEN) {
queue_task_t *qtask;
- if (session->sync_qtask)
- qtask = session->sync_qtask;
+ if (session->notify_qtask)
+ qtask = session->notify_qtask;
else
qtask = &session->reopen_qtask;
iscsi_login_eh(conn, qtask, ISCSI_ERR_TRANS);
@@ -816,11 +821,11 @@ __conn_error_handle(iscsi_session_t *session, iscsi_conn_t *conn)
log_debug(1, "ignoring conn error in login. "
"let it timeout");
return;
- case STATE_XPT_WAIT:
+ case ISCSI_CONN_STATE_XPT_WAIT:
log_debug(1, "ignoring conn error in XPT_WAIT. "
"let connection fail on its own");
return;
- case STATE_CLEANUP_WAIT:
+ case ISCSI_CONN_STATE_CLEANUP_WAIT:
log_debug(1, "ignoring conn error in CLEANUP_WAIT. "
"let connection stop");
return;
@@ -864,10 +869,10 @@ static void iscsi_login_timedout(void *data)
struct iscsi_conn *conn = qtask->conn;
switch (conn->state) {
- case STATE_XPT_WAIT:
+ case ISCSI_CONN_STATE_XPT_WAIT:
iscsi_login_eh(conn, qtask, ISCSI_ERR_TRANS_TIMEOUT);
break;
- case STATE_IN_LOGIN:
+ case ISCSI_CONN_STATE_IN_LOGIN:
iscsi_login_eh(conn, qtask, ISCSI_ERR_PDU_TIMEOUT);
break;
default:
@@ -943,7 +948,7 @@ static void conn_send_nop_out(void *data)
* we cannot start new request during logout and the logout timer
* will figure things out.
*/
- if (conn->state == STATE_IN_LOGOUT)
+ if (conn->state == ISCSI_CONN_STATE_IN_LOGOUT)
return;
__send_nopout(conn);
@@ -1022,7 +1027,7 @@ setup_full_feature_phase(iscsi_conn_t *conn)
return;
}
- conn->state = STATE_LOGGED_IN;
+ conn->state = ISCSI_CONN_STATE_LOGGED_IN;
if (session->r_stage == R_STAGE_NO_CHANGE ||
session->r_stage == R_STAGE_SESSION_REDIRECT) {
/*
@@ -1039,7 +1044,7 @@ setup_full_feature_phase(iscsi_conn_t *conn)
session->nrec.conn[conn->id].port,
session->nrec.iface.name);
} else {
- session->sync_qtask = NULL;
+ session->notify_qtask = NULL;
session_online_devs(session->hostno, session->id);
mgmt_ipc_write_rsp(c->qtask, ISCSI_SUCCESS);
@@ -1070,7 +1075,7 @@ static void iscsi_logout_timedout(void *data)
iscsi_ev_context_put(ev_context);
/*
- * assume we were in STATE_IN_LOGOUT or there
+ * assume we were in ISCSI_CONN_STATE_IN_LOGOUT or there
* was some nasty error
*/
log_debug(3, "logout timeout, dropping conn...\n");
@@ -1082,7 +1087,7 @@ static int iscsi_send_logout(iscsi_conn_t *conn)
struct iscsi_logout hdr;
struct iscsi_ev_context *ev_context;
- if (conn->state != STATE_LOGGED_IN)
+ if (conn->state != ISCSI_CONN_STATE_LOGGED_IN)
return EINVAL;
memset(&hdr, 0, sizeof(struct iscsi_logout));
@@ -1094,7 +1099,7 @@ static int iscsi_send_logout(iscsi_conn_t *conn)
if (!iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr,
ISCSI_DIGEST_NONE, NULL, ISCSI_DIGEST_NONE, 0))
return EIO;
- conn->state = STATE_IN_LOGOUT;
+ conn->state = ISCSI_CONN_STATE_IN_LOGOUT;
ev_context = iscsi_ev_context_get(conn, 0);
if (!ev_context)
@@ -1119,8 +1124,10 @@ static void iscsi_stop(void *data)
iscsi_ev_context_put(ev_context);
- if (!iscsi_send_logout(conn))
- return;
+ if (!(conn->session->t->caps & CAP_LOGIN_OFFLOAD)) {
+ if (!iscsi_send_logout(conn))
+ return;
+ }
rc = session_conn_shutdown(conn, conn->logout_qtask, ISCSI_SUCCESS);
if (rc)
@@ -1260,7 +1267,7 @@ static void iscsi_recv_login_rsp(struct iscsi_conn *conn)
if (conn->current_stage != ISCSI_FULL_FEATURE_PHASE) {
/* more nego. needed! */
- conn->state = STATE_IN_LOGIN;
+ conn->state = ISCSI_CONN_STATE_IN_LOGIN;
if (iscsi_login_req(session, c)) {
iscsi_login_eh(conn, c->qtask, ISCSI_ERR_LOGIN);
return;
@@ -1289,12 +1296,12 @@ static void session_conn_recv_pdu(void *data)
conn->recv_context = ev_context;
switch (conn->state) {
- case STATE_IN_LOGIN:
+ case ISCSI_CONN_STATE_IN_LOGIN:
iscsi_recv_login_rsp(conn);
break;
- case STATE_LOGGED_IN:
- case STATE_IN_LOGOUT:
- case STATE_LOGOUT_REQUESTED:
+ case ISCSI_CONN_STATE_LOGGED_IN:
+ case ISCSI_CONN_STATE_IN_LOGOUT:
+ case ISCSI_CONN_STATE_LOGOUT_REQUESTED:
/* read incoming PDU */
if (iscsi_io_recv_pdu(conn, &hdr, ISCSI_DIGEST_NONE,
conn->data, ISCSI_DEF_MAX_RECV_SEG_LEN,
@@ -1316,12 +1323,12 @@ static void session_conn_recv_pdu(void *data)
break;
}
break;
- case STATE_XPT_WAIT:
+ case ISCSI_CONN_STATE_XPT_WAIT:
iscsi_ev_context_put(ev_context);
log_debug(1, "ignoring incoming PDU in XPT_WAIT. "
"let connection re-establish or fail");
break;
- case STATE_CLEANUP_WAIT:
+ case ISCSI_CONN_STATE_CLEANUP_WAIT:
iscsi_ev_context_put(ev_context);
log_debug(1, "ignoring incoming PDU in XPT_WAIT. "
"let connection cleanup");
@@ -1448,6 +1455,37 @@ retry_create:
return err;
}
+static void setup_offload_login_phase(iscsi_conn_t *conn)
+{
+ iscsi_session_t *session = conn->session;
+ iscsi_login_context_t *c = &conn->login_context;
+ int rc;
+
+ actor_delete(&conn->login_timer);
+
+ if (iscsi_session_set_params(conn)) {
+ iscsi_login_eh(conn, c->qtask, ISCSI_ERR_LOGIN);
+ return;
+ }
+
+ if (iscsi_host_set_params(session)) {
+ iscsi_login_eh(conn, c->qtask, ISCSI_ERR_LOGIN);
+ return;
+ }
+
+ conn->state = ISCSI_CONN_STATE_IN_LOGIN;
+ if (ipc->start_conn(session->t->handle, session->id, conn->id,
+ &rc) || rc) {
+ log_error("can't start connection %d:%d retcode %d (%d)",
+ session->id, conn->id, rc, errno);
+ iscsi_login_eh(conn, c->qtask, ISCSI_ERR_INTERNAL);
+ return;
+ }
+
+ session->notify_qtask = c->qtask;
+}
+
+
static void session_conn_poll(void *data)
{
struct iscsi_ev_context *ev_context = data;
@@ -1460,7 +1498,7 @@ static void session_conn_poll(void *data)
iscsi_ev_context_put(ev_context);
- if (conn->state != STATE_XPT_WAIT)
+ if (conn->state != ISCSI_CONN_STATE_XPT_WAIT)
return;
rc = session->t->template->ep_poll(conn, 1);
@@ -1491,8 +1529,9 @@ static void session_conn_poll(void *data)
log_debug(3, "created new iSCSI session sid %d host "
"no %u", session->id, session->hostno);
- if (ipc->create_conn(session->t->handle,
- session->id, conn->id, &conn->id)) {
+ err = ipc->create_conn(session->t->handle,
+ session->id, conn->id, &conn->id);
+ if (err) {
log_error("Can't create connection.");
err = ISCSI_ERR_INTERNAL;
goto cleanup;
@@ -1529,12 +1568,17 @@ static void session_conn_poll(void *data)
conn->exp_statsn = iscsi_sysfs_get_exp_statsn(session->id);
+ if (session->t->caps & CAP_LOGIN_OFFLOAD) {
+ setup_offload_login_phase(conn);
+ return;
+ }
+
if (iscsi_login_begin(session, c)) {
iscsi_login_eh(conn, qtask, ISCSI_ERR_LOGIN);
return;
}
- conn->state = STATE_IN_LOGIN;
+ conn->state = ISCSI_CONN_STATE_IN_LOGIN;
if (iscsi_login_req(session, c)) {
iscsi_login_eh(conn, qtask, ISCSI_ERR_LOGIN);
return;
@@ -1550,6 +1594,71 @@ cleanup:
session_conn_shutdown(conn, qtask, err);
}
+static void session_conn_process_login(void *data)
+{
+ struct iscsi_ev_context *ev_context = data;
+ enum iscsi_conn_state state = *(enum iscsi_conn_state *)
+ ev_context->data;
+ struct iscsi_conn *conn = ev_context->conn;
+ struct iscsi_session *session = conn->session;
+ iscsi_login_context_t *c = &conn->login_context;
+ queue_task_t *qtask;
+
+ iscsi_ev_context_put(ev_context);
+ if (!(session->t->caps & CAP_LOGIN_OFFLOAD))
+ return;
+
+ if (state == ISCSI_CONN_STATE_FREE)
+ goto failed_login;
+
+ conn->state = ISCSI_CONN_STATE_LOGGED_IN;
+ /*
+ * ok we were in_login and now we got the notification that we are
+ * logged in
+ */
+ log_debug(3, "session created sid %u host no %d", session->id,
+ session->hostno);
+
+ if (session->r_stage == R_STAGE_NO_CHANGE ||
+ session->r_stage == R_STAGE_SESSION_REDIRECT) {
+ /*
+ * scan host is one-time deal. We
+ * don't want to re-scan it on recovery.
+ */
+ session_scan_host(session, session->hostno,
+ c->qtask);
+ session->notify_qtask = NULL;
+
+ log_warning("Connection%d:%d to [target: %s, portal: %s,%d] "
+ "through [iface: %s] is operational now",
+ session->id, conn->id, session->nrec.name,
+ session->nrec.conn[conn->id].address,
+ session->nrec.conn[conn->id].port,
+ session->nrec.iface.name);
+ } else
+ session->notify_qtask = NULL;
+
+
+ /*
+ * reset ERL=0 reopen counter
+ */
+ session->reopen_cnt = 0;
+ session->r_stage = R_STAGE_NO_CHANGE;
+
+ return;
+
+failed_login:
+ qtask = session->notify_qtask;
+ session->notify_qtask = NULL;
+ mgmt_ipc_write_rsp(qtask, ISCSI_ERR_LOGIN);
+ if (ipc->destroy_conn(session->t->handle, session->id, conn->id))
+ log_error("can not safely destroy connection %d", conn->id);
+ if (ipc->destroy_session(session->t->handle, session->id))
+ log_error("can not safely destroy session %d", session->id);
+ __session_destroy(session);
+
+}
+
static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context,
struct iscsi_conn *conn, unsigned long tmo,
int event)
@@ -1581,6 +1690,11 @@ static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context,
else
actor_schedule(&ev_context->actor);
break;
+ case EV_CONN_LOGIN:
+ actor_new(&ev_context->actor, session_conn_process_login,
+ ev_context);
+ actor_schedule(&ev_context->actor);
+ break;
case EV_CONN_POLL:
actor_new(&ev_context->actor, session_conn_poll,
ev_context);
@@ -1729,7 +1843,7 @@ session_login_task(node_rec_t *rec, queue_task_t *qtask)
"login errors iscsid may give up the initial "
"login early. You should manually login.");
- conn->state = STATE_XPT_WAIT;
+ conn->state = ISCSI_CONN_STATE_XPT_WAIT;
qtask->rsp.command = MGMT_IPC_SESSION_LOGIN;
qtask->rsp.err = ISCSI_SUCCESS;
@@ -1755,7 +1869,7 @@ sync_conn(iscsi_session_t *session, uint32_t cid)
conn = &session->conn[cid];
/* TODO: must export via sysfs so we can pick this up */
- conn->state = STATE_CLEANUP_WAIT;
+ conn->state = ISCSI_CONN_STATE_CLEANUP_WAIT;
return 0;
}
@@ -1787,11 +1901,13 @@ iscsi_sync_session(node_rec_t *rec, queue_task_t *qtask, uint32_t sid)
if (err)
goto destroy_session;
- session->sync_qtask = qtask;
qtask->rsp.command = MGMT_IPC_SESSION_SYNC;
- session_conn_reopen(&session->conn[0], qtask, STOP_CONN_RECOVER);
log_debug(3, "Started sync iSCSI session %d", session->id);
+ session->notify_qtask = qtask;
+ session_conn_reopen(&session->conn[0], qtask,
+ STOP_CONN_RECOVER);
+
return 0;
destroy_session:
@@ -1811,8 +1927,7 @@ static int session_unbind(struct iscsi_session *session)
return err;
}
-int
-session_logout_task(int sid, queue_task_t *qtask)
+int session_logout_task(int sid, queue_task_t *qtask)
{
iscsi_session_t *session;
iscsi_conn_t *conn;
@@ -1828,9 +1943,9 @@ session_logout_task(int sid, queue_task_t *qtask)
* If syncing up or if this is the initial login and mgmt_ipc
* has not been notified of that result fail the logout request
*/
- if (session->sync_qtask ||
- ((conn->state == STATE_XPT_WAIT ||
- conn->state == STATE_IN_LOGIN) &&
+ if (session->notify_qtask ||
+ ((conn->state == ISCSI_CONN_STATE_XPT_WAIT ||
+ conn->state == ISCSI_CONN_STATE_IN_LOGIN) &&
(session->r_stage == R_STAGE_NO_CHANGE ||
session->r_stage == R_STAGE_SESSION_REDIRECT))) {
invalid_state:
@@ -1841,7 +1956,6 @@ invalid_state:
/* FIXME: logout all active connections */
conn = &session->conn[0];
- /* FIXME: implement Logout Request */
if (conn->logout_qtask)
goto invalid_state;
@@ -1850,13 +1964,17 @@ invalid_state:
conn->logout_qtask = qtask;
switch (conn->state) {
- case STATE_LOGGED_IN:
+ case ISCSI_CONN_STATE_LOGGED_IN:
if (!session_unbind(session))
return ISCSI_SUCCESS;
- /* unbind is not supported so just do old logout */
- if (!iscsi_send_logout(conn))
- return ISCSI_SUCCESS;
+ /* LLDs that offload login also offload logout */
+ if (!(session->t->caps & CAP_LOGIN_OFFLOAD)) {
+ /* unbind is not supported so just do old logout */
+ if (!iscsi_send_logout(conn))
+ return ISCSI_SUCCESS;
+ }
+
log_error("Could not send logout pdu. Dropping session\n");
/* fallthrough */
default:
diff --git a/usr/initiator.h b/usr/initiator.h
index 8497c70..b45caab 100644
--- a/usr/initiator.h
+++ b/usr/initiator.h
@@ -45,21 +45,12 @@
#define LOCK_FILE LOCK_DIR"/lock"
#define LOCK_WRITE_FILE LOCK_DIR"/lock.write"
-typedef enum iscsi_conn_state_e {
- STATE_FREE,
- STATE_XPT_WAIT,
- STATE_IN_LOGIN,
- STATE_LOGGED_IN,
- STATE_IN_LOGOUT,
- STATE_LOGOUT_REQUESTED,
- STATE_CLEANUP_WAIT,
-} iscsi_conn_state_e;
-
typedef enum iscsi_session_r_stage_e {
R_STAGE_NO_CHANGE,
R_STAGE_SESSION_CLEANUP,
R_STAGE_SESSION_REOPEN,
R_STAGE_SESSION_REDIRECT,
+ R_STAGE_SESSION_DESTOYED,
} iscsi_session_r_stage_e;
typedef enum conn_login_status_e {
@@ -91,6 +82,7 @@ typedef enum iscsi_event_e {
EV_CONN_ERROR,
EV_CONN_LOGOUT_TIMER,
EV_CONN_STOP,
+ EV_CONN_LOGIN,
} iscsi_event_e;
struct queue_task;
@@ -126,7 +118,7 @@ typedef struct iscsi_conn {
struct queue_task *logout_qtask;
char data[ISCSI_DEF_MAX_RECV_SEG_LEN];
char host[NI_MAXHOST]; /* scratch */
- iscsi_conn_state_e state;
+ enum iscsi_conn_state state;
int userspace_nop;
struct timeval initial_connect_time;
@@ -264,8 +256,11 @@ typedef struct iscsi_session {
int lu_reset_timeout;
int abort_timeout;
- /* sync up fields */
- queue_task_t *sync_qtask;
+ /*
+ * used for hw and sync up to notify caller that the operation
+ * is complete
+ */
+ queue_task_t *notify_qtask;
} iscsi_session_t;
/* login.c */
diff --git a/usr/initiator_common.c b/usr/initiator_common.c
index 8e4e519..fa8846d 100644
--- a/usr/initiator_common.c
+++ b/usr/initiator_common.c
@@ -510,6 +510,12 @@ int iscsi_session_set_params(struct iscsi_conn *conn)
session->param_mask &= ~ISCSI_OFMARKER_EN;
}
+ /* some llds will send nops internally */
+ if (!iscsi_sysfs_session_supports_nop(session->id)) {
+ session->param_mask &= ~ISCSI_PING_TMO;
+ session->param_mask &= ~ISCSI_RECV_TMO;
+ }
+
/* Entered full-feature phase! */
for (i = 0; i < MAX_SESSION_PARAMS; i++) {
if (conn->id != 0 && !conntbl[i].conn_only)
diff --git a/usr/io.c b/usr/io.c
index 8d37d43..4a1c145 100644
--- a/usr/io.c
+++ b/usr/io.c
@@ -363,10 +363,8 @@ iscsi_io_tcp_connect(iscsi_conn_t *conn, int non_blocking)
return -1;
}
- if (conn->session) {
- if (bind_conn_to_iface(conn, &conn->session->nrec.iface))
- return -1;
- }
+ if (bind_conn_to_iface(conn, &conn->session->nrec.iface))
+ return -1;
onearg = 1;
rc = setsockopt(conn->socket_fd, IPPROTO_TCP, TCP_NODELAY, &onearg,
diff --git a/usr/iscsi_ipc.h b/usr/iscsi_ipc.h
index 8df3cdc..fc67c4a 100644
--- a/usr/iscsi_ipc.h
+++ b/usr/iscsi_ipc.h
@@ -132,6 +132,8 @@ struct iscsi_ipc {
int (*set_net_config) (uint64_t transport_handle, uint32_t host_no,
struct iovec *iovs, uint32_t param_count);
+
+ int (*recv_conn_state) (struct iscsi_conn *conn, uint32_t *state);
};
#endif /* ISCSI_IPC_H */
diff --git a/usr/iscsi_sysfs.c b/usr/iscsi_sysfs.c
index 373507d..d854e22 100644
--- a/usr/iscsi_sysfs.c
+++ b/usr/iscsi_sysfs.c
@@ -149,7 +149,6 @@ static int read_transports(void)
*/
if (!strcmp(t->name, "qla4xxx")) {
t->caps |= CAP_DATA_PATH_OFFLOAD;
- t->caps |= CAP_FW_DB;
}
if (list_empty(&t->list))
@@ -1192,6 +1191,19 @@ int iscsi_sysfs_get_exp_statsn(int sid)
return exp_statsn;
}
+int iscsi_sysfs_session_supports_nop(int sid)
+{
+ char id[NAME_SIZE];
+ uint32_t ping_tmo = 0;
+
+ snprintf(id, sizeof(id), ISCSI_CONN_ID, sid);
+ if (sysfs_get_uint(id, ISCSI_CONN_SUBSYS, "ping_tmo",
+ &ping_tmo)) {
+ return 0;
+ }
+ return 1;
+}
+
int iscsi_sysfs_for_each_device(void *data, int host_no, uint32_t sid,
void (* fn)(void *data, int host_no,
int target, int lun))
diff --git a/usr/iscsi_sysfs.h b/usr/iscsi_sysfs.h
index 6b5bed1..80c1b8b 100644
--- a/usr/iscsi_sysfs.h
+++ b/usr/iscsi_sysfs.h
@@ -89,6 +89,7 @@ extern struct iscsi_transport *iscsi_sysfs_get_transport_by_hba(uint32_t host_no
extern struct iscsi_transport *iscsi_sysfs_get_transport_by_session(char *sys_session);
extern struct iscsi_transport *iscsi_sysfs_get_transport_by_sid(uint32_t sid);
extern struct iscsi_transport *iscsi_sysfs_get_transport_by_name(char *transport_name);
+extern int iscsi_sysfs_session_supports_nop(int sid);
extern struct list_head transports;
diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c
index efc9e16..03b779e 100644
--- a/usr/iscsiadm.c
+++ b/usr/iscsiadm.c
@@ -566,15 +566,6 @@ static int iscsi_logout_matched_portal(void *data, struct list_head *list,
if (!iscsi_match_session(pattern_rec, info))
return -1;
- /* we do not support this yet */
- if (t->caps & CAP_FW_DB) {
- log_error("Could not logout session of [sid: %d, "
- "target: %s, portal: %s,%d].", info->sid,
- info->targetname, info->persistent_address,
- info->port);
- log_error("Logout not supported for driver: %s.", t->name);
- return -1;
- }
return iscsi_logout_portal(info, list);
}
@@ -1102,7 +1093,6 @@ do_sendtargets(discovery_rec_t *drec, struct list_head *ifaces,
free(iface);
continue;
}
-
host_no = iscsi_sysfs_get_host_no_from_hwinfo(iface, &rc);
if (rc || host_no == -1) {
log_debug(1, "Could not match iface" iface_fmt " to "
diff --git a/usr/netlink.c b/usr/netlink.c
index 8fc61e1..801ee6f 100644
--- a/usr/netlink.c
+++ b/usr/netlink.c
@@ -988,6 +988,28 @@ kset_net_config(uint64_t transport_handle, uint32_t host_no,
return 0;
}
+static int krecv_conn_state(struct iscsi_conn *conn, int *state)
+{
+ int rc;
+
+ rc = ipc->ctldev_handle();
+ if (rc == -ENXIO) {
+ /* event for some other conn */
+ rc = -EAGAIN;
+ goto exit;
+ } else if (rc < 0)
+ /* fatal handling error or conn error */
+ goto exit;
+
+ *state = *(enum iscsi_conn_state *)conn->recv_context->data;
+
+ ipc_ev_clbk->put_ev_context(conn->recv_context);
+ conn->recv_context = NULL;
+
+exit:
+ return rc;
+}
+
static void drop_data(struct nlmsghdr *nlh)
{
int ev_size;
@@ -1005,7 +1027,7 @@ static int ctldev_handle(void)
char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))];
struct nlmsghdr *nlh;
struct iscsi_ev_context *ev_context;
- uint32_t sid = 0, cid = 0;
+ uint32_t sid = 0, cid = 0, state = 0;
log_debug(7, "in %s", __FUNCTION__);
@@ -1042,6 +1064,11 @@ static int ctldev_handle(void)
sid = ev->r.connerror.sid;
cid = ev->r.connerror.cid;
break;
+ case ISCSI_KEVENT_CONN_LOGIN_STATE:
+ sid = ev->r.conn_login.sid;
+ cid = ev->r.conn_login.cid;
+ state = ev->r.conn_login.state;
+ break;
case ISCSI_KEVENT_UNBIND_SESSION:
sid = ev->r.unbind_session.sid;
/* session wide event so cid is 0 */
@@ -1113,6 +1140,12 @@ static int ctldev_handle(void)
rc = ipc_ev_clbk->sched_ev_context(ev_context, conn, 0,
EV_CONN_ERROR);
break;
+ case ISCSI_KEVENT_CONN_LOGIN_STATE:
+ memcpy(ev_context->data, &ev->r.conn_login.state,
+ sizeof(ev->r.conn_login.state));
+ rc = ipc_ev_clbk->sched_ev_context(ev_context, conn, 0,
+ EV_CONN_LOGIN);
+ break;
case ISCSI_KEVENT_UNBIND_SESSION:
rc = ipc_ev_clbk->sched_ev_context(ev_context, conn, 0,
EV_CONN_STOP);
@@ -1233,6 +1266,7 @@ struct iscsi_ipc nl_ipc = {
.recv_pdu_begin = krecv_pdu_begin,
.recv_pdu_end = krecv_pdu_end,
.set_net_config = kset_net_config,
+ .recv_conn_state = krecv_conn_state,
};
struct iscsi_ipc *ipc = &nl_ipc;
diff --git a/usr/session_info.c b/usr/session_info.c
index 13f79c0..d0a7f82 100644
--- a/usr/session_info.c
+++ b/usr/session_info.c
@@ -114,7 +114,7 @@ static int print_iscsi_state(int sid, char *prefix)
* anything here since it does not know about it.
*/
if (!err && rsp.u.session_state.conn_state >= 0 &&
- rsp.u.session_state.conn_state <= STATE_CLEANUP_WAIT)
+ rsp.u.session_state.conn_state <= ISCSI_CONN_STATE_CLEANUP_WAIT)
state = conn_state[rsp.u.session_state.conn_state];
printf("%s\t\tiSCSI Connection State: %s\n", prefix,
state ? state : "Unknown");
diff --git a/usr/session_mgmt.c b/usr/session_mgmt.c
index 914471c..ec1f43a 100644
--- a/usr/session_mgmt.c
+++ b/usr/session_mgmt.c
@@ -216,7 +216,6 @@ int iscsi_login_portal_nowait(struct node_rec *rec)
int err;
INIT_LIST_HEAD(&list);
-
err = iscsi_login_portal(NULL, &list, rec);
if (err > 0)
return err;
diff --git a/usr/transport.c b/usr/transport.c
index 7a0cde1..5d6bea4 100644
--- a/usr/transport.c
+++ b/usr/transport.c
@@ -79,6 +79,10 @@ struct iscsi_transport_template be2iscsi = {
struct iscsi_transport_template qla4xxx = {
.name = "qla4xxx",
+ .set_host_ip = 0,
+ .ep_connect = ktransport_ep_connect,
+ .ep_poll = ktransport_ep_poll,
+ .ep_disconnect = ktransport_ep_disconnect,
};
static struct iscsi_transport_template *iscsi_transport_templates[] = {