From 02cbaec37a24f39e83b51a519147f79921299e24 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 13 Dec 2013 17:21:27 +0800 Subject: netvsc: don't flush peers notifying work during setting mtu [ Upstream commit 50dc875f2e6e2e04aed3b3033eb0ac99192d6d02 ] There's a possible deadlock if we flush the peers notifying work during setting mtu: [ 22.991149] ====================================================== [ 22.991173] [ INFO: possible circular locking dependency detected ] [ 22.991198] 3.10.0-54.0.1.el7.x86_64.debug #1 Not tainted [ 22.991219] ------------------------------------------------------- [ 22.991243] ip/974 is trying to acquire lock: [ 22.991261] ((&(&net_device_ctx->dwork)->work)){+.+.+.}, at: [] flush_work+0x5/0x2e0 [ 22.991307] but task is already holding lock: [ 22.991330] (rtnl_mutex){+.+.+.}, at: [] rtnetlink_rcv+0x1b/0x40 [ 22.991367] which lock already depends on the new lock. [ 22.991398] the existing dependency chain (in reverse order) is: [ 22.991426] -> #1 (rtnl_mutex){+.+.+.}: [ 22.991449] [] __lock_acquire+0xb19/0x1260 [ 22.991477] [] lock_acquire+0xa2/0x1f0 [ 22.991501] [] mutex_lock_nested+0x89/0x4f0 [ 22.991529] [] rtnl_lock+0x17/0x20 [ 22.991552] [] netdev_notify_peers+0x12/0x30 [ 22.991579] [] netvsc_send_garp+0x22/0x30 [hv_netvsc] [ 22.991610] [] process_one_work+0x211/0x6e0 [ 22.991637] [] worker_thread+0x11b/0x3a0 [ 22.991663] [] kthread+0xed/0x100 [ 22.991686] [] ret_from_fork+0x7c/0xb0 [ 22.991715] -> #0 ((&(&net_device_ctx->dwork)->work)){+.+.+.}: [ 22.991715] [] check_prevs_add+0x967/0x970 [ 22.991715] [] __lock_acquire+0xb19/0x1260 [ 22.991715] [] lock_acquire+0xa2/0x1f0 [ 22.991715] [] flush_work+0x4e/0x2e0 [ 22.991715] [] __cancel_work_timer+0x95/0x130 [ 22.991715] [] cancel_delayed_work_sync+0x13/0x20 [ 22.991715] [] netvsc_change_mtu+0x84/0x200 [hv_netvsc] [ 22.991715] [] dev_set_mtu+0x34/0x80 [ 22.991715] [] do_setlink+0x23a/0xa00 [ 22.991715] [] rtnl_newlink+0x394/0x5e0 [ 22.991715] [] rtnetlink_rcv_msg+0x9c/0x260 [ 22.991715] [] netlink_rcv_skb+0xa9/0xc0 [ 22.991715] [] rtnetlink_rcv+0x2a/0x40 [ 22.991715] [] netlink_unicast+0xdd/0x190 [ 22.991715] [] netlink_sendmsg+0x337/0x750 [ 22.991715] [] sock_sendmsg+0x99/0xd0 [ 22.991715] [] ___sys_sendmsg+0x39e/0x3b0 [ 22.991715] [] __sys_sendmsg+0x42/0x80 [ 22.991715] [] SyS_sendmsg+0x12/0x20 [ 22.991715] [] system_call_fastpath+0x16/0x1b This is because we hold the rtnl_lock() before ndo_change_mtu() and try to flush the work in netvsc_change_mtu(), in the mean time, netdev_notify_peers() may be called from worker and also trying to hold the rtnl_lock. This will lead the flush won't succeed forever. Solve this by not canceling and flushing the work, this is safe because the transmission done by NETDEV_NOTIFY_PEERS was synchronized with the netif_tx_disable() called by netvsc_change_mtu(). Reported-by: Yaju Cao Tested-by: Yaju Cao Cc: K. Y. Srinivasan Cc: Haiyang Zhang Signed-off-by: Jason Wang Acked-by: Haiyang Zhang Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/hyperv/netvsc_drv.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net/hyperv') diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 23a0fff0df52..aea78fc2e48f 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -328,7 +328,6 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) return -EINVAL; nvdev->start_remove = true; - cancel_delayed_work_sync(&ndevctx->dwork); cancel_work_sync(&ndevctx->work); netif_tx_disable(ndev); rndis_filter_device_remove(hdev); -- cgit v1.2.3