]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.18-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 8 Aug 2017 00:03:28 +0000 (17:03 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 8 Aug 2017 00:03:28 +0000 (17:03 -0700)
added patches:
iscsi-target-always-wait-for-kthread_should_stop-before-kthread-exit.patch
iscsi-target-fix-delayed-logout-processing-greater-than-seconds_for_logout_comp.patch
iscsi-target-fix-early-sk_data_ready-login_flags_ready-race.patch
iscsi-target-fix-initial-login-pdu-asynchronous-socket-close-oops.patch
target-avoid-mappedlun-symlink-creation-during-lun-shutdown.patch

queue-3.18/iscsi-target-always-wait-for-kthread_should_stop-before-kthread-exit.patch [new file with mode: 0644]
queue-3.18/iscsi-target-fix-delayed-logout-processing-greater-than-seconds_for_logout_comp.patch [new file with mode: 0644]
queue-3.18/iscsi-target-fix-early-sk_data_ready-login_flags_ready-race.patch [new file with mode: 0644]
queue-3.18/iscsi-target-fix-initial-login-pdu-asynchronous-socket-close-oops.patch [new file with mode: 0644]
queue-3.18/series
queue-3.18/target-avoid-mappedlun-symlink-creation-during-lun-shutdown.patch [new file with mode: 0644]

diff --git a/queue-3.18/iscsi-target-always-wait-for-kthread_should_stop-before-kthread-exit.patch b/queue-3.18/iscsi-target-always-wait-for-kthread_should_stop-before-kthread-exit.patch
new file mode 100644 (file)
index 0000000..543ea01
--- /dev/null
@@ -0,0 +1,172 @@
+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;
+ }
diff --git a/queue-3.18/iscsi-target-fix-delayed-logout-processing-greater-than-seconds_for_logout_comp.patch b/queue-3.18/iscsi-target-fix-delayed-logout-processing-greater-than-seconds_for_logout_comp.patch
new file mode 100644 (file)
index 0000000..09c5134
--- /dev/null
@@ -0,0 +1,75 @@
+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);
diff --git a/queue-3.18/iscsi-target-fix-early-sk_data_ready-login_flags_ready-race.patch b/queue-3.18/iscsi-target-fix-early-sk_data_ready-login_flags_ready-race.patch
new file mode 100644 (file)
index 0000000..dd75c31
--- /dev/null
@@ -0,0 +1,69 @@
+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);
diff --git a/queue-3.18/iscsi-target-fix-initial-login-pdu-asynchronous-socket-close-oops.patch b/queue-3.18/iscsi-target-fix-initial-login-pdu-asynchronous-socket-close-oops.patch
new file mode 100644 (file)
index 0000000..da5678c
--- /dev/null
@@ -0,0 +1,376 @@
+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);
index 87d85e99c88f8078b1380ca12a19375ed98c8ddb..47975a8e73ebfbc673ad3c6aba539f9e0dd9f5cc 100644 (file)
@@ -62,3 +62,8 @@ mm-page_alloc-remove-kernel-address-exposure-in-free_reserved_area.patch
 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
diff --git a/queue-3.18/target-avoid-mappedlun-symlink-creation-during-lun-shutdown.patch b/queue-3.18/target-avoid-mappedlun-symlink-creation-during-lun-shutdown.patch
new file mode 100644 (file)
index 0000000..0f01041
--- /dev/null
@@ -0,0 +1,90 @@
+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;