From 7d0e35be810cb04862e74f5de9bd8f106c9536b6 Mon Sep 17 00:00:00 2001 From: Erez Shitrit Date: Thu, 29 Nov 2012 09:27:46 +0200 Subject: [PATCH] ib/ipoib: Fix removing call for update_pmtu from spin-lock context. 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 Signed-off-by: Yishai Hadas (Ported from Mellanox OFED 2.4) Signed-off-by: Mukesh Kacker --- drivers/infiniband/ulp/ipoib/ipoib.h | 7 +++++ drivers/infiniband/ulp/ipoib/ipoib_cm.c | 37 +++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index bd94b0a6e9e5..99fe6fa77575 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -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 { diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index cf32a778e7d0..5163111abac7 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -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) -- 2.50.1