From 9defc0b71839faa190b302976c2da9c52a3a0267 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 25 Jan 2015 09:39:00 -0800 Subject: [PATCH] 3.14-stable patches added patches: iscsi-iser-target-initiate-termination-only-once.patch iser-target-fix-connected_handler-teardown-flow-race.patch iser-target-fix-flush-disconnect-completion-handling.patch iser-target-fix-implicit-termination-of-connections.patch iser-target-handle-addr_change-event-for-listener-cm_id.patch iser-target-parallelize-cm-connection-establishment.patch --- ...arget-initiate-termination-only-once.patch | 218 ++++++++++++++++ ...connected_handler-teardown-flow-race.patch | 131 ++++++++++ ...flush-disconnect-completion-handling.patch | 165 ++++++++++++ ...-implicit-termination-of-connections.patch | 125 ++++++++++ ...addr_change-event-for-listener-cm_id.patch | 224 +++++++++++++++++ ...allelize-cm-connection-establishment.patch | 235 ++++++++++++++++++ queue-3.14/series | 6 + 7 files changed, 1104 insertions(+) create mode 100644 queue-3.14/iscsi-iser-target-initiate-termination-only-once.patch create mode 100644 queue-3.14/iser-target-fix-connected_handler-teardown-flow-race.patch create mode 100644 queue-3.14/iser-target-fix-flush-disconnect-completion-handling.patch create mode 100644 queue-3.14/iser-target-fix-implicit-termination-of-connections.patch create mode 100644 queue-3.14/iser-target-handle-addr_change-event-for-listener-cm_id.patch create mode 100644 queue-3.14/iser-target-parallelize-cm-connection-establishment.patch diff --git a/queue-3.14/iscsi-iser-target-initiate-termination-only-once.patch b/queue-3.14/iscsi-iser-target-initiate-termination-only-once.patch new file mode 100644 index 00000000000..b55c152897e --- /dev/null +++ b/queue-3.14/iscsi-iser-target-initiate-termination-only-once.patch @@ -0,0 +1,218 @@ +From 954f23722b5753305be490330cf2680b7a25f4a3 Mon Sep 17 00:00:00 2001 +From: Sagi Grimberg +Date: Tue, 2 Dec 2014 16:57:17 +0200 +Subject: iscsi,iser-target: Initiate termination only once + +From: Sagi Grimberg + +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 +Signed-off-by: Sagi Grimberg +Signed-off-by: Nicholas Bellinger +Signed-off-by: Greg Kroah-Hartman + + +--- + 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); + diff --git a/queue-3.14/iser-target-fix-connected_handler-teardown-flow-race.patch b/queue-3.14/iser-target-fix-connected_handler-teardown-flow-race.patch new file mode 100644 index 00000000000..4e12bb96723 --- /dev/null +++ b/queue-3.14/iser-target-fix-connected_handler-teardown-flow-race.patch @@ -0,0 +1,131 @@ +From 19e2090fb246ca21b3e569ead51a6a7a1748eadd Mon Sep 17 00:00:00 2001 +From: Sagi Grimberg +Date: Tue, 2 Dec 2014 16:57:26 +0200 +Subject: iser-target: Fix connected_handler + teardown flow race + +From: Sagi Grimberg + +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 +Signed-off-by: Nicholas Bellinger +Signed-off-by: Greg Kroah-Hartman + + +--- + 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); + } diff --git a/queue-3.14/iser-target-fix-flush-disconnect-completion-handling.patch b/queue-3.14/iser-target-fix-flush-disconnect-completion-handling.patch new file mode 100644 index 00000000000..beb3f6b7cfc --- /dev/null +++ b/queue-3.14/iser-target-fix-flush-disconnect-completion-handling.patch @@ -0,0 +1,165 @@ +From 128e9cc84566a84146baea2335b3824288eed817 Mon Sep 17 00:00:00 2001 +From: Sagi Grimberg +Date: Tue, 2 Dec 2014 16:57:20 +0200 +Subject: iser-target: Fix flush + disconnect completion handling + +From: Sagi Grimberg + +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 +Signed-off-by: Nicholas Bellinger +Signed-off-by: Greg Kroah-Hartman + +--- + 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; diff --git a/queue-3.14/iser-target-fix-implicit-termination-of-connections.patch b/queue-3.14/iser-target-fix-implicit-termination-of-connections.patch new file mode 100644 index 00000000000..3860ed5b4df --- /dev/null +++ b/queue-3.14/iser-target-fix-implicit-termination-of-connections.patch @@ -0,0 +1,125 @@ +From b02efbfc9a051b41e71fe8f94ddf967260e024a6 Mon Sep 17 00:00:00 2001 +From: Sagi Grimberg +Date: Tue, 2 Dec 2014 16:57:29 +0200 +Subject: iser-target: Fix implicit termination of connections + +From: Sagi Grimberg + +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 +Signed-off-by: Sagi Grimberg +Signed-off-by: Nicholas Bellinger +Signed-off-by: Greg Kroah-Hartman + +--- + 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; diff --git a/queue-3.14/iser-target-handle-addr_change-event-for-listener-cm_id.patch b/queue-3.14/iser-target-handle-addr_change-event-for-listener-cm_id.patch new file mode 100644 index 00000000000..83cdfecc5f0 --- /dev/null +++ b/queue-3.14/iser-target-handle-addr_change-event-for-listener-cm_id.patch @@ -0,0 +1,224 @@ +From ca6c1d82d12d8013fb75ce015900d62b9754623c Mon Sep 17 00:00:00 2001 +From: Sagi Grimberg +Date: Tue, 2 Dec 2014 16:57:27 +0200 +Subject: iser-target: Handle ADDR_CHANGE event for listener cm_id + +From: Sagi Grimberg + +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 +Signed-off-by: Sagi Grimberg +Signed-off-by: Nicholas Bellinger +Signed-off-by: Greg Kroah-Hartman + +--- + 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; diff --git a/queue-3.14/iser-target-parallelize-cm-connection-establishment.patch b/queue-3.14/iser-target-parallelize-cm-connection-establishment.patch new file mode 100644 index 00000000000..e1734644d22 --- /dev/null +++ b/queue-3.14/iser-target-parallelize-cm-connection-establishment.patch @@ -0,0 +1,235 @@ +From 2371e5da8cfe91443339b54444dec6254fdd6dfc Mon Sep 17 00:00:00 2001 +From: Sagi Grimberg +Date: Tue, 2 Dec 2014 16:57:21 +0200 +Subject: iser-target: Parallelize CM connection establishment + +From: Sagi Grimberg + +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 +Signed-off-by: Nicholas Bellinger +Signed-off-by: Greg Kroah-Hartman + +--- + 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; diff --git a/queue-3.14/series b/queue-3.14/series index 528255766d3..29a8ce19a2e 100644 --- a/queue-3.14/series +++ b/queue-3.14/series @@ -82,3 +82,9 @@ lockd-fix-a-race-when-initialising-nlmsvc_timeout.patch 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 -- 2.47.3