From: Greg Kroah-Hartman Date: Tue, 8 Aug 2017 00:03:45 +0000 (-0700) Subject: 4.4-stable patches X-Git-Tag: v4.12.6~35 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bea8d45ec6892e60f1ca96c5c10b831586d72499;p=thirdparty%2Fkernel%2Fstable-queue.git 4.4-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 iser-target-avoid-isert_conn-cm_id-dereference-in-isert_login_recv_done.patch target-avoid-mappedlun-symlink-creation-during-lun-shutdown.patch --- diff --git a/queue-4.4/iscsi-target-always-wait-for-kthread_should_stop-before-kthread-exit.patch b/queue-4.4/iscsi-target-always-wait-for-kthread_should_stop-before-kthread-exit.patch new file mode 100644 index 00000000000..b9fd9a7f367 --- /dev/null +++ b/queue-4.4/iscsi-target-always-wait-for-kthread_should_stop-before-kthread-exit.patch @@ -0,0 +1,170 @@ +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 +@@ -3965,6 +3965,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. +@@ -3990,12 +3992,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: +@@ -4005,8 +4009,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; + } + +@@ -4105,6 +4114,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. +@@ -4116,7 +4126,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; +@@ -4201,7 +4211,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 +@@ -930,8 +930,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); +@@ -942,6 +944,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; + } + +@@ -955,4 +958,5 @@ 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,6 +9,6 @@ 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 *); + + #endif /*** ISCSI_TARGET_ERL0_H ***/ +--- a/drivers/target/iscsi/iscsi_target_login.c ++++ b/drivers/target/iscsi/iscsi_target_login.c +@@ -1436,5 +1436,9 @@ int iscsi_target_login_thread(void *arg) + break; + } + ++ while (!kthread_should_stop()) { ++ msleep(100); ++ } ++ + return 0; + } diff --git a/queue-4.4/iscsi-target-fix-delayed-logout-processing-greater-than-seconds_for_logout_comp.patch b/queue-4.4/iscsi-target-fix-delayed-logout-processing-greater-than-seconds_for_logout_comp.patch new file mode 100644 index 00000000000..bac56808814 --- /dev/null +++ b/queue-4.4/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 +@@ -4591,8 +4591,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); +@@ -4608,8 +4611,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-4.4/iscsi-target-fix-early-sk_data_ready-login_flags_ready-race.patch b/queue-4.4/iscsi-target-fix-early-sk_data_ready-login_flags_ready-race.patch new file mode 100644 index 00000000000..d8336a43b98 --- /dev/null +++ b/queue-4.4/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 +@@ -1248,16 +1248,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-4.4/iscsi-target-fix-initial-login-pdu-asynchronous-socket-close-oops.patch b/queue-4.4/iscsi-target-fix-initial-login-pdu-asynchronous-socket-close-oops.patch new file mode 100644 index 00000000000..59b1ed91afe --- /dev/null +++ b/queue-4.4/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 +Signed-off-by: Nicholas Bellinger +Signed-off-by: Greg Kroah-Hartman + + +--- + drivers/target/iscsi/iscsi_target_nego.c | 204 ++++++++++++++++++++----------- + include/target/iscsi/iscsi_target_core.h | 1 + 2 files changed, 138 insertions(+), 67 deletions(-) + +--- a/drivers/target/iscsi/iscsi_target_nego.c ++++ b/drivers/target/iscsi/iscsi_target_nego.c +@@ -489,14 +489,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) +@@ -536,6 +582,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); +@@ -543,26 +603,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; +@@ -580,34 +626,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) +@@ -655,31 +696,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); + } + +@@ -944,6 +1008,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); +@@ -970,21 +1043,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; + } + +@@ -1251,13 +1309,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); +--- a/include/target/iscsi/iscsi_target_core.h ++++ b/include/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; diff --git a/queue-4.4/iser-target-avoid-isert_conn-cm_id-dereference-in-isert_login_recv_done.patch b/queue-4.4/iser-target-avoid-isert_conn-cm_id-dereference-in-isert_login_recv_done.patch new file mode 100644 index 00000000000..347f9820283 --- /dev/null +++ b/queue-4.4/iser-target-avoid-isert_conn-cm_id-dereference-in-isert_login_recv_done.patch @@ -0,0 +1,52 @@ +From fce50a2fa4e9c6e103915c351b6d4a98661341d6 Mon Sep 17 00:00:00 2001 +From: Nicholas Bellinger +Date: Thu, 29 Jun 2017 22:21:31 -0700 +Subject: iser-target: Avoid isert_conn->cm_id dereference in isert_login_recv_done + +From: Nicholas Bellinger + +commit fce50a2fa4e9c6e103915c351b6d4a98661341d6 upstream. + +This patch fixes a NULL pointer dereference in isert_login_recv_done() +of isert_conn->cm_id due to isert_cma_handler() -> isert_connect_error() +resetting isert_conn->cm_id = NULL during a failed login attempt. + +As per Sagi, we will always see the completion of all recv wrs posted +on the qp (given that we assigned a ->done handler), this is a FLUSH +error completion, we just don't get to verify that because we deref +NULL before. + +The issue here, was the assumption that dereferencing the connection +cm_id is always safe, which is not true since: + + commit 4a579da2586bd3b79b025947ea24ede2bbfede62 + Author: Sagi Grimberg + Date: Sun Mar 29 15:52:04 2015 +0300 + + iser-target: Fix possible deadlock in RDMA_CM connection error + +As I see it, we have a direct reference to the isert_device from +isert_conn which is the one-liner fix that we actually need like +we do in isert_rdma_read_done() and isert_rdma_write_done(). + +Reported-by: Andrea Righi +Tested-by: Andrea Righi +Reviewed-by: Sagi Grimberg +Signed-off-by: Nicholas Bellinger +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/infiniband/ulp/isert/ib_isert.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/infiniband/ulp/isert/ib_isert.c ++++ b/drivers/infiniband/ulp/isert/ib_isert.c +@@ -1581,7 +1581,7 @@ isert_rcv_completion(struct iser_rx_desc + struct isert_conn *isert_conn, + u32 xfer_len) + { +- struct ib_device *ib_dev = isert_conn->cm_id->device; ++ struct ib_device *ib_dev = isert_conn->device->ib_device; + struct iscsi_hdr *hdr; + u64 rx_dma; + int rx_buflen; diff --git a/queue-4.4/series b/queue-4.4/series index f6baa22c964..8e054e84120 100644 --- a/queue-4.4/series +++ b/queue-4.4/series @@ -9,3 +9,9 @@ ext4-fix-seek_hole-seek_data-for-blocksize-pagesize.patch ext4-fix-overflow-caused-by-missing-cast-in-ext4_resize_fs.patch arm-dts-armada-38x-fix-irq-type-for-pca955.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 +iser-target-avoid-isert_conn-cm_id-dereference-in-isert_login_recv_done.patch diff --git a/queue-4.4/target-avoid-mappedlun-symlink-creation-during-lun-shutdown.patch b/queue-4.4/target-avoid-mappedlun-symlink-creation-during-lun-shutdown.patch new file mode 100644 index 00000000000..d9317fa5313 --- /dev/null +++ b/queue-4.4/target-avoid-mappedlun-symlink-creation-during-lun-shutdown.patch @@ -0,0 +1,91 @@ +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 | 4 ++++ + include/target/target_core_base.h | 1 + + 3 files changed, 10 insertions(+) + +--- a/drivers/target/target_core_fabric_configfs.c ++++ b/drivers/target/target_core_fabric_configfs.c +@@ -92,6 +92,11 @@ static int target_fabric_mappedlun_link( + pr_err("Source se_lun->lun_se_dev 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_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 +@@ -673,6 +673,8 @@ void core_tpg_remove_lun( + */ + struct se_device *dev = rcu_dereference_raw(lun->lun_se_dev); + ++ lun->lun_shutdown = true; ++ + core_clear_lun_from_tpg(lun, tpg); + /* + * Wait for any active I/O references to percpu se_lun->lun_ref to +@@ -694,6 +696,8 @@ void core_tpg_remove_lun( + } + if (!(dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE)) + hlist_del_rcu(&lun->link); ++ ++ lun->lun_shutdown = false; + mutex_unlock(&tpg->tpg_lun_mutex); + + percpu_ref_exit(&lun->lun_ref); +--- a/include/target/target_core_base.h ++++ b/include/target/target_core_base.h +@@ -714,6 +714,7 @@ struct se_lun { + #define SE_LUN_LINK_MAGIC 0xffff7771 + u32 lun_link_magic; + u32 lun_access; ++ bool lun_shutdown; + u32 lun_index; + + /* RELATIVE TARGET PORT IDENTIFER */