isert: fix a race between timewait exit handler and poll cq

Timewait exit event handler start to close iscsi conn
before poll cq finish to handle all the good completions.
This may lead to NULL deref at poll cq context or post recv
after post drain.
This commit close iscsi conn only when start getting flush.
Flush is guaranteed if iscsi conn was created because when allocating
iscsi conn we call post recv.

Signed-off-by: Israel Rukshin <israelr@mellanox.com>

git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@6945 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Israel Rukshin
2016-08-17 07:27:53 +00:00
parent 1c3de9fa71
commit 807ae45a76
5 changed files with 48 additions and 30 deletions

View File

@@ -86,7 +86,8 @@ int isert_data_in_sent(struct iscsi_cmnd *cmd);
int isert_pdu_sent(struct iscsi_cmnd *pdu);
void isert_pdu_err(struct iscsi_cmnd *pdu);
int isert_connection_closed(struct iscsi_conn *iscsi_conn);
void isert_connection_closed(struct iscsi_conn *iscsi_conn);
void isert_connection_abort(struct iscsi_conn *iscsi_conn);
void *isert_get_priv(struct iscsi_conn *iscsi_conn);
void isert_set_priv(struct iscsi_conn *iscsi_conn, void *priv);

View File

@@ -610,8 +610,7 @@ static void isert_conn_closed_do_work(struct work_struct *work)
#endif
if (!test_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags))
if (!test_and_set_bit(ISERT_DISCON_CALLED, &isert_conn->flags))
isert_connection_closed(&isert_conn->iscsi);
isert_connection_abort(&isert_conn->iscsi);
/* if connection established we have another refcount */
if (test_bit(ISERT_CONNECTION_EST, &isert_conn->flags)) {
@@ -1657,9 +1656,8 @@ static int isert_cm_evt_handler(struct rdma_cm_id *cm_id,
break;
case RDMA_CM_EVENT_DEVICE_REMOVAL:
isert_cm_disconnect_handler(cm_id, cm_ev);
/* fallthrough */
case RDMA_CM_EVENT_TIMEWAIT_EXIT:
isert_cm_disconnect_handler(cm_id, cm_ev);
err = isert_cm_timewait_exit_handler(cm_id, cm_ev);
break;

View File

@@ -288,7 +288,7 @@ static void isert_free_conn(struct iscsi_conn *conn)
isert_free_connection(conn);
}
int isert_handle_close_connection(struct iscsi_conn *conn)
void isert_handle_close_connection(struct iscsi_conn *conn)
{
isert_mark_conn_closed(conn, 0);
/*
@@ -300,7 +300,6 @@ int isert_handle_close_connection(struct iscsi_conn *conn)
isert_free_connection(conn);
else
start_close_conn(conn);
return 0;
}
int isert_pdu_rx(struct iscsi_cmnd *cmnd)

View File

@@ -129,7 +129,7 @@ int isert_conn_alloc(struct iscsi_session *session,
struct iscsi_kern_conn_info *info,
struct iscsi_conn **new_conn,
struct iscsit_transport *t);
int isert_handle_close_connection(struct iscsi_conn *conn);
void isert_handle_close_connection(struct iscsi_conn *conn);
void isert_close_all_portals(void);
void isert_del_timer(struct isert_conn_dev *dev);

View File

@@ -49,6 +49,7 @@
#include "isert_dbg.h"
#include "../iscsi.h"
#include "isert.h"
#include "iser.h"
#include "iser_datamover.h"
static DEFINE_MUTEX(conn_mgmt_mutex);
@@ -444,41 +445,60 @@ int isert_conn_established(struct iscsi_conn *iscsi_conn,
return add_new_connection(&isert_listen_dev, iscsi_conn);
}
int isert_connection_closed(struct iscsi_conn *iscsi_conn)
static void isert_dev_disconnect(struct iscsi_conn* iscsi_conn)
{
int res = 0;
struct isert_conn_dev* dev = isert_get_priv(iscsi_conn);
if (dev) {
isert_del_timer(dev);
dev->state = CS_DISCONNECTED;
if (dev->login_req) {
isert_task_abort(dev->login_req);
spin_lock(&dev->pdu_lock);
dev->login_req = NULL;
spin_unlock(&dev->pdu_lock);
}
wake_up(&dev->waitqueue);
isert_dev_release(dev);
isert_set_priv(iscsi_conn, NULL);
}
}
void isert_connection_closed(struct iscsi_conn *iscsi_conn)
{
TRACE_ENTRY();
mutex_lock(&conn_mgmt_mutex);
if (iscsi_conn->rd_state) {
mutex_unlock(&conn_mgmt_mutex);
res = isert_handle_close_connection(iscsi_conn);
isert_handle_close_connection(iscsi_conn);
} else {
struct isert_conn_dev *dev = isert_get_priv(iscsi_conn);
if (dev) {
isert_del_timer(dev);
dev->state = CS_DISCONNECTED;
if (dev->login_req) {
res = isert_task_abort(dev->login_req);
spin_lock(&dev->pdu_lock);
dev->login_req = NULL;
spin_unlock(&dev->pdu_lock);
}
wake_up(&dev->waitqueue);
isert_dev_release(dev);
isert_set_priv(iscsi_conn, NULL);
}
isert_dev_disconnect(iscsi_conn);
mutex_unlock(&conn_mgmt_mutex);
isert_free_connection(iscsi_conn);
}
TRACE_EXIT_RES(res);
return res;
TRACE_EXIT();
}
void isert_connection_abort(struct iscsi_conn *iscsi_conn)
{
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
TRACE_ENTRY();
mutex_lock(&conn_mgmt_mutex);
if (!iscsi_conn->rd_state) {
if (!test_and_set_bit(ISERT_DISCON_CALLED, &isert_conn->flags)) {
isert_dev_disconnect(iscsi_conn);
isert_free_connection(iscsi_conn);
}
}
mutex_unlock(&conn_mgmt_mutex);
TRACE_EXIT();
}
static bool will_read_block(struct isert_conn_dev *dev)