From: Wei Lin Guay Date: Fri, 1 Jul 2016 08:28:46 +0000 (+0200) Subject: sif: rq: Use a workqueue to handle sif_flush_rq X-Git-Tag: v4.1.12-92~129^2~2 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=f1653befde921b0b7517faf0741d51ed4db34f77;p=users%2Fjedix%2Flinux-maple.git sif: rq: Use a workqueue to handle sif_flush_rq Orabug: 23491094 In sif_flush_rq, one of the required steps is to acquire the qp mutex for qp state transition. Thus, this commit moves the sif_flush_rq into a seperate singlethreaded workqueue to ensure that sif_flush_rq is safe to call from any context, including the interrupt context. Signed-off-by: Wei Lin Guay Reviewed-by: Knut Omang --- diff --git a/drivers/infiniband/hw/sif/sif_cq.c b/drivers/infiniband/hw/sif/sif_cq.c index 26270dee9e5be..6bc61e49ad3e0 100644 --- a/drivers/infiniband/hw/sif/sif_cq.c +++ b/drivers/infiniband/hw/sif/sif_cq.c @@ -468,7 +468,7 @@ static int handle_recv_wc(struct sif_dev *sdev, struct sif_cq *cq, struct ib_wc */ if (rq && !rq->is_srq && !test_bit(SIF_QPS_IN_RESET, &qp->persistent_state)) { - if (sif_flush_rq(sdev, rq, qp, rq_len)) + if (sif_flush_rq_wq(sdev, rq, qp, rq_len)) sif_log(sdev, SIF_INFO, "failed to flush RQ %d", rq->index); } diff --git a/drivers/infiniband/hw/sif/sif_dev.h b/drivers/infiniband/hw/sif/sif_dev.h index b767004191c72..c92aecae5a062 100644 --- a/drivers/infiniband/hw/sif/sif_dev.h +++ b/drivers/infiniband/hw/sif/sif_dev.h @@ -335,6 +335,7 @@ struct sif_dev { /* Owned by sif_r3.c - wa support */ struct sif_wa_stats wa_stats; + struct workqueue_struct *misc_wq; /* Used to flush send/receive queue */ }; /* TBD: These should probably come from common pci headers diff --git a/drivers/infiniband/hw/sif/sif_eq.c b/drivers/infiniband/hw/sif/sif_eq.c index 4bcf16c231f96..3c0f05c4537f7 100644 --- a/drivers/infiniband/hw/sif/sif_eq.c +++ b/drivers/infiniband/hw/sif/sif_eq.c @@ -642,7 +642,7 @@ static void handle_event_work(struct work_struct *work) ibqp->event_handler(&ibe, ibqp->qp_context); } else { /* WA #622: if reqular RQ, flush */ - if (sif_flush_rq(sdev, rq, qp, atomic_read(&rq_sw->length))) + if (sif_flush_rq_wq(sdev, rq, qp, atomic_read(&rq_sw->length))) sif_log(sdev, SIF_INFO, "failed to flush RQ %d", rq->index); } diff --git a/drivers/infiniband/hw/sif/sif_main.c b/drivers/infiniband/hw/sif/sif_main.c index b53877e777384..cb0001b328a66 100644 --- a/drivers/infiniband/hw/sif/sif_main.c +++ b/drivers/infiniband/hw/sif/sif_main.c @@ -253,6 +253,12 @@ static int sif_probe(struct pci_dev *pdev, err = -ENOMEM; goto wq_fail; } + sdev->misc_wq = create_singlethread_workqueue("sif_misc_wq"); + if (!sdev->misc_wq) { + sif_log(sdev, SIF_INFO, "Failed to allocate sif misc work queue"); + err = -ENOMEM; + goto wq_fail; + } err = sif_set_check_max_payload(sdev); if (err) @@ -420,7 +426,9 @@ static void sif_remove(struct pci_dev *dev) pci_clear_master(dev); pci_disable_device(dev); flush_workqueue(sdev->wq); + flush_workqueue(sdev->misc_wq); destroy_workqueue(sdev->wq); + destroy_workqueue(sdev->misc_wq); sif_log(sdev, SIF_INFO, "removed device %s", sdev->ib_dev.name); ib_dealloc_device(&sdev->ib_dev); } diff --git a/drivers/infiniband/hw/sif/sif_qp.c b/drivers/infiniband/hw/sif/sif_qp.c index 0e151111b9e5f..53b578e092e80 100644 --- a/drivers/infiniband/hw/sif/sif_qp.c +++ b/drivers/infiniband/hw/sif/sif_qp.c @@ -969,7 +969,7 @@ int modify_qp(struct sif_dev *sdev, struct sif_qp *qp, "flush requested for qp(type %s) with no rq defined", string_enum_psif_qp_trans(qp->type)); } else { - ret = sif_flush_rq(sdev, rq, qp, rq->entries); + ret = sif_flush_rq_wq(sdev, rq, qp, rq->entries); if (ret) sif_log(sdev, SIF_INFO, "failed to flush RQ %d", rq->index); } @@ -1068,7 +1068,7 @@ sif_mqp_ret: qp->ibqp.event_handler(&ibe, qp->ibqp.qp_context); } else if (!rq->is_srq) { /* WA #622: if reqular RQ, flush */ - ret = sif_flush_rq(sdev, rq, qp, rq->entries); + ret = sif_flush_rq_wq(sdev, rq, qp, rq->entries); if (ret) { sif_log(sdev, SIF_INFO, "failed to flush RQ %d", rq->index); diff --git a/drivers/infiniband/hw/sif/sif_rq.c b/drivers/infiniband/hw/sif/sif_rq.c index bf079af70f83d..e3154ae35319e 100644 --- a/drivers/infiniband/hw/sif/sif_rq.c +++ b/drivers/infiniband/hw/sif/sif_rq.c @@ -22,6 +22,8 @@ #include "sif_defs.h" #include +static void sif_flush_rq(struct work_struct *work); + int poll_wait_for_rq_writeback(struct sif_dev *sdev, struct sif_rq *rq) { unsigned long timeout = sdev->min_resp_ticks; @@ -277,6 +279,30 @@ static int find_recv_cqes_in_cq(struct sif_dev *sdev, struct sif_cq *cq, struct return n; } + +int sif_flush_rq_wq(struct sif_dev *sdev, struct sif_rq *rq, struct sif_qp *target_qp, + int max_flushed_in_err) +{ + struct flush_rq_work *work; + + work = kzalloc(sizeof(*work), GFP_ATOMIC); + if (!work) + return -ENOMEM; + + + memset(work, 0, sizeof(*work)); + work->qp = target_qp; + work->sdev = sdev; + work->rq = rq; + work->entries = max_flushed_in_err; + + INIT_WORK(&work->ws, sif_flush_rq); + + queue_work(sdev->misc_wq, &work->ws); + + return 0; +} + /* Invalidate the RQ cache and flush a desired amount of * the remaining entries in the given receive queue. * @target_qp indicates the value of the local_qp field in the generated @@ -296,10 +322,14 @@ static int find_recv_cqes_in_cq(struct sif_dev *sdev, struct sif_cq *cq, struct * Note: No locking of the RQ is neccessary as there are multiple trigger points * for flushing RQEs within OFED verbs model. */ -int sif_flush_rq(struct sif_dev *sdev, struct sif_rq *rq, struct sif_qp *target_qp, - int max_flushed_in_err) +static void sif_flush_rq(struct work_struct *work) { int len, real_len; + struct flush_rq_work *rq_work = container_of(work, struct flush_rq_work, ws); + struct sif_dev *sdev = rq_work->sdev; + struct sif_qp *target_qp = rq_work->qp; + struct sif_rq *rq = rq_work->rq; + int max_flushed_in_err = rq_work->entries; struct sif_rq_sw *rq_sw = get_sif_rq_sw(sdev, rq->index); int ret = 0; u32 head, tail; @@ -311,7 +341,7 @@ int sif_flush_rq(struct sif_dev *sdev, struct sif_rq *rq, struct sif_qp *target_ */ if (test_bit(FLUSH_RQ_IN_PROGRESS, &rq_sw->flags)) { set_bit(FLUSH_RQ_IN_FLIGHT, &rq_sw->flags); - return ret; + goto done; } /* if race condition happened while trying to flush RQ, @@ -319,7 +349,7 @@ int sif_flush_rq(struct sif_dev *sdev, struct sif_rq *rq, struct sif_qp *target_ */ if (test_and_set_bit(FLUSH_RQ_IN_PROGRESS, &rq_sw->flags)) { set_bit(FLUSH_RQ_IN_FLIGHT, &rq_sw->flags); - return ret; + goto done; } if (!sif_feature(disable_rq_flush)) @@ -544,7 +574,8 @@ free_rq_error: } error: clear_bit(FLUSH_RQ_IN_PROGRESS, &rq_sw->flags); - return ret = ret > 0 ? 0 : ret; +done: + kfree(rq_work); } diff --git a/drivers/infiniband/hw/sif/sif_rq.h b/drivers/infiniband/hw/sif/sif_rq.h index 8dd2fcb37cef6..f7aea2cb5a180 100644 --- a/drivers/infiniband/hw/sif/sif_rq.h +++ b/drivers/infiniband/hw/sif/sif_rq.h @@ -36,6 +36,14 @@ struct sif_rq { struct sif_mem *mem; /* Allocated queue memory */ }; +struct flush_rq_work { + struct work_struct ws; + struct sif_dev *sdev; + struct sif_rq *rq; + struct sif_qp *qp; + int entries; +}; + static inline struct sif_rq *to_srq(struct ib_srq *ibsrq) { return container_of(ibsrq, struct sif_rq, ibsrq); @@ -58,7 +66,7 @@ int alloc_rq(struct sif_dev *sdev, struct sif_pd *pd, * @target_qp indicates the value of the local_qp field in the generated * completion but is not interpreted by SIF in any way. */ -int sif_flush_rq(struct sif_dev *sdev, struct sif_rq *rq, +int sif_flush_rq_wq(struct sif_dev *sdev, struct sif_rq *rq, struct sif_qp *target_qp, int max_flushed_in_err); int free_rq(struct sif_dev *sdev, int rq_idx); diff --git a/drivers/infiniband/hw/sif/sif_sndrcv.c b/drivers/infiniband/hw/sif/sif_sndrcv.c index aad7fa0ece3b4..43ec8ed9a7d92 100644 --- a/drivers/infiniband/hw/sif/sif_sndrcv.c +++ b/drivers/infiniband/hw/sif/sif_sndrcv.c @@ -752,7 +752,7 @@ err_post_recv: /* WA #622, Check if QP in ERROR, flush RQ */ if (!rq->is_srq && qp->last_set_state == IB_QPS_ERR) { - if (sif_flush_rq(sdev, rq, qp, atomic_read(&rq_sw->length))) + if (sif_flush_rq_wq(sdev, rq, qp, atomic_read(&rq_sw->length))) sif_log(sdev, SIF_INFO, "failed to flush RQ %d", rq->index); }