Skip to content

Commit 5fadf30

Browse files
maurizio-lombardigregkh
authored andcommitted
scsi: target: fix hang when multiple threads try to destroy the same iscsi session
[ Upstream commit 57c46e9 ] A number of hangs have been reported against the target driver; they are due to the fact that multiple threads may try to destroy the iscsi session at the same time. This may be reproduced for example when a "targetcli iscsi/iqn.../tpg1 disable" command is executed while a logout operation is underway. When this happens, two or more threads may end up sleeping and waiting for iscsit_close_connection() to execute "complete(session_wait_comp)". Only one of the threads will wake up and proceed to destroy the session structure, the remaining threads will hang forever. Note that if the blocked threads are somehow forced to wake up with complete_all(), they will try to free the same iscsi session structure destroyed by the first thread, causing double frees, memory corruptions etc... With this patch, the threads that want to destroy the iscsi session will increase the session refcount and will set the "session_close" flag to 1; then they wait for the driver to close the remaining active connections. When the last connection is closed, iscsit_close_connection() will wake up all the threads and will wait for the session's refcount to reach zero; when this happens, iscsit_close_connection() will destroy the session structure because no one is referencing it anymore. INFO: task targetcli:5971 blocked for more than 120 seconds. Tainted: P OE 4.15.0-72-generic #81~16.04.1 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. targetcli D 0 5971 1 0x00000080 Call Trace: __schedule+0x3d6/0x8b0 ? vprintk_func+0x44/0xe0 schedule+0x36/0x80 schedule_timeout+0x1db/0x370 ? __dynamic_pr_debug+0x8a/0xb0 wait_for_completion+0xb4/0x140 ? wake_up_q+0x70/0x70 iscsit_free_session+0x13d/0x1a0 [iscsi_target_mod] iscsit_release_sessions_for_tpg+0x16b/0x1e0 [iscsi_target_mod] iscsit_tpg_disable_portal_group+0xca/0x1c0 [iscsi_target_mod] lio_target_tpg_enable_store+0x66/0xe0 [iscsi_target_mod] configfs_write_file+0xb9/0x120 __vfs_write+0x1b/0x40 vfs_write+0xb8/0x1b0 SyS_write+0x5c/0xe0 do_syscall_64+0x73/0x130 entry_SYSCALL_64_after_hwframe+0x3d/0xa2 Link: https://lore.kernel.org/r/[email protected] Reported-by: Matt Coleman <[email protected]> Tested-by: Matt Coleman <[email protected]> Tested-by: Rahul Kundu <[email protected]> Signed-off-by: Maurizio Lombardi <[email protected]> Signed-off-by: Martin K. Petersen <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent e834717 commit 5fadf30

File tree

4 files changed

+30
-17
lines changed

4 files changed

+30
-17
lines changed

drivers/target/iscsi/iscsi_target.c

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4321,30 +4321,37 @@ int iscsit_close_connection(
43214321
if (!atomic_read(&sess->session_reinstatement) &&
43224322
atomic_read(&sess->session_fall_back_to_erl0)) {
43234323
spin_unlock_bh(&sess->conn_lock);
4324+
complete_all(&sess->session_wait_comp);
43244325
iscsit_close_session(sess);
43254326

43264327
return 0;
43274328
} else if (atomic_read(&sess->session_logout)) {
43284329
pr_debug("Moving to TARG_SESS_STATE_FREE.\n");
43294330
sess->session_state = TARG_SESS_STATE_FREE;
4330-
spin_unlock_bh(&sess->conn_lock);
43314331

4332-
if (atomic_read(&sess->sleep_on_sess_wait_comp))
4333-
complete(&sess->session_wait_comp);
4332+
if (atomic_read(&sess->session_close)) {
4333+
spin_unlock_bh(&sess->conn_lock);
4334+
complete_all(&sess->session_wait_comp);
4335+
iscsit_close_session(sess);
4336+
} else {
4337+
spin_unlock_bh(&sess->conn_lock);
4338+
}
43344339

43354340
return 0;
43364341
} else {
43374342
pr_debug("Moving to TARG_SESS_STATE_FAILED.\n");
43384343
sess->session_state = TARG_SESS_STATE_FAILED;
43394344

4340-
if (!atomic_read(&sess->session_continuation)) {
4341-
spin_unlock_bh(&sess->conn_lock);
4345+
if (!atomic_read(&sess->session_continuation))
43424346
iscsit_start_time2retain_handler(sess);
4343-
} else
4344-
spin_unlock_bh(&sess->conn_lock);
43454347

4346-
if (atomic_read(&sess->sleep_on_sess_wait_comp))
4347-
complete(&sess->session_wait_comp);
4348+
if (atomic_read(&sess->session_close)) {
4349+
spin_unlock_bh(&sess->conn_lock);
4350+
complete_all(&sess->session_wait_comp);
4351+
iscsit_close_session(sess);
4352+
} else {
4353+
spin_unlock_bh(&sess->conn_lock);
4354+
}
43484355

43494356
return 0;
43504357
}
@@ -4453,9 +4460,9 @@ static void iscsit_logout_post_handler_closesession(
44534460
complete(&conn->conn_logout_comp);
44544461

44554462
iscsit_dec_conn_usage_count(conn);
4463+
atomic_set(&sess->session_close, 1);
44564464
iscsit_stop_session(sess, sleep, sleep);
44574465
iscsit_dec_session_usage_count(sess);
4458-
iscsit_close_session(sess);
44594466
}
44604467

44614468
static void iscsit_logout_post_handler_samecid(
@@ -4600,8 +4607,6 @@ void iscsit_stop_session(
46004607
int is_last;
46014608

46024609
spin_lock_bh(&sess->conn_lock);
4603-
if (session_sleep)
4604-
atomic_set(&sess->sleep_on_sess_wait_comp, 1);
46054610

46064611
if (connection_sleep) {
46074612
list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list,
@@ -4659,12 +4664,15 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
46594664
spin_lock(&sess->conn_lock);
46604665
if (atomic_read(&sess->session_fall_back_to_erl0) ||
46614666
atomic_read(&sess->session_logout) ||
4667+
atomic_read(&sess->session_close) ||
46624668
(sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
46634669
spin_unlock(&sess->conn_lock);
46644670
continue;
46654671
}
4672+
iscsit_inc_session_usage_count(sess);
46664673
atomic_set(&sess->session_reinstatement, 1);
46674674
atomic_set(&sess->session_fall_back_to_erl0, 1);
4675+
atomic_set(&sess->session_close, 1);
46684676
spin_unlock(&sess->conn_lock);
46694677

46704678
list_move_tail(&se_sess->sess_list, &free_list);
@@ -4674,8 +4682,9 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
46744682
list_for_each_entry_safe(se_sess, se_sess_tmp, &free_list, sess_list) {
46754683
sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
46764684

4685+
list_del_init(&se_sess->sess_list);
46774686
iscsit_stop_session(sess, 1, 1);
4678-
iscsit_close_session(sess);
4687+
iscsit_dec_session_usage_count(sess);
46794688
session_count++;
46804689
}
46814690

drivers/target/iscsi/iscsi_target_configfs.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1527,20 +1527,23 @@ static void lio_tpg_close_session(struct se_session *se_sess)
15271527
spin_lock(&sess->conn_lock);
15281528
if (atomic_read(&sess->session_fall_back_to_erl0) ||
15291529
atomic_read(&sess->session_logout) ||
1530+
atomic_read(&sess->session_close) ||
15301531
(sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
15311532
spin_unlock(&sess->conn_lock);
15321533
spin_unlock_bh(&se_tpg->session_lock);
15331534
return;
15341535
}
1536+
iscsit_inc_session_usage_count(sess);
15351537
atomic_set(&sess->session_reinstatement, 1);
15361538
atomic_set(&sess->session_fall_back_to_erl0, 1);
1539+
atomic_set(&sess->session_close, 1);
15371540
spin_unlock(&sess->conn_lock);
15381541

15391542
iscsit_stop_time2retain_timer(sess);
15401543
spin_unlock_bh(&se_tpg->session_lock);
15411544

15421545
iscsit_stop_session(sess, 1, 1);
1543-
iscsit_close_session(sess);
1546+
iscsit_dec_session_usage_count(sess);
15441547
}
15451548

15461549
static u32 lio_tpg_get_inst_index(struct se_portal_group *se_tpg)

drivers/target/iscsi/iscsi_target_login.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn)
195195
spin_lock(&sess_p->conn_lock);
196196
if (atomic_read(&sess_p->session_fall_back_to_erl0) ||
197197
atomic_read(&sess_p->session_logout) ||
198+
atomic_read(&sess_p->session_close) ||
198199
(sess_p->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
199200
spin_unlock(&sess_p->conn_lock);
200201
continue;
@@ -205,6 +206,7 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn)
205206
(sess_p->sess_ops->SessionType == sessiontype))) {
206207
atomic_set(&sess_p->session_reinstatement, 1);
207208
atomic_set(&sess_p->session_fall_back_to_erl0, 1);
209+
atomic_set(&sess_p->session_close, 1);
208210
spin_unlock(&sess_p->conn_lock);
209211
iscsit_inc_session_usage_count(sess_p);
210212
iscsit_stop_time2retain_timer(sess_p);
@@ -229,15 +231,13 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn)
229231
if (sess->session_state == TARG_SESS_STATE_FAILED) {
230232
spin_unlock_bh(&sess->conn_lock);
231233
iscsit_dec_session_usage_count(sess);
232-
iscsit_close_session(sess);
233234
return 0;
234235
}
235236
spin_unlock_bh(&sess->conn_lock);
236237

237238
iscsit_stop_session(sess, 1, 1);
238239
iscsit_dec_session_usage_count(sess);
239240

240-
iscsit_close_session(sess);
241241
return 0;
242242
}
243243

@@ -525,6 +525,7 @@ static int iscsi_login_non_zero_tsih_s2(
525525
sess_p = (struct iscsi_session *)se_sess->fabric_sess_ptr;
526526
if (atomic_read(&sess_p->session_fall_back_to_erl0) ||
527527
atomic_read(&sess_p->session_logout) ||
528+
atomic_read(&sess_p->session_close) ||
528529
(sess_p->time2retain_timer_flags & ISCSI_TF_EXPIRED))
529530
continue;
530531
if (!memcmp(sess_p->isid, pdu->isid, 6) &&

include/target/iscsi/iscsi_target_core.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ struct iscsi_session {
671671
atomic_t session_logout;
672672
atomic_t session_reinstatement;
673673
atomic_t session_stop_active;
674-
atomic_t sleep_on_sess_wait_comp;
674+
atomic_t session_close;
675675
/* connection list */
676676
struct list_head sess_conn_list;
677677
struct list_head cr_active_list;

0 commit comments

Comments
 (0)