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: Sagi Grimberg <sagig@mellanox.com> Cc: Or Gerlitz <ogerlitz@mellanox.com> Cc: <stable@vger.kernel.org> #3.10+ Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
This commit is contained in:
parent
5159d763f6
commit
defd884845
@ -492,8 +492,8 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
|
|||||||
isert_conn->state = ISER_CONN_INIT;
|
isert_conn->state = ISER_CONN_INIT;
|
||||||
INIT_LIST_HEAD(&isert_conn->conn_accept_node);
|
INIT_LIST_HEAD(&isert_conn->conn_accept_node);
|
||||||
init_completion(&isert_conn->conn_login_comp);
|
init_completion(&isert_conn->conn_login_comp);
|
||||||
init_waitqueue_head(&isert_conn->conn_wait);
|
init_completion(&isert_conn->conn_wait);
|
||||||
init_waitqueue_head(&isert_conn->conn_wait_comp_err);
|
init_completion(&isert_conn->conn_wait_comp_err);
|
||||||
kref_init(&isert_conn->conn_kref);
|
kref_init(&isert_conn->conn_kref);
|
||||||
kref_get(&isert_conn->conn_kref);
|
kref_get(&isert_conn->conn_kref);
|
||||||
mutex_init(&isert_conn->conn_mutex);
|
mutex_init(&isert_conn->conn_mutex);
|
||||||
@ -688,11 +688,11 @@ isert_disconnect_work(struct work_struct *work)
|
|||||||
|
|
||||||
pr_debug("isert_disconnect_work(): >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
|
pr_debug("isert_disconnect_work(): >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
|
||||||
mutex_lock(&isert_conn->conn_mutex);
|
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 &&
|
if (isert_conn->post_recv_buf_count == 0 &&
|
||||||
atomic_read(&isert_conn->post_send_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);
|
mutex_unlock(&isert_conn->conn_mutex);
|
||||||
goto wake_up;
|
goto wake_up;
|
||||||
}
|
}
|
||||||
@ -712,7 +712,7 @@ isert_disconnect_work(struct work_struct *work)
|
|||||||
mutex_unlock(&isert_conn->conn_mutex);
|
mutex_unlock(&isert_conn->conn_mutex);
|
||||||
|
|
||||||
wake_up:
|
wake_up:
|
||||||
wake_up(&isert_conn->conn_wait);
|
complete(&isert_conn->conn_wait);
|
||||||
isert_put_conn(isert_conn);
|
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");
|
pr_debug("Calling iscsit_logout_post_handler >>>>>>>>>>>>>>\n");
|
||||||
/*
|
/*
|
||||||
* Call atomic_dec(&isert_conn->post_send_buf_count)
|
* Call atomic_dec(&isert_conn->post_send_buf_count)
|
||||||
* from isert_free_conn()
|
* from isert_wait_conn()
|
||||||
*/
|
*/
|
||||||
isert_conn->logout_posted = true;
|
isert_conn->logout_posted = true;
|
||||||
iscsit_logout_post_handler(cmd, cmd->conn);
|
iscsit_logout_post_handler(cmd, cmd->conn);
|
||||||
@ -1691,31 +1691,39 @@ isert_send_completion(struct iser_tx_desc *tx_desc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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;
|
struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
|
||||||
|
struct isert_cmd *isert_cmd = tx_desc->isert_cmd;
|
||||||
|
|
||||||
if (tx_desc) {
|
if (!isert_cmd)
|
||||||
struct isert_cmd *isert_cmd = tx_desc->isert_cmd;
|
isert_unmap_tx_desc(tx_desc, ib_dev);
|
||||||
|
else
|
||||||
|
isert_completion_put(tx_desc, isert_cmd, ib_dev);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isert_cmd)
|
static void
|
||||||
isert_unmap_tx_desc(tx_desc, ib_dev);
|
isert_cq_rx_comp_err(struct isert_conn *isert_conn)
|
||||||
else
|
{
|
||||||
isert_completion_put(tx_desc, isert_cmd, ib_dev);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isert_conn->post_recv_buf_count == 0 &&
|
while (atomic_read(&isert_conn->post_send_buf_count))
|
||||||
atomic_read(&isert_conn->post_send_buf_count) == 0) {
|
msleep(3000);
|
||||||
pr_debug("isert_cq_comp_err >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
|
|
||||||
pr_debug("Calling wake_up from isert_cq_comp_err\n");
|
|
||||||
|
|
||||||
mutex_lock(&isert_conn->conn_mutex);
|
mutex_lock(&isert_conn->conn_mutex);
|
||||||
if (isert_conn->state != ISER_CONN_DOWN)
|
isert_conn->state = ISER_CONN_DOWN;
|
||||||
isert_conn->state = ISER_CONN_TERMINATING;
|
mutex_unlock(&isert_conn->conn_mutex);
|
||||||
mutex_unlock(&isert_conn->conn_mutex);
|
|
||||||
|
|
||||||
wake_up(&isert_conn->conn_wait_comp_err);
|
complete(&isert_conn->conn_wait_comp_err);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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 != IB_WC_SUCCESS >>>>>>>>>>>>>>\n");
|
||||||
pr_debug("TX wc.status: 0x%08x\n", wc.status);
|
pr_debug("TX wc.status: 0x%08x\n", wc.status);
|
||||||
pr_debug("TX wc.vendor_err: 0x%08x\n", wc.vendor_err);
|
pr_debug("TX wc.vendor_err: 0x%08x\n", wc.vendor_err);
|
||||||
|
|
||||||
atomic_dec(&isert_conn->post_send_buf_count);
|
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);
|
wc.vendor_err);
|
||||||
}
|
}
|
||||||
isert_conn->post_recv_buf_count--;
|
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);
|
kfree(isert_np);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int isert_check_state(struct isert_conn *isert_conn, int state)
|
static void isert_wait_conn(struct iscsi_conn *conn)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
struct isert_conn *isert_conn = conn->context;
|
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
|
* Decrement post_send_buf_count for special case when called
|
||||||
* from isert_do_control_comp() -> iscsit_logout_post_handler()
|
* 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);
|
atomic_dec(&isert_conn->post_send_buf_count);
|
||||||
|
|
||||||
if (isert_conn->conn_cm_id && isert_conn->state != ISER_CONN_DOWN) {
|
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);
|
rdma_disconnect(isert_conn->conn_cm_id);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Only wait for conn_wait_comp_err if the isert_conn made it
|
* Only wait for conn_wait_comp_err if the isert_conn made it
|
||||||
* into full feature phase..
|
* 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) {
|
if (isert_conn->state == ISER_CONN_INIT) {
|
||||||
mutex_unlock(&isert_conn->conn_mutex);
|
mutex_unlock(&isert_conn->conn_mutex);
|
||||||
isert_put_conn(isert_conn);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pr_debug("isert_free_conn: wait_event conn_wait %d\n",
|
if (isert_conn->state == ISER_CONN_UP)
|
||||||
isert_conn->state);
|
isert_conn->state = ISER_CONN_TERMINATING;
|
||||||
mutex_unlock(&isert_conn->conn_mutex);
|
mutex_unlock(&isert_conn->conn_mutex);
|
||||||
|
|
||||||
wait_event(isert_conn->conn_wait,
|
wait_for_completion(&isert_conn->conn_wait_comp_err);
|
||||||
(isert_check_state(isert_conn, ISER_CONN_DOWN)));
|
|
||||||
|
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);
|
isert_put_conn(isert_conn);
|
||||||
}
|
}
|
||||||
@ -2771,6 +2760,7 @@ static struct iscsit_transport iser_target_transport = {
|
|||||||
.iscsit_setup_np = isert_setup_np,
|
.iscsit_setup_np = isert_setup_np,
|
||||||
.iscsit_accept_np = isert_accept_np,
|
.iscsit_accept_np = isert_accept_np,
|
||||||
.iscsit_free_np = isert_free_np,
|
.iscsit_free_np = isert_free_np,
|
||||||
|
.iscsit_wait_conn = isert_wait_conn,
|
||||||
.iscsit_free_conn = isert_free_conn,
|
.iscsit_free_conn = isert_free_conn,
|
||||||
.iscsit_get_login_rx = isert_get_login_rx,
|
.iscsit_get_login_rx = isert_get_login_rx,
|
||||||
.iscsit_put_login_tx = isert_put_login_tx,
|
.iscsit_put_login_tx = isert_put_login_tx,
|
||||||
|
@ -116,8 +116,8 @@ struct isert_conn {
|
|||||||
struct isert_device *conn_device;
|
struct isert_device *conn_device;
|
||||||
struct work_struct conn_logout_work;
|
struct work_struct conn_logout_work;
|
||||||
struct mutex conn_mutex;
|
struct mutex conn_mutex;
|
||||||
wait_queue_head_t conn_wait;
|
struct completion conn_wait;
|
||||||
wait_queue_head_t conn_wait_comp_err;
|
struct completion conn_wait_comp_err;
|
||||||
struct kref conn_kref;
|
struct kref conn_kref;
|
||||||
struct list_head conn_fr_pool;
|
struct list_head conn_fr_pool;
|
||||||
int conn_fr_pool_size;
|
int conn_fr_pool_size;
|
||||||
|
@ -4196,6 +4196,10 @@ int iscsit_close_connection(
|
|||||||
iscsit_stop_timers_for_cmds(conn);
|
iscsit_stop_timers_for_cmds(conn);
|
||||||
iscsit_stop_nopin_response_timer(conn);
|
iscsit_stop_nopin_response_timer(conn);
|
||||||
iscsit_stop_nopin_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);
|
iscsit_free_queue_reqs_for_conn(conn);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -12,6 +12,7 @@ struct iscsit_transport {
|
|||||||
int (*iscsit_setup_np)(struct iscsi_np *, struct __kernel_sockaddr_storage *);
|
int (*iscsit_setup_np)(struct iscsi_np *, struct __kernel_sockaddr_storage *);
|
||||||
int (*iscsit_accept_np)(struct iscsi_np *, struct iscsi_conn *);
|
int (*iscsit_accept_np)(struct iscsi_np *, struct iscsi_conn *);
|
||||||
void (*iscsit_free_np)(struct iscsi_np *);
|
void (*iscsit_free_np)(struct iscsi_np *);
|
||||||
|
void (*iscsit_wait_conn)(struct iscsi_conn *);
|
||||||
void (*iscsit_free_conn)(struct iscsi_conn *);
|
void (*iscsit_free_conn)(struct iscsi_conn *);
|
||||||
int (*iscsit_get_login_rx)(struct iscsi_conn *, struct iscsi_login *);
|
int (*iscsit_get_login_rx)(struct iscsi_conn *, struct iscsi_login *);
|
||||||
int (*iscsit_put_login_tx)(struct iscsi_conn *, struct iscsi_login *, u32);
|
int (*iscsit_put_login_tx)(struct iscsi_conn *, struct iscsi_login *, u32);
|
||||||
|
Loading…
Reference in New Issue
Block a user