]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
scsi: iscsi: Stop queueing during ep_disconnect
authorMike Christie <michael.christie@oracle.com>
Tue, 25 May 2021 18:17:55 +0000 (13:17 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 20 Apr 2022 07:23:15 +0000 (09:23 +0200)
[ Upstream commit 891e2639deae721dc43764a44fa255890dc34313 ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
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 3690e28cc7ea2a1917971c4c15c9e4e188ba6be1..8857d83227104976b53ffc8d9a4c60ebcac17122 100644 (file)
@@ -988,6 +988,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 987dc8135a9b41f3e67c3aea65dd9efdd216d072..b977e039bb78993401395533a310a1af31cdd07a 100644 (file)
@@ -5810,6 +5810,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 21efc73b87beefa67b6ac0024c89d858d4086baa..173d8c6a8cbf7515feaa9cd83f47270e37efc1a9 100644 (file)
@@ -2278,6 +2278,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 37d99357120faeae6d16b6fdbc18993e29b96af9..edcd3fab6973c4cb0f813193312a2351b98f2c51 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 d4e66c595eb8764fc81830674ce298b1230b348c..05799b41974d5b8244ac98ab2f5a8b2399495bf3 100644 (file)
@@ -1367,23 +1367,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);
 
@@ -2117,6 +2126,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 f51723e2d5227d30646beb6cb057621f76a90856..8f8036ae9a5672db61d4ef0f9bee53524f1a7dfa 100644 (file)
@@ -1428,6 +1428,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 2c23b692e318c95f5d13022ff7b92c672ad56ad0..a8b8bf118c76e887683e4bf7d2d008d9cd56390f 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 a5759d0e388a851397ea2eb9fc9e8d72ea02d2f0..6490211b55d20adae907662919e5ebcb9b529391 100644 (file)
@@ -2957,7 +2957,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;
@@ -2974,6 +2974,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);
@@ -3005,7 +3007,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;
@@ -3730,7 +3733,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;
@@ -4649,6 +4652,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 2b5f97224f69365ab1ec2f3529cb5f4cfcdf8d61..fa00e2543ad654762ae6bb0f31ce65361fc4e33a 100644 (file)
@@ -421,6 +421,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 f28bb20d627134b7d072562cd4f816c824eabac7..eb6ed499324d384b02803367fa977426dcb1182e 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);