Skip to content

Commit 4d7e37a

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 1140ef7 commit 4d7e37a

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
@@ -4314,30 +4314,37 @@ int iscsit_close_connection(
43144314
if (!atomic_read(&sess->session_reinstatement) &&
43154315
atomic_read(&sess->session_fall_back_to_erl0)) {
43164316
spin_unlock_bh(&sess->conn_lock);
4317+
complete_all(&sess->session_wait_comp);
43174318
iscsit_close_session(sess);
43184319

43194320
return 0;
43204321
} else if (atomic_read(&sess->session_logout)) {
43214322
pr_debug("Moving to TARG_SESS_STATE_FREE.\n");
43224323
sess->session_state = TARG_SESS_STATE_FREE;
4323-
spin_unlock_bh(&sess->conn_lock);
43244324

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

43284333
return 0;
43294334
} else {
43304335
pr_debug("Moving to TARG_SESS_STATE_FAILED.\n");
43314336
sess->session_state = TARG_SESS_STATE_FAILED;
43324337

4333-
if (!atomic_read(&sess->session_continuation)) {
4334-
spin_unlock_bh(&sess->conn_lock);
4338+
if (!atomic_read(&sess->session_continuation))
43354339
iscsit_start_time2retain_handler(sess);
4336-
} else
4337-
spin_unlock_bh(&sess->conn_lock);
43384340

4339-
if (atomic_read(&sess->sleep_on_sess_wait_comp))
4340-
complete(&sess->session_wait_comp);
4341+
if (atomic_read(&sess->session_close)) {
4342+
spin_unlock_bh(&sess->conn_lock);
4343+
complete_all(&sess->session_wait_comp);
4344+
iscsit_close_session(sess);
4345+
} else {
4346+
spin_unlock_bh(&sess->conn_lock);
4347+
}
43414348

43424349
return 0;
43434350
}
@@ -4446,9 +4453,9 @@ static void iscsit_logout_post_handler_closesession(
44464453
complete(&conn->conn_logout_comp);
44474454

44484455
iscsit_dec_conn_usage_count(conn);
4456+
atomic_set(&sess->session_close, 1);
44494457
iscsit_stop_session(sess, sleep, sleep);
44504458
iscsit_dec_session_usage_count(sess);
4451-
iscsit_close_session(sess);
44524459
}
44534460

44544461
static void iscsit_logout_post_handler_samecid(
@@ -4593,8 +4600,6 @@ void iscsit_stop_session(
45934600
int is_last;
45944601

45954602
spin_lock_bh(&sess->conn_lock);
4596-
if (session_sleep)
4597-
atomic_set(&sess->sleep_on_sess_wait_comp, 1);
45984603

45994604
if (connection_sleep) {
46004605
list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list,
@@ -4652,12 +4657,15 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
46524657
spin_lock(&sess->conn_lock);
46534658
if (atomic_read(&sess->session_fall_back_to_erl0) ||
46544659
atomic_read(&sess->session_logout) ||
4660+
atomic_read(&sess->session_close) ||
46554661
(sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
46564662
spin_unlock(&sess->conn_lock);
46574663
continue;
46584664
}
4665+
iscsit_inc_session_usage_count(sess);
46594666
atomic_set(&sess->session_reinstatement, 1);
46604667
atomic_set(&sess->session_fall_back_to_erl0, 1);
4668+
atomic_set(&sess->session_close, 1);
46614669
spin_unlock(&sess->conn_lock);
46624670

46634671
list_move_tail(&se_sess->sess_list, &free_list);
@@ -4667,8 +4675,9 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
46674675
list_for_each_entry_safe(se_sess, se_sess_tmp, &free_list, sess_list) {
46684676
sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
46694677

4678+
list_del_init(&se_sess->sess_list);
46704679
iscsit_stop_session(sess, 1, 1);
4671-
iscsit_close_session(sess);
4680+
iscsit_dec_session_usage_count(sess);
46724681
session_count++;
46734682
}
46744683

drivers/target/iscsi/iscsi_target_configfs.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1503,20 +1503,23 @@ static void lio_tpg_close_session(struct se_session *se_sess)
15031503
spin_lock(&sess->conn_lock);
15041504
if (atomic_read(&sess->session_fall_back_to_erl0) ||
15051505
atomic_read(&sess->session_logout) ||
1506+
atomic_read(&sess->session_close) ||
15061507
(sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
15071508
spin_unlock(&sess->conn_lock);
15081509
spin_unlock_bh(&se_tpg->session_lock);
15091510
return;
15101511
}
1512+
iscsit_inc_session_usage_count(sess);
15111513
atomic_set(&sess->session_reinstatement, 1);
15121514
atomic_set(&sess->session_fall_back_to_erl0, 1);
1515+
atomic_set(&sess->session_close, 1);
15131516
spin_unlock(&sess->conn_lock);
15141517

15151518
iscsit_stop_time2retain_timer(sess);
15161519
spin_unlock_bh(&se_tpg->session_lock);
15171520

15181521
iscsit_stop_session(sess, 1, 1);
1519-
iscsit_close_session(sess);
1522+
iscsit_dec_session_usage_count(sess);
15201523
}
15211524

15221525
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
@@ -199,6 +199,7 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn)
199199
spin_lock(&sess_p->conn_lock);
200200
if (atomic_read(&sess_p->session_fall_back_to_erl0) ||
201201
atomic_read(&sess_p->session_logout) ||
202+
atomic_read(&sess_p->session_close) ||
202203
(sess_p->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
203204
spin_unlock(&sess_p->conn_lock);
204205
continue;
@@ -209,6 +210,7 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn)
209210
(sess_p->sess_ops->SessionType == sessiontype))) {
210211
atomic_set(&sess_p->session_reinstatement, 1);
211212
atomic_set(&sess_p->session_fall_back_to_erl0, 1);
213+
atomic_set(&sess_p->session_close, 1);
212214
spin_unlock(&sess_p->conn_lock);
213215
iscsit_inc_session_usage_count(sess_p);
214216
iscsit_stop_time2retain_timer(sess_p);
@@ -233,15 +235,13 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn)
233235
if (sess->session_state == TARG_SESS_STATE_FAILED) {
234236
spin_unlock_bh(&sess->conn_lock);
235237
iscsit_dec_session_usage_count(sess);
236-
iscsit_close_session(sess);
237238
return 0;
238239
}
239240
spin_unlock_bh(&sess->conn_lock);
240241

241242
iscsit_stop_session(sess, 1, 1);
242243
iscsit_dec_session_usage_count(sess);
243244

244-
iscsit_close_session(sess);
245245
return 0;
246246
}
247247

@@ -534,6 +534,7 @@ static int iscsi_login_non_zero_tsih_s2(
534534
sess_p = (struct iscsi_session *)se_sess->fabric_sess_ptr;
535535
if (atomic_read(&sess_p->session_fall_back_to_erl0) ||
536536
atomic_read(&sess_p->session_logout) ||
537+
atomic_read(&sess_p->session_close) ||
537538
(sess_p->time2retain_timer_flags & ISCSI_TF_EXPIRED))
538539
continue;
539540
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
@@ -673,7 +673,7 @@ struct iscsi_session {
673673
atomic_t session_logout;
674674
atomic_t session_reinstatement;
675675
atomic_t session_stop_active;
676-
atomic_t sleep_on_sess_wait_comp;
676+
atomic_t session_close;
677677
/* connection list */
678678
struct list_head sess_conn_list;
679679
struct list_head cr_active_list;

0 commit comments

Comments
 (0)