Skip to content

Commit d136f1b

Browse files
jmbergdavem330
authored andcommitted
genetlink: fix netns vs. netlink table locking
Since my commits introducing netns awareness into genetlink we can get this problem: BUG: scheduling while atomic: modprobe/1178/0x00000002 2 locks held by modprobe/1178: #0: (genl_mutex){+.+.+.}, at: [<ffffffff8135ee1a>] genl_register_mc_grou #1: (rcu_read_lock){.+.+..}, at: [<ffffffff8135eeb5>] genl_register_mc_g Pid: 1178, comm: modprobe Not tainted 2.6.31-rc8-wl-34789-g95cb731-dirty # Call Trace: [<ffffffff8103e285>] __schedule_bug+0x85/0x90 [<ffffffff81403138>] schedule+0x108/0x588 [<ffffffff8135b131>] netlink_table_grab+0xa1/0xf0 [<ffffffff8135c3a7>] netlink_change_ngroups+0x47/0x100 [<ffffffff8135ef0f>] genl_register_mc_group+0x12f/0x290 because I overlooked that netlink_table_grab() will schedule, thinking it was just the rwlock. However, in the contention case, that isn't actually true. Fix this by letting the code grab the netlink table lock first and then the RCU for netns protection. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 8be8057 commit d136f1b

File tree

3 files changed

+37
-23
lines changed

3 files changed

+37
-23
lines changed

include/linux/netlink.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,12 +176,16 @@ struct netlink_skb_parms
176176
#define NETLINK_CREDS(skb) (&NETLINK_CB((skb)).creds)
177177

178178

179+
extern void netlink_table_grab(void);
180+
extern void netlink_table_ungrab(void);
181+
179182
extern struct sock *netlink_kernel_create(struct net *net,
180183
int unit,unsigned int groups,
181184
void (*input)(struct sk_buff *skb),
182185
struct mutex *cb_mutex,
183186
struct module *module);
184187
extern void netlink_kernel_release(struct sock *sk);
188+
extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups);
185189
extern int netlink_change_ngroups(struct sock *sk, unsigned int groups);
186190
extern void netlink_clear_multicast_users(struct sock *sk, unsigned int group);
187191
extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);

net/netlink/af_netlink.c

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,11 @@ static void netlink_sock_destruct(struct sock *sk)
177177
* this, _but_ remember, it adds useless work on UP machines.
178178
*/
179179

180-
static void netlink_table_grab(void)
180+
void netlink_table_grab(void)
181181
__acquires(nl_table_lock)
182182
{
183+
might_sleep();
184+
183185
write_lock_irq(&nl_table_lock);
184186

185187
if (atomic_read(&nl_table_users)) {
@@ -200,7 +202,7 @@ static void netlink_table_grab(void)
200202
}
201203
}
202204

203-
static void netlink_table_ungrab(void)
205+
void netlink_table_ungrab(void)
204206
__releases(nl_table_lock)
205207
{
206208
write_unlock_irq(&nl_table_lock);
@@ -1549,37 +1551,21 @@ static void netlink_free_old_listeners(struct rcu_head *rcu_head)
15491551
kfree(lrh->ptr);
15501552
}
15511553

1552-
/**
1553-
* netlink_change_ngroups - change number of multicast groups
1554-
*
1555-
* This changes the number of multicast groups that are available
1556-
* on a certain netlink family. Note that it is not possible to
1557-
* change the number of groups to below 32. Also note that it does
1558-
* not implicitly call netlink_clear_multicast_users() when the
1559-
* number of groups is reduced.
1560-
*
1561-
* @sk: The kernel netlink socket, as returned by netlink_kernel_create().
1562-
* @groups: The new number of groups.
1563-
*/
1564-
int netlink_change_ngroups(struct sock *sk, unsigned int groups)
1554+
int __netlink_change_ngroups(struct sock *sk, unsigned int groups)
15651555
{
15661556
unsigned long *listeners, *old = NULL;
15671557
struct listeners_rcu_head *old_rcu_head;
15681558
struct netlink_table *tbl = &nl_table[sk->sk_protocol];
1569-
int err = 0;
15701559

15711560
if (groups < 32)
15721561
groups = 32;
15731562

1574-
netlink_table_grab();
15751563
if (NLGRPSZ(tbl->groups) < NLGRPSZ(groups)) {
15761564
listeners = kzalloc(NLGRPSZ(groups) +
15771565
sizeof(struct listeners_rcu_head),
15781566
GFP_ATOMIC);
1579-
if (!listeners) {
1580-
err = -ENOMEM;
1581-
goto out_ungrab;
1582-
}
1567+
if (!listeners)
1568+
return -ENOMEM;
15831569
old = tbl->listeners;
15841570
memcpy(listeners, old, NLGRPSZ(tbl->groups));
15851571
rcu_assign_pointer(tbl->listeners, listeners);
@@ -1597,8 +1583,29 @@ int netlink_change_ngroups(struct sock *sk, unsigned int groups)
15971583
}
15981584
tbl->groups = groups;
15991585

1600-
out_ungrab:
1586+
return 0;
1587+
}
1588+
1589+
/**
1590+
* netlink_change_ngroups - change number of multicast groups
1591+
*
1592+
* This changes the number of multicast groups that are available
1593+
* on a certain netlink family. Note that it is not possible to
1594+
* change the number of groups to below 32. Also note that it does
1595+
* not implicitly call netlink_clear_multicast_users() when the
1596+
* number of groups is reduced.
1597+
*
1598+
* @sk: The kernel netlink socket, as returned by netlink_kernel_create().
1599+
* @groups: The new number of groups.
1600+
*/
1601+
int netlink_change_ngroups(struct sock *sk, unsigned int groups)
1602+
{
1603+
int err;
1604+
1605+
netlink_table_grab();
1606+
err = __netlink_change_ngroups(sk, groups);
16011607
netlink_table_ungrab();
1608+
16021609
return err;
16031610
}
16041611

net/netlink/genetlink.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,10 @@ int genl_register_mc_group(struct genl_family *family,
176176
if (family->netnsok) {
177177
struct net *net;
178178

179+
netlink_table_grab();
179180
rcu_read_lock();
180181
for_each_net_rcu(net) {
181-
err = netlink_change_ngroups(net->genl_sock,
182+
err = __netlink_change_ngroups(net->genl_sock,
182183
mc_groups_longs * BITS_PER_LONG);
183184
if (err) {
184185
/*
@@ -188,10 +189,12 @@ int genl_register_mc_group(struct genl_family *family,
188189
* increased on some sockets which is ok.
189190
*/
190191
rcu_read_unlock();
192+
netlink_table_ungrab();
191193
goto out;
192194
}
193195
}
194196
rcu_read_unlock();
197+
netlink_table_ungrab();
195198
} else {
196199
err = netlink_change_ngroups(init_net.genl_sock,
197200
mc_groups_longs * BITS_PER_LONG);

0 commit comments

Comments
 (0)