--- /dev/null
+From 5e0cf5e6c43b9e19fc0284f69e5cd2b4a47523b0 Mon Sep 17 00:00:00 2001
+From: Jiang Yi <jiangyilism@gmail.com>
+Date: Tue, 16 May 2017 17:57:55 +0800
+Subject: iscsi-target: Always wait for kthread_should_stop() before kthread exit
+
+From: Jiang Yi <jiangyilism@gmail.com>
+
+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 <jiangyilism@gmail.com>
+Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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, &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(&conn->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(&conn->transport_failed, 1);
+- iscsit_take_action_for_connection_exit(conn);
++ iscsit_take_action_for_connection_exit(conn, &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(&conn->state_lock);
+ if (atomic_read(&conn->connection_exit)) {
+ spin_unlock_bh(&conn->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(&conn->state_lock);
+ iscsit_close_connection(conn);
++ *conn_freed = true;
+ return;
+ }
+
+@@ -956,6 +959,7 @@ void iscsit_take_action_for_connection_e
+ spin_unlock_bh(&conn->state_lock);
+
+ iscsit_handle_connection_cleanup(conn);
++ *conn_freed = true;
+ }
+
+ /*
+--- a/drivers/target/iscsi/iscsi_target_erl0.h
++++ b/drivers/target/iscsi/iscsi_target_erl0.h
+@@ -9,7 +9,7 @@ extern int iscsit_stop_time2retain_timer
+ extern void iscsit_connection_reinstatement_rcfr(struct iscsi_conn *);
+ extern void iscsit_cause_connection_reinstatement(struct iscsi_conn *, int);
+ extern void iscsit_fall_back_to_erl0(struct iscsi_session *);
+-extern void iscsit_take_action_for_connection_exit(struct iscsi_conn *);
++extern void iscsit_take_action_for_connection_exit(struct iscsi_conn *, bool *);
+ extern int iscsit_recover_from_unknown_opcode(struct iscsi_conn *);
+
+ #endif /*** ISCSI_TARGET_ERL0_H ***/
+--- a/drivers/target/iscsi/iscsi_target_login.c
++++ b/drivers/target/iscsi/iscsi_target_login.c
+@@ -1492,5 +1492,9 @@ int iscsi_target_login_thread(void *arg)
+ break;
+ }
+
++ while (!kthread_should_stop()) {
++ msleep(100);
++ }
++
+ return 0;
+ }
--- /dev/null
+From 105fa2f44e504c830697b0c794822112d79808dc Mon Sep 17 00:00:00 2001
+From: Nicholas Bellinger <nab@linux-iscsi.org>
+Date: Sat, 3 Jun 2017 05:35:47 -0700
+Subject: iscsi-target: Fix delayed logout processing greater than SECONDS_FOR_LOGOUT_COMP
+
+From: Nicholas Bellinger <nab@linux-iscsi.org>
+
+commit 105fa2f44e504c830697b0c794822112d79808dc upstream.
+
+This patch fixes a BUG() in iscsit_close_session() that could be
+triggered when iscsit_logout_post_handler() execution from within
+tx thread context was not run for more than SECONDS_FOR_LOGOUT_COMP
+(15 seconds), and the TCP connection didn't already close before
+then forcing tx thread context to automatically exit.
+
+This would manifest itself during explicit logout as:
+
+[33206.974254] 1 connection(s) still exist for iSCSI session to iqn.1993-08.org.debian:01:3f5523242179
+[33206.980184] INFO: NMI handler (kgdb_nmi_handler) took too long to run: 2100.772 msecs
+[33209.078643] ------------[ cut here ]------------
+[33209.078646] kernel BUG at drivers/target/iscsi/iscsi_target.c:4346!
+
+Normally when explicit logout attempt fails, the tx thread context
+exits and iscsit_close_connection() from rx thread context does the
+extra cleanup once it detects conn->conn_logout_remove has not been
+cleared by the logout type specific post handlers.
+
+To address this special case, if the logout post handler in tx thread
+context detects conn->tx_thread_active has already been cleared, simply
+return and exit in order for existing iscsit_close_connection()
+logic from rx thread context do failed logout cleanup.
+
+Reported-by: Bart Van Assche <bart.vanassche@sandisk.com>
+Tested-by: Bart Van Assche <bart.vanassche@sandisk.com>
+Cc: Mike Christie <mchristi@redhat.com>
+Cc: Hannes Reinecke <hare@suse.de>
+Cc: Sagi Grimberg <sagig@mellanox.com>
+Tested-by: Gary Guo <ghg@datera.io>
+Tested-by: Chu Yuan Lin <cyl@datera.io>
+Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+---
+ drivers/target/iscsi/iscsi_target.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+--- a/drivers/target/iscsi/iscsi_target.c
++++ b/drivers/target/iscsi/iscsi_target.c
+@@ -4561,8 +4561,11 @@ static void iscsit_logout_post_handler_c
+ * always sleep waiting for RX/TX thread shutdown to complete
+ * within iscsit_close_connection().
+ */
+- if (conn->conn_transport->transport_type == ISCSI_TCP)
++ if (conn->conn_transport->transport_type == ISCSI_TCP) {
+ sleep = cmpxchg(&conn->tx_thread_active, true, false);
++ if (!sleep)
++ return;
++ }
+
+ atomic_set(&conn->conn_logout_remove, 0);
+ complete(&conn->conn_logout_comp);
+@@ -4578,8 +4581,11 @@ static void iscsit_logout_post_handler_s
+ {
+ int sleep = 1;
+
+- if (conn->conn_transport->transport_type == ISCSI_TCP)
++ if (conn->conn_transport->transport_type == ISCSI_TCP) {
+ sleep = cmpxchg(&conn->tx_thread_active, true, false);
++ if (!sleep)
++ return;
++ }
+
+ atomic_set(&conn->conn_logout_remove, 0);
+ complete(&conn->conn_logout_comp);
--- /dev/null
+From 8f0dfb3d8b1120c61f6e2cc3729290db10772b2d Mon Sep 17 00:00:00 2001
+From: Nicholas Bellinger <nab@linux-iscsi.org>
+Date: Sat, 27 Feb 2016 18:15:46 -0800
+Subject: iscsi-target: Fix early sk_data_ready LOGIN_FLAGS_READY race
+
+From: Nicholas Bellinger <nab@linux-iscsi.org>
+
+commit 8f0dfb3d8b1120c61f6e2cc3729290db10772b2d upstream.
+
+There is a iscsi-target/tcp login race in LOGIN_FLAGS_READY
+state assignment that can result in frequent errors during
+iscsi discovery:
+
+ "iSCSI Login negotiation failed."
+
+To address this bug, move the initial LOGIN_FLAGS_READY
+assignment ahead of iscsi_target_do_login() when handling
+the initial iscsi_target_start_negotiation() request PDU
+during connection login.
+
+As iscsi_target_do_login_rx() work_struct callback is
+clearing LOGIN_FLAGS_READ_ACTIVE after subsequent calls
+to iscsi_target_do_login(), the early sk_data_ready
+ahead of the first iscsi_target_do_login() expects
+LOGIN_FLAGS_READY to also be set for the initial
+login request PDU.
+
+As reported by Maged, this was first obsered using an
+MSFT initiator running across multiple VMWare host
+virtual machines with iscsi-target/tcp.
+
+Reported-by: Maged Mokhtar <mmokhtar@binarykinetics.com>
+Tested-by: Maged Mokhtar <mmokhtar@binarykinetics.com>
+Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+---
+ drivers/target/iscsi/iscsi_target_nego.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+--- a/drivers/target/iscsi/iscsi_target_nego.c
++++ b/drivers/target/iscsi/iscsi_target_nego.c
+@@ -1251,16 +1251,16 @@ int iscsi_target_start_negotiation(
+ {
+ int ret;
+
+- ret = iscsi_target_do_login(conn, login);
+- if (!ret) {
+- if (conn->sock) {
+- struct sock *sk = conn->sock->sk;
++ if (conn->sock) {
++ struct sock *sk = conn->sock->sk;
+
+- write_lock_bh(&sk->sk_callback_lock);
+- set_bit(LOGIN_FLAGS_READY, &conn->login_flags);
+- write_unlock_bh(&sk->sk_callback_lock);
+- }
+- } else if (ret < 0) {
++ write_lock_bh(&sk->sk_callback_lock);
++ set_bit(LOGIN_FLAGS_READY, &conn->login_flags);
++ write_unlock_bh(&sk->sk_callback_lock);
++ }
++
++ ret = iscsi_target_do_login(conn, login);
++ if (ret < 0) {
+ cancel_delayed_work_sync(&conn->login_work);
+ cancel_delayed_work_sync(&conn->login_cleanup_work);
+ iscsi_target_restore_sock_callbacks(conn);
--- /dev/null
+From 25cdda95fda78d22d44157da15aa7ea34be3c804 Mon Sep 17 00:00:00 2001
+From: Nicholas Bellinger <nab@linux-iscsi.org>
+Date: Wed, 24 May 2017 21:47:09 -0700
+Subject: iscsi-target: Fix initial login PDU asynchronous socket close OOPs
+
+From: Nicholas Bellinger <nab@linux-iscsi.org>
+
+commit 25cdda95fda78d22d44157da15aa7ea34be3c804 upstream.
+
+This patch fixes a OOPs originally introduced by:
+
+ commit bb048357dad6d604520c91586334c9c230366a14
+ Author: Nicholas Bellinger <nab@linux-iscsi.org>
+ Date: Thu Sep 5 14:54:04 2013 -0700
+
+ iscsi-target: Add sk->sk_state_change to cleanup after TCP failure
+
+which would trigger a NULL pointer dereference when a TCP connection
+was closed asynchronously via iscsi_target_sk_state_change(), but only
+when the initial PDU processing in iscsi_target_do_login() from iscsi_np
+process context was blocked waiting for backend I/O to complete.
+
+To address this issue, this patch makes the following changes.
+
+First, it introduces some common helper functions used for checking
+socket closing state, checking login_flags, and atomically checking
+socket closing state + setting login_flags.
+
+Second, it introduces a LOGIN_FLAGS_INITIAL_PDU bit to know when a TCP
+connection has dropped via iscsi_target_sk_state_change(), but the
+initial PDU processing within iscsi_target_do_login() in iscsi_np
+context is still running. For this case, it sets LOGIN_FLAGS_CLOSED,
+but doesn't invoke schedule_delayed_work().
+
+The original NULL pointer dereference case reported by MNC is now handled
+by iscsi_target_do_login() doing a iscsi_target_sk_check_close() before
+transitioning to FFP to determine when the socket has already closed,
+or iscsi_target_start_negotiation() if the login needs to exchange
+more PDUs (eg: iscsi_target_do_login returned 0) but the socket has
+closed. For both of these cases, the cleanup up of remaining connection
+resources will occur in iscsi_target_start_negotiation() from iscsi_np
+process context once the failure is detected.
+
+Finally, to handle to case where iscsi_target_sk_state_change() is
+called after the initial PDU procesing is complete, it now invokes
+conn->login_work -> iscsi_target_do_login_rx() to perform cleanup once
+existing iscsi_target_sk_check_close() checks detect connection failure.
+For this case, the cleanup of remaining connection resources will occur
+in iscsi_target_do_login_rx() from delayed workqueue process context
+once the failure is detected.
+
+Reported-by: Mike Christie <mchristi@redhat.com>
+Reviewed-by: Mike Christie <mchristi@redhat.com>
+Tested-by: Mike Christie <mchristi@redhat.com>
+Cc: Mike Christie <mchristi@redhat.com>
+Reported-by: Hannes Reinecke <hare@suse.com>
+Cc: Hannes Reinecke <hare@suse.com>
+Cc: Sagi Grimberg <sagi@grimberg.me>
+Cc: Varun Prakash <varun@chelsio.com>
+Cc: <stable@vger.kernel.org> # v3.12+
+Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/target/iscsi/iscsi_target_core.h | 1
+ drivers/target/iscsi/iscsi_target_nego.c | 204 ++++++++++++++++++++-----------
+ 2 files changed, 138 insertions(+), 67 deletions(-)
+
+--- a/drivers/target/iscsi/iscsi_target_core.h
++++ b/drivers/target/iscsi/iscsi_target_core.h
+@@ -562,6 +562,7 @@ struct iscsi_conn {
+ #define LOGIN_FLAGS_READ_ACTIVE 1
+ #define LOGIN_FLAGS_CLOSED 2
+ #define LOGIN_FLAGS_READY 4
++#define LOGIN_FLAGS_INITIAL_PDU 8
+ unsigned long login_flags;
+ struct delayed_work login_work;
+ struct delayed_work login_cleanup_work;
+--- a/drivers/target/iscsi/iscsi_target_nego.c
++++ b/drivers/target/iscsi/iscsi_target_nego.c
+@@ -494,14 +494,60 @@ static void iscsi_target_restore_sock_ca
+
+ static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *);
+
+-static bool iscsi_target_sk_state_check(struct sock *sk)
++static bool __iscsi_target_sk_check_close(struct sock *sk)
+ {
+ if (sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) {
+- pr_debug("iscsi_target_sk_state_check: TCP_CLOSE_WAIT|TCP_CLOSE,"
++ pr_debug("__iscsi_target_sk_check_close: TCP_CLOSE_WAIT|TCP_CLOSE,"
+ "returning FALSE\n");
+- return false;
++ return true;
+ }
+- return true;
++ return false;
++}
++
++static bool iscsi_target_sk_check_close(struct iscsi_conn *conn)
++{
++ bool state = false;
++
++ if (conn->sock) {
++ struct sock *sk = conn->sock->sk;
++
++ read_lock_bh(&sk->sk_callback_lock);
++ state = (__iscsi_target_sk_check_close(sk) ||
++ test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags));
++ read_unlock_bh(&sk->sk_callback_lock);
++ }
++ return state;
++}
++
++static bool iscsi_target_sk_check_flag(struct iscsi_conn *conn, unsigned int flag)
++{
++ bool state = false;
++
++ if (conn->sock) {
++ struct sock *sk = conn->sock->sk;
++
++ read_lock_bh(&sk->sk_callback_lock);
++ state = test_bit(flag, &conn->login_flags);
++ read_unlock_bh(&sk->sk_callback_lock);
++ }
++ return state;
++}
++
++static bool iscsi_target_sk_check_and_clear(struct iscsi_conn *conn, unsigned int flag)
++{
++ bool state = false;
++
++ if (conn->sock) {
++ struct sock *sk = conn->sock->sk;
++
++ write_lock_bh(&sk->sk_callback_lock);
++ state = (__iscsi_target_sk_check_close(sk) ||
++ test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags));
++ if (!state)
++ clear_bit(flag, &conn->login_flags);
++ write_unlock_bh(&sk->sk_callback_lock);
++ }
++ return state;
+ }
+
+ static void iscsi_target_login_drop(struct iscsi_conn *conn, struct iscsi_login *login)
+@@ -541,6 +587,20 @@ static void iscsi_target_do_login_rx(str
+
+ pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n",
+ conn, current->comm, current->pid);
++ /*
++ * If iscsi_target_do_login_rx() has been invoked by ->sk_data_ready()
++ * before initial PDU processing in iscsi_target_start_negotiation()
++ * has completed, go ahead and retry until it's cleared.
++ *
++ * Otherwise if the TCP connection drops while this is occuring,
++ * iscsi_target_start_negotiation() will detect the failure, call
++ * cancel_delayed_work_sync(&conn->login_work), and cleanup the
++ * remaining iscsi connection resources from iscsi_np process context.
++ */
++ if (iscsi_target_sk_check_flag(conn, LOGIN_FLAGS_INITIAL_PDU)) {
++ schedule_delayed_work(&conn->login_work, msecs_to_jiffies(10));
++ return;
++ }
+
+ spin_lock(&tpg->tpg_state_lock);
+ state = (tpg->tpg_state == TPG_STATE_ACTIVE);
+@@ -548,26 +608,12 @@ static void iscsi_target_do_login_rx(str
+
+ if (!state) {
+ pr_debug("iscsi_target_do_login_rx: tpg_state != TPG_STATE_ACTIVE\n");
+- iscsi_target_restore_sock_callbacks(conn);
+- iscsi_target_login_drop(conn, login);
+- iscsit_deaccess_np(np, tpg, tpg_np);
+- return;
++ goto err;
+ }
+
+- if (conn->sock) {
+- struct sock *sk = conn->sock->sk;
+-
+- read_lock_bh(&sk->sk_callback_lock);
+- state = iscsi_target_sk_state_check(sk);
+- read_unlock_bh(&sk->sk_callback_lock);
+-
+- if (!state) {
+- pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n");
+- iscsi_target_restore_sock_callbacks(conn);
+- iscsi_target_login_drop(conn, login);
+- iscsit_deaccess_np(np, tpg, tpg_np);
+- return;
+- }
++ if (iscsi_target_sk_check_close(conn)) {
++ pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n");
++ goto err;
+ }
+
+ conn->login_kworker = current;
+@@ -585,34 +631,29 @@ static void iscsi_target_do_login_rx(str
+ flush_signals(current);
+ conn->login_kworker = NULL;
+
+- if (rc < 0) {
+- iscsi_target_restore_sock_callbacks(conn);
+- iscsi_target_login_drop(conn, login);
+- iscsit_deaccess_np(np, tpg, tpg_np);
+- return;
+- }
++ if (rc < 0)
++ goto err;
+
+ pr_debug("iscsi_target_do_login_rx after rx_login_io, %p, %s:%d\n",
+ conn, current->comm, current->pid);
+
+ rc = iscsi_target_do_login(conn, login);
+ if (rc < 0) {
+- iscsi_target_restore_sock_callbacks(conn);
+- iscsi_target_login_drop(conn, login);
+- iscsit_deaccess_np(np, tpg, tpg_np);
++ goto err;
+ } else if (!rc) {
+- if (conn->sock) {
+- struct sock *sk = conn->sock->sk;
+-
+- write_lock_bh(&sk->sk_callback_lock);
+- clear_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags);
+- write_unlock_bh(&sk->sk_callback_lock);
+- }
++ if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_READ_ACTIVE))
++ goto err;
+ } else if (rc == 1) {
+ iscsi_target_nego_release(conn);
+ iscsi_post_login_handler(np, conn, zero_tsih);
+ iscsit_deaccess_np(np, tpg, tpg_np);
+ }
++ return;
++
++err:
++ iscsi_target_restore_sock_callbacks(conn);
++ iscsi_target_login_drop(conn, login);
++ iscsit_deaccess_np(np, tpg, tpg_np);
+ }
+
+ static void iscsi_target_do_cleanup(struct work_struct *work)
+@@ -660,31 +701,54 @@ static void iscsi_target_sk_state_change
+ orig_state_change(sk);
+ return;
+ }
++ state = __iscsi_target_sk_check_close(sk);
++ pr_debug("__iscsi_target_sk_close_change: state: %d\n", state);
++
+ if (test_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags)) {
+ pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1 sk_state_change"
+ " conn: %p\n", conn);
++ if (state)
++ set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags);
+ write_unlock_bh(&sk->sk_callback_lock);
+ orig_state_change(sk);
+ return;
+ }
+- if (test_and_set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
++ if (test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
+ pr_debug("Got LOGIN_FLAGS_CLOSED=1 sk_state_change conn: %p\n",
+ conn);
+ write_unlock_bh(&sk->sk_callback_lock);
+ orig_state_change(sk);
+ return;
+ }
++ /*
++ * If the TCP connection has dropped, go ahead and set LOGIN_FLAGS_CLOSED,
++ * but only queue conn->login_work -> iscsi_target_do_login_rx()
++ * processing if LOGIN_FLAGS_INITIAL_PDU has already been cleared.
++ *
++ * When iscsi_target_do_login_rx() runs, iscsi_target_sk_check_close()
++ * will detect the dropped TCP connection from delayed workqueue context.
++ *
++ * If LOGIN_FLAGS_INITIAL_PDU is still set, which means the initial
++ * iscsi_target_start_negotiation() is running, iscsi_target_do_login()
++ * via iscsi_target_sk_check_close() or iscsi_target_start_negotiation()
++ * via iscsi_target_sk_check_and_clear() is responsible for detecting the
++ * dropped TCP connection in iscsi_np process context, and cleaning up
++ * the remaining iscsi connection resources.
++ */
++ if (state) {
++ pr_debug("iscsi_target_sk_state_change got failed state\n");
++ set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags);
++ state = test_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags);
++ write_unlock_bh(&sk->sk_callback_lock);
+
+- state = iscsi_target_sk_state_check(sk);
+- write_unlock_bh(&sk->sk_callback_lock);
+-
+- pr_debug("iscsi_target_sk_state_change: state: %d\n", state);
++ orig_state_change(sk);
+
+- if (!state) {
+- pr_debug("iscsi_target_sk_state_change got failed state\n");
+- schedule_delayed_work(&conn->login_cleanup_work, 0);
++ if (!state)
++ schedule_delayed_work(&conn->login_work, 0);
+ return;
+ }
++ write_unlock_bh(&sk->sk_callback_lock);
++
+ orig_state_change(sk);
+ }
+
+@@ -947,6 +1011,15 @@ static int iscsi_target_do_login(struct
+ if (iscsi_target_handle_csg_one(conn, login) < 0)
+ return -1;
+ if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
++ /*
++ * Check to make sure the TCP connection has not
++ * dropped asynchronously while session reinstatement
++ * was occuring in this kthread context, before
++ * transitioning to full feature phase operation.
++ */
++ if (iscsi_target_sk_check_close(conn))
++ return -1;
++
+ login->tsih = conn->sess->tsih;
+ login->login_complete = 1;
+ iscsi_target_restore_sock_callbacks(conn);
+@@ -973,21 +1046,6 @@ static int iscsi_target_do_login(struct
+ break;
+ }
+
+- if (conn->sock) {
+- struct sock *sk = conn->sock->sk;
+- bool state;
+-
+- read_lock_bh(&sk->sk_callback_lock);
+- state = iscsi_target_sk_state_check(sk);
+- read_unlock_bh(&sk->sk_callback_lock);
+-
+- if (!state) {
+- pr_debug("iscsi_target_do_login() failed state for"
+- " conn: %p\n", conn);
+- return -1;
+- }
+- }
+-
+ return 0;
+ }
+
+@@ -1254,13 +1312,25 @@ int iscsi_target_start_negotiation(
+ if (conn->sock) {
+ struct sock *sk = conn->sock->sk;
+
+- write_lock_bh(&sk->sk_callback_lock);
+- set_bit(LOGIN_FLAGS_READY, &conn->login_flags);
+- write_unlock_bh(&sk->sk_callback_lock);
+- }
++ write_lock_bh(&sk->sk_callback_lock);
++ set_bit(LOGIN_FLAGS_READY, &conn->login_flags);
++ set_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags);
++ write_unlock_bh(&sk->sk_callback_lock);
++ }
++ /*
++ * If iscsi_target_do_login returns zero to signal more PDU
++ * exchanges are required to complete the login, go ahead and
++ * clear LOGIN_FLAGS_INITIAL_PDU but only if the TCP connection
++ * is still active.
++ *
++ * Otherwise if TCP connection dropped asynchronously, go ahead
++ * and perform connection cleanup now.
++ */
++ ret = iscsi_target_do_login(conn, login);
++ if (!ret && iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
++ ret = -1;
+
+- ret = iscsi_target_do_login(conn, login);
+- if (ret < 0) {
++ if (ret < 0) {
+ cancel_delayed_work_sync(&conn->login_work);
+ cancel_delayed_work_sync(&conn->login_cleanup_work);
+ iscsi_target_restore_sock_callbacks(conn);
ext4-fix-seek_hole-seek_data-for-blocksize-pagesize.patch
ext4-fix-overflow-caused-by-missing-cast-in-ext4_resize_fs.patch
media-platform-davinci-return-einval-for-vpfe_cmd_s_ccdc_raw_params-ioctl.patch
+target-avoid-mappedlun-symlink-creation-during-lun-shutdown.patch
+iscsi-target-always-wait-for-kthread_should_stop-before-kthread-exit.patch
+iscsi-target-fix-early-sk_data_ready-login_flags_ready-race.patch
+iscsi-target-fix-initial-login-pdu-asynchronous-socket-close-oops.patch
+iscsi-target-fix-delayed-logout-processing-greater-than-seconds_for_logout_comp.patch
--- /dev/null
+From 49cb77e297dc611a1b795cfeb79452b3002bd331 Mon Sep 17 00:00:00 2001
+From: Nicholas Bellinger <nab@linux-iscsi.org>
+Date: Mon, 27 Mar 2017 16:12:43 -0700
+Subject: target: Avoid mappedlun symlink creation during lun shutdown
+
+From: Nicholas Bellinger <nab@linux-iscsi.org>
+
+commit 49cb77e297dc611a1b795cfeb79452b3002bd331 upstream.
+
+This patch closes a race between se_lun deletion during configfs
+unlink in target_fabric_port_unlink() -> core_dev_del_lun()
+-> core_tpg_remove_lun(), when transport_clear_lun_ref() blocks
+waiting for percpu_ref RCU grace period to finish, but a new
+NodeACL mappedlun is added before the RCU grace period has
+completed.
+
+This can happen in target_fabric_mappedlun_link() because it
+only checks for se_lun->lun_se_dev, which is not cleared until
+after transport_clear_lun_ref() percpu_ref RCU grace period
+finishes.
+
+This bug originally manifested as NULL pointer dereference
+OOPsen in target_stat_scsi_att_intr_port_show_attr_dev() on
+v4.1.y code, because it dereferences lun->lun_se_dev without
+a explicit NULL pointer check.
+
+In post v4.1 code with target-core RCU conversion, the code
+in target_stat_scsi_att_intr_port_show_attr_dev() no longer
+uses se_lun->lun_se_dev, but the same race still exists.
+
+To address the bug, go ahead and set se_lun>lun_shutdown as
+early as possible in core_tpg_remove_lun(), and ensure new
+NodeACL mappedlun creation in target_fabric_mappedlun_link()
+fails during se_lun shutdown.
+
+Reported-by: James Shen <jcs@datera.io>
+Cc: James Shen <jcs@datera.io>
+Tested-by: James Shen <jcs@datera.io>
+Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/target/target_core_fabric_configfs.c | 5 +++++
+ drivers/target/target_core_tpg.c | 3 +++
+ include/target/target_core_base.h | 1 +
+ 3 files changed, 9 insertions(+)
+
+--- a/drivers/target/target_core_fabric_configfs.c
++++ b/drivers/target/target_core_fabric_configfs.c
+@@ -84,6 +84,11 @@ static int target_fabric_mappedlun_link(
+ "_tpg does not exist\n");
+ return -EINVAL;
+ }
++ if (lun->lun_shutdown) {
++ pr_err("Unable to create mappedlun symlink because"
++ " lun->lun_shutdown=true\n");
++ return -EINVAL;
++ }
+ se_tpg = lun->lun_sep->sep_tpg;
+
+ nacl_ci = &lun_acl_ci->ci_parent->ci_group->cg_item;
+--- a/drivers/target/target_core_tpg.c
++++ b/drivers/target/target_core_tpg.c
+@@ -843,6 +843,8 @@ void core_tpg_remove_lun(
+ struct se_portal_group *tpg,
+ struct se_lun *lun)
+ {
++ lun->lun_shutdown = true;
++
+ core_clear_lun_from_tpg(lun, tpg);
+ transport_clear_lun_ref(lun);
+
+@@ -850,6 +852,7 @@ void core_tpg_remove_lun(
+
+ spin_lock(&tpg->tpg_lun_lock);
+ lun->lun_status = TRANSPORT_LUN_STATUS_FREE;
++ lun->lun_shutdown = false;
+ spin_unlock(&tpg->tpg_lun_lock);
+
+ percpu_ref_exit(&lun->lun_ref);
+--- a/include/target/target_core_base.h
++++ b/include/target/target_core_base.h
+@@ -719,6 +719,7 @@ struct se_lun {
+ u32 lun_access;
+ u32 lun_flags;
+ u32 unpacked_lun;
++ bool lun_shutdown;
+ atomic_t lun_acl_count;
+ spinlock_t lun_acl_lock;
+ spinlock_t lun_sep_lock;