From d12059dba0b774f52f43475b22ed18d4d52dfe3b Mon Sep 17 00:00:00 2001 From: Erez Shitrit Date: Tue, 29 Jan 2013 17:00:49 +0200 Subject: [PATCH] ib/ipoib: Fix deadlock between rmmod and set_mode set_mod called from sys/fs, takes sys/fs lock and tries to take rtnl_lock, rmmod takes rtnl_lock and now tries to take sys/fs lock, that causes deadlock. deadloc a->b, b->a The problem starts when ipoib_set_mod free it's rtnl_lck and tries to get it after that. set_mod: [] ? check_preempt_curr+0x6d/0x90 [] __mutex_lock_slowpath+0x13e/0x180 [] ? __rtnl_unlock+0x15/0x20 [] mutex_lock+0x2b/0x50 [] rtnl_lock+0x15/0x20 [] ipoib_set_mode+0x97/0x160 [ib_ipoib] [] set_mode+0x3b/0x80 [ib_ipoib] [] dev_attr_store+0x20/0x30 [] sysfs_write_file+0xe5/0x170 [] vfs_write+0xb8/0x1a0 [] sys_write+0x51/0x90 [] system_call_fastpath+0x16/0x1b INFO: task rmmod:8057 blocked for more than 120 seconds. "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. rmmod: [] ? put_dec+0x10c/0x110 [] ? number+0x2ee/0x320 [] schedule_timeout+0x215/0x2e0 [] ? vsnprintf+0x484/0x5f0 [] ? string+0x40/0x100 [] wait_for_common+0x123/0x180 [] ? default_wake_function+0x0/0x20 [] ? ifind_fast+0x5e/0xb0 [] wait_for_completion+0x1d/0x20 [] sysfs_addrm_finish+0x228/0x270 [] sysfs_remove_dir+0xa3/0xf0 [] kobject_del+0x16/0x40 [] device_del+0x184/0x1e0 [] netdev_unregister_kobject+0xab/0xc0 [] rollback_registered+0xae/0x130 [] unregister_netdevice+0x22/0x70 [] unregister_netdev+0x1e/0x30 [] ipoib_remove_one+0xe0/0x120 [ib_ipoib] [] ib_unregister_device+0x4f/0x100 [ib_core] [] mlx4_ib_remove+0x41/0x180 [mlx4_ib] [] mlx4_remove_device+0x71/0x90 [mlx4_core] [] mlx4_unregister_interface+0x43/0x80 [mlx4_core] [] __exit_compat+0x15/0x4e [mlx4_ib] [] sys_delete_module+0x194/0x260 [] ? do_page_fault+0x3e/0xa0 [] system_call_fastpath+0x16/0x1b Signed-off-by: Erez Shitrit Signed-off-by: Erez Shitrit (Ported from Mellanox OFED 2.4) Signed-off-by: Mukesh Kacker --- drivers/infiniband/ulp/ipoib/ipoib_cm.c | 9 +++++++-- drivers/infiniband/ulp/ipoib/ipoib_main.c | 10 ++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index a4569d77d1a5..cf7d15323fee 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -1518,9 +1518,14 @@ static ssize_t set_mode(struct device *d, struct device_attribute *attr, ret = ipoib_set_mode(dev, buf); - rtnl_unlock(); + /* the assumption is that the func ipoib_set_mode returned with the + * rtnl held by it, if not the value -EBUSY returned, + * so no need to rtnl_unlock + */ + if (ret != -EBUSY) + rtnl_unlock(); - if (!ret) + if (!ret || ret == -EBUSY) return count; return ret; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 53c012ddffcc..194bce6fc00b 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -236,7 +236,10 @@ int ipoib_set_mode(struct net_device *dev, const char *buf) priv->tx_wr.send_flags &= ~IB_SEND_IP_CSUM; ipoib_flush_paths(dev); - rtnl_lock(); + + if (!rtnl_trylock()) + return -EBUSY; + return 0; } @@ -246,7 +249,10 @@ int ipoib_set_mode(struct net_device *dev, const char *buf) dev_set_mtu(dev, min(priv->mcast_mtu, dev->mtu)); rtnl_unlock(); ipoib_flush_paths(dev); - rtnl_lock(); + + if (!rtnl_trylock()) + return -EBUSY; + return 0; } -- 2.50.1