i40iw: Fix double free of QP
authorMustafa Ismail <mustafa.ismail@intel.com>
Tue, 6 Dec 2016 21:49:30 +0000 (15:49 -0600)
committerDoug Ledford <dledford@redhat.com>
Mon, 12 Dec 2016 22:20:26 +0000 (17:20 -0500)
A QP can be double freed if i40iw_cm_disconn() is
called while it is currently being freed by
i40iw_rem_ref(). The fix in i40iw_cm_disconn() will
first check if the QP is already freed before
making another request for the QP to be freed.

Signed-off-by: Mustafa Ismail <mustafa.ismail@intel.com>
Signed-off-by: Shiraz Saleem <shiraz.saleem@intel.com>
Signed-off-by: Henry Orosco <henry.orosco@intel.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
drivers/infiniband/hw/i40iw/i40iw.h
drivers/infiniband/hw/i40iw/i40iw_cm.c
drivers/infiniband/hw/i40iw/i40iw_hw.c

index ef188e6b1f89fb4972b8b894d3d96bee6025b814..51b828026b539e381a8606ea1aa55d1f23979450 100644 (file)
@@ -512,7 +512,7 @@ u32 i40iw_initialize_hw_resources(struct i40iw_device *iwdev);
 
 int i40iw_register_rdma_device(struct i40iw_device *iwdev);
 void i40iw_port_ibevent(struct i40iw_device *iwdev);
-int i40iw_cm_disconn(struct i40iw_qp *);
+void i40iw_cm_disconn(struct i40iw_qp *iwqp);
 void i40iw_cm_disconn_worker(void *);
 int mini_cm_recv_pkt(struct i40iw_cm_core *, struct i40iw_device *,
                     struct sk_buff *);
index 25af89a3cdce5fc55d471847545e482cc950eb54..ff95feaee105f6bb914c1982e11d723c7e620fd1 100644 (file)
@@ -3359,21 +3359,33 @@ static void i40iw_cm_init_tsa_conn(struct i40iw_qp *iwqp,
  * i40iw_cm_disconn - when a connection is being closed
  * @iwqp: associate qp for the connection
  */
-int i40iw_cm_disconn(struct i40iw_qp *iwqp)
+void i40iw_cm_disconn(struct i40iw_qp *iwqp)
 {
        struct disconn_work *work;
        struct i40iw_device *iwdev = iwqp->iwdev;
        struct i40iw_cm_core *cm_core = &iwdev->cm_core;
+       unsigned long flags;
 
        work = kzalloc(sizeof(*work), GFP_ATOMIC);
        if (!work)
-               return -ENOMEM; /* Timer will clean up */
-
+               return; /* Timer will clean up */
+
+       spin_lock_irqsave(&iwdev->qptable_lock, flags);
+       if (!iwdev->qp_table[iwqp->ibqp.qp_num]) {
+               spin_unlock_irqrestore(&iwdev->qptable_lock, flags);
+               i40iw_debug(&iwdev->sc_dev, I40IW_DEBUG_CM,
+                           "%s qp_id %d is already freed\n",
+                            __func__, iwqp->ibqp.qp_num);
+               kfree(work);
+               return;
+       }
        i40iw_add_ref(&iwqp->ibqp);
+       spin_unlock_irqrestore(&iwdev->qptable_lock, flags);
+
        work->iwqp = iwqp;
        INIT_WORK(&work->work, i40iw_disconnect_worker);
        queue_work(cm_core->disconn_wq, &work->work);
-       return 0;
+       return;
 }
 
 /**
index 5e2c16c725e3f9e0190296594b4a691925fcf3ea..b2854b11a240d6fbadeeea53f7fd3098981eeb7b 100644 (file)
@@ -308,7 +308,9 @@ void i40iw_process_aeq(struct i40iw_device *iwdev)
                        iwqp = iwdev->qp_table[info->qp_cq_id];
                        if (!iwqp) {
                                spin_unlock_irqrestore(&iwdev->qptable_lock, flags);
-                               i40iw_pr_err("qp_id %d is already freed\n", info->qp_cq_id);
+                               i40iw_debug(dev, I40IW_DEBUG_AEQ,
+                                           "%s qp_id %d is already freed\n",
+                                           __func__, info->qp_cq_id);
                                continue;
                        }
                        i40iw_add_ref(&iwqp->ibqp);