diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/netlink/af_netlink.c | 51 | ||||
-rw-r--r-- | net/netlink/genetlink.c | 5 |
2 files changed, 33 insertions, 23 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index d0ff382c40ca..c5aab6a368ce 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -177,9 +177,11 @@ static void netlink_sock_destruct(struct sock *sk) | |||
177 | * this, _but_ remember, it adds useless work on UP machines. | 177 | * this, _but_ remember, it adds useless work on UP machines. |
178 | */ | 178 | */ |
179 | 179 | ||
180 | static void netlink_table_grab(void) | 180 | void netlink_table_grab(void) |
181 | __acquires(nl_table_lock) | 181 | __acquires(nl_table_lock) |
182 | { | 182 | { |
183 | might_sleep(); | ||
184 | |||
183 | write_lock_irq(&nl_table_lock); | 185 | write_lock_irq(&nl_table_lock); |
184 | 186 | ||
185 | if (atomic_read(&nl_table_users)) { | 187 | if (atomic_read(&nl_table_users)) { |
@@ -200,7 +202,7 @@ static void netlink_table_grab(void) | |||
200 | } | 202 | } |
201 | } | 203 | } |
202 | 204 | ||
203 | static void netlink_table_ungrab(void) | 205 | void netlink_table_ungrab(void) |
204 | __releases(nl_table_lock) | 206 | __releases(nl_table_lock) |
205 | { | 207 | { |
206 | write_unlock_irq(&nl_table_lock); | 208 | write_unlock_irq(&nl_table_lock); |
@@ -1549,37 +1551,21 @@ static void netlink_free_old_listeners(struct rcu_head *rcu_head) | |||
1549 | kfree(lrh->ptr); | 1551 | kfree(lrh->ptr); |
1550 | } | 1552 | } |
1551 | 1553 | ||
1552 | /** | 1554 | int __netlink_change_ngroups(struct sock *sk, unsigned int groups) |
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) | ||
1565 | { | 1555 | { |
1566 | unsigned long *listeners, *old = NULL; | 1556 | unsigned long *listeners, *old = NULL; |
1567 | struct listeners_rcu_head *old_rcu_head; | 1557 | struct listeners_rcu_head *old_rcu_head; |
1568 | struct netlink_table *tbl = &nl_table[sk->sk_protocol]; | 1558 | struct netlink_table *tbl = &nl_table[sk->sk_protocol]; |
1569 | int err = 0; | ||
1570 | 1559 | ||
1571 | if (groups < 32) | 1560 | if (groups < 32) |
1572 | groups = 32; | 1561 | groups = 32; |
1573 | 1562 | ||
1574 | netlink_table_grab(); | ||
1575 | if (NLGRPSZ(tbl->groups) < NLGRPSZ(groups)) { | 1563 | if (NLGRPSZ(tbl->groups) < NLGRPSZ(groups)) { |
1576 | listeners = kzalloc(NLGRPSZ(groups) + | 1564 | listeners = kzalloc(NLGRPSZ(groups) + |
1577 | sizeof(struct listeners_rcu_head), | 1565 | sizeof(struct listeners_rcu_head), |
1578 | GFP_ATOMIC); | 1566 | GFP_ATOMIC); |
1579 | if (!listeners) { | 1567 | if (!listeners) |
1580 | err = -ENOMEM; | 1568 | return -ENOMEM; |
1581 | goto out_ungrab; | ||
1582 | } | ||
1583 | old = tbl->listeners; | 1569 | old = tbl->listeners; |
1584 | memcpy(listeners, old, NLGRPSZ(tbl->groups)); | 1570 | memcpy(listeners, old, NLGRPSZ(tbl->groups)); |
1585 | rcu_assign_pointer(tbl->listeners, listeners); | 1571 | rcu_assign_pointer(tbl->listeners, listeners); |
@@ -1597,8 +1583,29 @@ int netlink_change_ngroups(struct sock *sk, unsigned int groups) | |||
1597 | } | 1583 | } |
1598 | tbl->groups = groups; | 1584 | tbl->groups = groups; |
1599 | 1585 | ||
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); | ||
1601 | netlink_table_ungrab(); | 1607 | netlink_table_ungrab(); |
1608 | |||
1602 | return err; | 1609 | return err; |
1603 | } | 1610 | } |
1604 | 1611 | ||
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 66f6ba0bab11..566941e03363 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c | |||
@@ -176,9 +176,10 @@ int genl_register_mc_group(struct genl_family *family, | |||
176 | if (family->netnsok) { | 176 | if (family->netnsok) { |
177 | struct net *net; | 177 | struct net *net; |
178 | 178 | ||
179 | netlink_table_grab(); | ||
179 | rcu_read_lock(); | 180 | rcu_read_lock(); |
180 | for_each_net_rcu(net) { | 181 | for_each_net_rcu(net) { |
181 | err = netlink_change_ngroups(net->genl_sock, | 182 | err = __netlink_change_ngroups(net->genl_sock, |
182 | mc_groups_longs * BITS_PER_LONG); | 183 | mc_groups_longs * BITS_PER_LONG); |
183 | if (err) { | 184 | if (err) { |
184 | /* | 185 | /* |
@@ -188,10 +189,12 @@ int genl_register_mc_group(struct genl_family *family, | |||
188 | * increased on some sockets which is ok. | 189 | * increased on some sockets which is ok. |
189 | */ | 190 | */ |
190 | rcu_read_unlock(); | 191 | rcu_read_unlock(); |
192 | netlink_table_ungrab(); | ||
191 | goto out; | 193 | goto out; |
192 | } | 194 | } |
193 | } | 195 | } |
194 | rcu_read_unlock(); | 196 | rcu_read_unlock(); |
197 | netlink_table_ungrab(); | ||
195 | } else { | 198 | } else { |
196 | err = netlink_change_ngroups(init_net.genl_sock, | 199 | err = netlink_change_ngroups(init_net.genl_sock, |
197 | mc_groups_longs * BITS_PER_LONG); | 200 | mc_groups_longs * BITS_PER_LONG); |