]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
scsi: target: iscsi: Fix hang in iscsit_access_np() when getting tpg->np_login_sem
authorHou Pu <houpu@bytedance.com>
Wed, 29 Jul 2020 13:03:43 +0000 (09:03 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 23 Sep 2020 08:46:30 +0000 (10:46 +0200)
commit ed43ffea78dcc97db3f561da834f1a49c8961e33 upstream.

The iSCSI target login thread might get stuck with the following stack:

cat /proc/`pidof iscsi_np`/stack
[<0>] down_interruptible+0x42/0x50
[<0>] iscsit_access_np+0xe3/0x167
[<0>] iscsi_target_locate_portal+0x695/0x8ac
[<0>] __iscsi_target_login_thread+0x855/0xb82
[<0>] iscsi_target_login_thread+0x2f/0x5a
[<0>] kthread+0xfa/0x130
[<0>] ret_from_fork+0x1f/0x30

This can be reproduced via the following steps:

1. Initiator A tries to log in to iqn1-tpg1 on port 3260. After finishing
   PDU exchange in the login thread and before the negotiation is finished
   the the network link goes down. At this point A has not finished login
   and tpg->np_login_sem is held.

2. Initiator B tries to log in to iqn2-tpg1 on port 3260. After finishing
   PDU exchange in the login thread the target expects to process remaining
   login PDUs in workqueue context.

3. Initiator A' tries to log in to iqn1-tpg1 on port 3260 from a new
   socket. A' will wait for tpg->np_login_sem with np->np_login_timer
   loaded to wait for at most 15 seconds. The lock is held by A so A'
   eventually times out.

4. Before A' got timeout initiator B gets negotiation failed and calls
   iscsi_target_login_drop()->iscsi_target_login_sess_out().  The
   np->np_login_timer is canceled and initiator A' will hang forever.
   Because A' is now in the login thread, no new login requests can be
   serviced.

Fix this by moving iscsi_stop_login_thread_timer() out of
iscsi_target_login_sess_out(). Also remove iscsi_np parameter from
iscsi_target_login_sess_out().

Link: https://lore.kernel.org/r/20200729130343.24976-1-houpu@bytedance.com
Cc: stable@vger.kernel.org
Reviewed-by: Mike Christie <michael.christie@oracle.com>
Signed-off-by: Hou Pu <houpu@bytedance.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/target/iscsi/iscsi_target_login.c
drivers/target/iscsi/iscsi_target_login.h
drivers/target/iscsi/iscsi_target_nego.c

index 55df6f99e66917ed74f9f05bc22be6ac81904d76..c36d98e901a98d01ddb7e1a9a36822096078082e 100644 (file)
@@ -1158,7 +1158,7 @@ iscsit_conn_set_transport(struct iscsi_conn *conn, struct iscsit_transport *t)
 }
 
 void iscsi_target_login_sess_out(struct iscsi_conn *conn,
-               struct iscsi_np *np, bool zero_tsih, bool new_sess)
+                                bool zero_tsih, bool new_sess)
 {
        if (!new_sess)
                goto old_sess_out;
@@ -1180,7 +1180,6 @@ void iscsi_target_login_sess_out(struct iscsi_conn *conn,
        conn->sess = NULL;
 
 old_sess_out:
-       iscsi_stop_login_thread_timer(np);
        /*
         * If login negotiation fails check if the Time2Retain timer
         * needs to be restarted.
@@ -1440,8 +1439,9 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
 new_sess_out:
        new_sess = true;
 old_sess_out:
+       iscsi_stop_login_thread_timer(np);
        tpg_np = conn->tpg_np;
-       iscsi_target_login_sess_out(conn, np, zero_tsih, new_sess);
+       iscsi_target_login_sess_out(conn, zero_tsih, new_sess);
        new_sess = false;
 
        if (tpg) {
index c2495e03625c5b6ecd68fc0caea25eb42b3eb3e4..9b08b24d223da2fede6b4d3c0d02b019be66e858 100644 (file)
@@ -22,8 +22,7 @@ extern int iscsit_put_login_tx(struct iscsi_conn *, struct iscsi_login *, u32);
 extern void iscsit_free_conn(struct iscsi_np *, struct iscsi_conn *);
 extern int iscsit_start_kthreads(struct iscsi_conn *);
 extern void iscsi_post_login_handler(struct iscsi_np *, struct iscsi_conn *, u8);
-extern void iscsi_target_login_sess_out(struct iscsi_conn *, struct iscsi_np *,
-                               bool, bool);
+extern void iscsi_target_login_sess_out(struct iscsi_conn *, bool, bool);
 extern int iscsi_target_login_thread(void *);
 
 #endif   /*** ISCSI_TARGET_LOGIN_H ***/
index 87248a2512e5975fbfb541f9c0ad1d80299645dc..07335357418c851bf9829deb26720eb0427c6a1a 100644 (file)
@@ -554,12 +554,11 @@ static bool iscsi_target_sk_check_and_clear(struct iscsi_conn *conn, unsigned in
 
 static void iscsi_target_login_drop(struct iscsi_conn *conn, struct iscsi_login *login)
 {
-       struct iscsi_np *np = login->np;
        bool zero_tsih = login->zero_tsih;
 
        iscsi_remove_failed_auth_entry(conn);
        iscsi_target_nego_release(conn);
-       iscsi_target_login_sess_out(conn, np, zero_tsih, true);
+       iscsi_target_login_sess_out(conn, zero_tsih, true);
 }
 
 static void iscsi_target_login_timeout(unsigned long data)