Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 81a9c5e7 authored by Mikulas Patocka's avatar Mikulas Patocka Committed by Nicholas Bellinger
Browse files

iscsi-target: fix iscsit_del_np deadlock on unload



On uniprocessor preemptible kernel, target core deadlocks on unload. The
following events happen:
* iscsit_del_np is called
* it calls send_sig(SIGINT, np->np_thread, 1);
* the scheduler switches to the np_thread
* the np_thread is woken up, it sees that kthread_should_stop() returns
  false, so it doesn't terminate
* the np_thread clears signals with flush_signals(current); and goes back
  to sleep in iscsit_accept_np
* the scheduler switches back to iscsit_del_np
* iscsit_del_np calls kthread_stop(np->np_thread);
* the np_thread is waiting in iscsit_accept_np and it doesn't respond to
  kthread_stop

The deadlock could be resolved if the administrator sends SIGINT signal to
the np_thread with killall -INT iscsi_np

The reproducible deadlock was introduced in commit
db6077fd, but the thread-stopping code was
racy even before.

This patch fixes the problem. Using kthread_should_stop to stop the
np_thread is unreliable, so we test np_thread_state instead. If
np_thread_state equals ISCSI_NP_THREAD_SHUTDOWN, the thread exits.

Signed-off-by: default avatarMikulas Patocka <mpatocka@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarNicholas Bellinger <nab@linux-iscsi.org>
parent ac5ccdba
Loading
Loading
Loading
Loading
+7 −6
Original line number Diff line number Diff line
@@ -1216,7 +1216,7 @@ old_sess_out:
static int __iscsi_target_login_thread(struct iscsi_np *np)
{
	u8 *buffer, zero_tsih = 0;
	int ret = 0, rc, stop;
	int ret = 0, rc;
	struct iscsi_conn *conn = NULL;
	struct iscsi_login *login;
	struct iscsi_portal_group *tpg = NULL;
@@ -1230,6 +1230,9 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
	if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
		np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
		complete(&np->np_restart_comp);
	} else if (np->np_thread_state == ISCSI_NP_THREAD_SHUTDOWN) {
		spin_unlock_bh(&np->np_thread_lock);
		goto exit;
	} else {
		np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
	}
@@ -1422,10 +1425,8 @@ old_sess_out:
	}

out:
	stop = kthread_should_stop();
	/* Wait for another socket.. */
	if (!stop)
	return 1;

exit:
	iscsi_stop_login_thread_timer(np);
	spin_lock_bh(&np->np_thread_lock);
@@ -1442,7 +1443,7 @@ int iscsi_target_login_thread(void *arg)

	allow_signal(SIGINT);

	while (!kthread_should_stop()) {
	while (1) {
		ret = __iscsi_target_login_thread(np);
		/*
		 * We break and exit here unless another sock_accept() call