scsi: iscsi: Stop queueing during ep_disconnect
authorMike Christie <michael.christie@oracle.com>
Tue, 25 May 2021 18:17:55 +0000 (13:17 -0500)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 2 Jun 2021 05:28:19 +0000 (01:28 -0400)
During ep_disconnect we have been doing iscsi_suspend_tx/queue to block new
I/O but every driver except cxgbi and iscsi_tcp can still get I/O from
__iscsi_conn_send_pdu() if we haven't called iscsi_conn_failure() before
ep_disconnect. This could happen if we were terminating the session, and
the logout timed out before it was even sent to libiscsi.

Fix the issue by adding a helper which reverses the bind_conn call that
allows new I/O to be queued. Drivers implementing ep_disconnect can use this
to make sure new I/O is not queued to them when handling the disconnect.

Link: https://lore.kernel.org/r/20210525181821.7617-3-michael.christie@oracle.com
Reviewed-by: Lee Duncan <lduncan@suse.com>
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/infiniband/ulp/iser/iscsi_iser.c
drivers/scsi/be2iscsi/be_main.c
drivers/scsi/bnx2i/bnx2i_iscsi.c
drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
drivers/scsi/libiscsi.c
drivers/scsi/qedi/qedi_iscsi.c
drivers/scsi/qla4xxx/ql4_os.c
drivers/scsi/scsi_transport_iscsi.c
include/scsi/libiscsi.h
include/scsi/scsi_transport_iscsi.h

index 8fcaa1136f2cd42b45b38cd520230b6e8bd88627..6baebcb6d14da9eeee88d430821d42f2045f6ba4 100644 (file)
@@ -1002,6 +1002,7 @@ static struct iscsi_transport iscsi_iser_transport = {
        /* connection management */
        .create_conn            = iscsi_iser_conn_create,
        .bind_conn              = iscsi_iser_conn_bind,
+       .unbind_conn            = iscsi_conn_unbind,
        .destroy_conn           = iscsi_conn_teardown,
        .attr_is_visible        = iser_attr_is_visible,
        .set_param              = iscsi_iser_set_param,
index c15cc6c164d9b0929add23765b0798124958647c..58f81a79088f6d2db35172830dd0aa609e2cbb9d 100644 (file)
@@ -5807,6 +5807,7 @@ struct iscsi_transport beiscsi_iscsi_transport = {
        .destroy_session = beiscsi_session_destroy,
        .create_conn = beiscsi_conn_create,
        .bind_conn = beiscsi_conn_bind,
+       .unbind_conn = iscsi_conn_unbind,
        .destroy_conn = iscsi_conn_teardown,
        .attr_is_visible = beiscsi_attr_is_visible,
        .set_iface_param = beiscsi_iface_set_param,
index 1e6d8f62ea3c2280ab0b5ffa8b22292f9a366046..b6c1da46d5824b273a73abdddfce2009108f0110 100644 (file)
@@ -2276,6 +2276,7 @@ struct iscsi_transport bnx2i_iscsi_transport = {
        .destroy_session        = bnx2i_session_destroy,
        .create_conn            = bnx2i_conn_create,
        .bind_conn              = bnx2i_conn_bind,
+       .unbind_conn            = iscsi_conn_unbind,
        .destroy_conn           = bnx2i_conn_destroy,
        .attr_is_visible        = bnx2i_attr_is_visible,
        .set_param              = iscsi_set_param,
index 203f938fca7e53145f6ae049bde7b53e63bd9e6b..f949a4e007834fb2a0b356c46ecf3d5e9d4f3f23 100644 (file)
@@ -117,6 +117,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = {
        /* connection management */
        .create_conn    = cxgbi_create_conn,
        .bind_conn      = cxgbi_bind_conn,
+       .unbind_conn    = iscsi_conn_unbind,
        .destroy_conn   = iscsi_tcp_conn_teardown,
        .start_conn     = iscsi_conn_start,
        .stop_conn      = iscsi_conn_stop,
index 2c3491528d4245873505bcb09c481215569a5fdc..efb3e2b3398e2274026a1e26721be6a53b173b94 100644 (file)
@@ -134,6 +134,7 @@ static struct iscsi_transport cxgb4i_iscsi_transport = {
        /* connection management */
        .create_conn    = cxgbi_create_conn,
        .bind_conn              = cxgbi_bind_conn,
+       .unbind_conn    = iscsi_conn_unbind,
        .destroy_conn   = iscsi_tcp_conn_teardown,
        .start_conn             = iscsi_conn_start,
        .stop_conn              = iscsi_conn_stop,
index 4834219497eeb78c0c079315c8a19596e9b4c7c7..2aaf836786548bea9c2955b84b6e554dd5fb7d10 100644 (file)
@@ -1387,23 +1387,32 @@ 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 bool 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);
-               return;
-       }
+       if (session->state == ISCSI_STATE_FAILED)
+               return false;
 
        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);
-       iscsi_conn_error_event(conn->cls_conn, err);
+       return true;
+}
+
+void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
+{
+       struct iscsi_session *session = conn->session;
+       bool needs_evt;
+
+       spin_lock_bh(&session->frwd_lock);
+       needs_evt = iscsi_set_conn_failed(conn);
+       spin_unlock_bh(&session->frwd_lock);
+
+       if (needs_evt)
+               iscsi_conn_error_event(conn->cls_conn, err);
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_failure);
 
@@ -2180,6 +2189,51 @@ done:
        spin_unlock(&session->frwd_lock);
 }
 
+/**
+ * iscsi_conn_unbind - prevent queueing to conn.
+ * @cls_conn: iscsi conn ep is bound to.
+ * @is_active: is the conn in use for boot or is this for EH/termination
+ *
+ * This must be called by drivers implementing the ep_disconnect callout.
+ * It disables queueing to the connection from libiscsi in preparation for
+ * an ep_disconnect call.
+ */
+void iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active)
+{
+       struct iscsi_session *session;
+       struct iscsi_conn *conn;
+
+       if (!cls_conn)
+               return;
+
+       conn = cls_conn->dd_data;
+       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);
+
+       iscsi_suspend_queue(conn);
+       iscsi_suspend_tx(conn);
+
+       spin_lock_bh(&session->frwd_lock);
+       if (!is_active) {
+               /*
+                * 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_conn_unbind);
+
 static void iscsi_prep_abort_task_pdu(struct iscsi_task *task,
                                      struct iscsi_tm *hdr)
 {
index 08c05403cd720f3bf75fad0c1856488ef842ff32..ef16537c523c8f983669b5a26e846b754d2fcb01 100644 (file)
@@ -1401,6 +1401,7 @@ struct iscsi_transport qedi_iscsi_transport = {
        .destroy_session = qedi_session_destroy,
        .create_conn = qedi_conn_create,
        .bind_conn = qedi_conn_bind,
+       .unbind_conn = iscsi_conn_unbind,
        .start_conn = qedi_conn_start,
        .stop_conn = iscsi_conn_stop,
        .destroy_conn = qedi_conn_destroy,
index ad3afe30f617dfd86ab8252c65dc790d24f145f9..74d0d1bc208d3a3c626429419cead8ad04282690 100644 (file)
@@ -259,6 +259,7 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
        .start_conn             = qla4xxx_conn_start,
        .create_conn            = qla4xxx_conn_create,
        .bind_conn              = qla4xxx_conn_bind,
+       .unbind_conn            = iscsi_conn_unbind,
        .stop_conn              = iscsi_conn_stop,
        .destroy_conn           = qla4xxx_conn_destroy,
        .set_param              = iscsi_set_param,
index 441f0152193f726c88ceb6afe44550dda9ba3a6e..82491343e94a7dcd02bdbd63e5380ee9acdb0ba5 100644 (file)
@@ -2964,7 +2964,7 @@ release_host:
 }
 
 static int iscsi_if_ep_disconnect(struct iscsi_transport *transport,
-                                 u64 ep_handle)
+                                 u64 ep_handle, bool is_active)
 {
        struct iscsi_cls_conn *conn;
        struct iscsi_endpoint *ep;
@@ -2981,6 +2981,8 @@ static int iscsi_if_ep_disconnect(struct iscsi_transport *transport,
                conn->ep = NULL;
                mutex_unlock(&conn->ep_mutex);
                conn->state = ISCSI_CONN_FAILED;
+
+               transport->unbind_conn(conn, is_active);
        }
 
        transport->ep_disconnect(ep);
@@ -3012,7 +3014,8 @@ iscsi_if_transport_ep(struct iscsi_transport *transport,
                break;
        case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
                rc = iscsi_if_ep_disconnect(transport,
-                                           ev->u.ep_disconnect.ep_handle);
+                                           ev->u.ep_disconnect.ep_handle,
+                                           false);
                break;
        }
        return rc;
@@ -3737,7 +3740,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
                conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid);
 
                if (conn && conn->ep)
-                       iscsi_if_ep_disconnect(transport, conn->ep->id);
+                       iscsi_if_ep_disconnect(transport, conn->ep->id, true);
 
                if (!session || !conn) {
                        err = -EINVAL;
@@ -4656,6 +4659,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
        int err;
 
        BUG_ON(!tt);
+       WARN_ON(tt->ep_disconnect && !tt->unbind_conn);
 
        priv = iscsi_if_transport_lookup(tt);
        if (priv)
index 8c6d358a8abc11268ff3af4bb05a8a977ad3ff19..13d413a0b8b62ac7a95afd6f11ed055e7a7d4a6d 100644 (file)
@@ -431,6 +431,7 @@ extern int iscsi_conn_start(struct iscsi_cls_conn *);
 extern void iscsi_conn_stop(struct iscsi_cls_conn *, int);
 extern int iscsi_conn_bind(struct iscsi_cls_session *, struct iscsi_cls_conn *,
                           int);
+extern void iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active);
 extern void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err);
 extern void iscsi_session_failure(struct iscsi_session *session,
                                  enum iscsi_err err);
index fc5a39839b4b02cc32642379f5eeeee662020af9..8874016b3c9a84db9b6df5ffc7bdf0d632be42fb 100644 (file)
@@ -82,6 +82,7 @@ struct iscsi_transport {
        void (*destroy_session) (struct iscsi_cls_session *session);
        struct iscsi_cls_conn *(*create_conn) (struct iscsi_cls_session *sess,
                                uint32_t cid);
+       void (*unbind_conn) (struct iscsi_cls_conn *conn, bool is_active);
        int (*bind_conn) (struct iscsi_cls_session *session,
                          struct iscsi_cls_conn *cls_conn,
                          uint64_t transport_eph, int is_leading);