[PATCH 3.18 66/92] iscsi-target: Always wait for kthread_should_stop() before kthread exit

2017-08-09 Thread Greg Kroah-Hartman
3.18-stable review patch.  If anyone has any objections, please let me know.

--

From: Jiang Yi 

commit 5e0cf5e6c43b9e19fc0284f69e5cd2b4a47523b0 upstream.

There are three timing problems in the kthread usages of iscsi_target_mod:

 - np_thread of struct iscsi_np
 - rx_thread and tx_thread of struct iscsi_conn

In iscsit_close_connection(), it calls

 send_sig(SIGINT, conn->tx_thread, 1);
 kthread_stop(conn->tx_thread);

In conn->tx_thread, which is iscsi_target_tx_thread(), when it receive
SIGINT the kthread will exit without checking the return value of
kthread_should_stop().

So if iscsi_target_tx_thread() exit right between send_sig(SIGINT...)
and kthread_stop(...), the kthread_stop() will try to stop an already
stopped kthread.

This is invalid according to the documentation of kthread_stop().

(Fix -ECONNRESET logout handling in iscsi_target_tx_thread and
 early iscsi_target_rx_thread failure case - nab)

Signed-off-by: Jiang Yi 
Signed-off-by: Nicholas Bellinger 
Signed-off-by: Greg Kroah-Hartman 

---
 drivers/target/iscsi/iscsi_target.c   |   28 ++--
 drivers/target/iscsi/iscsi_target_erl0.c  |6 +-
 drivers/target/iscsi/iscsi_target_erl0.h  |2 +-
 drivers/target/iscsi/iscsi_target_login.c |4 
 4 files changed, 32 insertions(+), 8 deletions(-)

--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -3931,6 +3931,8 @@ int iscsi_target_tx_thread(void *arg)
 {
int ret = 0;
struct iscsi_conn *conn = arg;
+   bool conn_freed = false;
+
/*
 * Allow ourselves to be interrupted by SIGINT so that a
 * connection recovery / failure event can be triggered externally.
@@ -3956,12 +3958,14 @@ get_immediate:
goto transport_err;
 
ret = iscsit_handle_response_queue(conn);
-   if (ret == 1)
+   if (ret == 1) {
goto get_immediate;
-   else if (ret == -ECONNRESET)
+   } else if (ret == -ECONNRESET) {
+   conn_freed = true;
goto out;
-   else if (ret < 0)
+   } else if (ret < 0) {
goto transport_err;
+   }
}
 
 transport_err:
@@ -3971,8 +3975,13 @@ transport_err:
 * responsible for cleaning up the early connection failure.
 */
if (conn->conn_state != TARG_CONN_STATE_IN_LOGIN)
-   iscsit_take_action_for_connection_exit(conn);
+   iscsit_take_action_for_connection_exit(conn, _freed);
 out:
+   if (!conn_freed) {
+   while (!kthread_should_stop()) {
+   msleep(100);
+   }
+   }
return 0;
 }
 
@@ -4073,6 +4082,7 @@ int iscsi_target_rx_thread(void *arg)
u32 checksum = 0, digest = 0;
struct iscsi_conn *conn = arg;
struct kvec iov;
+   bool conn_freed = false;
/*
 * Allow ourselves to be interrupted by SIGINT so that a
 * connection recovery / failure event can be triggered externally.
@@ -4084,7 +4094,7 @@ int iscsi_target_rx_thread(void *arg)
 */
rc = wait_for_completion_interruptible(>rx_login_comp);
if (rc < 0 || iscsi_target_check_conn_state(conn))
-   return 0;
+   goto out;
 
if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) {
struct completion comp;
@@ -4169,7 +4179,13 @@ int iscsi_target_rx_thread(void *arg)
 transport_err:
if (!signal_pending(current))
atomic_set(>transport_failed, 1);
-   iscsit_take_action_for_connection_exit(conn);
+   iscsit_take_action_for_connection_exit(conn, _freed);
+out:
+   if (!conn_freed) {
+   while (!kthread_should_stop()) {
+   msleep(100);
+   }
+   }
return 0;
 }
 
--- a/drivers/target/iscsi/iscsi_target_erl0.c
+++ b/drivers/target/iscsi/iscsi_target_erl0.c
@@ -931,8 +931,10 @@ static void iscsit_handle_connection_cle
}
 }
 
-void iscsit_take_action_for_connection_exit(struct iscsi_conn *conn)
+void iscsit_take_action_for_connection_exit(struct iscsi_conn *conn, bool 
*conn_freed)
 {
+   *conn_freed = false;
+
spin_lock_bh(>state_lock);
if (atomic_read(>connection_exit)) {
spin_unlock_bh(>state_lock);
@@ -943,6 +945,7 @@ void iscsit_take_action_for_connection_e
if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT) {
spin_unlock_bh(>state_lock);
iscsit_close_connection(conn);
+   *conn_freed = true;
return;
}
 
@@ -956,6 +959,7 @@ void iscsit_take_action_for_connection_e
spin_unlock_bh(>state_lock);
 
iscsit_handle_connection_cleanup(conn);
+   *conn_freed = 

[PATCH 3.18 66/92] iscsi-target: Always wait for kthread_should_stop() before kthread exit

2017-08-09 Thread Greg Kroah-Hartman
3.18-stable review patch.  If anyone has any objections, please let me know.

--

From: Jiang Yi 

commit 5e0cf5e6c43b9e19fc0284f69e5cd2b4a47523b0 upstream.

There are three timing problems in the kthread usages of iscsi_target_mod:

 - np_thread of struct iscsi_np
 - rx_thread and tx_thread of struct iscsi_conn

In iscsit_close_connection(), it calls

 send_sig(SIGINT, conn->tx_thread, 1);
 kthread_stop(conn->tx_thread);

In conn->tx_thread, which is iscsi_target_tx_thread(), when it receive
SIGINT the kthread will exit without checking the return value of
kthread_should_stop().

So if iscsi_target_tx_thread() exit right between send_sig(SIGINT...)
and kthread_stop(...), the kthread_stop() will try to stop an already
stopped kthread.

This is invalid according to the documentation of kthread_stop().

(Fix -ECONNRESET logout handling in iscsi_target_tx_thread and
 early iscsi_target_rx_thread failure case - nab)

Signed-off-by: Jiang Yi 
Signed-off-by: Nicholas Bellinger 
Signed-off-by: Greg Kroah-Hartman 

---
 drivers/target/iscsi/iscsi_target.c   |   28 ++--
 drivers/target/iscsi/iscsi_target_erl0.c  |6 +-
 drivers/target/iscsi/iscsi_target_erl0.h  |2 +-
 drivers/target/iscsi/iscsi_target_login.c |4 
 4 files changed, 32 insertions(+), 8 deletions(-)

--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -3931,6 +3931,8 @@ int iscsi_target_tx_thread(void *arg)
 {
int ret = 0;
struct iscsi_conn *conn = arg;
+   bool conn_freed = false;
+
/*
 * Allow ourselves to be interrupted by SIGINT so that a
 * connection recovery / failure event can be triggered externally.
@@ -3956,12 +3958,14 @@ get_immediate:
goto transport_err;
 
ret = iscsit_handle_response_queue(conn);
-   if (ret == 1)
+   if (ret == 1) {
goto get_immediate;
-   else if (ret == -ECONNRESET)
+   } else if (ret == -ECONNRESET) {
+   conn_freed = true;
goto out;
-   else if (ret < 0)
+   } else if (ret < 0) {
goto transport_err;
+   }
}
 
 transport_err:
@@ -3971,8 +3975,13 @@ transport_err:
 * responsible for cleaning up the early connection failure.
 */
if (conn->conn_state != TARG_CONN_STATE_IN_LOGIN)
-   iscsit_take_action_for_connection_exit(conn);
+   iscsit_take_action_for_connection_exit(conn, _freed);
 out:
+   if (!conn_freed) {
+   while (!kthread_should_stop()) {
+   msleep(100);
+   }
+   }
return 0;
 }
 
@@ -4073,6 +4082,7 @@ int iscsi_target_rx_thread(void *arg)
u32 checksum = 0, digest = 0;
struct iscsi_conn *conn = arg;
struct kvec iov;
+   bool conn_freed = false;
/*
 * Allow ourselves to be interrupted by SIGINT so that a
 * connection recovery / failure event can be triggered externally.
@@ -4084,7 +4094,7 @@ int iscsi_target_rx_thread(void *arg)
 */
rc = wait_for_completion_interruptible(>rx_login_comp);
if (rc < 0 || iscsi_target_check_conn_state(conn))
-   return 0;
+   goto out;
 
if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) {
struct completion comp;
@@ -4169,7 +4179,13 @@ int iscsi_target_rx_thread(void *arg)
 transport_err:
if (!signal_pending(current))
atomic_set(>transport_failed, 1);
-   iscsit_take_action_for_connection_exit(conn);
+   iscsit_take_action_for_connection_exit(conn, _freed);
+out:
+   if (!conn_freed) {
+   while (!kthread_should_stop()) {
+   msleep(100);
+   }
+   }
return 0;
 }
 
--- a/drivers/target/iscsi/iscsi_target_erl0.c
+++ b/drivers/target/iscsi/iscsi_target_erl0.c
@@ -931,8 +931,10 @@ static void iscsit_handle_connection_cle
}
 }
 
-void iscsit_take_action_for_connection_exit(struct iscsi_conn *conn)
+void iscsit_take_action_for_connection_exit(struct iscsi_conn *conn, bool 
*conn_freed)
 {
+   *conn_freed = false;
+
spin_lock_bh(>state_lock);
if (atomic_read(>connection_exit)) {
spin_unlock_bh(>state_lock);
@@ -943,6 +945,7 @@ void iscsit_take_action_for_connection_e
if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT) {
spin_unlock_bh(>state_lock);
iscsit_close_connection(conn);
+   *conn_freed = true;
return;
}
 
@@ -956,6 +959,7 @@ void iscsit_take_action_for_connection_e
spin_unlock_bh(>state_lock);
 
iscsit_handle_connection_cleanup(conn);
+   *conn_freed = true;
 }
 
 /*
--- a/drivers/target/iscsi/iscsi_target_erl0.h
+++