]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
ib/ipoib: Fix removing call for update_pmtu from spin-lock context.
authorErez Shitrit <erezsh@mellanox.co.il>
Thu, 29 Nov 2012 07:27:46 +0000 (09:27 +0200)
committerMukesh Kacker <mukesh.kacker@oracle.com>
Tue, 7 Jul 2015 21:45:06 +0000 (14:45 -0700)
    The function ipoib_cm_send can call the function
    ipoib_cm_skb_too_long under spin_lock_irq, the
    ipoib_cm_skb_too_long function calls update_pmtu which also
    tries to get spin_lock, that can cause to a deadlock.
    In order to solve that I took update_pmtu to workqueue that
    it is not under spin_lock.

V2: Adjusted for kernel 3.7-rc4

Signed-off-by: Erez Shitrit <erezsh@mellanox.com>
Signed-off-by: Yishai Hadas <yishaih@mellanox.com>
(Ported from Mellanox OFED 2.4)

Signed-off-by: Mukesh Kacker <mukesh.kacker@oracle.com>
drivers/infiniband/ulp/ipoib/ipoib.h
drivers/infiniband/ulp/ipoib/ipoib_cm.c

index bd94b0a6e9e535f8d8b4a9e1fa1428e0696e2947..99fe6fa77575e4413e5174481e5aa7cf24c6b42c 100644 (file)
@@ -181,6 +181,13 @@ struct ipoib_cm_tx_buf {
        u64             mapping;
 };
 
+/* in order to call dst->ops->update_pmtu out of spin-lock*/
+struct ipoib_pmtu_update {
+       struct work_struct work;
+       struct sk_buff *skb;
+       unsigned int mtu;
+};
+
 struct ib_cm_id;
 
 struct ipoib_cm_data {
index cf32a778e7d0ccc0b6225d9c01442f5d2ec4cdb1..5163111abac7b6dcc877c9573a8a12004da24370 100644 (file)
@@ -1406,14 +1406,47 @@ static void ipoib_cm_skb_reap(struct work_struct *work)
        netif_tx_unlock_bh(dev);
 }
 
+static void ipoib_cm_update_pmtu_task(struct work_struct *work)
+{
+       struct ipoib_pmtu_update *pmtu_update =
+               container_of(work, struct ipoib_pmtu_update, work);
+       struct sk_buff *skb = pmtu_update->skb;
+
+       skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, pmtu_update->mtu);
+
+       atomic_dec(&skb->users);
+
+       kfree(pmtu_update);
+}
+
+
 void ipoib_cm_skb_too_long(struct net_device *dev, struct sk_buff *skb,
                           unsigned int mtu)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
        int e = skb_queue_empty(&priv->cm.skb_queue);
 
-       if (skb_dst(skb))
-               skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
+       struct ipoib_pmtu_update *pmtu_update;
+
+       if (skb_dst(skb)) {
+               /* take the pmtu_update ouf ot spin-lock context */
+               pmtu_update = kzalloc(sizeof *pmtu_update, GFP_ATOMIC);
+               if (pmtu_update) {
+                       pmtu_update->skb = skb;
+                       pmtu_update->mtu = mtu;
+                       /* in order to keep the skb available */
+                       atomic_inc(&skb->users);
+                       INIT_WORK(&pmtu_update->work, ipoib_cm_update_pmtu_task);
+                       /*
+                       * in order to have it serial, push that task to
+                       * the same queue which the function  will push
+                       * the priv->cm.skb_task work.
+                       */
+                       queue_work(ipoib_workqueue, &pmtu_update->work);
+               } else
+                        ipoib_warn(priv, "Failed alloc pmtu_update and update_pmtu(skb->dst, mtu)\n");
+       }
+
 
        skb_queue_tail(&priv->cm.skb_queue, skb);
        if (e)