diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2013-07-03 06:05:37 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-04 04:50:33 -0400 |
commit | d9e507c05ca19ad2ec166577edd8b47e17c8961e (patch) | |
tree | a3369adbf8ab02bc88eca6549c5ad4bc4a12b21e | |
parent | a5d56a2217664c7bc06cb7deeac9a2a155ba4345 (diff) |
iser-target: Fix session reset bug with RDMA_CM_EVENT_DISCONNECTED
commit b2cb96494d83b894a43ba8b9023eead8ff50684b upstream.
This patch addresses a bug where RDMA_CM_EVENT_DISCONNECTED may occur
before the connection shutdown has been completed by rx/tx threads,
that causes isert_free_conn() to wait indefinately on ->conn_wait.
This patch allows isert_disconnect_work code to invoke rdma_disconnect
when isert_disconnect_work() process context is started by client
session reset before isert_free_conn() code has been reached.
It also adds isert_conn->conn_mutex protection for ->state within
isert_disconnect_work(), isert_cq_comp_err() and isert_free_conn()
code, along with isert_check_state() for wait_event usage.
(v2: Add explicit iscsit_cause_connection_reinstatement call
during isert_disconnect_work() to force conn reset)
Cc: Or Gerlitz <ogerlitz@mellanox.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/infiniband/ulp/isert/ib_isert.c | 70 | ||||
-rw-r--r-- | drivers/infiniband/ulp/isert/ib_isert.h | 1 | ||||
-rw-r--r-- | drivers/target/iscsi/iscsi_target_erl0.c | 1 | ||||
-rw-r--r-- | include/target/iscsi/iscsi_transport.h | 4 |
4 files changed, 66 insertions, 10 deletions
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 6559febd255e..6b20a9d09cbb 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c | |||
@@ -388,6 +388,7 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) | |||
388 | init_waitqueue_head(&isert_conn->conn_wait_comp_err); | 388 | init_waitqueue_head(&isert_conn->conn_wait_comp_err); |
389 | kref_init(&isert_conn->conn_kref); | 389 | kref_init(&isert_conn->conn_kref); |
390 | kref_get(&isert_conn->conn_kref); | 390 | kref_get(&isert_conn->conn_kref); |
391 | mutex_init(&isert_conn->conn_mutex); | ||
391 | 392 | ||
392 | cma_id->context = isert_conn; | 393 | cma_id->context = isert_conn; |
393 | isert_conn->conn_cm_id = cma_id; | 394 | isert_conn->conn_cm_id = cma_id; |
@@ -540,15 +541,32 @@ isert_disconnect_work(struct work_struct *work) | |||
540 | struct isert_conn, conn_logout_work); | 541 | struct isert_conn, conn_logout_work); |
541 | 542 | ||
542 | pr_debug("isert_disconnect_work(): >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); | 543 | pr_debug("isert_disconnect_work(): >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); |
543 | 544 | mutex_lock(&isert_conn->conn_mutex); | |
544 | isert_conn->state = ISER_CONN_DOWN; | 545 | isert_conn->state = ISER_CONN_DOWN; |
545 | 546 | ||
546 | if (isert_conn->post_recv_buf_count == 0 && | 547 | if (isert_conn->post_recv_buf_count == 0 && |
547 | atomic_read(&isert_conn->post_send_buf_count) == 0) { | 548 | atomic_read(&isert_conn->post_send_buf_count) == 0) { |
548 | pr_debug("Calling wake_up(&isert_conn->conn_wait);\n"); | 549 | pr_debug("Calling wake_up(&isert_conn->conn_wait);\n"); |
549 | wake_up(&isert_conn->conn_wait); | 550 | mutex_unlock(&isert_conn->conn_mutex); |
551 | goto wake_up; | ||
552 | } | ||
553 | if (!isert_conn->conn_cm_id) { | ||
554 | mutex_unlock(&isert_conn->conn_mutex); | ||
555 | isert_put_conn(isert_conn); | ||
556 | return; | ||
557 | } | ||
558 | if (!isert_conn->logout_posted) { | ||
559 | pr_debug("Calling rdma_disconnect for !logout_posted from" | ||
560 | " isert_disconnect_work\n"); | ||
561 | rdma_disconnect(isert_conn->conn_cm_id); | ||
562 | mutex_unlock(&isert_conn->conn_mutex); | ||
563 | iscsit_cause_connection_reinstatement(isert_conn->conn, 0); | ||
564 | goto wake_up; | ||
550 | } | 565 | } |
566 | mutex_unlock(&isert_conn->conn_mutex); | ||
551 | 567 | ||
568 | wake_up: | ||
569 | wake_up(&isert_conn->conn_wait); | ||
552 | isert_put_conn(isert_conn); | 570 | isert_put_conn(isert_conn); |
553 | } | 571 | } |
554 | 572 | ||
@@ -1423,7 +1441,11 @@ isert_cq_comp_err(struct iser_tx_desc *tx_desc, struct isert_conn *isert_conn) | |||
1423 | pr_debug("isert_cq_comp_err >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); | 1441 | pr_debug("isert_cq_comp_err >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); |
1424 | pr_debug("Calling wake_up from isert_cq_comp_err\n"); | 1442 | pr_debug("Calling wake_up from isert_cq_comp_err\n"); |
1425 | 1443 | ||
1426 | isert_conn->state = ISER_CONN_TERMINATING; | 1444 | mutex_lock(&isert_conn->conn_mutex); |
1445 | if (isert_conn->state != ISER_CONN_DOWN) | ||
1446 | isert_conn->state = ISER_CONN_TERMINATING; | ||
1447 | mutex_unlock(&isert_conn->conn_mutex); | ||
1448 | |||
1427 | wake_up(&isert_conn->conn_wait_comp_err); | 1449 | wake_up(&isert_conn->conn_wait_comp_err); |
1428 | } | 1450 | } |
1429 | } | 1451 | } |
@@ -2193,6 +2215,17 @@ isert_free_np(struct iscsi_np *np) | |||
2193 | kfree(isert_np); | 2215 | kfree(isert_np); |
2194 | } | 2216 | } |
2195 | 2217 | ||
2218 | static int isert_check_state(struct isert_conn *isert_conn, int state) | ||
2219 | { | ||
2220 | int ret; | ||
2221 | |||
2222 | mutex_lock(&isert_conn->conn_mutex); | ||
2223 | ret = (isert_conn->state == state); | ||
2224 | mutex_unlock(&isert_conn->conn_mutex); | ||
2225 | |||
2226 | return ret; | ||
2227 | } | ||
2228 | |||
2196 | static void isert_free_conn(struct iscsi_conn *conn) | 2229 | static void isert_free_conn(struct iscsi_conn *conn) |
2197 | { | 2230 | { |
2198 | struct isert_conn *isert_conn = conn->context; | 2231 | struct isert_conn *isert_conn = conn->context; |
@@ -2202,26 +2235,43 @@ static void isert_free_conn(struct iscsi_conn *conn) | |||
2202 | * Decrement post_send_buf_count for special case when called | 2235 | * Decrement post_send_buf_count for special case when called |
2203 | * from isert_do_control_comp() -> iscsit_logout_post_handler() | 2236 | * from isert_do_control_comp() -> iscsit_logout_post_handler() |
2204 | */ | 2237 | */ |
2238 | mutex_lock(&isert_conn->conn_mutex); | ||
2205 | if (isert_conn->logout_posted) | 2239 | if (isert_conn->logout_posted) |
2206 | atomic_dec(&isert_conn->post_send_buf_count); | 2240 | atomic_dec(&isert_conn->post_send_buf_count); |
2207 | 2241 | ||
2208 | if (isert_conn->conn_cm_id) | 2242 | if (isert_conn->conn_cm_id && isert_conn->state != ISER_CONN_DOWN) { |
2243 | pr_debug("Calling rdma_disconnect from isert_free_conn\n"); | ||
2209 | rdma_disconnect(isert_conn->conn_cm_id); | 2244 | rdma_disconnect(isert_conn->conn_cm_id); |
2245 | } | ||
2210 | /* | 2246 | /* |
2211 | * Only wait for conn_wait_comp_err if the isert_conn made it | 2247 | * Only wait for conn_wait_comp_err if the isert_conn made it |
2212 | * into full feature phase.. | 2248 | * into full feature phase.. |
2213 | */ | 2249 | */ |
2214 | if (isert_conn->state > ISER_CONN_INIT) { | 2250 | if (isert_conn->state == ISER_CONN_UP) { |
2215 | pr_debug("isert_free_conn: Before wait_event comp_err %d\n", | 2251 | pr_debug("isert_free_conn: Before wait_event comp_err %d\n", |
2216 | isert_conn->state); | 2252 | isert_conn->state); |
2253 | mutex_unlock(&isert_conn->conn_mutex); | ||
2254 | |||
2217 | wait_event(isert_conn->conn_wait_comp_err, | 2255 | wait_event(isert_conn->conn_wait_comp_err, |
2218 | isert_conn->state == ISER_CONN_TERMINATING); | 2256 | (isert_check_state(isert_conn, ISER_CONN_TERMINATING))); |
2219 | pr_debug("isert_free_conn: After wait_event #1 >>>>>>>>>>>>\n"); | 2257 | |
2258 | wait_event(isert_conn->conn_wait, | ||
2259 | (isert_check_state(isert_conn, ISER_CONN_DOWN))); | ||
2260 | |||
2261 | isert_put_conn(isert_conn); | ||
2262 | return; | ||
2263 | } | ||
2264 | if (isert_conn->state == ISER_CONN_INIT) { | ||
2265 | mutex_unlock(&isert_conn->conn_mutex); | ||
2266 | isert_put_conn(isert_conn); | ||
2267 | return; | ||
2220 | } | 2268 | } |
2269 | pr_debug("isert_free_conn: wait_event conn_wait %d\n", | ||
2270 | isert_conn->state); | ||
2271 | mutex_unlock(&isert_conn->conn_mutex); | ||
2221 | 2272 | ||
2222 | pr_debug("isert_free_conn: wait_event conn_wait %d\n", isert_conn->state); | 2273 | wait_event(isert_conn->conn_wait, |
2223 | wait_event(isert_conn->conn_wait, isert_conn->state == ISER_CONN_DOWN); | 2274 | (isert_check_state(isert_conn, ISER_CONN_DOWN))); |
2224 | pr_debug("isert_free_conn: After wait_event #2 >>>>>>>>>>>>>>>>>>>>\n"); | ||
2225 | 2275 | ||
2226 | isert_put_conn(isert_conn); | 2276 | isert_put_conn(isert_conn); |
2227 | } | 2277 | } |
diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h index b104f4c2cd38..5795c82a2306 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.h +++ b/drivers/infiniband/ulp/isert/ib_isert.h | |||
@@ -102,6 +102,7 @@ struct isert_conn { | |||
102 | struct ib_qp *conn_qp; | 102 | struct ib_qp *conn_qp; |
103 | struct isert_device *conn_device; | 103 | struct isert_device *conn_device; |
104 | struct work_struct conn_logout_work; | 104 | struct work_struct conn_logout_work; |
105 | struct mutex conn_mutex; | ||
105 | wait_queue_head_t conn_wait; | 106 | wait_queue_head_t conn_wait; |
106 | wait_queue_head_t conn_wait_comp_err; | 107 | wait_queue_head_t conn_wait_comp_err; |
107 | struct kref conn_kref; | 108 | struct kref conn_kref; |
diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c index dcb199da06b9..7f1116635d17 100644 --- a/drivers/target/iscsi/iscsi_target_erl0.c +++ b/drivers/target/iscsi/iscsi_target_erl0.c | |||
@@ -909,6 +909,7 @@ void iscsit_cause_connection_reinstatement(struct iscsi_conn *conn, int sleep) | |||
909 | wait_for_completion(&conn->conn_wait_comp); | 909 | wait_for_completion(&conn->conn_wait_comp); |
910 | complete(&conn->conn_post_wait_comp); | 910 | complete(&conn->conn_post_wait_comp); |
911 | } | 911 | } |
912 | EXPORT_SYMBOL(iscsit_cause_connection_reinstatement); | ||
912 | 913 | ||
913 | void iscsit_fall_back_to_erl0(struct iscsi_session *sess) | 914 | void iscsit_fall_back_to_erl0(struct iscsi_session *sess) |
914 | { | 915 | { |
diff --git a/include/target/iscsi/iscsi_transport.h b/include/target/iscsi/iscsi_transport.h index 23a87d0cd72c..6db632eb3821 100644 --- a/include/target/iscsi/iscsi_transport.h +++ b/include/target/iscsi/iscsi_transport.h | |||
@@ -67,6 +67,10 @@ extern int iscsit_logout_post_handler(struct iscsi_cmd *, struct iscsi_conn *); | |||
67 | */ | 67 | */ |
68 | extern void iscsit_increment_maxcmdsn(struct iscsi_cmd *, struct iscsi_session *); | 68 | extern void iscsit_increment_maxcmdsn(struct iscsi_cmd *, struct iscsi_session *); |
69 | /* | 69 | /* |
70 | * From iscsi_target_erl0.c | ||
71 | */ | ||
72 | extern void iscsit_cause_connection_reinstatement(struct iscsi_conn *, int); | ||
73 | /* | ||
70 | * From iscsi_target_erl1.c | 74 | * From iscsi_target_erl1.c |
71 | */ | 75 | */ |
72 | extern void iscsit_stop_dataout_timer(struct iscsi_cmd *); | 76 | extern void iscsit_stop_dataout_timer(struct iscsi_cmd *); |