For erdma, hardware won't process any WRs after modifying QP state to error, so the default __ib_drain_sq and __ib_drain_rq can not work for erdma device. Here, we introduce custom implementation of drain_sq and drain_rq interface to fit erdma hardware. Signed-off-by: Cheng Xu <chengyou@xxxxxxxxxxxxxxxxx> --- drivers/infiniband/hw/erdma/erdma_qp.c | 71 +++++++++++++++++++++++ drivers/infiniband/hw/erdma/erdma_verbs.h | 10 ++++ 2 files changed, 81 insertions(+) diff --git a/drivers/infiniband/hw/erdma/erdma_qp.c b/drivers/infiniband/hw/erdma/erdma_qp.c index abf8b134d076..57fdb946fbfd 100644 --- a/drivers/infiniband/hw/erdma/erdma_qp.c +++ b/drivers/infiniband/hw/erdma/erdma_qp.c @@ -599,3 +599,74 @@ int erdma_post_recv_nodrain(struct ib_qp *ibqp, { return erdma_post_recv(ibqp, recv_wr, bad_recv_wr, false); } + +static void erdma_drain_qp_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct erdma_drain_cqe *cqe = + container_of(wc->wr_cqe, struct erdma_drain_cqe, cqe); + + complete(&cqe->done); +} + +static void erdma_drain_qp_common(struct ib_qp *ibqp, struct completion *comp, + struct ib_cq *ibcq) +{ + struct ib_qp_attr attr = { .qp_state = IB_QPS_ERR }; + struct erdma_qp *qp = to_eqp(ibqp); + const struct ib_send_wr *bad_swr; + const struct ib_recv_wr *bad_rwr; + struct ib_rdma_wr swr = { + .wr = { + .next = NULL, + { .wr_cqe = &qp->kern_qp.sdrain.cqe, }, + .opcode = IB_WR_RDMA_WRITE, + .send_flags = IB_SEND_SIGNALED, + }, + }; + struct ib_recv_wr rwr = { + .next = NULL, + .wr_cqe = &qp->kern_qp.rdrain.cqe, + .num_sge = 0, + }; + + if (qp->flags & ERDMA_QP_FLAGS_DRAIN_ISSUED) + goto wait_for_completion; + + qp->flags |= ERDMA_QP_FLAGS_DRAIN_ISSUED; + + qp->kern_qp.rdrain.cqe.done = erdma_drain_qp_done; + init_completion(&qp->kern_qp.rdrain.done); + + qp->kern_qp.sdrain.cqe.done = erdma_drain_qp_done; + init_completion(&qp->kern_qp.sdrain.done); + + if (erdma_post_recv(ibqp, &rwr, &bad_rwr, true)) + return; + + if (erdma_post_send(ibqp, &swr.wr, &bad_swr, true)) + return; + + if (ib_modify_qp(ibqp, &attr, IB_QP_STATE)) + return; + +wait_for_completion: + if (ibcq->poll_ctx == IB_POLL_DIRECT) + while (wait_for_completion_timeout(comp, HZ / 10) <= 0) + ib_process_cq_direct(ibcq, -1); + else + wait_for_completion(comp); +} + +void erdma_drain_sq(struct ib_qp *ibqp) +{ + struct erdma_qp *qp = to_eqp(ibqp); + + erdma_drain_qp_common(ibqp, &qp->kern_qp.sdrain.done, ibqp->send_cq); +} + +void erdma_drain_rq(struct ib_qp *ibqp) +{ + struct erdma_qp *qp = to_eqp(ibqp); + + erdma_drain_qp_common(ibqp, &qp->kern_qp.rdrain.done, ibqp->recv_cq); +} diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.h b/drivers/infiniband/hw/erdma/erdma_verbs.h index f4148fbac878..4cec92c8a737 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.h +++ b/drivers/infiniband/hw/erdma/erdma_verbs.h @@ -133,6 +133,11 @@ struct erdma_uqp { u32 rq_offset; }; +struct erdma_drain_cqe { + struct ib_cqe cqe; + struct completion done; +}; + struct erdma_kqp { u16 sq_pi; u16 sq_ci; @@ -155,6 +160,9 @@ struct erdma_kqp { void *sq_db_info; void *rq_db_info; + struct erdma_drain_cqe sdrain; + struct erdma_drain_cqe rdrain; + u8 sig_all; }; @@ -341,6 +349,8 @@ int erdma_post_send_nodrain(struct ib_qp *ibqp, int erdma_post_recv_nodrain(struct ib_qp *ibqp, const struct ib_recv_wr *recv_wr, const struct ib_recv_wr **bad_recv_wr); +void erdma_drain_sq(struct ib_qp *ibqp); +void erdma_drain_rq(struct ib_qp *ibqp); int erdma_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc); struct ib_mr *erdma_ib_alloc_mr(struct ib_pd *ibpd, enum ib_mr_type mr_type, u32 max_num_sg); -- 2.27.0