]> git.openfabrics.org - ~aditr/compat-rdma.git/commitdiff
bnxt_re: Avoid Hard lockup during error CQE
authorSelvin Xavier <selvin.xavier@broadcom.com>
Wed, 7 Mar 2018 09:10:01 +0000 (01:10 -0800)
committerSelvin Xavier <selvin.xavier@broadcom.com>
Wed, 7 Mar 2018 09:10:01 +0000 (01:10 -0800)
processing

Cherry picking the hard lockup fix from upstream.
Patch has some additional backporting changes
because the upstream code is different.

Bug: 2681

Signed-off-by: Selvin Xavier <selvin.xavier@broadcom.com>
linux-next-pending/0028-RDMA-bnxt_re-Avoid-Hard-lockup-during-error-CQE-proc.patch [new file with mode: 0644]

diff --git a/linux-next-pending/0028-RDMA-bnxt_re-Avoid-Hard-lockup-during-error-CQE-proc.patch b/linux-next-pending/0028-RDMA-bnxt_re-Avoid-Hard-lockup-during-error-CQE-proc.patch
new file mode 100644 (file)
index 0000000..6cecb2c
--- /dev/null
@@ -0,0 +1,587 @@
+From 910e92525d921c427cb9bdd1c1d5a2786bf5facc Mon Sep 17 00:00:00 2001
+From: Selvin Xavier <selvin.xavier@broadcom.com>
+Date: Wed, 7 Mar 2018 01:04:02 -0800
+Subject: [PATCH] RDMA/bnxt_re: Avoid Hard lockup during error CQE processing
+
+Hitting the following hardlockup due to a race condition in
+error CQE processing.
+
+[26146.879798] bnxt_en 0000:04:00.0: QPLIB: FP: CQ Processed Req
+[26146.886346] bnxt_en 0000:04:00.0: QPLIB: wr_id[1251] = 0x0 with status 0xa
+[26156.350935] NMI watchdog: Watchdog detected hard LOCKUP on cpu 4
+[26156.357470] Modules linked in: nfsd auth_rpcgss nfs_acl lockd grace
+[26156.447957] CPU: 4 PID: 3413 Comm: kworker/4:1H Kdump: loaded
+[26156.457994] Hardware name: Dell Inc. PowerEdge R430/0CN7X8,
+[26156.466390] Workqueue: ib-comp-wq ib_cq_poll_work [ib_core]
+[26156.472639] Call Trace:
+[26156.475379]  <NMI>  [<ffffffff98d0d722>] dump_stack+0x19/0x1b
+[26156.481833]  [<ffffffff9873f775>] watchdog_overflow_callback+0x135/0x140
+[26156.489341]  [<ffffffff9877f237>] __perf_event_overflow+0x57/0x100
+[26156.496256]  [<ffffffff98787c24>] perf_event_overflow+0x14/0x20
+[26156.502887]  [<ffffffff9860a580>] intel_pmu_handle_irq+0x220/0x510
+[26156.509813]  [<ffffffff98d16031>] perf_event_nmi_handler+0x31/0x50
+[26156.516738]  [<ffffffff98d1790c>] nmi_handle.isra.0+0x8c/0x150
+[26156.523273]  [<ffffffff98d17be8>] do_nmi+0x218/0x460
+[26156.528834]  [<ffffffff98d16d79>] end_repeat_nmi+0x1e/0x7e
+[26156.534980]  [<ffffffff987089c0>] ? native_queued_spin_lock_slowpath+0x1d0/0x200
+[26156.543268]  [<ffffffff987089c0>] ? native_queued_spin_lock_slowpath+0x1d0/0x200
+[26156.551556]  [<ffffffff987089c0>] ? native_queued_spin_lock_slowpath+0x1d0/0x200
+[26156.559842]  <EOE>  [<ffffffff98d083e4>] queued_spin_lock_slowpath+0xb/0xf
+[26156.567555]  [<ffffffff98d15690>] _raw_spin_lock+0x20/0x30
+[26156.573696]  [<ffffffffc08381a1>] bnxt_qplib_lock_buddy_cq+0x31/0x40 [bnxt_re]
+[26156.581789]  [<ffffffffc083bbaa>] bnxt_qplib_poll_cq+0x43a/0xf10 [bnxt_re]
+[26156.589493]  [<ffffffffc083239b>] bnxt_re_poll_cq+0x9b/0x760 [bnxt_re]
+
+The issue happens if RQ poll_cq or SQ poll_cq or Async error event tries to
+put the error QP in flush list. Since SQ and RQ of each error qp are added
+to two different flush list, we need to protect it using locks of
+corresponding CQs. Difference in order of acquiring the lock in
+SQ poll_cq and RQ poll_cq can cause a hard lockup.
+
+Revisits the locking strategy and removes the usage of qplib_cq.hwq.lock.
+Instead of this lock, introduces qplib_cq.flush_lock to handle
+addition/deletion of QPs in flush list. Also, always invoke the flush_lock
+in order (SQ CQ lock first and then RQ CQ lock) to avoid any potential
+deadlock.
+
+Other than the poll_cq context, the movement of QP to/from flush list can
+be done in modify_qp context or from an async error event from HW.
+Synchronize these operations using the bnxt_re verbs layer CQ locks.
+To achieve this, adds a call back to the HW abstraction layer(qplib) to
+bnxt_re ib_verbs layer in case of async error event. Also, removes the
+buddy cq functions as it is no longer required.
+
+Signed-off-by: Sriharsha Basavapatna <sriharsha.basavapatna@broadcom.com>
+Signed-off-by: Somnath Kotur <somnath.kotur@broadcom.com>
+Signed-off-by: Devesh Sharma <devesh.sharma@broadcom.com>
+Signed-off-by: Selvin Xavier <selvin.xavier@broadcom.com>
+---
+ drivers/infiniband/hw/bnxt_re/ib_verbs.c   |  11 ++-
+ drivers/infiniband/hw/bnxt_re/ib_verbs.h   |   3 +
+ drivers/infiniband/hw/bnxt_re/main.c       | 123 ++++++++++++++++++++++-------
+ drivers/infiniband/hw/bnxt_re/qplib_fp.c   | 109 ++++++-------------------
+ drivers/infiniband/hw/bnxt_re/qplib_fp.h   |  12 +++
+ drivers/infiniband/hw/bnxt_re/qplib_rcfw.c |   5 +-
+ drivers/infiniband/hw/bnxt_re/qplib_rcfw.h |   4 +-
+ 7 files changed, 146 insertions(+), 121 deletions(-)
+
+diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+index e9709d4..9d6bfdb 100644
+--- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c
++++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+@@ -860,7 +860,7 @@ int bnxt_re_query_ah(struct ib_ah *ib_ah, struct ib_ah_attr *ah_attr)
+       return 0;
+ }
+-static unsigned long bnxt_re_lock_cqs(struct bnxt_re_qp *qp)
++unsigned long bnxt_re_lock_cqs(struct bnxt_re_qp *qp)
+       __acquires(&qp->scq->cq_lock) __acquires(&qp->rcq->cq_lock)
+ {
+       unsigned long flags;
+@@ -874,8 +874,8 @@ static unsigned long bnxt_re_lock_cqs(struct bnxt_re_qp *qp)
+       return flags;
+ }
+-static void bnxt_re_unlock_cqs(struct bnxt_re_qp *qp,
+-                             unsigned long flags)
++void bnxt_re_unlock_cqs(struct bnxt_re_qp *qp,
++                      unsigned long flags)
+       __releases(&qp->scq->cq_lock) __releases(&qp->rcq->cq_lock)
+ {
+       if (qp->rcq != qp->scq)
+@@ -1448,6 +1448,7 @@ int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr,
+       int status;
+       union ib_gid sgid;
+       struct ib_gid_attr sgid_attr;
++      unsigned int flags;
+       u8 nw_type;
+       qp->qplib_qp.modify_flags = 0;
+@@ -1476,14 +1477,18 @@ int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr,
+                       dev_dbg(rdev_to_dev(rdev),
+                               "Move QP = %p to flush list\n",
+                               qp);
++                      flags = bnxt_re_lock_cqs(qp);
+                       bnxt_qplib_add_flush_qp(&qp->qplib_qp);
++                      bnxt_re_unlock_cqs(qp, flags);
+               }
+               if (!qp->sumem &&
+                   qp->qplib_qp.state == CMDQ_MODIFY_QP_NEW_STATE_RESET) {
+                       dev_dbg(rdev_to_dev(rdev),
+                               "Move QP = %p out of flush list\n",
+                               qp);
++                      flags = bnxt_re_lock_cqs(qp);
+                       bnxt_qplib_clean_qp(&qp->qplib_qp);
++                      bnxt_re_unlock_cqs(qp, flags);
+               }
+       }
+       if (qp_attr_mask & IB_QP_EN_SQD_ASYNC_NOTIFY) {
+diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h
+index ed1890d..96cbbed 100644
+--- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h
++++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h
+@@ -204,4 +204,7 @@ struct ib_ucontext *bnxt_re_alloc_ucontext(struct ib_device *ibdev,
+                                          struct ib_udata *udata);
+ int bnxt_re_dealloc_ucontext(struct ib_ucontext *context);
+ int bnxt_re_mmap(struct ib_ucontext *context, struct vm_area_struct *vma);
++
++unsigned long bnxt_re_lock_cqs(struct bnxt_re_qp *qp);
++void bnxt_re_unlock_cqs(struct bnxt_re_qp *qp, unsigned long flags);
+ #endif /* __BNXT_RE_IB_VERBS_H__ */
+diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c
+index 22e616c..06dd85a 100644
+--- a/drivers/infiniband/hw/bnxt_re/main.c
++++ b/drivers/infiniband/hw/bnxt_re/main.c
+@@ -618,36 +618,103 @@ static struct bnxt_re_dev *bnxt_re_dev_add(struct net_device *netdev,
+       return rdev;
+ }
++
++static int bnxt_re_handle_unaffi_async_event(struct creq_func_event
++                                             *unaffi_async)
++{
++        switch (unaffi_async->event) {
++        case CREQ_FUNC_EVENT_EVENT_TX_WQE_ERROR:
++                break;
++        case CREQ_FUNC_EVENT_EVENT_TX_DATA_ERROR:
++                break;
++        case CREQ_FUNC_EVENT_EVENT_RX_WQE_ERROR:
++                break;
++        case CREQ_FUNC_EVENT_EVENT_RX_DATA_ERROR:
++                break;
++        case CREQ_FUNC_EVENT_EVENT_CQ_ERROR:
++                break;
++        case CREQ_FUNC_EVENT_EVENT_TQM_ERROR:
++                break;
++        case CREQ_FUNC_EVENT_EVENT_CFCQ_ERROR:
++                break;
++        case CREQ_FUNC_EVENT_EVENT_CFCS_ERROR:
++                break;
++        case CREQ_FUNC_EVENT_EVENT_CFCC_ERROR:
++                break;
++        case CREQ_FUNC_EVENT_EVENT_CFCM_ERROR:
++                break;
++        case CREQ_FUNC_EVENT_EVENT_TIM_ERROR:
++                break;
++        default:
++                return -EINVAL;
++        }
++        return 0;
++}
++
++
++static int bnxt_re_handle_qp_async_event(struct creq_qp_event *qp_event,
++                                         struct bnxt_re_qp *qp)
++{
++        struct ib_event event;
++        unsigned int flags;
++
++        if (qp->qplib_qp.state == CMDQ_MODIFY_QP_NEW_STATE_ERR) {
++                flags = bnxt_re_lock_cqs(qp);
++                bnxt_qplib_add_flush_qp(&qp->qplib_qp);
++                bnxt_re_unlock_cqs(qp, flags);
++        }
++
++        memset(&event, 0, sizeof(event));
++        if (qp->qplib_qp.srq) {
++                event.device = &qp->rdev->ibdev;
++                event.element.qp = &qp->ib_qp;
++                event.event = IB_EVENT_QP_LAST_WQE_REACHED;
++        }
++
++        if (event.device && qp->ib_qp.event_handler)
++                qp->ib_qp.event_handler(&event, qp->ib_qp.qp_context);
++
++        return 0;
++}
++
++static int bnxt_re_handle_affi_async_event(struct creq_qp_event *affi_async,
++                                           void *obj)
++{
++        int rc = 0;
++        u8 event;
++
++        if (!obj)
++                return rc; /* QP was already dead, still return success */
++
++        event = affi_async->event;
++        if (event == CREQ_QP_EVENT_EVENT_QP_ERROR_NOTIFICATION) {
++                struct bnxt_qplib_qp *lib_qp = obj;
++                struct bnxt_re_qp *qp = container_of(lib_qp, struct bnxt_re_qp,
++                                                     qplib_qp);
++                rc = bnxt_re_handle_qp_async_event(affi_async, qp);
++        }
++        return rc;
++}
++
++
+ static int bnxt_re_aeq_handler(struct bnxt_qplib_rcfw *rcfw,
+-                             struct creq_func_event *aeqe)
++                               void *aeqe, void *obj)
+ {
+-      switch (aeqe->event) {
+-      case CREQ_FUNC_EVENT_EVENT_TX_WQE_ERROR:
+-              break;
+-      case CREQ_FUNC_EVENT_EVENT_TX_DATA_ERROR:
+-              break;
+-      case CREQ_FUNC_EVENT_EVENT_RX_WQE_ERROR:
+-              break;
+-      case CREQ_FUNC_EVENT_EVENT_RX_DATA_ERROR:
+-              break;
+-      case CREQ_FUNC_EVENT_EVENT_CQ_ERROR:
+-              break;
+-      case CREQ_FUNC_EVENT_EVENT_TQM_ERROR:
+-              break;
+-      case CREQ_FUNC_EVENT_EVENT_CFCQ_ERROR:
+-              break;
+-      case CREQ_FUNC_EVENT_EVENT_CFCS_ERROR:
+-              break;
+-      case CREQ_FUNC_EVENT_EVENT_CFCC_ERROR:
+-              break;
+-      case CREQ_FUNC_EVENT_EVENT_CFCM_ERROR:
+-              break;
+-      case CREQ_FUNC_EVENT_EVENT_TIM_ERROR:
+-              break;
+-      default:
+-              return -EINVAL;
+-      }
+-      return 0;
++        struct creq_qp_event *affi_async;
++        struct creq_func_event *unaffi_async;
++        u8 type;
++        int rc;
++
++        type = ((struct creq_base *)aeqe)->type;
++        if (type == CREQ_BASE_TYPE_FUNC_EVENT) {
++                unaffi_async = aeqe;
++                rc = bnxt_re_handle_unaffi_async_event(unaffi_async);
++        } else {
++                affi_async = aeqe;
++                rc = bnxt_re_handle_affi_async_event(affi_async, obj);
++        }
++
++        return rc;
+ }
+ static int bnxt_re_cqn_handler(struct bnxt_qplib_nq *nq,
+diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c
+index f6248bf..ea87a09 100644
+--- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c
++++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c
+@@ -87,75 +87,35 @@ static void __bnxt_qplib_add_flush_qp(struct bnxt_qplib_qp *qp)
+       }
+ }
+-void bnxt_qplib_acquire_cq_locks(struct bnxt_qplib_qp *qp,
+-                               unsigned long *flags)
+-      __acquires(&qp->scq->hwq.lock) __acquires(&qp->rcq->hwq.lock)
++static void bnxt_qplib_acquire_cq_flush_locks(struct bnxt_qplib_qp *qp,
++                                     unsigned long *flags)
++      __acquires(&qp->scq->flush_lock) __acquires(&qp->rcq->flush_lock)
+ {
+-      spin_lock_irqsave(&qp->scq->hwq.lock, *flags);
++      spin_lock_irqsave(&qp->scq->flush_lock, *flags);
+       if (qp->scq == qp->rcq)
+-              __acquire(&qp->rcq->hwq.lock);
++              __acquire(&qp->rcq->flush_lock);
+       else
+-              spin_lock(&qp->rcq->hwq.lock);
++              spin_lock(&qp->rcq->flush_lock);
+ }
+-void bnxt_qplib_release_cq_locks(struct bnxt_qplib_qp *qp,
+-                               unsigned long *flags)
+-      __releases(&qp->scq->hwq.lock) __releases(&qp->rcq->hwq.lock)
++static void bnxt_qplib_release_cq_flush_locks(struct bnxt_qplib_qp *qp,
++                                     unsigned long *flags)
++      __releases(&qp->scq->flush_lock) __releases(&qp->rcq->flush_lock)
+ {
+       if (qp->scq == qp->rcq)
+-              __release(&qp->rcq->hwq.lock);
++              __release(&qp->rcq->flush_lock);
+       else
+-              spin_unlock(&qp->rcq->hwq.lock);
+-      spin_unlock_irqrestore(&qp->scq->hwq.lock, *flags);
+-}
+-
+-static struct bnxt_qplib_cq *bnxt_qplib_find_buddy_cq(struct bnxt_qplib_qp *qp,
+-                                                    struct bnxt_qplib_cq *cq)
+-{
+-      struct bnxt_qplib_cq *buddy_cq = NULL;
+-
+-      if (qp->scq == qp->rcq)
+-              buddy_cq = NULL;
+-      else if (qp->scq == cq)
+-              buddy_cq = qp->rcq;
+-      else
+-              buddy_cq = qp->scq;
+-      return buddy_cq;
+-}
+-
+-static void bnxt_qplib_lock_buddy_cq(struct bnxt_qplib_qp *qp,
+-                                   struct bnxt_qplib_cq *cq)
+-      __acquires(&buddy_cq->hwq.lock)
+-{
+-      struct bnxt_qplib_cq *buddy_cq = NULL;
+-
+-      buddy_cq = bnxt_qplib_find_buddy_cq(qp, cq);
+-      if (!buddy_cq)
+-              __acquire(&cq->hwq.lock);
+-      else
+-              spin_lock(&buddy_cq->hwq.lock);
+-}
+-
+-static void bnxt_qplib_unlock_buddy_cq(struct bnxt_qplib_qp *qp,
+-                                     struct bnxt_qplib_cq *cq)
+-      __releases(&buddy_cq->hwq.lock)
+-{
+-      struct bnxt_qplib_cq *buddy_cq = NULL;
+-
+-      buddy_cq = bnxt_qplib_find_buddy_cq(qp, cq);
+-      if (!buddy_cq)
+-              __release(&cq->hwq.lock);
+-      else
+-              spin_unlock(&buddy_cq->hwq.lock);
++              spin_unlock(&qp->rcq->flush_lock);
++      spin_unlock_irqrestore(&qp->scq->flush_lock, *flags);
+ }
+ void bnxt_qplib_add_flush_qp(struct bnxt_qplib_qp *qp)
+ {
+       unsigned long flags;
+-      bnxt_qplib_acquire_cq_locks(qp, &flags);
++      bnxt_qplib_acquire_cq_flush_locks(qp, &flags);
+       __bnxt_qplib_add_flush_qp(qp);
+-      bnxt_qplib_release_cq_locks(qp, &flags);
++      bnxt_qplib_release_cq_flush_locks(qp, &flags);
+ }
+ static void __bnxt_qplib_del_flush_qp(struct bnxt_qplib_qp *qp)
+@@ -181,7 +141,7 @@ void bnxt_qplib_clean_qp(struct bnxt_qplib_qp *qp)
+ {
+       unsigned long flags;
+-      bnxt_qplib_acquire_cq_locks(qp, &flags);
++      bnxt_qplib_acquire_cq_flush_locks(qp, &flags);
+       __clean_cq(qp->scq, (u64)(unsigned long)qp);
+       qp->sq.hwq.prod = 0;
+       qp->sq.hwq.cons = 0;
+@@ -190,7 +150,7 @@ void bnxt_qplib_clean_qp(struct bnxt_qplib_qp *qp)
+       qp->rq.hwq.cons = 0;
+       __bnxt_qplib_del_flush_qp(qp);
+-      bnxt_qplib_release_cq_locks(qp, &flags);
++      bnxt_qplib_release_cq_flush_locks(qp, &flags);
+ }
+ static void bnxt_qpn_cqn_sched_task(struct work_struct *work)
+@@ -1849,9 +1809,6 @@ void bnxt_qplib_mark_qp_error(void *qp_handle)
+       /* Must block new posting of SQ and RQ */
+       qp->state = CMDQ_MODIFY_QP_NEW_STATE_ERR;
+       bnxt_qplib_cancel_phantom_processing(qp);
+-
+-      /* Add qp to flush list of the CQ */
+-      __bnxt_qplib_add_flush_qp(qp);
+ }
+ /* Note: SQE is valid from sw_sq_cons up to cqe_sq_cons (exclusive)
+@@ -2027,9 +1984,9 @@ static int bnxt_qplib_cq_process_req(struct bnxt_qplib_cq *cq,
+                               sw_sq_cons, cqe->wr_id, cqe->status);
+                       cqe++;
+                       (*budget)--;
+-                      bnxt_qplib_lock_buddy_cq(qp, cq);
+                       bnxt_qplib_mark_qp_error(qp);
+-                      bnxt_qplib_unlock_buddy_cq(qp, cq);
++                      /* Add qp to flush list of the CQ */
++                      bnxt_qplib_add_flush_qp(qp);
+               } else {
+                       if (swq->flags & SQ_SEND_FLAGS_SIGNAL_COMP) {
+                               /* Before we complete, do WA 9060 */
+@@ -2116,9 +2073,7 @@ static int bnxt_qplib_cq_process_res_rc(struct bnxt_qplib_cq *cq,
+       if (hwcqe->status != CQ_RES_RC_STATUS_OK) {
+               qp->state = CMDQ_MODIFY_QP_NEW_STATE_ERR;
+                /* Add qp to flush list of the CQ */
+-              bnxt_qplib_lock_buddy_cq(qp, cq);
+-              __bnxt_qplib_add_flush_qp(qp);
+-              bnxt_qplib_unlock_buddy_cq(qp, cq);
++              bnxt_qplib_add_flush_qp(qp);
+       }
+ done:
+@@ -2180,9 +2135,7 @@ static int bnxt_qplib_cq_process_res_ud(struct bnxt_qplib_cq *cq,
+       if (hwcqe->status != CQ_RES_RC_STATUS_OK) {
+               qp->state = CMDQ_MODIFY_QP_NEW_STATE_ERR;
+               /* Add qp to flush list of the CQ */
+-              bnxt_qplib_lock_buddy_cq(qp, cq);
+-              __bnxt_qplib_add_flush_qp(qp);
+-              bnxt_qplib_unlock_buddy_cq(qp, cq);
++              bnxt_qplib_add_flush_qp(qp);
+       }
+ done:
+       return rc;
+@@ -2191,11 +2144,9 @@ done:
+ bool bnxt_qplib_is_cq_empty(struct bnxt_qplib_cq *cq)
+ {
+       struct cq_base *hw_cqe, **hw_cqe_ptr;
+-      unsigned long flags;
+       u32 sw_cons, raw_cons;
+       bool rc = true;
+-      spin_lock_irqsave(&cq->hwq.lock, flags);
+       raw_cons = cq->hwq.cons;
+       sw_cons = HWQ_CMP(raw_cons, &cq->hwq);
+       hw_cqe_ptr = (struct cq_base **)cq->hwq.pbl_ptr;
+@@ -2203,7 +2154,6 @@ bool bnxt_qplib_is_cq_empty(struct bnxt_qplib_cq *cq)
+        /* Check for Valid bit. If the CQE is valid, return false */
+       rc = !CQE_CMP_VALID(hw_cqe, raw_cons, cq->hwq.max_elements);
+-      spin_unlock_irqrestore(&cq->hwq.lock, flags);
+       return rc;
+ }
+@@ -2268,9 +2218,7 @@ static int bnxt_qplib_cq_process_res_raweth_qp1(struct bnxt_qplib_cq *cq,
+       if (hwcqe->status != CQ_RES_RC_STATUS_OK) {
+               qp->state = CMDQ_MODIFY_QP_NEW_STATE_ERR;
+               /* Add qp to flush list of the CQ */
+-              bnxt_qplib_lock_buddy_cq(qp, cq);
+-              __bnxt_qplib_add_flush_qp(qp);
+-              bnxt_qplib_unlock_buddy_cq(qp, cq);
++              bnxt_qplib_add_flush_qp(qp);
+       }
+ done:
+@@ -2384,9 +2332,7 @@ do_rq:
+        */
+       /* Add qp to flush list of the CQ */
+-      bnxt_qplib_lock_buddy_cq(qp, cq);
+-      __bnxt_qplib_add_flush_qp(qp);
+-      bnxt_qplib_unlock_buddy_cq(qp, cq);
++      bnxt_qplib_add_flush_qp(qp);
+ done:
+       return rc;
+ }
+@@ -2415,7 +2361,7 @@ int bnxt_qplib_process_flush_list(struct bnxt_qplib_cq *cq,
+       u32 budget = num_cqes;
+       unsigned long flags;
+-      spin_lock_irqsave(&cq->hwq.lock, flags);
++      spin_lock_irqsave(&cq->flush_lock, flags);
+       list_for_each_entry(qp, &cq->sqf_head, sq_flush) {
+               dev_dbg(&cq->hwq.pdev->dev,
+                       "QPLIB: FP: Flushing SQ QP= %p",
+@@ -2429,7 +2375,7 @@ int bnxt_qplib_process_flush_list(struct bnxt_qplib_cq *cq,
+                       qp);
+               __flush_rq(&qp->rq, qp, &cqe, &budget);
+       }
+-      spin_unlock_irqrestore(&cq->hwq.lock, flags);
++      spin_unlock_irqrestore(&cq->flush_lock, flags);
+       return num_cqes - budget;
+ }
+@@ -2438,11 +2384,9 @@ int bnxt_qplib_poll_cq(struct bnxt_qplib_cq *cq, struct bnxt_qplib_cqe *cqe,
+                      int num_cqes, struct bnxt_qplib_qp **lib_qp)
+ {
+       struct cq_base *hw_cqe, **hw_cqe_ptr;
+-      unsigned long flags;
+       u32 sw_cons, raw_cons;
+       int budget, rc = 0;
+-      spin_lock_irqsave(&cq->hwq.lock, flags);
+       raw_cons = cq->hwq.cons;
+       budget = num_cqes;
+@@ -2518,20 +2462,15 @@ int bnxt_qplib_poll_cq(struct bnxt_qplib_cq *cq, struct bnxt_qplib_cqe *cqe,
+               bnxt_qplib_arm_cq(cq, DBR_DBR_TYPE_CQ);
+       }
+ exit:
+-      spin_unlock_irqrestore(&cq->hwq.lock, flags);
+       return num_cqes - budget;
+ }
+ void bnxt_qplib_req_notify_cq(struct bnxt_qplib_cq *cq, u32 arm_type)
+ {
+-      unsigned long flags;
+-
+-      spin_lock_irqsave(&cq->hwq.lock, flags);
+       if (arm_type)
+               bnxt_qplib_arm_cq(cq, arm_type);
+       /* Using cq->arm_state variable to track whether to issue cq handler */
+       atomic_set(&cq->arm_state, 1);
+-      spin_unlock_irqrestore(&cq->hwq.lock, flags);
+ }
+ void bnxt_qplib_flush_cqn_wq(struct bnxt_qplib_qp *qp)
+diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.h b/drivers/infiniband/hw/bnxt_re/qplib_fp.h
+index 90ace2f..1396b32 100644
+--- a/drivers/infiniband/hw/bnxt_re/qplib_fp.h
++++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.h
+@@ -367,6 +367,18 @@ struct bnxt_qplib_cq {
+       struct list_head                sqf_head, rqf_head;
+       atomic_t                        arm_state;
+       spinlock_t                      compl_lock; /* synch CQ handlers */
++/* Locking Notes:
++ * QP can move to error state from modify_qp, async error event or error
++ * CQE as part of poll_cq. When QP is moved to error state, it gets added
++ * to two flush lists, one each for SQ and RQ.
++ * Each flush list is protected by qplib_cq->flush_lock. Both scq and rcq
++ * flush_locks should be acquired when QP is moved to error. The control path
++ * operations(modify_qp and async error events) are synchronized with poll_cq
++ * using upper level CQ locks (bnxt_re_cq->cq_lock) of both SCQ and RCQ.
++ * The qplib_cq->flush_lock is required to synchronize two instances of poll_cq
++ * of the same QP while manipulating the flush list.
++ */
++      spinlock_t                      flush_lock; /* QP flush management */
+ };
+ #define BNXT_QPLIB_MAX_IRRQE_ENTRY_SIZE       sizeof(struct xrrq_irrq)
+diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c
+index f8837d8..e521684 100644
+--- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c
++++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c
+@@ -308,9 +308,8 @@ static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw,
+                       err_event->res_err_state_reason);
+               if (!qp)
+                       break;
+-              bnxt_qplib_acquire_cq_locks(qp, &flags);
+               bnxt_qplib_mark_qp_error(qp);
+-              bnxt_qplib_release_cq_locks(qp, &flags);
++              rcfw->aeq_handler(rcfw, qp_event, qp);
+               break;
+       default:
+               /* Command Response */
+@@ -623,7 +622,7 @@ int bnxt_qplib_enable_rcfw_channel(struct pci_dev *pdev,
+                                  int msix_vector,
+                                  int cp_bar_reg_off, int virt_fn,
+                                  int (*aeq_handler)(struct bnxt_qplib_rcfw *,
+-                                                    struct creq_func_event *))
++                                                    void *, void *))
+ {
+       resource_size_t res_base;
+       struct cmdq_init init;
+diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h
+index 7c85e3c..fdbafda 100644
+--- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h
++++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h
+@@ -168,7 +168,7 @@ struct bnxt_qplib_rcfw {
+ #define FIRMWARE_TIMED_OUT            BIT(3)
+       wait_queue_head_t       waitq;
+       int                     (*aeq_handler)(struct bnxt_qplib_rcfw *,
+-                                             struct creq_func_event *);
++                                             void *, void*);
+       u32                     seq_num;
+       /* Bar region info */
+@@ -202,7 +202,7 @@ int bnxt_qplib_enable_rcfw_channel(struct pci_dev *pdev,
+                                  int cp_bar_reg_off, int virt_fn,
+                                  int (*aeq_handler)
+                                       (struct bnxt_qplib_rcfw *,
+-                                       struct creq_func_event *));
++                                       void *, void *));
+ struct bnxt_qplib_rcfw_sbuf *bnxt_qplib_rcfw_alloc_sbuf(
+                               struct bnxt_qplib_rcfw *rcfw,
+-- 
+1.8.3.1
+