Skip to content

Commit 856a74f

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 hardkernel#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 992e469 commit 856a74f

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
@@ -4303,30 +4303,37 @@ int iscsit_close_connection(
43034303
if (!atomic_read(&sess->session_reinstatement) &&
43044304
atomic_read(&sess->session_fall_back_to_erl0)) {
43054305
spin_unlock_bh(&sess->conn_lock);
4306+
complete_all(&sess->session_wait_comp);
43064307
iscsit_close_session(sess);
43074308

43084309
return 0;
43094310
} else if (atomic_read(&sess->session_logout)) {
43104311
pr_debug("Moving to TARG_SESS_STATE_FREE.\n");
43114312
sess->session_state = TARG_SESS_STATE_FREE;
4312-
spin_unlock_bh(&sess->conn_lock);
43134313

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

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

4322-
if (!atomic_read(&sess->session_continuation)) {
4323-
spin_unlock_bh(&sess->conn_lock);
4327+
if (!atomic_read(&sess->session_continuation))
43244328
iscsit_start_time2retain_handler(sess);
4325-
} else
4326-
spin_unlock_bh(&sess->conn_lock);
43274329

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

43314338
return 0;
43324339
}
@@ -4432,9 +4439,9 @@ static void iscsit_logout_post_handler_closesession(
44324439
complete(&conn->conn_logout_comp);
44334440

44344441
iscsit_dec_conn_usage_count(conn);
4442+
atomic_set(&sess->session_close, 1);
44354443
iscsit_stop_session(sess, sleep, sleep);
44364444
iscsit_dec_session_usage_count(sess);
4437-
iscsit_close_session(sess);
44384445
}
44394446

44404447
static void iscsit_logout_post_handler_samecid(
@@ -4579,8 +4586,6 @@ void iscsit_stop_session(
45794586
int is_last;
45804587

45814588
spin_lock_bh(&sess->conn_lock);
4582-
if (session_sleep)
4583-
atomic_set(&sess->sleep_on_sess_wait_comp, 1);
45844589

45854590
if (connection_sleep) {
45864591
list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list,
@@ -4638,12 +4643,15 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
46384643
spin_lock(&sess->conn_lock);
46394644
if (atomic_read(&sess->session_fall_back_to_erl0) ||
46404645
atomic_read(&sess->session_logout) ||
4646+
atomic_read(&sess->session_close) ||
46414647
(sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
46424648
spin_unlock(&sess->conn_lock);
46434649
continue;
46444650
}
4651+
iscsit_inc_session_usage_count(sess);
46454652
atomic_set(&sess->session_reinstatement, 1);
46464653
atomic_set(&sess->session_fall_back_to_erl0, 1);
4654+
atomic_set(&sess->session_close, 1);
46474655
spin_unlock(&sess->conn_lock);
46484656

46494657
list_move_tail(&se_sess->sess_list, &free_list);
@@ -4653,8 +4661,9 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
46534661
list_for_each_entry_safe(se_sess, se_sess_tmp, &free_list, sess_list) {
46544662
sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
46554663

4664+
list_del_init(&se_sess->sess_list);
46564665
iscsit_stop_session(sess, 1, 1);
4657-
iscsit_close_session(sess);
4666+
iscsit_dec_session_usage_count(sess);
46584667
session_count++;
46594668
}
46604669

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)