From 3ce0f6604d72cb7b79f0a04857b57851fd3b83ad Mon Sep 17 00:00:00 2001 From: Israel Rukshin Date: Wed, 17 Aug 2016 07:27:57 +0000 Subject: [PATCH] isert: fix a race when drain wr is not the last flush We must wait for both the send and recv cqs to flush all pending work requests. To make sure that the drain will be the last flush we post a second drain work request on the recv queue. Signed-off-by: Israel Rukshin git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@6946 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser.h | 10 ++- iscsi-scst/kernel/isert-scst/iser_rdma.c | 94 ++++++++++++++++-------- 2 files changed, 71 insertions(+), 33 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser.h b/iscsi-scst/kernel/isert-scst/iser.h index f7f89e831..104f8d5e7 100644 --- a/iscsi-scst/kernel/isert-scst/iser.h +++ b/iscsi-scst/kernel/isert-scst/iser.h @@ -158,9 +158,10 @@ struct isert_cq { #define ISERT_CONNECTION_ABORTED 0 #define ISERT_DRAIN_POSTED 1 -#define ISERT_DRAIN_FAILED 2 -#define ISERT_DISCON_CALLED 3 -#define ISERT_CONNECTION_EST 4 +#define ISERT_DISCON_CALLED 2 +#define ISERT_CONNECTION_EST 3 +#define ISERT_DRAINED_RQ 4 +#define ISERT_DRAINED_SQ 5 struct isert_connection { struct iscsi_conn iscsi ____cacheline_aligned; @@ -218,7 +219,8 @@ struct isert_connection { struct work_struct drain_work; struct work_struct discon_work; struct work_struct free_work; - struct isert_wr drain_wr; + struct isert_wr drain_wr_sq; + struct isert_wr drain_wr_rq; struct kref kref; struct isert_portal *portal; diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index af33d4fa9..c0ad08749 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -142,36 +142,61 @@ int isert_post_send(struct isert_connection *isert_conn, return err; } +static void isert_post_drain_sq(struct isert_connection* isert_conn) +{ + struct ib_send_wr* bad_wr; + struct isert_wr *drain_wr_sq = &isert_conn->drain_wr_sq; + int err; + + isert_wr_set_fields(drain_wr_sq, isert_conn, NULL); + drain_wr_sq->wr_op = ISER_WR_SEND; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + drain_wr_sq->send_wr.wr_id = _ptr_to_u64(drain_wr_sq); + drain_wr_sq->send_wr.opcode = IB_WR_SEND; + err = ib_post_send(isert_conn->qp, + &drain_wr_sq->send_wr, &bad_wr); +#else + drain_wr_sq->send_wr.wr.wr_id = _ptr_to_u64(drain_wr_sq); + drain_wr_sq->send_wr.wr.opcode = IB_WR_SEND; + err = ib_post_send(isert_conn->qp, + &drain_wr_sq->send_wr.wr, &bad_wr); +#endif + if (unlikely(err)) { + PRINT_ERROR("Failed to post drain wr to send queue, err:%d", err); + /* We need to decrement iser_conn->kref in order to be able to cleanup + * the connection */ + set_bit(ISERT_DRAINED_SQ, &isert_conn->flags); + if (test_bit(ISERT_DRAINED_RQ, &isert_conn->flags)) { + isert_sched_conn_free(isert_conn); + } + } +} + +static void isert_post_drain_rq(struct isert_connection *isert_conn) +{ + struct ib_recv_wr *bad_wr; + struct isert_wr *drain_wr_rq = &isert_conn->drain_wr_rq; + int err; + + isert_wr_set_fields(drain_wr_rq, isert_conn, NULL); + drain_wr_rq->wr_op = ISER_WR_RECV; + drain_wr_rq->recv_wr.wr_id = _ptr_to_u64(drain_wr_rq); + err = ib_post_recv(isert_conn->qp, + &drain_wr_rq->recv_wr, &bad_wr); + if (unlikely(err)) { + PRINT_ERROR("Failed to post drain wr to receive queue, err:%d", err); + set_bit(ISERT_DRAINED_RQ, &isert_conn->flags); + if (test_bit(ISERT_DRAINED_SQ, &isert_conn->flags)) { + isert_sched_conn_free(isert_conn); + } + } +} + void isert_post_drain(struct isert_connection *isert_conn) { if (!test_and_set_bit(ISERT_DRAIN_POSTED, &isert_conn->flags)) { - struct ib_send_wr *bad_wr; - int err; - - isert_wr_set_fields(&isert_conn->drain_wr, isert_conn, NULL); - isert_conn->drain_wr.wr_op = ISER_WR_SEND; -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) - isert_conn->drain_wr.send_wr.wr_id = - _ptr_to_u64(&isert_conn->drain_wr); - isert_conn->drain_wr.send_wr.opcode = IB_WR_SEND; - err = ib_post_send(isert_conn->qp, - &isert_conn->drain_wr.send_wr, &bad_wr); -#else - isert_conn->drain_wr.send_wr.wr.wr_id = - _ptr_to_u64(&isert_conn->drain_wr); - isert_conn->drain_wr.send_wr.wr.opcode = IB_WR_SEND; - err = ib_post_send(isert_conn->qp, - &isert_conn->drain_wr.send_wr.wr, &bad_wr); -#endif - if (unlikely(err)) { - PRINT_ERROR("Failed to post drain wr, err:%d", err); - /* - * We need to decrement iser_conn->kref in order to be - * able to cleanup the connection. - */ - set_bit(ISERT_DRAIN_FAILED, &isert_conn->flags); - isert_conn_free(isert_conn); - } + isert_post_drain_rq(isert_conn); + isert_post_drain_sq(isert_conn); } } @@ -684,8 +709,12 @@ static void isert_handle_wc_error(struct ib_wc *wc) #else num_sge = wr->send_wr.wr.num_sge; #endif - if (unlikely(num_sge == 0)) /* Drain WR */ - isert_sched_conn_drained(isert_conn); + if (unlikely(num_sge == 0)) { /* Drain WR */ + set_bit(ISERT_DRAINED_SQ, &isert_conn->flags); + if (test_bit(ISERT_DRAINED_RQ, &isert_conn->flags)) { + isert_sched_conn_drained(isert_conn); + } + } else if (!isert_pdu->is_fake_rx) isert_pdu_err(&isert_pdu->iscsi); break; @@ -700,6 +729,13 @@ static void isert_handle_wc_error(struct ib_wc *wc) break; case ISER_WR_RECV: /* this should be the Flush, no task has been created yet */ + num_sge = wr->recv_wr.num_sge; + if (unlikely(num_sge == 0)) { /* Drain WR */ + set_bit(ISERT_DRAINED_RQ, &isert_conn->flags); + if (test_bit(ISERT_DRAINED_SQ, &isert_conn->flags)) { + isert_sched_conn_drained(isert_conn); + } + } break; case ISER_WR_RDMA_WRITE: if (isert_buf->sg_cnt != 0) {