Skip to content

Commit 57c46e9

Browse files
maurizio-lombardimartinkpetersen
authored andcommitted
scsi: target: fix hang when multiple threads try to destroy the same iscsi session
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/20200313170656.9716-3-mlombard@redhat.com Reported-by: Matt Coleman <mcoleman@datto.com> Tested-by: Matt Coleman <mcoleman@datto.com> Tested-by: Rahul Kundu <rahul.kundu@chelsio.com> Signed-off-by: Maurizio Lombardi <mlombard@redhat.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
1 parent e49a7d9 commit 57c46e9

File tree

4 files changed

+30
-17
lines changed

4 files changed

+30
-17
lines changed

Diff for: drivers/target/iscsi/iscsi_target.c

+22-13
Original file line numberDiff line numberDiff line change
@@ -4307,30 +4307,37 @@ int iscsit_close_connection(
43074307
if (!atomic_read(&sess->session_reinstatement) &&
43084308
atomic_read(&sess->session_fall_back_to_erl0)) {
43094309
spin_unlock_bh(&sess->conn_lock);
4310+
complete_all(&sess->session_wait_comp);
43104311
iscsit_close_session(sess);
43114312

43124313
return 0;
43134314
} else if (atomic_read(&sess->session_logout)) {
43144315
pr_debug("Moving to TARG_SESS_STATE_FREE.\n");
43154316
sess->session_state = TARG_SESS_STATE_FREE;
4316-
spin_unlock_bh(&sess->conn_lock);
43174317

4318-
if (atomic_read(&sess->sleep_on_sess_wait_comp))
4319-
complete(&sess->session_wait_comp);
4318+
if (atomic_read(&sess->session_close)) {
4319+
spin_unlock_bh(&sess->conn_lock);
4320+
complete_all(&sess->session_wait_comp);
4321+
iscsit_close_session(sess);
4322+
} else {
4323+
spin_unlock_bh(&sess->conn_lock);
4324+
}
43204325

43214326
return 0;
43224327
} else {
43234328
pr_debug("Moving to TARG_SESS_STATE_FAILED.\n");
43244329
sess->session_state = TARG_SESS_STATE_FAILED;
43254330

4326-
if (!atomic_read(&sess->session_continuation)) {
4327-
spin_unlock_bh(&sess->conn_lock);
4331+
if (!atomic_read(&sess->session_continuation))
43284332
iscsit_start_time2retain_handler(sess);
4329-
} else
4330-
spin_unlock_bh(&sess->conn_lock);
43314333

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

43354342
return 0;
43364343
}
@@ -4436,9 +4443,9 @@ static void iscsit_logout_post_handler_closesession(
44364443
complete(&conn->conn_logout_comp);
44374444

44384445
iscsit_dec_conn_usage_count(conn);
4446+
atomic_set(&sess->session_close, 1);
44394447
iscsit_stop_session(sess, sleep, sleep);
44404448
iscsit_dec_session_usage_count(sess);
4441-
iscsit_close_session(sess);
44424449
}
44434450

44444451
static void iscsit_logout_post_handler_samecid(
@@ -4583,8 +4590,6 @@ void iscsit_stop_session(
45834590
int is_last;
45844591

45854592
spin_lock_bh(&sess->conn_lock);
4586-
if (session_sleep)
4587-
atomic_set(&sess->sleep_on_sess_wait_comp, 1);
45884593

45894594
if (connection_sleep) {
45904595
list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list,
@@ -4642,12 +4647,15 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
46424647
spin_lock(&sess->conn_lock);
46434648
if (atomic_read(&sess->session_fall_back_to_erl0) ||
46444649
atomic_read(&sess->session_logout) ||
4650+
atomic_read(&sess->session_close) ||
46454651
(sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
46464652
spin_unlock(&sess->conn_lock);
46474653
continue;
46484654
}
4655+
iscsit_inc_session_usage_count(sess);
46494656
atomic_set(&sess->session_reinstatement, 1);
46504657
atomic_set(&sess->session_fall_back_to_erl0, 1);
4658+
atomic_set(&sess->session_close, 1);
46514659
spin_unlock(&sess->conn_lock);
46524660

46534661
list_move_tail(&se_sess->sess_list, &free_list);
@@ -4657,8 +4665,9 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
46574665
list_for_each_entry_safe(se_sess, se_sess_tmp, &free_list, sess_list) {
46584666
sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
46594667

4668+
list_del_init(&se_sess->sess_list);
46604669
iscsit_stop_session(sess, 1, 1);
4661-
iscsit_close_session(sess);
4670+
iscsit_dec_session_usage_count(sess);
46624671
session_count++;
46634672
}
46644673

Diff for: drivers/target/iscsi/iscsi_target_configfs.c

+4-1
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)

Diff for: drivers/target/iscsi/iscsi_target_login.c

+3-2
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) &&

Diff for: include/target/iscsi/iscsi_target_core.h

+1-1
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)