From mboxrd@z Thu Jan 1 00:00:00 1970 From: Alexei Starovoitov Subject: [PATCH net-next] fix rtnl notification in atomic context Date: Wed, 23 Oct 2013 11:32:41 -0700 Message-ID: <1382553161-3498-1-git-send-email-ast@plumgrid.com> Cc: Nicolas Dichtel , Cong Wang , netdev@vger.kernel.org To: "David S. Miller" Return-path: Received: from mail-pa0-f43.google.com ([209.85.220.43]:46040 "EHLO mail-pa0-f43.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751719Ab3JWScq (ORCPT ); Wed, 23 Oct 2013 14:32:46 -0400 Received: by mail-pa0-f43.google.com with SMTP id hz1so1333807pad.2 for ; Wed, 23 Oct 2013 11:32:45 -0700 (PDT) Sender: netdev-owner@vger.kernel.org List-ID: commit 991fb3f74c "dev: always advertise rx_flags changes via netlink" introduced rtnl notification from __dev_set_promiscuity(), which can be called in atomic context. Steps to reproduce: ip tuntap add dev tap1 mode tap ifconfig tap1 up tcpdump -nei tap1 & ip tuntap del dev tap1 mode tap [ 271.627994] device tap1 left promiscuous mode [ 271.639897] BUG: sleeping function called from invalid context at mm/slub.c:940 [ 271.664491] in_atomic(): 1, irqs_disabled(): 0, pid: 3394, name: ip [ 271.677525] INFO: lockdep is turned off. [ 271.690503] CPU: 0 PID: 3394 Comm: ip Tainted: G W 3.12.0-rc3+ #73 [ 271.703996] Hardware name: System manufacturer System Product Name/P8Z77 WS, BIOS 3007 07/26/2012 [ 271.731254] ffffffff81a58506 ffff8807f0d57a58 ffffffff817544e5 ffff88082fa0f428 [ 271.760261] ffff8808071f5f40 ffff8807f0d57a88 ffffffff8108bad1 ffffffff81110ff8 [ 271.790683] 0000000000000010 00000000000000d0 00000000000000d0 ffff8807f0d57af8 [ 271.822332] Call Trace: [ 271.838234] [] dump_stack+0x55/0x76 [ 271.854446] [] __might_sleep+0x181/0x240 [ 271.870836] [] ? rcu_irq_exit+0x68/0xb0 [ 271.887076] [] kmem_cache_alloc_node+0x4e/0x2a0 [ 271.903368] [] ? vprintk_emit+0x1dc/0x5a0 [ 271.919716] [] ? __alloc_skb+0x57/0x2a0 [ 271.936088] [] ? vprintk_emit+0x1e0/0x5a0 [ 271.952504] [] __alloc_skb+0x57/0x2a0 [ 271.968902] [] rtmsg_ifinfo+0x52/0x100 [ 271.985302] [] __dev_notify_flags+0xad/0xc0 [ 272.001642] [] __dev_set_promiscuity+0x8c/0x1c0 [ 272.017917] [] ? packet_notifier+0x5/0x380 [ 272.033961] [] dev_set_promiscuity+0x29/0x50 [ 272.049855] [] packet_dev_mc+0x87/0xc0 [ 272.065494] [] packet_notifier+0x1b2/0x380 [ 272.080915] [] ? packet_notifier+0x5/0x380 [ 272.096009] [] notifier_call_chain+0x66/0x150 [ 272.110803] [] __raw_notifier_call_chain+0xe/0x10 [ 272.125468] [] raw_notifier_call_chain+0x16/0x20 [ 272.139984] [] call_netdevice_notifiers_info+0x40/0x70 [ 272.154523] [] call_netdevice_notifiers+0x16/0x20 [ 272.168552] [] rollback_registered_many+0x145/0x240 [ 272.182263] [] rollback_registered+0x31/0x40 [ 272.195369] [] unregister_netdevice_queue+0x58/0x90 [ 272.208230] [] __tun_detach+0x140/0x340 [ 272.220686] [] tun_chr_close+0x36/0x60 Signed-off-by: Alexei Starovoitov --- include/linux/rtnetlink.h | 4 +++- net/core/dev.c | 2 +- net/core/rtnetlink.c | 12 +++++++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index f28544b..26f5edc 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -15,7 +15,9 @@ extern int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics); extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id, long expires, u32 error); -extern void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change); +void __rtmsg_ifinfo(int type, struct net_device *dev, unsigned change, + gfp_t flags); +void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change); /* RTNL is used as a global lock for all changes to network configuration */ extern void rtnl_lock(void); diff --git a/net/core/dev.c b/net/core/dev.c index 0918aad..59b90fe 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5257,7 +5257,7 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags, unsigned int changes = dev->flags ^ old_flags; if (gchanges) - rtmsg_ifinfo(RTM_NEWLINK, dev, gchanges); + __rtmsg_ifinfo(RTM_NEWLINK, dev, gchanges, GFP_ATOMIC); if (changes & IFF_UP) { if (dev->flags & IFF_UP) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 4aedf03..5931af9 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1984,14 +1984,15 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) return skb->len; } -void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change) +void __rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change, + gfp_t flags) { struct net *net = dev_net(dev); struct sk_buff *skb; int err = -ENOBUFS; size_t if_info_size; - skb = nlmsg_new((if_info_size = if_nlmsg_size(dev, 0)), GFP_KERNEL); + skb = nlmsg_new((if_info_size = if_nlmsg_size(dev, 0)), flags); if (skb == NULL) goto errout; @@ -2002,12 +2003,17 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change) kfree_skb(skb); goto errout; } - rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL); + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, flags); return; errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_LINK, err); } + +void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change) +{ + __rtmsg_ifinfo(type, dev, change, GFP_KERNEL); +} EXPORT_SYMBOL(rtmsg_ifinfo); static int nlmsg_populate_fdb_fill(struct sk_buff *skb, -- 1.7.9.5