diff options
-rw-r--r-- | include/iscsi_if.h | 19 | ||||
-rw-r--r-- | usr/discovery.c | 93 | ||||
-rw-r--r-- | usr/initiator.c | 242 | ||||
-rw-r--r-- | usr/initiator.h | 21 | ||||
-rw-r--r-- | usr/initiator_common.c | 6 | ||||
-rw-r--r-- | usr/io.c | 6 | ||||
-rw-r--r-- | usr/iscsi_ipc.h | 2 | ||||
-rw-r--r-- | usr/iscsi_sysfs.c | 14 | ||||
-rw-r--r-- | usr/iscsi_sysfs.h | 1 | ||||
-rw-r--r-- | usr/iscsiadm.c | 10 | ||||
-rw-r--r-- | usr/netlink.c | 36 | ||||
-rw-r--r-- | usr/session_info.c | 2 | ||||
-rw-r--r-- | usr/session_mgmt.c | 1 | ||||
-rw-r--r-- | usr/transport.c | 4 |
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) @@ -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[] = { |