From fe5611b79b0c2661b8b5f0227e0987634cc86036 Mon Sep 17 00:00:00 2001 From: Shuhei Matsumoto Date: Tue, 8 Oct 2019 09:30:42 +0900 Subject: [PATCH] lib/iscsi: Request logout to asynchronously to initiator Request logout to the initiator asynchronously when exiting the running connection from the target. Based on this patch, the next patch will make flushing PDUs possible only when the connection is RUNNING (or INVALID) and remove the tight loop in iscsi_conn_flush_pdus(). Set timeout of logout request as 30 seconds. 30 seconds is as same as NOP timeout and will be reasonable for now. Add and use logout_request_timer to check the timeout. When the connection gets internal a logout request, move the connection to EXITING state if the connection is in INVALID state, request logout to initiator and start 30 seconds timer if the connection is in RUNNING state and logout is not requested yet, or do nothing otherwise. Signed-off-by: Shuhei Matsumoto Change-Id: I43192be9fd7112ad444152c0dd88f99a14aa8d30 Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/470705 Tested-by: SPDK CI Jenkins Reviewed-by: Jim Harris Reviewed-by: Changpeng Liu --- lib/iscsi/conn.c | 68 +++++++++++++++++++++++++++++++----- lib/iscsi/conn.h | 7 +++- lib/iscsi/iscsi.h | 5 +++ lib/iscsi/tgt_node.c | 2 +- test/unit/lib/iscsi/common.c | 2 +- 5 files changed, 73 insertions(+), 11 deletions(-) diff --git a/lib/iscsi/conn.c b/lib/iscsi/conn.c index 269d2bacf..eb0fece5b 100644 --- a/lib/iscsi/conn.c +++ b/lib/iscsi/conn.c @@ -287,6 +287,7 @@ spdk_iscsi_conn_construct(struct spdk_iscsi_portal *portal, SPDK_ERRLOG("iscsi_conn_params_init() failed\n"); goto error_return; } + conn->logout_request_timer = NULL; conn->logout_timer = NULL; conn->shutdown_timer = NULL; SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Launching connection on acceptor thread\n"); @@ -639,6 +640,7 @@ _iscsi_conn_destruct(struct spdk_iscsi_conn *conn) iscsi_poll_group_remove_conn(conn->pg, conn); spdk_sock_close(&conn->sock); + spdk_poller_unregister(&conn->logout_request_timer); spdk_poller_unregister(&conn->logout_timer); rc = iscsi_conn_free_tasks(conn); @@ -731,8 +733,63 @@ iscsi_conn_check_shutdown(void *arg) return 1; } +static void +iscsi_send_logout_request(struct spdk_iscsi_conn *conn) +{ + struct spdk_iscsi_pdu *rsp_pdu; + struct iscsi_bhs_async *rsph; + + rsp_pdu = spdk_get_pdu(); + assert(rsp_pdu != NULL); + + rsph = (struct iscsi_bhs_async *)&rsp_pdu->bhs; + rsp_pdu->data = NULL; + + rsph->opcode = ISCSI_OP_ASYNC; + to_be32(&rsph->ffffffff, 0xFFFFFFFF); + rsph->async_event = 1; + to_be16(&rsph->param3, ISCSI_LOGOUT_REQUEST_TIMEOUT); + + to_be32(&rsph->stat_sn, conn->StatSN); + to_be32(&rsph->exp_cmd_sn, conn->sess->ExpCmdSN); + to_be32(&rsph->max_cmd_sn, conn->sess->MaxCmdSN); + + spdk_iscsi_conn_write_pdu(conn, rsp_pdu); +} + +static int +logout_request_timeout(void *arg) +{ + struct spdk_iscsi_conn *conn = arg; + + if (conn->state < ISCSI_CONN_STATE_EXITING) { + conn->state = ISCSI_CONN_STATE_EXITING; + } + + return -1; +} + +static void +iscsi_conn_request_logout(struct spdk_iscsi_conn *conn) +{ + if (conn->state == ISCSI_CONN_STATE_INVALID) { + /* Move it to EXITING state if the connection is in login. */ + conn->state = ISCSI_CONN_STATE_EXITING; + } else if (conn->state == ISCSI_CONN_STATE_RUNNING && + conn->logout_request_timer == NULL) { + /* If the connection is running and logout is not requested yet, + * request logout to initiator and wait for the logout process + * to start. + */ + iscsi_send_logout_request(conn); + + conn->logout_request_timer = spdk_poller_register(logout_request_timeout, + conn, ISCSI_LOGOUT_REQUEST_TIMEOUT * 1000000); + } +} + void -spdk_iscsi_conns_start_exit(struct spdk_iscsi_tgt_node *target) +spdk_iscsi_conns_request_logout(struct spdk_iscsi_tgt_node *target) { struct spdk_iscsi_conn *conn; int i; @@ -749,12 +806,7 @@ spdk_iscsi_conns_start_exit(struct spdk_iscsi_tgt_node *target) continue; } - /* Do not set conn->state if the connection has already started exiting. - * This ensures we do not move a connection from EXITED state back to EXITING. - */ - if (conn->state < ISCSI_CONN_STATE_EXITING) { - conn->state = ISCSI_CONN_STATE_EXITING; - } + iscsi_conn_request_logout(conn); } pthread_mutex_unlock(&g_conns_mutex); @@ -763,7 +815,7 @@ spdk_iscsi_conns_start_exit(struct spdk_iscsi_tgt_node *target) void spdk_shutdown_iscsi_conns(void) { - spdk_iscsi_conns_start_exit(NULL); + spdk_iscsi_conns_request_logout(NULL); g_shutdown_timer = spdk_poller_register(iscsi_conn_check_shutdown, NULL, 1000); } diff --git a/lib/iscsi/conn.h b/lib/iscsi/conn.h index e0983a04d..2b4b23791 100644 --- a/lib/iscsi/conn.h +++ b/lib/iscsi/conn.h @@ -94,6 +94,11 @@ struct spdk_iscsi_conn { uint64_t last_fill; uint64_t last_nopin; + /* Timer used to destroy connection after requesting logout if + * initiator does not send logout request. + */ + struct spdk_poller *logout_request_timer; + /* Timer used to destroy connection after logout if initiator does * not close the connection. */ @@ -174,7 +179,7 @@ extern struct spdk_iscsi_conn *g_conns_array; int spdk_initialize_iscsi_conns(void); void spdk_shutdown_iscsi_conns(void); -void spdk_iscsi_conns_start_exit(struct spdk_iscsi_tgt_node *target); +void spdk_iscsi_conns_request_logout(struct spdk_iscsi_tgt_node *target); int spdk_iscsi_get_active_conns(struct spdk_iscsi_tgt_node *target); int spdk_iscsi_conn_construct(struct spdk_iscsi_portal *portal, struct spdk_sock *sock); diff --git a/lib/iscsi/iscsi.h b/lib/iscsi/iscsi.h index e5fe3ecfd..b48601061 100644 --- a/lib/iscsi/iscsi.h +++ b/lib/iscsi/iscsi.h @@ -122,6 +122,11 @@ */ #define DEFAULT_MAX_QUEUE_DEPTH 64 +/** Defines how long we should wait for a logout request when the target + * requests logout to the initiator asynchronously. + */ +#define ISCSI_LOGOUT_REQUEST_TIMEOUT 30 /* in seconds */ + /** Defines how long we should wait for a TCP close after responding to a * logout request, before terminating the connection ourselves. */ diff --git a/lib/iscsi/tgt_node.c b/lib/iscsi/tgt_node.c index 20998602e..4e8d5578c 100644 --- a/lib/iscsi/tgt_node.c +++ b/lib/iscsi/tgt_node.c @@ -689,7 +689,7 @@ iscsi_tgt_node_destruct(struct spdk_iscsi_tgt_node *target, target->destruct_cb_fn = cb_fn; target->destruct_cb_arg = cb_arg; - spdk_iscsi_conns_start_exit(target); + spdk_iscsi_conns_request_logout(target); if (spdk_iscsi_get_active_conns(target) != 0) { target->destruct_poller = spdk_poller_register(iscsi_tgt_node_check_active_conns, diff --git a/test/unit/lib/iscsi/common.c b/test/unit/lib/iscsi/common.c index 62eb8ed46..496e78da2 100644 --- a/test/unit/lib/iscsi/common.c +++ b/test/unit/lib/iscsi/common.c @@ -119,7 +119,7 @@ DEFINE_STUB(spdk_scsi_dev_delete_port, int, DEFINE_STUB_V(spdk_shutdown_iscsi_conns, (void)); -DEFINE_STUB_V(spdk_iscsi_conns_start_exit, (struct spdk_iscsi_tgt_node *target)); +DEFINE_STUB_V(spdk_iscsi_conns_request_logout, (struct spdk_iscsi_tgt_node *target)); DEFINE_STUB(spdk_iscsi_get_active_conns, int, (struct spdk_iscsi_tgt_node *target), 0);