Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netconsole: don't call __netpoll_cleanup() while atomic

__netpoll_cleanup() is called in netconsole_netdev_event() while holding a
spinlock. Release/acquire the spinlock before/after it and restart the
loop. Also, disable the netconsole completely, because we won't have chance
after the restart of the loop, and might end up in a situation where
nt->enabled == 1 and nt->np.dev == NULL.

Signed-off-by: Veaceslav Falico <vfalico@redhat.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Veaceslav Falico and committed by
David S. Miller
3f315bef 3da889b6

+9 -6
+9 -6
drivers/net/netconsole.c
··· 666 666 goto done; 667 667 668 668 spin_lock_irqsave(&target_list_lock, flags); 669 + restart: 669 670 list_for_each_entry(nt, &target_list, list) { 670 671 netconsole_target_get(nt); 671 672 if (nt->np.dev == dev) { ··· 679 678 case NETDEV_UNREGISTER: 680 679 /* 681 680 * rtnl_lock already held 681 + * we might sleep in __netpoll_cleanup() 682 682 */ 683 - if (nt->np.dev) { 684 - __netpoll_cleanup(&nt->np); 685 - dev_put(nt->np.dev); 686 - nt->np.dev = NULL; 687 - } 683 + spin_unlock_irqrestore(&target_list_lock, flags); 684 + __netpoll_cleanup(&nt->np); 685 + spin_lock_irqsave(&target_list_lock, flags); 686 + dev_put(nt->np.dev); 687 + nt->np.dev = NULL; 688 688 nt->enabled = 0; 689 689 stopped = true; 690 - break; 690 + netconsole_target_put(nt); 691 + goto restart; 691 692 } 692 693 } 693 694 netconsole_target_put(nt);