RDMA/cxgb4: Serialize calls to CQ's comp_handler
authorKumar Sanghvi <kumaras@chelsio.com>
Mon, 24 Oct 2011 15:50:21 +0000 (21:20 +0530)
committerRoland Dreier <roland@purestorage.com>
Mon, 31 Oct 2011 18:34:53 +0000 (11:34 -0700)
Commit 01e7da6ba53c ("RDMA/cxgb4: Make sure flush CQ entries are
collected on connection close") introduced a potential problem where a
CQ's comp_handler can get called simultaneously from different places
in the iw_cxgb4 driver.  This does not comply with
Documentation/infiniband/core_locking.txt, which states that at a
given point of time, there should be only one callback per CQ should
be active.

This problem was reported by Parav Pandit <Parav.Pandit@Emulex.Com>.
Based on discussion between Parav Pandit and Steve Wise, this patch
fixes the above problem by serializing the calls to a CQ's
comp_handler using a spin_lock.

Reported-by: Parav Pandit <Parav.Pandit@Emulex.Com>
Signed-off-by: Kumar Sanghvi <kumaras@chelsio.com>
Acked-by: Steve Wise <swise@opengridcomputing.com>
Signed-off-by: Roland Dreier <roland@purestorage.com>
drivers/infiniband/hw/cxgb4/cq.c
drivers/infiniband/hw/cxgb4/ev.c
drivers/infiniband/hw/cxgb4/iw_cxgb4.h
drivers/infiniband/hw/cxgb4/qp.c

index 901c5fbf71a41833646a6f1463da0b11fe6056db..f35a935267e77e7a58c3be85437cbcb1a1f35cb7 100644 (file)
@@ -818,6 +818,7 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
        chp->cq.size--;                         /* status page */
        chp->ibcq.cqe = entries - 2;
        spin_lock_init(&chp->lock);
+       spin_lock_init(&chp->comp_handler_lock);
        atomic_set(&chp->refcnt, 1);
        init_waitqueue_head(&chp->wait);
        ret = insert_handle(rhp, &rhp->cqidr, chp, chp->cq.cqid);
index c13041a0aeba90b033b7b338e26ecc80efc42753..397cb36cf1037d7cc14b34130d804c8bc68edd07 100644 (file)
@@ -42,6 +42,7 @@ static void post_qp_event(struct c4iw_dev *dev, struct c4iw_cq *chp,
 {
        struct ib_event event;
        struct c4iw_qp_attributes attrs;
+       unsigned long flag;
 
        if ((qhp->attr.state == C4IW_QP_STATE_ERROR) ||
            (qhp->attr.state == C4IW_QP_STATE_TERMINATE)) {
@@ -72,7 +73,9 @@ static void post_qp_event(struct c4iw_dev *dev, struct c4iw_cq *chp,
        if (qhp->ibqp.event_handler)
                (*qhp->ibqp.event_handler)(&event, qhp->ibqp.qp_context);
 
+       spin_lock_irqsave(&chp->comp_handler_lock, flag);
        (*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context);
+       spin_unlock_irqrestore(&chp->comp_handler_lock, flag);
 }
 
 void c4iw_ev_dispatch(struct c4iw_dev *dev, struct t4_cqe *err_cqe)
@@ -183,11 +186,14 @@ out:
 int c4iw_ev_handler(struct c4iw_dev *dev, u32 qid)
 {
        struct c4iw_cq *chp;
+       unsigned long flag;
 
        chp = get_chp(dev, qid);
-       if (chp)
+       if (chp) {
+               spin_lock_irqsave(&chp->comp_handler_lock, flag);
                (*chp->ibcq.comp_handler)(&chp->ibcq, chp->ibcq.cq_context);
-       else
+               spin_unlock_irqrestore(&chp->comp_handler_lock, flag);
+       } else
                PDBG("%s unknown cqid 0x%x\n", __func__, qid);
        return 0;
 }
index 4f045375c8e27b8fb7dc93776c9b1a7e6eb7a4da..02f015fc3ed299e7760b874de191f4fd9ea4ea16 100644 (file)
@@ -309,6 +309,7 @@ struct c4iw_cq {
        struct c4iw_dev *rhp;
        struct t4_cq cq;
        spinlock_t lock;
+       spinlock_t comp_handler_lock;
        atomic_t refcnt;
        wait_queue_head_t wait;
 };
index 892fa7c6d310ad9f34d0ab2c80f5c0cf9f33128c..62c7262a9eb3fcf14188a1e3becd149e86531a5d 100644 (file)
@@ -941,8 +941,11 @@ static void __flush_qp(struct c4iw_qp *qhp, struct c4iw_cq *rchp,
        flushed = c4iw_flush_rq(&qhp->wq, &rchp->cq, count);
        spin_unlock(&qhp->lock);
        spin_unlock_irqrestore(&rchp->lock, flag);
-       if (flushed)
+       if (flushed) {
+               spin_lock_irqsave(&rchp->comp_handler_lock, flag);
                (*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context);
+               spin_unlock_irqrestore(&rchp->comp_handler_lock, flag);
+       }
 
        /* locking hierarchy: cq lock first, then qp lock. */
        spin_lock_irqsave(&schp->lock, flag);
@@ -952,13 +955,17 @@ static void __flush_qp(struct c4iw_qp *qhp, struct c4iw_cq *rchp,
        flushed = c4iw_flush_sq(&qhp->wq, &schp->cq, count);
        spin_unlock(&qhp->lock);
        spin_unlock_irqrestore(&schp->lock, flag);
-       if (flushed)
+       if (flushed) {
+               spin_lock_irqsave(&schp->comp_handler_lock, flag);
                (*schp->ibcq.comp_handler)(&schp->ibcq, schp->ibcq.cq_context);
+               spin_unlock_irqrestore(&schp->comp_handler_lock, flag);
+       }
 }
 
 static void flush_qp(struct c4iw_qp *qhp)
 {
        struct c4iw_cq *rchp, *schp;
+       unsigned long flag;
 
        rchp = get_chp(qhp->rhp, qhp->attr.rcq);
        schp = get_chp(qhp->rhp, qhp->attr.scq);
@@ -966,11 +973,15 @@ static void flush_qp(struct c4iw_qp *qhp)
        if (qhp->ibqp.uobject) {
                t4_set_wq_in_error(&qhp->wq);
                t4_set_cq_in_error(&rchp->cq);
+               spin_lock_irqsave(&rchp->comp_handler_lock, flag);
                (*rchp->ibcq.comp_handler)(&rchp->ibcq, rchp->ibcq.cq_context);
+               spin_unlock_irqrestore(&rchp->comp_handler_lock, flag);
                if (schp != rchp) {
                        t4_set_cq_in_error(&schp->cq);
+                       spin_lock_irqsave(&schp->comp_handler_lock, flag);
                        (*schp->ibcq.comp_handler)(&schp->ibcq,
                                        schp->ibcq.cq_context);
+                       spin_unlock_irqrestore(&schp->comp_handler_lock, flag);
                }
                return;
        }