From 557f17075b08c447d3275baeff96b2fec971ce04 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 7 Aug 2017 17:03:28 -0700 Subject: [PATCH] 3.18-stable patches 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 --- ...read_should_stop-before-kthread-exit.patch | 172 ++++++++ ...greater-than-seconds_for_logout_comp.patch | 75 ++++ ...sk_data_ready-login_flags_ready-race.patch | 69 ++++ ...n-pdu-asynchronous-socket-close-oops.patch | 376 ++++++++++++++++++ queue-3.18/series | 5 + ...symlink-creation-during-lun-shutdown.patch | 90 +++++ 6 files changed, 787 insertions(+) create mode 100644 queue-3.18/iscsi-target-always-wait-for-kthread_should_stop-before-kthread-exit.patch create mode 100644 queue-3.18/iscsi-target-fix-delayed-logout-processing-greater-than-seconds_for_logout_comp.patch create mode 100644 queue-3.18/iscsi-target-fix-early-sk_data_ready-login_flags_ready-race.patch create mode 100644 queue-3.18/iscsi-target-fix-initial-login-pdu-asynchronous-socket-close-oops.patch create mode 100644 queue-3.18/target-avoid-mappedlun-symlink-creation-during-lun-shutdown.patch 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 index 00000000000..543ea016769 --- /dev/null +++ b/queue-3.18/iscsi-target-always-wait-for-kthread_should_stop-before-kthread-exit.patch @@ -0,0 +1,172 @@ +From 5e0cf5e6c43b9e19fc0284f69e5cd2b4a47523b0 Mon Sep 17 00:00:00 2001 +From: Jiang Yi +Date: Tue, 16 May 2017 17:57:55 +0800 +Subject: iscsi-target: Always wait for kthread_should_stop() before kthread exit + +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, &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 index 00000000000..09c51347505 --- /dev/null +++ b/queue-3.18/iscsi-target-fix-delayed-logout-processing-greater-than-seconds_for_logout_comp.patch @@ -0,0 +1,75 @@ +From 105fa2f44e504c830697b0c794822112d79808dc Mon Sep 17 00:00:00 2001 +From: Nicholas Bellinger +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 + +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 +Tested-by: Bart Van Assche +Cc: Mike Christie +Cc: Hannes Reinecke +Cc: Sagi Grimberg +Tested-by: Gary Guo +Tested-by: Chu Yuan Lin +Signed-off-by: Nicholas Bellinger +Signed-off-by: Greg Kroah-Hartman + + +--- + 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 index 00000000000..dd75c312a86 --- /dev/null +++ b/queue-3.18/iscsi-target-fix-early-sk_data_ready-login_flags_ready-race.patch @@ -0,0 +1,69 @@ +From 8f0dfb3d8b1120c61f6e2cc3729290db10772b2d Mon Sep 17 00:00:00 2001 +From: Nicholas Bellinger +Date: Sat, 27 Feb 2016 18:15:46 -0800 +Subject: iscsi-target: Fix early sk_data_ready LOGIN_FLAGS_READY race + +From: Nicholas Bellinger + +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 +Tested-by: Maged Mokhtar +Signed-off-by: Nicholas Bellinger +Signed-off-by: Greg Kroah-Hartman + + +--- + 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 index 00000000000..da5678c8095 --- /dev/null +++ b/queue-3.18/iscsi-target-fix-initial-login-pdu-asynchronous-socket-close-oops.patch @@ -0,0 +1,376 @@ +From 25cdda95fda78d22d44157da15aa7ea34be3c804 Mon Sep 17 00:00:00 2001 +From: Nicholas Bellinger +Date: Wed, 24 May 2017 21:47:09 -0700 +Subject: iscsi-target: Fix initial login PDU asynchronous socket close OOPs + +From: Nicholas Bellinger + +commit 25cdda95fda78d22d44157da15aa7ea34be3c804 upstream. + +This patch fixes a OOPs originally introduced by: + + commit bb048357dad6d604520c91586334c9c230366a14 + Author: Nicholas Bellinger + 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 +Reviewed-by: Mike Christie +Tested-by: Mike Christie +Cc: Mike Christie +Reported-by: Hannes Reinecke +Cc: Hannes Reinecke +Cc: Sagi Grimberg +Cc: Varun Prakash +Cc: # v3.12+ +Signed-off-by: Nicholas Bellinger +Signed-off-by: Greg Kroah-Hartman + +--- + 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); diff --git a/queue-3.18/series b/queue-3.18/series index 87d85e99c88..47975a8e73e 100644 --- a/queue-3.18/series +++ b/queue-3.18/series @@ -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 index 00000000000..0f010411a30 --- /dev/null +++ b/queue-3.18/target-avoid-mappedlun-symlink-creation-during-lun-shutdown.patch @@ -0,0 +1,90 @@ +From 49cb77e297dc611a1b795cfeb79452b3002bd331 Mon Sep 17 00:00:00 2001 +From: Nicholas Bellinger +Date: Mon, 27 Mar 2017 16:12:43 -0700 +Subject: target: Avoid mappedlun symlink creation during lun shutdown + +From: Nicholas Bellinger + +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 +Cc: James Shen +Tested-by: James Shen +Signed-off-by: Nicholas Bellinger +Signed-off-by: Greg Kroah-Hartman + +--- + 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; -- 2.47.3