@@ -899,6 +899,8 @@ iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
iser_info("ep %p iser conn %p\n", ep, iser_conn);
+ iscsi_ep_prep_disconnect(iser_conn->iscsi_conn);
+
mutex_lock(&iser_conn->state_mutex);
iser_conn_terminate(iser_conn);
@@ -1314,7 +1314,7 @@ void beiscsi_ep_disconnect(struct iscsi_endpoint *ep)
if (beiscsi_ep->conn) {
beiscsi_conn = beiscsi_ep->conn;
- iscsi_suspend_queue(beiscsi_conn->conn);
+ iscsi_ep_prep_disconnect(beiscsi_conn->conn);
}
if (!beiscsi_hba_is_online(phba)) {
@@ -2129,7 +2129,7 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep)
if (bnx2i_ep->conn) {
bnx2i_conn = bnx2i_ep->conn;
conn = bnx2i_conn->cls_conn->dd_data;
- iscsi_suspend_queue(conn);
+ iscsi_ep_prep_disconnect(conn);
}
hba = bnx2i_ep->hba;
@@ -2968,7 +2968,8 @@ void cxgbi_ep_disconnect(struct iscsi_endpoint *ep)
ep, cep, cconn, csk, csk->state, csk->flags);
if (cconn && cconn->iconn) {
- iscsi_suspend_tx(cconn->iconn);
+ iscsi_ep_prep_disconnect(cconn->iconn);
+
write_lock_bh(&csk->callback_lock);
cep->csk->user_data = NULL;
cconn->cep = NULL;
@@ -1387,22 +1387,28 @@ void iscsi_session_failure(struct iscsi_session *session,
}
EXPORT_SYMBOL_GPL(iscsi_session_failure);
-void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
+static void iscsi_set_conn_failed(struct iscsi_conn *conn)
{
struct iscsi_session *session = conn->session;
- spin_lock_bh(&session->frwd_lock);
- if (session->state == ISCSI_STATE_FAILED) {
- spin_unlock_bh(&session->frwd_lock);
+ if (session->state == ISCSI_STATE_FAILED)
return;
- }
if (conn->stop_stage == 0)
session->state = ISCSI_STATE_FAILED;
- spin_unlock_bh(&session->frwd_lock);
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
+}
+
+void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
+{
+ struct iscsi_session *session = conn->session;
+
+ spin_lock_bh(&session->frwd_lock);
+ iscsi_set_conn_failed(conn);
+ spin_unlock_bh(&session->frwd_lock);
+
iscsi_conn_error_event(conn->cls_conn, err);
}
EXPORT_SYMBOL_GPL(iscsi_conn_failure);
@@ -2220,6 +2226,44 @@ static void iscsi_check_transport_timeouts(struct timer_list *t)
spin_unlock(&session->frwd_lock);
}
+/*
+ * iscsi_ep_prep_disconnect - prepare the conn for a ep disconnect
+ * @conn: iscsi conn ep is bound to.
+ *
+ * This must be called by drivers implementing the ep_disconnect callout.
+ */
+void iscsi_ep_prep_disconnect(struct iscsi_conn *conn)
+{
+ struct iscsi_session *session;
+
+ /* Check if bound for the driver */
+ if (!conn)
+ return;
+
+ session = conn->session;
+ /*
+ * Wait for iscsi_eh calls to exit. We don't wait for the tmf to
+ * complete or timeout. The caller just wants to know what's running
+ * is everything that needs to be cleaned up, and no cmds will be
+ * queued.
+ */
+ mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->frwd_lock);
+
+ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+ /*
+ * if logout timed out before userspace could even send a PDU the
+ * state might still be in ISCSI_STATE_LOGGED_IN and allowing new cmds
+ * and TMFs.
+ */
+ if (session->state == ISCSI_STATE_LOGGED_IN)
+ iscsi_set_conn_failed(conn);
+
+ spin_unlock_bh(&session->frwd_lock);
+ mutex_unlock(&session->eh_mutex);
+}
+EXPORT_SYMBOL_GPL(iscsi_ep_prep_disconnect);
+
static void iscsi_prep_abort_task_pdu(struct iscsi_task *task,
struct iscsi_tm *hdr)
{
@@ -1008,7 +1008,7 @@ static void qedi_ep_disconnect(struct iscsi_endpoint *ep)
if (qedi_ep->conn) {
qedi_conn = qedi_ep->conn;
conn = qedi_conn->cls_conn->dd_data;
- iscsi_suspend_queue(conn);
+ iscsi_ep_prep_disconnect(conn);
abrt_conn = qedi_conn->abrt_conn;
while (count--) {
@@ -838,6 +838,7 @@ struct ql4_task_data {
struct qla_endpoint {
struct Scsi_Host *host;
struct sockaddr_storage dst_addr;
+ struct iscsi_conn *conn;
};
struct qla_conn {
@@ -1772,6 +1772,7 @@ static void qla4xxx_ep_disconnect(struct iscsi_endpoint *ep)
ha = to_qla_host(qla_ep->host);
DEBUG2(ql4_printk(KERN_INFO, ha, "%s: host: %ld\n", __func__,
ha->host_no));
+ iscsi_ep_prep_disconnect(qla_ep->conn);
iscsi_destroy_endpoint(ep);
}
@@ -3234,6 +3235,7 @@ static int qla4xxx_conn_bind(struct iscsi_cls_session *cls_session,
conn = cls_conn->dd_data;
qla_conn = conn->dd_data;
qla_conn->qla_ep = ep->dd_data;
+ qla_conn->qla_ep->conn = conn;
return 0;
}
@@ -441,6 +441,7 @@ extern int iscsi_conn_get_addr_param(struct sockaddr_storage *addr,
extern void iscsi_suspend_tx(struct iscsi_conn *conn);
extern void iscsi_suspend_queue(struct iscsi_conn *conn);
extern void iscsi_conn_queue_work(struct iscsi_conn *conn);
+extern void iscsi_ep_prep_disconnect(struct iscsi_conn *conn);
#define iscsi_conn_printk(prefix, _c, fmt, a...) \
iscsi_cls_conn_printk(prefix, ((struct iscsi_conn *)_c)->cls_conn, \
When we added iser and all thes offload drivers I goofed and didn't add a unbind_conn nl cmd to undo the bind_conn. So during ep_disconnect we have been doing iscsi_suspend_tx/queue to stop new IO but depending on the driver we can still get IO from: 1. __iscsi_conn_send_pdu for TMFs and nops if we haven't called iscsi_conn_failure before ep_disconnect. 2. Userspace did a ep_disconnect during shutdown before we saw a logout command and userspace didn't do an session unbind event. This patch fixes the issue by adding a helper which drivers implementing ep_disconnect can use to make sure new IO is not queued to them after calling it and until we do a new conn_bind. Signed-off-by: Mike Christie <michael.christie@oracle.com> --- drivers/infiniband/ulp/iser/iscsi_iser.c | 2 + drivers/scsi/be2iscsi/be_iscsi.c | 2 +- drivers/scsi/bnx2i/bnx2i_iscsi.c | 2 +- drivers/scsi/cxgbi/libcxgbi.c | 3 +- drivers/scsi/libiscsi.c | 56 +++++++++++++++++++++--- drivers/scsi/qedi/qedi_iscsi.c | 2 +- drivers/scsi/qla4xxx/ql4_def.h | 1 + drivers/scsi/qla4xxx/ql4_os.c | 2 + include/scsi/libiscsi.h | 1 + 9 files changed, 61 insertions(+), 10 deletions(-)