--- /dev/null
+From 954f23722b5753305be490330cf2680b7a25f4a3 Mon Sep 17 00:00:00 2001
+From: Sagi Grimberg <sagig@mellanox.com>
+Date: Tue, 2 Dec 2014 16:57:17 +0200
+Subject: iscsi,iser-target: Initiate termination only once
+
+From: Sagi Grimberg <sagig@mellanox.com>
+
+commit 954f23722b5753305be490330cf2680b7a25f4a3 upstream.
+
+Since commit 0fc4ea701fcf ("Target/iser: Don't put isert_conn inside
+disconnected handler") we put the conn kref in isert_wait_conn, so we
+need .wait_conn to be invoked also in the error path.
+
+Introduce call to isert_conn_terminate (called under lock)
+which transitions the connection state to TERMINATING and calls
+rdma_disconnect. If the state is already teminating, just bail
+out back (temination started).
+
+Also, make sure to destroy the connection when getting a connect
+error event if didn't get to connected (state UP). Same for the
+handling of REJECTED and UNREACHABLE cma events.
+
+Squashed:
+
+iscsi-target: Add call to wait_conn in establishment error flow
+
+Reported-by: Slava Shwartsman <valyushash@gmail.com>
+Signed-off-by: Sagi Grimberg <sagig@mellanox.com>
+Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+---
+ drivers/infiniband/ulp/isert/ib_isert.c | 84 ++++++++++++++++++------------
+ drivers/infiniband/ulp/isert/ib_isert.h | 1
+ drivers/target/iscsi/iscsi_target_login.c | 3 +
+ 3 files changed, 54 insertions(+), 34 deletions(-)
+
+--- a/drivers/infiniband/ulp/isert/ib_isert.c
++++ b/drivers/infiniband/ulp/isert/ib_isert.c
+@@ -692,6 +692,33 @@ isert_put_conn(struct isert_conn *isert_
+ kref_put(&isert_conn->conn_kref, isert_release_conn_kref);
+ }
+
++/**
++ * isert_conn_terminate() - Initiate connection termination
++ * @isert_conn: isert connection struct
++ *
++ * Notes:
++ * In case the connection state is UP, move state
++ * to TEMINATING and start teardown sequence (rdma_disconnect).
++ *
++ * This routine must be called with conn_mutex held. Thus it is
++ * safe to call multiple times.
++ */
++static void
++isert_conn_terminate(struct isert_conn *isert_conn)
++{
++ int err;
++
++ if (isert_conn->state == ISER_CONN_UP) {
++ isert_conn->state = ISER_CONN_TERMINATING;
++ pr_info("Terminating conn %p state %d\n",
++ isert_conn, isert_conn->state);
++ err = rdma_disconnect(isert_conn->conn_cm_id);
++ if (err)
++ pr_warn("Failed rdma_disconnect isert_conn %p\n",
++ isert_conn);
++ }
++}
++
+ static void
+ isert_disconnect_work(struct work_struct *work)
+ {
+@@ -700,33 +727,15 @@ isert_disconnect_work(struct work_struct
+
+ pr_debug("isert_disconnect_work(): >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+ mutex_lock(&isert_conn->conn_mutex);
+- if (isert_conn->state == ISER_CONN_UP)
+- isert_conn->state = ISER_CONN_TERMINATING;
+-
+- if (isert_conn->post_recv_buf_count == 0 &&
+- atomic_read(&isert_conn->post_send_buf_count) == 0) {
+- mutex_unlock(&isert_conn->conn_mutex);
+- goto wake_up;
+- }
+- if (!isert_conn->conn_cm_id) {
+- mutex_unlock(&isert_conn->conn_mutex);
+- isert_put_conn(isert_conn);
+- return;
+- }
+-
+- if (isert_conn->disconnect) {
+- /* Send DREQ/DREP towards our initiator */
+- rdma_disconnect(isert_conn->conn_cm_id);
+- }
+-
++ isert_conn_terminate(isert_conn);
+ mutex_unlock(&isert_conn->conn_mutex);
+
+-wake_up:
++ pr_info("conn %p completing conn_wait\n", isert_conn);
+ complete(&isert_conn->conn_wait);
+ }
+
+ static int
+-isert_disconnected_handler(struct rdma_cm_id *cma_id, bool disconnect)
++isert_disconnected_handler(struct rdma_cm_id *cma_id)
+ {
+ struct isert_conn *isert_conn;
+
+@@ -739,18 +748,24 @@ isert_disconnected_handler(struct rdma_c
+
+ isert_conn = (struct isert_conn *)cma_id->context;
+
+- isert_conn->disconnect = disconnect;
+ INIT_WORK(&isert_conn->conn_logout_work, isert_disconnect_work);
+ schedule_work(&isert_conn->conn_logout_work);
+
+ return 0;
+ }
+
++static void
++isert_connect_error(struct rdma_cm_id *cma_id)
++{
++ struct isert_conn *isert_conn = (struct isert_conn *)cma_id->context;
++
++ isert_put_conn(isert_conn);
++}
++
+ static int
+ isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
+ {
+ int ret = 0;
+- bool disconnect = false;
+
+ pr_debug("isert_cma_handler: event %d status %d conn %p id %p\n",
+ event->event, event->status, cma_id->context, cma_id);
+@@ -768,11 +783,14 @@ isert_cma_handler(struct rdma_cm_id *cma
+ case RDMA_CM_EVENT_ADDR_CHANGE: /* FALLTHRU */
+ case RDMA_CM_EVENT_DISCONNECTED: /* FALLTHRU */
+ case RDMA_CM_EVENT_DEVICE_REMOVAL: /* FALLTHRU */
+- disconnect = true;
+ case RDMA_CM_EVENT_TIMEWAIT_EXIT: /* FALLTHRU */
+- ret = isert_disconnected_handler(cma_id, disconnect);
++ ret = isert_disconnected_handler(cma_id);
+ break;
++ case RDMA_CM_EVENT_REJECTED: /* FALLTHRU */
++ case RDMA_CM_EVENT_UNREACHABLE: /* FALLTHRU */
+ case RDMA_CM_EVENT_CONNECT_ERROR:
++ isert_connect_error(cma_id);
++ break;
+ default:
+ pr_err("Unhandled RDMA CMA event: %d\n", event->event);
+ break;
+@@ -1799,7 +1817,7 @@ isert_cq_rx_comp_err(struct isert_conn *
+ msleep(3000);
+
+ mutex_lock(&isert_conn->conn_mutex);
+- isert_conn->state = ISER_CONN_DOWN;
++ isert_conn_terminate(isert_conn);
+ mutex_unlock(&isert_conn->conn_mutex);
+
+ iscsit_cause_connection_reinstatement(isert_conn->conn, 0);
+@@ -2798,10 +2816,6 @@ static void isert_wait_conn(struct iscsi
+ pr_debug("isert_wait_conn: Starting \n");
+
+ mutex_lock(&isert_conn->conn_mutex);
+- if (isert_conn->conn_cm_id) {
+- pr_debug("Calling rdma_disconnect from isert_wait_conn\n");
+- rdma_disconnect(isert_conn->conn_cm_id);
+- }
+ /*
+ * Only wait for conn_wait_comp_err if the isert_conn made it
+ * into full feature phase..
+@@ -2810,13 +2824,17 @@ static void isert_wait_conn(struct iscsi
+ mutex_unlock(&isert_conn->conn_mutex);
+ return;
+ }
+- if (isert_conn->state == ISER_CONN_UP)
+- isert_conn->state = ISER_CONN_TERMINATING;
++ isert_conn_terminate(isert_conn);
+ mutex_unlock(&isert_conn->conn_mutex);
+
+ wait_for_completion(&isert_conn->conn_wait_comp_err);
+-
+ wait_for_completion(&isert_conn->conn_wait);
++
++ mutex_lock(&isert_conn->conn_mutex);
++ isert_conn->state = ISER_CONN_DOWN;
++ mutex_unlock(&isert_conn->conn_mutex);
++
++ pr_info("Destroying conn %p\n", isert_conn);
+ isert_put_conn(isert_conn);
+ }
+
+--- a/drivers/infiniband/ulp/isert/ib_isert.h
++++ b/drivers/infiniband/ulp/isert/ib_isert.h
+@@ -127,7 +127,6 @@ struct isert_conn {
+ #define ISERT_COMP_BATCH_COUNT 8
+ int conn_comp_batch;
+ struct llist_head conn_comp_llist;
+- bool disconnect;
+ };
+
+ #define ISERT_MAX_CQ 64
+--- a/drivers/target/iscsi/iscsi_target_login.c
++++ b/drivers/target/iscsi/iscsi_target_login.c
+@@ -1186,6 +1186,9 @@ old_sess_out:
+ conn->sock = NULL;
+ }
+
++ if (conn->conn_transport->iscsit_wait_conn)
++ conn->conn_transport->iscsit_wait_conn(conn);
++
+ if (conn->conn_transport->iscsit_free_conn)
+ conn->conn_transport->iscsit_free_conn(conn);
+
--- /dev/null
+From 19e2090fb246ca21b3e569ead51a6a7a1748eadd Mon Sep 17 00:00:00 2001
+From: Sagi Grimberg <sagig@mellanox.com>
+Date: Tue, 2 Dec 2014 16:57:26 +0200
+Subject: iser-target: Fix connected_handler + teardown flow race
+
+From: Sagi Grimberg <sagig@mellanox.com>
+
+commit 19e2090fb246ca21b3e569ead51a6a7a1748eadd upstream.
+
+Take isert_conn pointer from cm_id->qp->qp_context. This
+will allow us to know that the cm_id context is always
+the network portal. This will make the cm_id event check
+(connection or network portal) more reliable.
+
+In order to avoid a NULL dereference in cma_id->qp->qp_context
+we destroy the qp after we destroy the cm_id (and make the
+dereference safe). session stablishment/teardown sequences
+can happen in parallel, we should take into account that
+connected_handler might race with connection teardown flow.
+
+Also, protect isert_conn->conn_device->active_qps decrement
+within the error patch during QP creation failure and the
+normal teardown path in isert_connect_release().
+
+Squashed:
+
+iser-target: Decrement completion context active_qps in error flow
+
+Signed-off-by: Sagi Grimberg <sagig@mellanox.com>
+Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+---
+ drivers/infiniband/ulp/isert/ib_isert.c | 31 +++++++++++++++++++------------
+ 1 file changed, 19 insertions(+), 12 deletions(-)
+
+--- a/drivers/infiniband/ulp/isert/ib_isert.c
++++ b/drivers/infiniband/ulp/isert/ib_isert.c
+@@ -136,12 +136,18 @@ isert_conn_setup_qp(struct isert_conn *i
+ ret = rdma_create_qp(cma_id, isert_conn->conn_pd, &attr);
+ if (ret) {
+ pr_err("rdma_create_qp failed for cma_id %d\n", ret);
+- return ret;
++ goto err;
+ }
+ isert_conn->conn_qp = cma_id->qp;
+ pr_debug("rdma_create_qp() returned success >>>>>>>>>>>>>>>>>>>>>>>>>.\n");
+
+ return 0;
++err:
++ mutex_lock(&device_list_mutex);
++ device->cq_active_qps[min_index]--;
++ mutex_unlock(&device_list_mutex);
++
++ return ret;
+ }
+
+ static void
+@@ -527,7 +533,6 @@ isert_connect_request(struct rdma_cm_id
+ spin_lock_init(&isert_conn->conn_lock);
+ INIT_LIST_HEAD(&isert_conn->conn_fr_pool);
+
+- cma_id->context = isert_conn;
+ isert_conn->conn_cm_id = cma_id;
+ isert_conn->responder_resources = event->param.conn.responder_resources;
+ isert_conn->initiator_depth = event->param.conn.initiator_depth;
+@@ -649,18 +654,20 @@ isert_connect_release(struct isert_conn
+ if (device && device->use_fastreg)
+ isert_conn_free_fastreg_pool(isert_conn);
+
++ isert_free_rx_descriptors(isert_conn);
++ rdma_destroy_id(isert_conn->conn_cm_id);
++
+ if (isert_conn->conn_qp) {
+ cq_index = ((struct isert_cq_desc *)
+ isert_conn->conn_qp->recv_cq->cq_context)->cq_index;
+ pr_debug("isert_connect_release: cq_index: %d\n", cq_index);
++ mutex_lock(&device_list_mutex);
+ isert_conn->conn_device->cq_active_qps[cq_index]--;
++ mutex_unlock(&device_list_mutex);
+
+- rdma_destroy_qp(isert_conn->conn_cm_id);
++ ib_destroy_qp(isert_conn->conn_qp);
+ }
+
+- isert_free_rx_descriptors(isert_conn);
+- rdma_destroy_id(isert_conn->conn_cm_id);
+-
+ ib_dereg_mr(isert_conn->conn_mr);
+ ib_dealloc_pd(isert_conn->conn_pd);
+
+@@ -683,7 +690,7 @@ isert_connect_release(struct isert_conn
+ static void
+ isert_connected_handler(struct rdma_cm_id *cma_id)
+ {
+- struct isert_conn *isert_conn = cma_id->context;
++ struct isert_conn *isert_conn = cma_id->qp->qp_context;
+
+ pr_info("conn %p\n", isert_conn);
+
+@@ -761,16 +768,16 @@ isert_conn_terminate(struct isert_conn *
+ static int
+ isert_disconnected_handler(struct rdma_cm_id *cma_id)
+ {
++ struct iscsi_np *np = cma_id->context;
++ struct isert_np *isert_np = np->np_context;
+ struct isert_conn *isert_conn;
+
+- if (!cma_id->qp) {
+- struct isert_np *isert_np = cma_id->context;
+-
++ if (isert_np->np_cm_id == cma_id) {
+ isert_np->np_cm_id = NULL;
+ return -1;
+ }
+
+- isert_conn = (struct isert_conn *)cma_id->context;
++ isert_conn = cma_id->qp->qp_context;
+
+ mutex_lock(&isert_conn->conn_mutex);
+ isert_conn_terminate(isert_conn);
+@@ -785,7 +792,7 @@ isert_disconnected_handler(struct rdma_c
+ static void
+ isert_connect_error(struct rdma_cm_id *cma_id)
+ {
+- struct isert_conn *isert_conn = (struct isert_conn *)cma_id->context;
++ struct isert_conn *isert_conn = cma_id->qp->qp_context;
+
+ isert_put_conn(isert_conn);
+ }
--- /dev/null
+From 128e9cc84566a84146baea2335b3824288eed817 Mon Sep 17 00:00:00 2001
+From: Sagi Grimberg <sagig@mellanox.com>
+Date: Tue, 2 Dec 2014 16:57:20 +0200
+Subject: iser-target: Fix flush + disconnect completion handling
+
+From: Sagi Grimberg <sagig@mellanox.com>
+
+commit 128e9cc84566a84146baea2335b3824288eed817 upstream.
+
+ISER_CONN_UP state is not sufficient to know if
+we should wait for completion of flush errors and
+disconnected_handler event.
+
+Instead, split it to 2 states:
+- ISER_CONN_UP: Got to CM connected phase, This state
+indicates that we need to wait for a CM disconnect
+event before going to teardown.
+
+- ISER_CONN_FULL_FEATURE: Got to full feature phase
+after we posted login response, This state indicates
+that we posted recv buffers and we need to wait for
+flush completions before going to teardown.
+
+Also avoid deffering disconnected handler to a work,
+and handle it within disconnected handler.
+More work here is needed to handle DEVICE_REMOVAL event
+correctly (cleanup all resources).
+
+Squashed:
+
+iser-target: Don't deffer disconnected handler to a work
+
+Signed-off-by: Sagi Grimberg <sagig@mellanox.com>
+Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/infiniband/ulp/isert/ib_isert.c | 52 ++++++++++++++++++--------------
+ drivers/infiniband/ulp/isert/ib_isert.h | 2 -
+ 2 files changed, 31 insertions(+), 23 deletions(-)
+
+--- a/drivers/infiniband/ulp/isert/ib_isert.c
++++ b/drivers/infiniband/ulp/isert/ib_isert.c
+@@ -671,6 +671,9 @@ isert_connected_handler(struct rdma_cm_i
+ {
+ struct isert_conn *isert_conn = cma_id->context;
+
++ pr_info("conn %p\n", isert_conn);
++
++ isert_conn->state = ISER_CONN_UP;
+ kref_get(&isert_conn->conn_kref);
+ }
+
+@@ -697,8 +700,9 @@ isert_put_conn(struct isert_conn *isert_
+ * @isert_conn: isert connection struct
+ *
+ * Notes:
+- * In case the connection state is UP, move state
++ * In case the connection state is FULL_FEATURE, move state
+ * to TEMINATING and start teardown sequence (rdma_disconnect).
++ * In case the connection state is UP, complete flush as well.
+ *
+ * This routine must be called with conn_mutex held. Thus it is
+ * safe to call multiple times.
+@@ -708,32 +712,31 @@ isert_conn_terminate(struct isert_conn *
+ {
+ int err;
+
+- if (isert_conn->state == ISER_CONN_UP) {
+- isert_conn->state = ISER_CONN_TERMINATING;
++ switch (isert_conn->state) {
++ case ISER_CONN_TERMINATING:
++ break;
++ case ISER_CONN_UP:
++ /*
++ * No flush completions will occur as we didn't
++ * get to ISER_CONN_FULL_FEATURE yet, complete
++ * to allow teardown progress.
++ */
++ complete(&isert_conn->conn_wait_comp_err);
++ case ISER_CONN_FULL_FEATURE: /* FALLTHRU */
+ pr_info("Terminating conn %p state %d\n",
+ isert_conn, isert_conn->state);
++ isert_conn->state = ISER_CONN_TERMINATING;
+ err = rdma_disconnect(isert_conn->conn_cm_id);
+ if (err)
+ pr_warn("Failed rdma_disconnect isert_conn %p\n",
+ isert_conn);
++ break;
++ default:
++ pr_warn("conn %p teminating in state %d\n",
++ isert_conn, isert_conn->state);
+ }
+ }
+
+-static void
+-isert_disconnect_work(struct work_struct *work)
+-{
+- struct isert_conn *isert_conn = container_of(work,
+- struct isert_conn, conn_logout_work);
+-
+- pr_debug("isert_disconnect_work(): >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+- mutex_lock(&isert_conn->conn_mutex);
+- isert_conn_terminate(isert_conn);
+- mutex_unlock(&isert_conn->conn_mutex);
+-
+- pr_info("conn %p completing conn_wait\n", isert_conn);
+- complete(&isert_conn->conn_wait);
+-}
+-
+ static int
+ isert_disconnected_handler(struct rdma_cm_id *cma_id)
+ {
+@@ -748,8 +751,12 @@ isert_disconnected_handler(struct rdma_c
+
+ isert_conn = (struct isert_conn *)cma_id->context;
+
+- INIT_WORK(&isert_conn->conn_logout_work, isert_disconnect_work);
+- schedule_work(&isert_conn->conn_logout_work);
++ mutex_lock(&isert_conn->conn_mutex);
++ isert_conn_terminate(isert_conn);
++ mutex_unlock(&isert_conn->conn_mutex);
++
++ pr_info("conn %p completing conn_wait\n", isert_conn);
++ complete(&isert_conn->conn_wait);
+
+ return 0;
+ }
+@@ -924,7 +931,7 @@ isert_init_send_wr(struct isert_conn *is
+ * bit for every ISERT_COMP_BATCH_COUNT number of ib_post_send() calls.
+ */
+ mutex_lock(&isert_conn->conn_mutex);
+- if (coalesce && isert_conn->state == ISER_CONN_UP &&
++ if (coalesce && isert_conn->state == ISER_CONN_FULL_FEATURE &&
+ ++isert_conn->conn_comp_batch < ISERT_COMP_BATCH_COUNT) {
+ tx_desc->llnode_active = true;
+ llist_add(&tx_desc->comp_llnode, &isert_conn->conn_comp_llist);
+@@ -1021,7 +1028,8 @@ isert_put_login_tx(struct iscsi_conn *co
+ if (ret)
+ return ret;
+
+- isert_conn->state = ISER_CONN_UP;
++ /* Now we are in FULL_FEATURE phase */
++ isert_conn->state = ISER_CONN_FULL_FEATURE;
+ goto post_send;
+ }
+
+--- a/drivers/infiniband/ulp/isert/ib_isert.h
++++ b/drivers/infiniband/ulp/isert/ib_isert.h
+@@ -23,6 +23,7 @@ enum iser_ib_op_code {
+ enum iser_conn_state {
+ ISER_CONN_INIT,
+ ISER_CONN_UP,
++ ISER_CONN_FULL_FEATURE,
+ ISER_CONN_TERMINATING,
+ ISER_CONN_DOWN,
+ };
+@@ -115,7 +116,6 @@ struct isert_conn {
+ struct ib_mr *conn_mr;
+ struct ib_qp *conn_qp;
+ struct isert_device *conn_device;
+- struct work_struct conn_logout_work;
+ struct mutex conn_mutex;
+ struct completion conn_wait;
+ struct completion conn_wait_comp_err;
--- /dev/null
+From b02efbfc9a051b41e71fe8f94ddf967260e024a6 Mon Sep 17 00:00:00 2001
+From: Sagi Grimberg <sagig@mellanox.com>
+Date: Tue, 2 Dec 2014 16:57:29 +0200
+Subject: iser-target: Fix implicit termination of connections
+
+From: Sagi Grimberg <sagig@mellanox.com>
+
+commit b02efbfc9a051b41e71fe8f94ddf967260e024a6 upstream.
+
+In situations such as bond failover, The new session establishment
+implicitly invokes the termination of the old connection.
+
+So, we don't want to wait for the old connection wait_conn to completely
+terminate before we accept the new connection and post a login response.
+
+The solution is to deffer the comp_wait completion and the conn_put to
+a work so wait_conn will effectively be non-blocking (flush errors are
+assumed to come very fast).
+
+We allocate isert_release_wq with WQ_UNBOUND and WQ_UNBOUND_MAX_ACTIVE
+to spread the concurrency of release works.
+
+Reported-by: Slava Shwartsman <valyushash@gmail.com>
+Signed-off-by: Sagi Grimberg <sagig@mellanox.com>
+Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/infiniband/ulp/isert/ib_isert.c | 42 +++++++++++++++++++++++++-------
+ drivers/infiniband/ulp/isert/ib_isert.h | 1
+ 2 files changed, 35 insertions(+), 8 deletions(-)
+
+--- a/drivers/infiniband/ulp/isert/ib_isert.c
++++ b/drivers/infiniband/ulp/isert/ib_isert.c
+@@ -41,6 +41,7 @@ static DEFINE_MUTEX(device_list_mutex);
+ static LIST_HEAD(device_list);
+ static struct workqueue_struct *isert_rx_wq;
+ static struct workqueue_struct *isert_comp_wq;
++static struct workqueue_struct *isert_release_wq;
+
+ static void
+ isert_unmap_cmd(struct isert_cmd *isert_cmd, struct isert_conn *isert_conn);
+@@ -2905,6 +2906,24 @@ isert_free_np(struct iscsi_np *np)
+ kfree(isert_np);
+ }
+
++static void isert_release_work(struct work_struct *work)
++{
++ struct isert_conn *isert_conn = container_of(work,
++ struct isert_conn,
++ release_work);
++
++ pr_info("Starting release conn %p\n", isert_conn);
++
++ wait_for_completion(&isert_conn->conn_wait);
++
++ mutex_lock(&isert_conn->conn_mutex);
++ isert_conn->state = ISER_CONN_DOWN;
++ mutex_unlock(&isert_conn->conn_mutex);
++
++ pr_info("Destroying conn %p\n", isert_conn);
++ isert_put_conn(isert_conn);
++}
++
+ static void isert_wait_conn(struct iscsi_conn *conn)
+ {
+ struct isert_conn *isert_conn = conn->context;
+@@ -2924,14 +2943,9 @@ static void isert_wait_conn(struct iscsi
+ mutex_unlock(&isert_conn->conn_mutex);
+
+ wait_for_completion(&isert_conn->conn_wait_comp_err);
+- wait_for_completion(&isert_conn->conn_wait);
+-
+- mutex_lock(&isert_conn->conn_mutex);
+- isert_conn->state = ISER_CONN_DOWN;
+- mutex_unlock(&isert_conn->conn_mutex);
+
+- pr_info("Destroying conn %p\n", isert_conn);
+- isert_put_conn(isert_conn);
++ INIT_WORK(&isert_conn->release_work, isert_release_work);
++ queue_work(isert_release_wq, &isert_conn->release_work);
+ }
+
+ static void isert_free_conn(struct iscsi_conn *conn)
+@@ -2977,10 +2991,21 @@ static int __init isert_init(void)
+ goto destroy_rx_wq;
+ }
+
++ isert_release_wq = alloc_workqueue("isert_release_wq", WQ_UNBOUND,
++ WQ_UNBOUND_MAX_ACTIVE);
++ if (!isert_release_wq) {
++ pr_err("Unable to allocate isert_release_wq\n");
++ ret = -ENOMEM;
++ goto destroy_comp_wq;
++ }
++
+ iscsit_register_transport(&iser_target_transport);
+- pr_debug("iSER_TARGET[0] - Loaded iser_target_transport\n");
++ pr_info("iSER_TARGET[0] - Loaded iser_target_transport\n");
++
+ return 0;
+
++destroy_comp_wq:
++ destroy_workqueue(isert_comp_wq);
+ destroy_rx_wq:
+ destroy_workqueue(isert_rx_wq);
+ return ret;
+@@ -2989,6 +3014,7 @@ destroy_rx_wq:
+ static void __exit isert_exit(void)
+ {
+ flush_scheduled_work();
++ destroy_workqueue(isert_release_wq);
+ destroy_workqueue(isert_comp_wq);
+ destroy_workqueue(isert_rx_wq);
+ iscsit_unregister_transport(&iser_target_transport);
+--- a/drivers/infiniband/ulp/isert/ib_isert.h
++++ b/drivers/infiniband/ulp/isert/ib_isert.h
+@@ -126,6 +126,7 @@ struct isert_conn {
+ int conn_fr_pool_size;
+ /* lock to protect fastreg pool */
+ spinlock_t conn_lock;
++ struct work_struct release_work;
+ #define ISERT_COMP_BATCH_COUNT 8
+ int conn_comp_batch;
+ struct llist_head conn_comp_llist;
--- /dev/null
+From ca6c1d82d12d8013fb75ce015900d62b9754623c Mon Sep 17 00:00:00 2001
+From: Sagi Grimberg <sagig@mellanox.com>
+Date: Tue, 2 Dec 2014 16:57:27 +0200
+Subject: iser-target: Handle ADDR_CHANGE event for listener cm_id
+
+From: Sagi Grimberg <sagig@mellanox.com>
+
+commit ca6c1d82d12d8013fb75ce015900d62b9754623c upstream.
+
+The np listener cm_id will also get ADDR_CHANGE event
+upcall (in case it is bound to a specific IP). Handle
+it correctly by creating a new cm_id and implicitly
+destroy the old one.
+
+Since this is the second event a listener np cm_id may
+encounter, we move the np cm_id event handling to a
+routine.
+
+Squashed:
+
+iser-target: Move cma_id setup to a function
+
+Reported-by: Slava Shwartsman <valyushash@gmail.com>
+Signed-off-by: Sagi Grimberg <sagig@mellanox.com>
+Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/infiniband/ulp/isert/ib_isert.c | 107 ++++++++++++++++++++++----------
+ drivers/infiniband/ulp/isert/ib_isert.h | 1
+ 2 files changed, 77 insertions(+), 31 deletions(-)
+
+--- a/drivers/infiniband/ulp/isert/ib_isert.c
++++ b/drivers/infiniband/ulp/isert/ib_isert.c
+@@ -56,6 +56,7 @@ static int
+ isert_rdma_post_recvl(struct isert_conn *isert_conn);
+ static int
+ isert_rdma_accept(struct isert_conn *isert_conn);
++struct rdma_cm_id *isert_setup_id(struct isert_np *isert_np);
+
+ static void
+ isert_qp_event_callback(struct ib_event *e, void *context)
+@@ -499,8 +500,8 @@ err:
+ static int
+ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
+ {
+- struct iscsi_np *np = cma_id->context;
+- struct isert_np *isert_np = np->np_context;
++ struct isert_np *isert_np = cma_id->context;
++ struct iscsi_np *np = isert_np->np;
+ struct isert_conn *isert_conn;
+ struct isert_device *device;
+ struct ib_device *ib_dev = cma_id->device;
+@@ -766,17 +767,41 @@ isert_conn_terminate(struct isert_conn *
+ }
+
+ static int
+-isert_disconnected_handler(struct rdma_cm_id *cma_id)
++isert_np_cma_handler(struct isert_np *isert_np,
++ enum rdma_cm_event_type event)
+ {
+- struct iscsi_np *np = cma_id->context;
+- struct isert_np *isert_np = np->np_context;
+- struct isert_conn *isert_conn;
++ pr_debug("isert np %p, handling event %d\n", isert_np, event);
+
+- if (isert_np->np_cm_id == cma_id) {
++ switch (event) {
++ case RDMA_CM_EVENT_DEVICE_REMOVAL:
+ isert_np->np_cm_id = NULL;
+- return -1;
++ break;
++ case RDMA_CM_EVENT_ADDR_CHANGE:
++ isert_np->np_cm_id = isert_setup_id(isert_np);
++ if (IS_ERR(isert_np->np_cm_id)) {
++ pr_err("isert np %p setup id failed: %ld\n",
++ isert_np, PTR_ERR(isert_np->np_cm_id));
++ isert_np->np_cm_id = NULL;
++ }
++ break;
++ default:
++ pr_err("isert np %p Unexpected event %d\n",
++ isert_np, event);
+ }
+
++ return -1;
++}
++
++static int
++isert_disconnected_handler(struct rdma_cm_id *cma_id,
++ enum rdma_cm_event_type event)
++{
++ struct isert_np *isert_np = cma_id->context;
++ struct isert_conn *isert_conn;
++
++ if (isert_np->np_cm_id == cma_id)
++ return isert_np_cma_handler(cma_id->context, event);
++
+ isert_conn = cma_id->qp->qp_context;
+
+ mutex_lock(&isert_conn->conn_mutex);
+@@ -819,7 +844,7 @@ isert_cma_handler(struct rdma_cm_id *cma
+ case RDMA_CM_EVENT_DISCONNECTED: /* FALLTHRU */
+ case RDMA_CM_EVENT_DEVICE_REMOVAL: /* FALLTHRU */
+ case RDMA_CM_EVENT_TIMEWAIT_EXIT: /* FALLTHRU */
+- ret = isert_disconnected_handler(cma_id);
++ ret = isert_disconnected_handler(cma_id, event->event);
+ break;
+ case RDMA_CM_EVENT_REJECTED: /* FALLTHRU */
+ case RDMA_CM_EVENT_UNREACHABLE: /* FALLTHRU */
+@@ -2643,13 +2668,51 @@ isert_response_queue(struct iscsi_conn *
+ return ret;
+ }
+
++struct rdma_cm_id *
++isert_setup_id(struct isert_np *isert_np)
++{
++ struct iscsi_np *np = isert_np->np;
++ struct rdma_cm_id *id;
++ struct sockaddr *sa;
++ int ret;
++
++ sa = (struct sockaddr *)&np->np_sockaddr;
++ pr_debug("ksockaddr: %p, sa: %p\n", &np->np_sockaddr, sa);
++
++ id = rdma_create_id(isert_cma_handler, isert_np,
++ RDMA_PS_TCP, IB_QPT_RC);
++ if (IS_ERR(id)) {
++ pr_err("rdma_create_id() failed: %ld\n", PTR_ERR(id));
++ ret = PTR_ERR(id);
++ goto out;
++ }
++ pr_debug("id %p context %p\n", id, id->context);
++
++ ret = rdma_bind_addr(id, sa);
++ if (ret) {
++ pr_err("rdma_bind_addr() failed: %d\n", ret);
++ goto out_id;
++ }
++
++ ret = rdma_listen(id, ISERT_RDMA_LISTEN_BACKLOG);
++ if (ret) {
++ pr_err("rdma_listen() failed: %d\n", ret);
++ goto out_id;
++ }
++
++ return id;
++out_id:
++ rdma_destroy_id(id);
++out:
++ return ERR_PTR(ret);
++}
++
+ static int
+ isert_setup_np(struct iscsi_np *np,
+ struct __kernel_sockaddr_storage *ksockaddr)
+ {
+ struct isert_np *isert_np;
+ struct rdma_cm_id *isert_lid;
+- struct sockaddr *sa;
+ int ret;
+
+ isert_np = kzalloc(sizeof(struct isert_np), GFP_KERNEL);
+@@ -2661,9 +2724,8 @@ isert_setup_np(struct iscsi_np *np,
+ mutex_init(&isert_np->np_accept_mutex);
+ INIT_LIST_HEAD(&isert_np->np_accept_list);
+ init_completion(&isert_np->np_login_comp);
++ isert_np->np = np;
+
+- sa = (struct sockaddr *)ksockaddr;
+- pr_debug("ksockaddr: %p, sa: %p\n", ksockaddr, sa);
+ /*
+ * Setup the np->np_sockaddr from the passed sockaddr setup
+ * in iscsi_target_configfs.c code..
+@@ -2671,37 +2733,20 @@ isert_setup_np(struct iscsi_np *np,
+ memcpy(&np->np_sockaddr, ksockaddr,
+ sizeof(struct __kernel_sockaddr_storage));
+
+- isert_lid = rdma_create_id(isert_cma_handler, np, RDMA_PS_TCP,
+- IB_QPT_RC);
++ isert_lid = isert_setup_id(isert_np);
+ if (IS_ERR(isert_lid)) {
+- pr_err("rdma_create_id() for isert_listen_handler failed: %ld\n",
+- PTR_ERR(isert_lid));
+ ret = PTR_ERR(isert_lid);
+ goto out;
+ }
+
+- ret = rdma_bind_addr(isert_lid, sa);
+- if (ret) {
+- pr_err("rdma_bind_addr() for isert_lid failed: %d\n", ret);
+- goto out_lid;
+- }
+-
+- ret = rdma_listen(isert_lid, ISERT_RDMA_LISTEN_BACKLOG);
+- if (ret) {
+- pr_err("rdma_listen() for isert_lid failed: %d\n", ret);
+- goto out_lid;
+- }
+-
+ isert_np->np_cm_id = isert_lid;
+ np->np_context = isert_np;
+- pr_debug("Setup isert_lid->context: %p\n", isert_lid->context);
+
+ return 0;
+
+-out_lid:
+- rdma_destroy_id(isert_lid);
+ out:
+ kfree(isert_np);
++
+ return ret;
+ }
+
+--- a/drivers/infiniband/ulp/isert/ib_isert.h
++++ b/drivers/infiniband/ulp/isert/ib_isert.h
+@@ -159,6 +159,7 @@ struct isert_device {
+ };
+
+ struct isert_np {
++ struct iscsi_np *np;
+ struct semaphore np_sem;
+ struct rdma_cm_id *np_cm_id;
+ struct mutex np_accept_mutex;
--- /dev/null
+From 2371e5da8cfe91443339b54444dec6254fdd6dfc Mon Sep 17 00:00:00 2001
+From: Sagi Grimberg <sagig@mellanox.com>
+Date: Tue, 2 Dec 2014 16:57:21 +0200
+Subject: iser-target: Parallelize CM connection establishment
+
+From: Sagi Grimberg <sagig@mellanox.com>
+
+commit 2371e5da8cfe91443339b54444dec6254fdd6dfc upstream.
+
+There is no point in accepting a new CM request only
+when we are completely done with the last iscsi login.
+Instead we accept immediately, this will also cause the
+CM connection to reach connected state and the initiator
+is allowed to send the first login. We mark that we got
+the initial login and let iscsi layer pick it up when it
+gets there.
+
+This reduces the parallel login sequence by a factor of
+more then 4 (and more for multi-login) and also prevents
+the initiator (who does all logins in parallel) from
+giving up on login timeout expiration.
+
+In order to support multiple login requests sequence (CHAP)
+we call isert_rx_login_req from isert_rx_completion insead
+of letting isert_get_login_rx call it.
+
+Squashed:
+
+iser-target: Use kref_get_unless_zero in connected_handler
+iser-target: Acquire conn_mutex when changing connection state
+iser-target: Reject connect request in failure path
+
+Signed-off-by: Sagi Grimberg <sagig@mellanox.com>
+Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/infiniband/ulp/isert/ib_isert.c | 84 ++++++++++++++++++++++----------
+ drivers/infiniband/ulp/isert/ib_isert.h | 2
+ 2 files changed, 62 insertions(+), 24 deletions(-)
+
+--- a/drivers/infiniband/ulp/isert/ib_isert.c
++++ b/drivers/infiniband/ulp/isert/ib_isert.c
+@@ -52,6 +52,10 @@ isert_unreg_rdma(struct isert_cmd *isert
+ static int
+ isert_reg_rdma(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ struct isert_rdma_wr *wr);
++static int
++isert_rdma_post_recvl(struct isert_conn *isert_conn);
++static int
++isert_rdma_accept(struct isert_conn *isert_conn);
+
+ static void
+ isert_qp_event_callback(struct ib_event *e, void *context)
+@@ -515,6 +519,7 @@ isert_connect_request(struct rdma_cm_id
+ isert_conn->state = ISER_CONN_INIT;
+ INIT_LIST_HEAD(&isert_conn->conn_accept_node);
+ init_completion(&isert_conn->conn_login_comp);
++ init_completion(&isert_conn->login_req_comp);
+ init_completion(&isert_conn->conn_wait);
+ init_completion(&isert_conn->conn_wait_comp_err);
+ kref_init(&isert_conn->conn_kref);
+@@ -596,6 +601,14 @@ isert_connect_request(struct rdma_cm_id
+ if (ret)
+ goto out_conn_dev;
+
++ ret = isert_rdma_post_recvl(isert_conn);
++ if (ret)
++ goto out_conn_dev;
++
++ ret = isert_rdma_accept(isert_conn);
++ if (ret)
++ goto out_conn_dev;
++
+ mutex_lock(&isert_np->np_accept_mutex);
+ list_add_tail(&isert_conn->conn_accept_node, &isert_np->np_accept_list);
+ mutex_unlock(&isert_np->np_accept_mutex);
+@@ -620,6 +633,7 @@ out_login_buf:
+ kfree(isert_conn->login_buf);
+ out:
+ kfree(isert_conn);
++ rdma_reject(cma_id, NULL, 0);
+ return ret;
+ }
+
+@@ -673,8 +687,15 @@ isert_connected_handler(struct rdma_cm_i
+
+ pr_info("conn %p\n", isert_conn);
+
+- isert_conn->state = ISER_CONN_UP;
+- kref_get(&isert_conn->conn_kref);
++ if (!kref_get_unless_zero(&isert_conn->conn_kref)) {
++ pr_warn("conn %p connect_release is running\n", isert_conn);
++ return;
++ }
++
++ mutex_lock(&isert_conn->conn_mutex);
++ if (isert_conn->state != ISER_CONN_FULL_FEATURE)
++ isert_conn->state = ISER_CONN_UP;
++ mutex_unlock(&isert_conn->conn_mutex);
+ }
+
+ static void
+@@ -1029,7 +1050,9 @@ isert_put_login_tx(struct iscsi_conn *co
+ return ret;
+
+ /* Now we are in FULL_FEATURE phase */
++ mutex_lock(&isert_conn->conn_mutex);
+ isert_conn->state = ISER_CONN_FULL_FEATURE;
++ mutex_unlock(&isert_conn->conn_mutex);
+ goto post_send;
+ }
+
+@@ -1046,18 +1069,17 @@ post_send:
+ }
+
+ static void
+-isert_rx_login_req(struct iser_rx_desc *rx_desc, int rx_buflen,
+- struct isert_conn *isert_conn)
++isert_rx_login_req(struct isert_conn *isert_conn)
+ {
++ struct iser_rx_desc *rx_desc = (void *)isert_conn->login_req_buf;
++ int rx_buflen = isert_conn->login_req_len;
+ struct iscsi_conn *conn = isert_conn->conn;
+ struct iscsi_login *login = conn->conn_login;
+ int size;
+
+- if (!login) {
+- pr_err("conn->conn_login is NULL\n");
+- dump_stack();
+- return;
+- }
++ pr_info("conn %p\n", isert_conn);
++
++ WARN_ON_ONCE(!login);
+
+ if (login->first_request) {
+ struct iscsi_login_req *login_req =
+@@ -1420,11 +1442,20 @@ isert_rx_completion(struct iser_rx_desc
+ hdr->opcode, hdr->itt, hdr->flags,
+ (int)(xfer_len - ISER_HEADERS_LEN));
+
+- if ((char *)desc == isert_conn->login_req_buf)
+- isert_rx_login_req(desc, xfer_len - ISER_HEADERS_LEN,
+- isert_conn);
+- else
++ if ((char *)desc == isert_conn->login_req_buf) {
++ isert_conn->login_req_len = xfer_len - ISER_HEADERS_LEN;
++ if (isert_conn->conn) {
++ struct iscsi_login *login = isert_conn->conn->conn_login;
++
++ if (login && !login->first_request)
++ isert_rx_login_req(isert_conn);
++ }
++ mutex_lock(&isert_conn->conn_mutex);
++ complete(&isert_conn->login_req_comp);
++ mutex_unlock(&isert_conn->conn_mutex);
++ } else {
+ isert_rx_do_work(desc, isert_conn);
++ }
+
+ ib_dma_sync_single_for_device(ib_dev, rx_dma, rx_buflen,
+ DMA_FROM_DEVICE);
+@@ -2699,7 +2730,15 @@ isert_get_login_rx(struct iscsi_conn *co
+ struct isert_conn *isert_conn = (struct isert_conn *)conn->context;
+ int ret;
+
+- pr_debug("isert_get_login_rx before conn_login_comp conn: %p\n", conn);
++ pr_info("before login_req comp conn: %p\n", isert_conn);
++ ret = wait_for_completion_interruptible(&isert_conn->login_req_comp);
++ if (ret) {
++ pr_err("isert_conn %p interrupted before got login req\n",
++ isert_conn);
++ return ret;
++ }
++ reinit_completion(&isert_conn->login_req_comp);
++
+ /*
+ * For login requests after the first PDU, isert_rx_login_req() will
+ * kick schedule_delayed_work(&conn->login_work) as the packet is
+@@ -2709,11 +2748,15 @@ isert_get_login_rx(struct iscsi_conn *co
+ if (!login->first_request)
+ return 0;
+
++ isert_rx_login_req(isert_conn);
++
++ pr_info("before conn_login_comp conn: %p\n", conn);
+ ret = wait_for_completion_interruptible(&isert_conn->conn_login_comp);
+ if (ret)
+ return ret;
+
+- pr_debug("isert_get_login_rx processing login->req: %p\n", login->req);
++ pr_info("processing login->req: %p\n", login->req);
++
+ return 0;
+ }
+
+@@ -2791,17 +2834,10 @@ accept_wait:
+ isert_conn->conn = conn;
+ max_accept = 0;
+
+- ret = isert_rdma_post_recvl(isert_conn);
+- if (ret)
+- return ret;
+-
+- ret = isert_rdma_accept(isert_conn);
+- if (ret)
+- return ret;
+-
+ isert_set_conn_info(np, conn, isert_conn);
+
+- pr_debug("Processing isert_accept_np: isert_conn: %p\n", isert_conn);
++ pr_debug("Processing isert_conn: %p\n", isert_conn);
++
+ return 0;
+ }
+
+--- a/drivers/infiniband/ulp/isert/ib_isert.h
++++ b/drivers/infiniband/ulp/isert/ib_isert.h
+@@ -103,6 +103,7 @@ struct isert_conn {
+ char *login_req_buf;
+ char *login_rsp_buf;
+ u64 login_req_dma;
++ int login_req_len;
+ u64 login_rsp_dma;
+ unsigned int conn_rx_desc_head;
+ struct iser_rx_desc *conn_rx_descs;
+@@ -110,6 +111,7 @@ struct isert_conn {
+ struct iscsi_conn *conn;
+ struct list_head conn_accept_node;
+ struct completion conn_login_comp;
++ struct completion login_req_comp;
+ struct iser_tx_desc conn_login_tx_desc;
+ struct rdma_cm_id *conn_cm_id;
+ struct ib_pd *conn_pd;
tcm_loop-fixup-tag-handling.patch
tcm_loop-fix-wrong-i_t-nexus-association.patch
vhost-scsi-add-missing-virtio-scsi-tcm-attribute-conversion.patch
+iscsi-iser-target-initiate-termination-only-once.patch
+iser-target-fix-flush-disconnect-completion-handling.patch
+iser-target-parallelize-cm-connection-establishment.patch
+iser-target-fix-connected_handler-teardown-flow-race.patch
+iser-target-handle-addr_change-event-for-listener-cm_id.patch
+iser-target-fix-implicit-termination-of-connections.patch