Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit defd8848 authored by Nicholas Bellinger's avatar Nicholas Bellinger
Browse files

iscsi/iser-target: Fix isert_conn->state hung shutdown issues



This patch addresses a couple of different hug shutdown issues
related to wait_event() + isert_conn->state.  First, it changes
isert_conn->conn_wait + isert_conn->conn_wait_comp_err from
waitqueues to completions, and sets ISER_CONN_TERMINATING from
within isert_disconnect_work().

Second, it splits isert_free_conn() into isert_wait_conn() that
is called earlier in iscsit_close_connection() to ensure that
all outstanding commands have completed before continuing.

Finally, it breaks isert_cq_comp_err() into seperate TX / RX
related code, and adds logic in isert_cq_rx_comp_err() to wait
for outstanding commands to complete before setting ISER_CONN_DOWN
and calling complete(&isert_conn->conn_wait_comp_err).

Acked-by: default avatarSagi Grimberg <sagig@mellanox.com>
Cc: Or Gerlitz <ogerlitz@mellanox.com>
Cc: <stable@vger.kernel.org> #3.10+
Signed-off-by: default avatarNicholas Bellinger <nab@linux-iscsi.org>
parent 5159d763
Loading
Loading
Loading
Loading
+48 −58
Original line number Diff line number Diff line
@@ -492,8 +492,8 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
	isert_conn->state = ISER_CONN_INIT;
	INIT_LIST_HEAD(&isert_conn->conn_accept_node);
	init_completion(&isert_conn->conn_login_comp);
	init_waitqueue_head(&isert_conn->conn_wait);
	init_waitqueue_head(&isert_conn->conn_wait_comp_err);
	init_completion(&isert_conn->conn_wait);
	init_completion(&isert_conn->conn_wait_comp_err);
	kref_init(&isert_conn->conn_kref);
	kref_get(&isert_conn->conn_kref);
	mutex_init(&isert_conn->conn_mutex);
@@ -688,11 +688,11 @@ isert_disconnect_work(struct work_struct *work)

	pr_debug("isert_disconnect_work(): >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
	mutex_lock(&isert_conn->conn_mutex);
	isert_conn->state = ISER_CONN_DOWN;
	if (isert_conn->state == ISER_CONN_UP)
		isert_conn->state = ISER_CONN_TERMINATING;

	if (isert_conn->post_recv_buf_count == 0 &&
	    atomic_read(&isert_conn->post_send_buf_count) == 0) {
		pr_debug("Calling wake_up(&isert_conn->conn_wait);\n");
		mutex_unlock(&isert_conn->conn_mutex);
		goto wake_up;
	}
@@ -712,7 +712,7 @@ isert_disconnect_work(struct work_struct *work)
	mutex_unlock(&isert_conn->conn_mutex);

wake_up:
	wake_up(&isert_conn->conn_wait);
	complete(&isert_conn->conn_wait);
	isert_put_conn(isert_conn);
}

@@ -1589,7 +1589,7 @@ isert_do_control_comp(struct work_struct *work)
		pr_debug("Calling iscsit_logout_post_handler >>>>>>>>>>>>>>\n");
		/*
		 * Call atomic_dec(&isert_conn->post_send_buf_count)
		 * from isert_free_conn()
		 * from isert_wait_conn()
		 */
		isert_conn->logout_posted = true;
		iscsit_logout_post_handler(cmd, cmd->conn);
@@ -1691,11 +1691,9 @@ isert_send_completion(struct iser_tx_desc *tx_desc,
}

static void
isert_cq_comp_err(struct iser_tx_desc *tx_desc, struct isert_conn *isert_conn)
isert_cq_tx_comp_err(struct iser_tx_desc *tx_desc, struct isert_conn *isert_conn)
{
	struct ib_device *ib_dev = isert_conn->conn_cm_id->device;

	if (tx_desc) {
	struct isert_cmd *isert_cmd = tx_desc->isert_cmd;

	if (!isert_cmd)
@@ -1704,18 +1702,28 @@ isert_cq_comp_err(struct iser_tx_desc *tx_desc, struct isert_conn *isert_conn)
		isert_completion_put(tx_desc, isert_cmd, ib_dev);
}

	if (isert_conn->post_recv_buf_count == 0 &&
	    atomic_read(&isert_conn->post_send_buf_count) == 0) {
		pr_debug("isert_cq_comp_err >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
		pr_debug("Calling wake_up from isert_cq_comp_err\n");
static void
isert_cq_rx_comp_err(struct isert_conn *isert_conn)
{
	struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
	struct iscsi_conn *conn = isert_conn->conn;

	if (isert_conn->post_recv_buf_count)
		return;

	if (conn->sess) {
		target_sess_cmd_list_set_waiting(conn->sess->se_sess);
		target_wait_for_sess_cmds(conn->sess->se_sess);
	}

	while (atomic_read(&isert_conn->post_send_buf_count))
		msleep(3000);

	mutex_lock(&isert_conn->conn_mutex);
		if (isert_conn->state != ISER_CONN_DOWN)
			isert_conn->state = ISER_CONN_TERMINATING;
	isert_conn->state = ISER_CONN_DOWN;
	mutex_unlock(&isert_conn->conn_mutex);

		wake_up(&isert_conn->conn_wait_comp_err);
	}
	complete(&isert_conn->conn_wait_comp_err);
}

static void
@@ -1740,8 +1748,9 @@ isert_cq_tx_work(struct work_struct *work)
			pr_debug("TX wc.status != IB_WC_SUCCESS >>>>>>>>>>>>>>\n");
			pr_debug("TX wc.status: 0x%08x\n", wc.status);
			pr_debug("TX wc.vendor_err: 0x%08x\n", wc.vendor_err);

			atomic_dec(&isert_conn->post_send_buf_count);
			isert_cq_comp_err(tx_desc, isert_conn);
			isert_cq_tx_comp_err(tx_desc, isert_conn);
		}
	}

@@ -1784,7 +1793,7 @@ isert_cq_rx_work(struct work_struct *work)
					 wc.vendor_err);
			}
			isert_conn->post_recv_buf_count--;
			isert_cq_comp_err(NULL, isert_conn);
			isert_cq_rx_comp_err(isert_conn);
		}
	}

@@ -2702,22 +2711,11 @@ isert_free_np(struct iscsi_np *np)
	kfree(isert_np);
}

static int isert_check_state(struct isert_conn *isert_conn, int state)
{
	int ret;

	mutex_lock(&isert_conn->conn_mutex);
	ret = (isert_conn->state == state);
	mutex_unlock(&isert_conn->conn_mutex);

	return ret;
}

static void isert_free_conn(struct iscsi_conn *conn)
static void isert_wait_conn(struct iscsi_conn *conn)
{
	struct isert_conn *isert_conn = conn->context;

	pr_debug("isert_free_conn: Starting \n");
	pr_debug("isert_wait_conn: Starting \n");
	/*
	 * Decrement post_send_buf_count for special case when called
	 * from isert_do_control_comp() -> iscsit_logout_post_handler()
@@ -2727,38 +2725,29 @@ static void isert_free_conn(struct iscsi_conn *conn)
		atomic_dec(&isert_conn->post_send_buf_count);

	if (isert_conn->conn_cm_id && isert_conn->state != ISER_CONN_DOWN) {
		pr_debug("Calling rdma_disconnect from isert_free_conn\n");
		pr_debug("Calling rdma_disconnect from isert_wait_conn\n");
		rdma_disconnect(isert_conn->conn_cm_id);
	}
	/*
	 * Only wait for conn_wait_comp_err if the isert_conn made it
	 * into full feature phase..
	 */
	if (isert_conn->state == ISER_CONN_UP) {
		pr_debug("isert_free_conn: Before wait_event comp_err %d\n",
			 isert_conn->state);
		mutex_unlock(&isert_conn->conn_mutex);

		wait_event(isert_conn->conn_wait_comp_err,
			  (isert_check_state(isert_conn, ISER_CONN_TERMINATING)));

		wait_event(isert_conn->conn_wait,
			  (isert_check_state(isert_conn, ISER_CONN_DOWN)));

		isert_put_conn(isert_conn);
		return;
	}
	if (isert_conn->state == ISER_CONN_INIT) {
		mutex_unlock(&isert_conn->conn_mutex);
		isert_put_conn(isert_conn);
		return;
	}
	pr_debug("isert_free_conn: wait_event conn_wait %d\n",
		 isert_conn->state);
	if (isert_conn->state == ISER_CONN_UP)
		isert_conn->state = ISER_CONN_TERMINATING;
	mutex_unlock(&isert_conn->conn_mutex);

	wait_event(isert_conn->conn_wait,
		  (isert_check_state(isert_conn, ISER_CONN_DOWN)));
	wait_for_completion(&isert_conn->conn_wait_comp_err);

	wait_for_completion(&isert_conn->conn_wait);
}

static void isert_free_conn(struct iscsi_conn *conn)
{
	struct isert_conn *isert_conn = conn->context;

	isert_put_conn(isert_conn);
}
@@ -2771,6 +2760,7 @@ static struct iscsit_transport iser_target_transport = {
	.iscsit_setup_np	= isert_setup_np,
	.iscsit_accept_np	= isert_accept_np,
	.iscsit_free_np		= isert_free_np,
	.iscsit_wait_conn	= isert_wait_conn,
	.iscsit_free_conn	= isert_free_conn,
	.iscsit_get_login_rx	= isert_get_login_rx,
	.iscsit_put_login_tx	= isert_put_login_tx,
+2 −2
Original line number Diff line number Diff line
@@ -116,8 +116,8 @@ struct isert_conn {
	struct isert_device	*conn_device;
	struct work_struct	conn_logout_work;
	struct mutex		conn_mutex;
	wait_queue_head_t	conn_wait;
	wait_queue_head_t	conn_wait_comp_err;
	struct completion	conn_wait;
	struct completion	conn_wait_comp_err;
	struct kref		conn_kref;
	struct list_head	conn_fr_pool;
	int			conn_fr_pool_size;
+4 −0
Original line number Diff line number Diff line
@@ -4196,6 +4196,10 @@ int iscsit_close_connection(
	iscsit_stop_timers_for_cmds(conn);
	iscsit_stop_nopin_response_timer(conn);
	iscsit_stop_nopin_timer(conn);

	if (conn->conn_transport->iscsit_wait_conn)
		conn->conn_transport->iscsit_wait_conn(conn);

	iscsit_free_queue_reqs_for_conn(conn);

	/*
+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ struct iscsit_transport {
	int (*iscsit_setup_np)(struct iscsi_np *, struct __kernel_sockaddr_storage *);
	int (*iscsit_accept_np)(struct iscsi_np *, struct iscsi_conn *);
	void (*iscsit_free_np)(struct iscsi_np *);
	void (*iscsit_wait_conn)(struct iscsi_conn *);
	void (*iscsit_free_conn)(struct iscsi_conn *);
	int (*iscsit_get_login_rx)(struct iscsi_conn *, struct iscsi_login *);
	int (*iscsit_put_login_tx)(struct iscsi_conn *, struct iscsi_login *, u32);