Commit 9fb20801 authored by Florian Fainelli's avatar Florian Fainelli Committed by David S. Miller

net: Fix ip_mc_{dec,inc}_group allocation context

After 4effd28c ("bridge: join all-snoopers multicast address"), I
started seeing the following sleep in atomic warnings:

[   26.763893] BUG: sleeping function called from invalid context at mm/slab.h:421
[   26.771425] in_atomic(): 1, irqs_disabled(): 0, pid: 1658, name: sh
[   26.777855] INFO: lockdep is turned off.
[   26.781916] CPU: 0 PID: 1658 Comm: sh Not tainted 5.0.0-rc4 #20
[   26.787943] Hardware name: BCM97278SV (DT)
[   26.792118] Call trace:
[   26.794645]  dump_backtrace+0x0/0x170
[   26.798391]  show_stack+0x24/0x30
[   26.801787]  dump_stack+0xa4/0xe4
[   26.805182]  ___might_sleep+0x208/0x218
[   26.809102]  __might_sleep+0x78/0x88
[   26.812762]  kmem_cache_alloc_trace+0x64/0x28c
[   26.817301]  igmp_group_dropped+0x150/0x230
[   26.821573]  ip_mc_dec_group+0x1b0/0x1f8
[   26.825585]  br_ip4_multicast_leave_snoopers.isra.11+0x174/0x190
[   26.831704]  br_multicast_toggle+0x78/0xcc
[   26.835887]  store_bridge_parm+0xc4/0xfc
[   26.839894]  multicast_snooping_store+0x3c/0x4c
[   26.844517]  dev_attr_store+0x44/0x5c
[   26.848262]  sysfs_kf_write+0x50/0x68
[   26.852006]  kernfs_fop_write+0x14c/0x1b4
[   26.856102]  __vfs_write+0x60/0x190
[   26.859668]  vfs_write+0xc8/0x168
[   26.863059]  ksys_write+0x70/0xc8
[   26.866449]  __arm64_sys_write+0x24/0x30
[   26.870458]  el0_svc_common+0xa0/0x11c
[   26.874291]  el0_svc_handler+0x38/0x70
[   26.878120]  el0_svc+0x8/0xc

while toggling the bridge's multicast_snooping attribute dynamically.

Pass a gfp_t down to igmpv3_add_delrec(), introduce
__igmp_group_dropped() and introduce __ip_mc_dec_group() to take a gfp_t
argument.

Similarly introduce ____ip_mc_inc_group() and __ip_mc_inc_group() to
allow caller to specify gfp_t.

IPv6 part of the patch appears fine.

Fixes: 4effd28c ("bridge: join all-snoopers multicast address")
Signed-off-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bff5731d
...@@ -137,7 +137,13 @@ extern void ip_mc_up(struct in_device *); ...@@ -137,7 +137,13 @@ extern void ip_mc_up(struct in_device *);
extern void ip_mc_down(struct in_device *); extern void ip_mc_down(struct in_device *);
extern void ip_mc_unmap(struct in_device *); extern void ip_mc_unmap(struct in_device *);
extern void ip_mc_remap(struct in_device *); extern void ip_mc_remap(struct in_device *);
extern void ip_mc_dec_group(struct in_device *in_dev, __be32 addr); extern void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp);
static inline void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
{
return __ip_mc_dec_group(in_dev, addr, GFP_KERNEL);
}
extern void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
gfp_t gfp);
extern void ip_mc_inc_group(struct in_device *in_dev, __be32 addr); extern void ip_mc_inc_group(struct in_device *in_dev, __be32 addr);
int ip_mc_check_igmp(struct sk_buff *skb); int ip_mc_check_igmp(struct sk_buff *skb);
......
...@@ -1841,7 +1841,7 @@ static void br_ip4_multicast_join_snoopers(struct net_bridge *br) ...@@ -1841,7 +1841,7 @@ static void br_ip4_multicast_join_snoopers(struct net_bridge *br)
if (!in_dev) if (!in_dev)
return; return;
ip_mc_inc_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP)); __ip_mc_inc_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC);
in_dev_put(in_dev); in_dev_put(in_dev);
} }
...@@ -1872,7 +1872,7 @@ static void br_ip4_multicast_leave_snoopers(struct net_bridge *br) ...@@ -1872,7 +1872,7 @@ static void br_ip4_multicast_leave_snoopers(struct net_bridge *br)
if (WARN_ON(!in_dev)) if (WARN_ON(!in_dev))
return; return;
ip_mc_dec_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP)); __ip_mc_dec_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC);
in_dev_put(in_dev); in_dev_put(in_dev);
} }
......
...@@ -159,7 +159,8 @@ static int unsolicited_report_interval(struct in_device *in_dev) ...@@ -159,7 +159,8 @@ static int unsolicited_report_interval(struct in_device *in_dev)
return interval_jiffies; return interval_jiffies;
} }
static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im); static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im,
gfp_t gfp);
static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im); static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im);
static void igmpv3_clear_delrec(struct in_device *in_dev); static void igmpv3_clear_delrec(struct in_device *in_dev);
static int sf_setstate(struct ip_mc_list *pmc); static int sf_setstate(struct ip_mc_list *pmc);
...@@ -1145,7 +1146,8 @@ static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr) ...@@ -1145,7 +1146,8 @@ static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr)
/* /*
* deleted ip_mc_list manipulation * deleted ip_mc_list manipulation
*/ */
static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im) static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im,
gfp_t gfp)
{ {
struct ip_mc_list *pmc; struct ip_mc_list *pmc;
struct net *net = dev_net(in_dev->dev); struct net *net = dev_net(in_dev->dev);
...@@ -1156,7 +1158,7 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im) ...@@ -1156,7 +1158,7 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im)
* for deleted items allows change reports to use common code with * for deleted items allows change reports to use common code with
* non-deleted or query-response MCA's. * non-deleted or query-response MCA's.
*/ */
pmc = kzalloc(sizeof(*pmc), GFP_KERNEL); pmc = kzalloc(sizeof(*pmc), gfp);
if (!pmc) if (!pmc)
return; return;
spin_lock_init(&pmc->lock); spin_lock_init(&pmc->lock);
...@@ -1261,7 +1263,7 @@ static void igmpv3_clear_delrec(struct in_device *in_dev) ...@@ -1261,7 +1263,7 @@ static void igmpv3_clear_delrec(struct in_device *in_dev)
} }
#endif #endif
static void igmp_group_dropped(struct ip_mc_list *im) static void __igmp_group_dropped(struct ip_mc_list *im, gfp_t gfp)
{ {
struct in_device *in_dev = im->interface; struct in_device *in_dev = im->interface;
#ifdef CONFIG_IP_MULTICAST #ifdef CONFIG_IP_MULTICAST
...@@ -1292,13 +1294,18 @@ static void igmp_group_dropped(struct ip_mc_list *im) ...@@ -1292,13 +1294,18 @@ static void igmp_group_dropped(struct ip_mc_list *im)
return; return;
} }
/* IGMPv3 */ /* IGMPv3 */
igmpv3_add_delrec(in_dev, im); igmpv3_add_delrec(in_dev, im, gfp);
igmp_ifc_event(in_dev); igmp_ifc_event(in_dev);
} }
#endif #endif
} }
static void igmp_group_dropped(struct ip_mc_list *im)
{
__igmp_group_dropped(im, GFP_KERNEL);
}
static void igmp_group_added(struct ip_mc_list *im) static void igmp_group_added(struct ip_mc_list *im)
{ {
struct in_device *in_dev = im->interface; struct in_device *in_dev = im->interface;
...@@ -1400,8 +1407,8 @@ static void ip_mc_hash_remove(struct in_device *in_dev, ...@@ -1400,8 +1407,8 @@ static void ip_mc_hash_remove(struct in_device *in_dev,
/* /*
* A socket has joined a multicast group on device dev. * A socket has joined a multicast group on device dev.
*/ */
static void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
unsigned int mode) unsigned int mode, gfp_t gfp)
{ {
struct ip_mc_list *im; struct ip_mc_list *im;
...@@ -1415,7 +1422,7 @@ static void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, ...@@ -1415,7 +1422,7 @@ static void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
} }
} }
im = kzalloc(sizeof(*im), GFP_KERNEL); im = kzalloc(sizeof(*im), gfp);
if (!im) if (!im)
goto out; goto out;
...@@ -1448,6 +1455,12 @@ static void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, ...@@ -1448,6 +1455,12 @@ static void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
return; return;
} }
void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, gfp_t gfp)
{
____ip_mc_inc_group(in_dev, addr, MCAST_EXCLUDE, gfp);
}
EXPORT_SYMBOL(__ip_mc_inc_group);
void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
{ {
__ip_mc_inc_group(in_dev, addr, MCAST_EXCLUDE); __ip_mc_inc_group(in_dev, addr, MCAST_EXCLUDE);
...@@ -1634,7 +1647,7 @@ static void ip_mc_rejoin_groups(struct in_device *in_dev) ...@@ -1634,7 +1647,7 @@ static void ip_mc_rejoin_groups(struct in_device *in_dev)
* A socket has left a multicast group on device dev * A socket has left a multicast group on device dev
*/ */
void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp)
{ {
struct ip_mc_list *i; struct ip_mc_list *i;
struct ip_mc_list __rcu **ip; struct ip_mc_list __rcu **ip;
...@@ -1649,7 +1662,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) ...@@ -1649,7 +1662,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
ip_mc_hash_remove(in_dev, i); ip_mc_hash_remove(in_dev, i);
*ip = i->next_rcu; *ip = i->next_rcu;
in_dev->mc_count--; in_dev->mc_count--;
igmp_group_dropped(i); __igmp_group_dropped(i, gfp);
ip_mc_clear_src(i); ip_mc_clear_src(i);
if (!in_dev->dead) if (!in_dev->dead)
...@@ -1662,7 +1675,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) ...@@ -1662,7 +1675,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
} }
} }
} }
EXPORT_SYMBOL(ip_mc_dec_group); EXPORT_SYMBOL(__ip_mc_dec_group);
/* Device changing type */ /* Device changing type */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment