Skip to content

Commit a542ea4

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 1609ab7 commit a542ea4

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
@@ -4301,30 +4301,37 @@ int iscsit_close_connection(
43014301
if (!atomic_read(&sess->session_reinstatement) &&
43024302
atomic_read(&sess->session_fall_back_to_erl0)) {
43034303
spin_unlock_bh(&sess->conn_lock);
4304+
complete_all(&sess->session_wait_comp);
43044305
iscsit_close_session(sess);
43054306

43064307
return 0;
43074308
} else if (atomic_read(&sess->session_logout)) {
43084309
pr_debug("Moving to TARG_SESS_STATE_FREE.\n");
43094310
sess->session_state = TARG_SESS_STATE_FREE;
4310-
spin_unlock_bh(&sess->conn_lock);
43114311

4312-
if (atomic_read(&sess->sleep_on_sess_wait_comp))
4313-
complete(&sess->session_wait_comp);
4312+
if (atomic_read(&sess->session_close)) {
4313+
spin_unlock_bh(&sess->conn_lock);
4314+
complete_all(&sess->session_wait_comp);
4315+
iscsit_close_session(sess);
4316+
} else {
4317+
spin_unlock_bh(&sess->conn_lock);
4318+
}
43144319

43154320
return 0;
43164321
} else {
43174322
pr_debug("Moving to TARG_SESS_STATE_FAILED.\n");
43184323
sess->session_state = TARG_SESS_STATE_FAILED;
43194324

4320-
if (!atomic_read(&sess->session_continuation)) {
4321-
spin_unlock_bh(&sess->conn_lock);
4325+
if (!atomic_read(&sess->session_continuation))
43224326
iscsit_start_time2retain_handler(sess);
4323-
} else
4324-
spin_unlock_bh(&sess->conn_lock);
43254327

4326-
if (atomic_read(&sess->sleep_on_sess_wait_comp))
4327-
complete(&sess->session_wait_comp);
4328+
if (atomic_read(&sess->session_close)) {
4329+
spin_unlock_bh(&sess->conn_lock);
4330+
complete_all(&sess->session_wait_comp);
4331+
iscsit_close_session(sess);
4332+
} else {
4333+
spin_unlock_bh(&sess->conn_lock);
4334+
}
43284335

43294336
return 0;
43304337
}
@@ -4429,9 +4436,9 @@ static void iscsit_logout_post_handler_closesession(
44294436
complete(&conn->conn_logout_comp);
44304437

44314438
iscsit_dec_conn_usage_count(conn);
4439+
atomic_set(&sess->session_close, 1);
44324440
iscsit_stop_session(sess, sleep, sleep);
44334441
iscsit_dec_session_usage_count(sess);
4434-
iscsit_close_session(sess);
44354442
}
44364443

44374444
static void iscsit_logout_post_handler_samecid(
@@ -4576,8 +4583,6 @@ void iscsit_stop_session(
45764583
int is_last;
45774584

45784585
spin_lock_bh(&sess->conn_lock);
4579-
if (session_sleep)
4580-
atomic_set(&sess->sleep_on_sess_wait_comp, 1);
45814586

45824587
if (connection_sleep) {
45834588
list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list,
@@ -4635,12 +4640,15 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
46354640
spin_lock(&sess->conn_lock);
46364641
if (atomic_read(&sess->session_fall_back_to_erl0) ||
46374642
atomic_read(&sess->session_logout) ||
4643+
atomic_read(&sess->session_close) ||
46384644
(sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
46394645
spin_unlock(&sess->conn_lock);
46404646
continue;
46414647
}
4648+
iscsit_inc_session_usage_count(sess);
46424649
atomic_set(&sess->session_reinstatement, 1);
46434650
atomic_set(&sess->session_fall_back_to_erl0, 1);
4651+
atomic_set(&sess->session_close, 1);
46444652
spin_unlock(&sess->conn_lock);
46454653

46464654
list_move_tail(&se_sess->sess_list, &free_list);
@@ -4650,8 +4658,9 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
46504658
list_for_each_entry_safe(se_sess, se_sess_tmp, &free_list, sess_list) {
46514659
sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
46524660

4661+
list_del_init(&se_sess->sess_list);
46534662
iscsit_stop_session(sess, 1, 1);
4654-
iscsit_close_session(sess);
4663+
iscsit_dec_session_usage_count(sess);
46554664
session_count++;
46564665
}
46574666

drivers/target/iscsi/iscsi_target_configfs.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1476,20 +1476,23 @@ static void lio_tpg_close_session(struct se_session *se_sess)
14761476
spin_lock(&sess->conn_lock);
14771477
if (atomic_read(&sess->session_fall_back_to_erl0) ||
14781478
atomic_read(&sess->session_logout) ||
1479+
atomic_read(&sess->session_close) ||
14791480
(sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
14801481
spin_unlock(&sess->conn_lock);
14811482
spin_unlock_bh(&se_tpg->session_lock);
14821483
return;
14831484
}
1485+
iscsit_inc_session_usage_count(sess);
14841486
atomic_set(&sess->session_reinstatement, 1);
14851487
atomic_set(&sess->session_fall_back_to_erl0, 1);
1488+
atomic_set(&sess->session_close, 1);
14861489
spin_unlock(&sess->conn_lock);
14871490

14881491
iscsit_stop_time2retain_timer(sess);
14891492
spin_unlock_bh(&se_tpg->session_lock);
14901493

14911494
iscsit_stop_session(sess, 1, 1);
1492-
iscsit_close_session(sess);
1495+
iscsit_dec_session_usage_count(sess);
14931496
}
14941497

14951498
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
@@ -156,6 +156,7 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn)
156156
spin_lock(&sess_p->conn_lock);
157157
if (atomic_read(&sess_p->session_fall_back_to_erl0) ||
158158
atomic_read(&sess_p->session_logout) ||
159+
atomic_read(&sess_p->session_close) ||
159160
(sess_p->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
160161
spin_unlock(&sess_p->conn_lock);
161162
continue;
@@ -166,6 +167,7 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn)
166167
(sess_p->sess_ops->SessionType == sessiontype))) {
167168
atomic_set(&sess_p->session_reinstatement, 1);
168169
atomic_set(&sess_p->session_fall_back_to_erl0, 1);
170+
atomic_set(&sess_p->session_close, 1);
169171
spin_unlock(&sess_p->conn_lock);
170172
iscsit_inc_session_usage_count(sess_p);
171173
iscsit_stop_time2retain_timer(sess_p);
@@ -190,15 +192,13 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn)
190192
if (sess->session_state == TARG_SESS_STATE_FAILED) {
191193
spin_unlock_bh(&sess->conn_lock);
192194
iscsit_dec_session_usage_count(sess);
193-
iscsit_close_session(sess);
194195
return 0;
195196
}
196197
spin_unlock_bh(&sess->conn_lock);
197198

198199
iscsit_stop_session(sess, 1, 1);
199200
iscsit_dec_session_usage_count(sess);
200201

201-
iscsit_close_session(sess);
202202
return 0;
203203
}
204204

@@ -486,6 +486,7 @@ static int iscsi_login_non_zero_tsih_s2(
486486
sess_p = (struct iscsi_session *)se_sess->fabric_sess_ptr;
487487
if (atomic_read(&sess_p->session_fall_back_to_erl0) ||
488488
atomic_read(&sess_p->session_logout) ||
489+
atomic_read(&sess_p->session_close) ||
489490
(sess_p->time2retain_timer_flags & ISCSI_TF_EXPIRED))
490491
continue;
491492
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
@@ -676,7 +676,7 @@ struct iscsi_session {
676676
atomic_t session_logout;
677677
atomic_t session_reinstatement;
678678
atomic_t session_stop_active;
679-
atomic_t sleep_on_sess_wait_comp;
679+
atomic_t session_close;
680680
/* connection list */
681681
struct list_head sess_conn_list;
682682
struct list_head cr_active_list;

0 commit comments

Comments
 (0)