Skip to content

Commit 1324701

Browse files
maurizio-lombardimartinkpetersen
authored andcommitted
scsi: target: iscsi: Fix hang in the iSCSI login code
If the initiator suddenly stops sending data during a login while keeping the TCP connection open, the login_work won't be scheduled and will never release the login semaphore; concurrent login operations will therefore get stuck and fail. The bug is due to the inability of the login timeout code to properly handle this particular case. Fix the problem by replacing the old per-NP login timer with a new per-connection timer. The timer is started when an initiator connects to the target; if it expires, it sends a SIGINT signal to the thread pointed at by the conn->login_kworker pointer. conn->login_kworker is set by calling the iscsit_set_login_timer_kworker() helper, initially it will point to the np thread; When the login operation's control is in the process of being passed from the NP-thread to login_work, the conn->login_worker pointer is set to NULL. Finally, login_kworker will be changed to point to the worker thread executing the login_work job. If conn->login_kworker is NULL when the timer expires, it means that the login operation hasn't been completed yet but login_work isn't running, in this case the timer will mark the login process as failed and will schedule login_work so the latter will be forced to free the resources it holds. Signed-off-by: Maurizio Lombardi <mlombard@redhat.com> Link: https://lore.kernel.org/r/20230508162219.1731964-2-mlombard@redhat.com Reviewed-by: Mike Christie <michael.christie@oracle.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
1 parent 09e797c commit 1324701

File tree

6 files changed

+103
-93
lines changed

6 files changed

+103
-93
lines changed

drivers/target/iscsi/iscsi_target.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,6 @@ struct iscsi_np *iscsit_add_np(
364364
init_completion(&np->np_restart_comp);
365365
INIT_LIST_HEAD(&np->np_list);
366366

367-
timer_setup(&np->np_login_timer, iscsi_handle_login_thread_timeout, 0);
368-
369367
ret = iscsi_target_setup_login_socket(np, sockaddr);
370368
if (ret != 0) {
371369
kfree(np);

drivers/target/iscsi/iscsi_target_login.c

Lines changed: 5 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -811,59 +811,6 @@ void iscsi_post_login_handler(
811811
iscsit_dec_conn_usage_count(conn);
812812
}
813813

814-
void iscsi_handle_login_thread_timeout(struct timer_list *t)
815-
{
816-
struct iscsi_np *np = from_timer(np, t, np_login_timer);
817-
818-
spin_lock_bh(&np->np_thread_lock);
819-
pr_err("iSCSI Login timeout on Network Portal %pISpc\n",
820-
&np->np_sockaddr);
821-
822-
if (np->np_login_timer_flags & ISCSI_TF_STOP) {
823-
spin_unlock_bh(&np->np_thread_lock);
824-
return;
825-
}
826-
827-
if (np->np_thread)
828-
send_sig(SIGINT, np->np_thread, 1);
829-
830-
np->np_login_timer_flags &= ~ISCSI_TF_RUNNING;
831-
spin_unlock_bh(&np->np_thread_lock);
832-
}
833-
834-
static void iscsi_start_login_thread_timer(struct iscsi_np *np)
835-
{
836-
/*
837-
* This used the TA_LOGIN_TIMEOUT constant because at this
838-
* point we do not have access to ISCSI_TPG_ATTRIB(tpg)->login_timeout
839-
*/
840-
spin_lock_bh(&np->np_thread_lock);
841-
np->np_login_timer_flags &= ~ISCSI_TF_STOP;
842-
np->np_login_timer_flags |= ISCSI_TF_RUNNING;
843-
mod_timer(&np->np_login_timer, jiffies + TA_LOGIN_TIMEOUT * HZ);
844-
845-
pr_debug("Added timeout timer to iSCSI login request for"
846-
" %u seconds.\n", TA_LOGIN_TIMEOUT);
847-
spin_unlock_bh(&np->np_thread_lock);
848-
}
849-
850-
static void iscsi_stop_login_thread_timer(struct iscsi_np *np)
851-
{
852-
spin_lock_bh(&np->np_thread_lock);
853-
if (!(np->np_login_timer_flags & ISCSI_TF_RUNNING)) {
854-
spin_unlock_bh(&np->np_thread_lock);
855-
return;
856-
}
857-
np->np_login_timer_flags |= ISCSI_TF_STOP;
858-
spin_unlock_bh(&np->np_thread_lock);
859-
860-
del_timer_sync(&np->np_login_timer);
861-
862-
spin_lock_bh(&np->np_thread_lock);
863-
np->np_login_timer_flags &= ~ISCSI_TF_RUNNING;
864-
spin_unlock_bh(&np->np_thread_lock);
865-
}
866-
867814
int iscsit_setup_np(
868815
struct iscsi_np *np,
869816
struct sockaddr_storage *sockaddr)
@@ -1123,10 +1070,13 @@ static struct iscsit_conn *iscsit_alloc_conn(struct iscsi_np *np)
11231070
spin_lock_init(&conn->nopin_timer_lock);
11241071
spin_lock_init(&conn->response_queue_lock);
11251072
spin_lock_init(&conn->state_lock);
1073+
spin_lock_init(&conn->login_worker_lock);
1074+
spin_lock_init(&conn->login_timer_lock);
11261075

11271076
timer_setup(&conn->nopin_response_timer,
11281077
iscsit_handle_nopin_response_timeout, 0);
11291078
timer_setup(&conn->nopin_timer, iscsit_handle_nopin_timeout, 0);
1079+
timer_setup(&conn->login_timer, iscsit_login_timeout, 0);
11301080

11311081
if (iscsit_conn_set_transport(conn, np->np_transport) < 0)
11321082
goto free_conn;
@@ -1304,7 +1254,7 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
13041254
goto new_sess_out;
13051255
}
13061256

1307-
iscsi_start_login_thread_timer(np);
1257+
iscsit_start_login_timer(conn, current);
13081258

13091259
pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n");
13101260
conn->conn_state = TARG_CONN_STATE_XPT_UP;
@@ -1417,8 +1367,6 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
14171367
if (ret < 0)
14181368
goto new_sess_out;
14191369

1420-
iscsi_stop_login_thread_timer(np);
1421-
14221370
if (ret == 1) {
14231371
tpg_np = conn->tpg_np;
14241372

@@ -1434,7 +1382,7 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
14341382
new_sess_out:
14351383
new_sess = true;
14361384
old_sess_out:
1437-
iscsi_stop_login_thread_timer(np);
1385+
iscsit_stop_login_timer(conn);
14381386
tpg_np = conn->tpg_np;
14391387
iscsi_target_login_sess_out(conn, zero_tsih, new_sess);
14401388
new_sess = false;
@@ -1448,7 +1396,6 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
14481396
return 1;
14491397

14501398
exit:
1451-
iscsi_stop_login_thread_timer(np);
14521399
spin_lock_bh(&np->np_thread_lock);
14531400
np->np_thread_state = ISCSI_NP_THREAD_EXIT;
14541401
spin_unlock_bh(&np->np_thread_lock);

drivers/target/iscsi/iscsi_target_nego.c

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -535,25 +535,6 @@ static void iscsi_target_login_drop(struct iscsit_conn *conn, struct iscsi_login
535535
iscsi_target_login_sess_out(conn, zero_tsih, true);
536536
}
537537

538-
struct conn_timeout {
539-
struct timer_list timer;
540-
struct iscsit_conn *conn;
541-
};
542-
543-
static void iscsi_target_login_timeout(struct timer_list *t)
544-
{
545-
struct conn_timeout *timeout = from_timer(timeout, t, timer);
546-
struct iscsit_conn *conn = timeout->conn;
547-
548-
pr_debug("Entering iscsi_target_login_timeout >>>>>>>>>>>>>>>>>>>\n");
549-
550-
if (conn->login_kworker) {
551-
pr_debug("Sending SIGINT to conn->login_kworker %s/%d\n",
552-
conn->login_kworker->comm, conn->login_kworker->pid);
553-
send_sig(SIGINT, conn->login_kworker, 1);
554-
}
555-
}
556-
557538
static void iscsi_target_do_login_rx(struct work_struct *work)
558539
{
559540
struct iscsit_conn *conn = container_of(work,
@@ -562,12 +543,15 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
562543
struct iscsi_np *np = login->np;
563544
struct iscsi_portal_group *tpg = conn->tpg;
564545
struct iscsi_tpg_np *tpg_np = conn->tpg_np;
565-
struct conn_timeout timeout;
566546
int rc, zero_tsih = login->zero_tsih;
567547
bool state;
568548

569549
pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n",
570550
conn, current->comm, current->pid);
551+
552+
spin_lock(&conn->login_worker_lock);
553+
set_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags);
554+
spin_unlock(&conn->login_worker_lock);
571555
/*
572556
* If iscsi_target_do_login_rx() has been invoked by ->sk_data_ready()
573557
* before initial PDU processing in iscsi_target_start_negotiation()
@@ -597,19 +581,16 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
597581
goto err;
598582
}
599583

600-
conn->login_kworker = current;
601584
allow_signal(SIGINT);
602-
603-
timeout.conn = conn;
604-
timer_setup_on_stack(&timeout.timer, iscsi_target_login_timeout, 0);
605-
mod_timer(&timeout.timer, jiffies + TA_LOGIN_TIMEOUT * HZ);
606-
pr_debug("Starting login timer for %s/%d\n", current->comm, current->pid);
585+
rc = iscsit_set_login_timer_kworker(conn, current);
586+
if (rc < 0) {
587+
/* The login timer has already expired */
588+
pr_debug("iscsi_target_do_login_rx, login failed\n");
589+
goto err;
590+
}
607591

608592
rc = conn->conn_transport->iscsit_get_login_rx(conn, login);
609-
del_timer_sync(&timeout.timer);
610-
destroy_timer_on_stack(&timeout.timer);
611593
flush_signals(current);
612-
conn->login_kworker = NULL;
613594

614595
if (rc < 0)
615596
goto err;
@@ -646,7 +627,17 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
646627
if (iscsi_target_sk_check_and_clear(conn,
647628
LOGIN_FLAGS_WRITE_ACTIVE))
648629
goto err;
630+
631+
/*
632+
* Set the login timer thread pointer to NULL to prevent the
633+
* login process from getting stuck if the initiator
634+
* stops sending data.
635+
*/
636+
rc = iscsit_set_login_timer_kworker(conn, NULL);
637+
if (rc < 0)
638+
goto err;
649639
} else if (rc == 1) {
640+
iscsit_stop_login_timer(conn);
650641
cancel_delayed_work(&conn->login_work);
651642
iscsi_target_nego_release(conn);
652643
iscsi_post_login_handler(np, conn, zero_tsih);
@@ -656,6 +647,7 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
656647

657648
err:
658649
iscsi_target_restore_sock_callbacks(conn);
650+
iscsit_stop_login_timer(conn);
659651
cancel_delayed_work(&conn->login_work);
660652
iscsi_target_login_drop(conn, login);
661653
iscsit_deaccess_np(np, tpg, tpg_np);
@@ -1368,14 +1360,30 @@ int iscsi_target_start_negotiation(
13681360
* and perform connection cleanup now.
13691361
*/
13701362
ret = iscsi_target_do_login(conn, login);
1371-
if (!ret && iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
1372-
ret = -1;
1363+
if (!ret) {
1364+
spin_lock(&conn->login_worker_lock);
1365+
1366+
if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
1367+
ret = -1;
1368+
else if (!test_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags)) {
1369+
if (iscsit_set_login_timer_kworker(conn, NULL) < 0) {
1370+
/*
1371+
* The timeout has expired already.
1372+
* Schedule login_work to perform the cleanup.
1373+
*/
1374+
schedule_delayed_work(&conn->login_work, 0);
1375+
}
1376+
}
1377+
1378+
spin_unlock(&conn->login_worker_lock);
1379+
}
13731380

13741381
if (ret < 0) {
13751382
iscsi_target_restore_sock_callbacks(conn);
13761383
iscsi_remove_failed_auth_entry(conn);
13771384
}
13781385
if (ret != 0) {
1386+
iscsit_stop_login_timer(conn);
13791387
cancel_delayed_work_sync(&conn->login_work);
13801388
iscsi_target_nego_release(conn);
13811389
}

drivers/target/iscsi/iscsi_target_util.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,57 @@ void iscsit_stop_nopin_timer(struct iscsit_conn *conn)
10401040
spin_unlock_bh(&conn->nopin_timer_lock);
10411041
}
10421042

1043+
void iscsit_login_timeout(struct timer_list *t)
1044+
{
1045+
struct iscsit_conn *conn = from_timer(conn, t, login_timer);
1046+
struct iscsi_login *login = conn->login;
1047+
1048+
pr_debug("Entering iscsi_target_login_timeout >>>>>>>>>>>>>>>>>>>\n");
1049+
1050+
spin_lock_bh(&conn->login_timer_lock);
1051+
login->login_failed = 1;
1052+
1053+
if (conn->login_kworker) {
1054+
pr_debug("Sending SIGINT to conn->login_kworker %s/%d\n",
1055+
conn->login_kworker->comm, conn->login_kworker->pid);
1056+
send_sig(SIGINT, conn->login_kworker, 1);
1057+
} else {
1058+
schedule_delayed_work(&conn->login_work, 0);
1059+
}
1060+
spin_unlock_bh(&conn->login_timer_lock);
1061+
}
1062+
1063+
void iscsit_start_login_timer(struct iscsit_conn *conn, struct task_struct *kthr)
1064+
{
1065+
pr_debug("Login timer started\n");
1066+
1067+
conn->login_kworker = kthr;
1068+
mod_timer(&conn->login_timer, jiffies + TA_LOGIN_TIMEOUT * HZ);
1069+
}
1070+
1071+
int iscsit_set_login_timer_kworker(struct iscsit_conn *conn, struct task_struct *kthr)
1072+
{
1073+
struct iscsi_login *login = conn->login;
1074+
int ret = 0;
1075+
1076+
spin_lock_bh(&conn->login_timer_lock);
1077+
if (login->login_failed) {
1078+
/* The timer has already expired */
1079+
ret = -1;
1080+
} else {
1081+
conn->login_kworker = kthr;
1082+
}
1083+
spin_unlock_bh(&conn->login_timer_lock);
1084+
1085+
return ret;
1086+
}
1087+
1088+
void iscsit_stop_login_timer(struct iscsit_conn *conn)
1089+
{
1090+
pr_debug("Login timer stopped\n");
1091+
timer_delete_sync(&conn->login_timer);
1092+
}
1093+
10431094
int iscsit_send_tx_data(
10441095
struct iscsit_cmd *cmd,
10451096
struct iscsit_conn *conn,

drivers/target/iscsi/iscsi_target_util.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ extern void iscsit_handle_nopin_timeout(struct timer_list *t);
5656
extern void __iscsit_start_nopin_timer(struct iscsit_conn *);
5757
extern void iscsit_start_nopin_timer(struct iscsit_conn *);
5858
extern void iscsit_stop_nopin_timer(struct iscsit_conn *);
59+
extern void iscsit_login_timeout(struct timer_list *t);
60+
extern void iscsit_start_login_timer(struct iscsit_conn *, struct task_struct *kthr);
61+
extern void iscsit_stop_login_timer(struct iscsit_conn *);
62+
extern int iscsit_set_login_timer_kworker(struct iscsit_conn *, struct task_struct *kthr);
5963
extern int iscsit_send_tx_data(struct iscsit_cmd *, struct iscsit_conn *, int);
6064
extern int iscsit_fe_sendpage_sg(struct iscsit_cmd *, struct iscsit_conn *);
6165
extern int iscsit_tx_login_rsp(struct iscsit_conn *, u8, u8);

include/target/iscsi/iscsi_target_core.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,12 +562,14 @@ struct iscsit_conn {
562562
#define LOGIN_FLAGS_READ_ACTIVE 2
563563
#define LOGIN_FLAGS_WRITE_ACTIVE 3
564564
#define LOGIN_FLAGS_CLOSED 4
565+
#define LOGIN_FLAGS_WORKER_RUNNING 5
565566
unsigned long login_flags;
566567
struct delayed_work login_work;
567568
struct iscsi_login *login;
568569
struct timer_list nopin_timer;
569570
struct timer_list nopin_response_timer;
570571
struct timer_list transport_timer;
572+
struct timer_list login_timer;
571573
struct task_struct *login_kworker;
572574
/* Spinlock used for add/deleting cmd's from conn_cmd_list */
573575
spinlock_t cmd_lock;
@@ -576,6 +578,8 @@ struct iscsit_conn {
576578
spinlock_t nopin_timer_lock;
577579
spinlock_t response_queue_lock;
578580
spinlock_t state_lock;
581+
spinlock_t login_timer_lock;
582+
spinlock_t login_worker_lock;
579583
/* libcrypto RX and TX contexts for crc32c */
580584
struct ahash_request *conn_rx_hash;
581585
struct ahash_request *conn_tx_hash;
@@ -792,15 +796,13 @@ struct iscsi_np {
792796
enum np_thread_state_table np_thread_state;
793797
bool enabled;
794798
atomic_t np_reset_count;
795-
enum iscsi_timer_flags_table np_login_timer_flags;
796799
u32 np_exports;
797800
enum np_flags_table np_flags;
798801
spinlock_t np_thread_lock;
799802
struct completion np_restart_comp;
800803
struct socket *np_socket;
801804
struct sockaddr_storage np_sockaddr;
802805
struct task_struct *np_thread;
803-
struct timer_list np_login_timer;
804806
void *np_context;
805807
struct iscsit_transport *np_transport;
806808
struct list_head np_list;

0 commit comments

Comments
 (0)