aboutsummaryrefslogtreecommitdiffstats
path: root/net/netlink
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2007-07-18 18:46:06 -0400
committerDavid S. Miller <davem@davemloft.net>2007-07-18 18:46:06 -0400
commitb4ff4f0419ae5db83553fab79d03a89c10d540a8 (patch)
tree17695af46692c31ec1f33ffb68bc686064227913 /net/netlink
parentc81de6addb913423acef6e692fd70688180ab5dd (diff)
[NETLINK]: allocate group bitmaps dynamically
Allow changing the number of groups for a netlink family after it has been created, use RCU to protect the listeners bitmap keeping netlink_has_listeners() lock-free. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Acked-by: Patrick McHardy <kaber@trash.net> Acked-by: Jamal Hadi Salim <hadi@cyberus.ca> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlink')
-rw-r--r--net/netlink/af_netlink.c106
1 files changed, 82 insertions, 24 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 641cfbc278d8..c386eaf6ad5b 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -62,6 +62,7 @@
62#include <net/netlink.h> 62#include <net/netlink.h>
63 63
64#define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8) 64#define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8)
65#define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long))
65 66
66struct netlink_sock { 67struct netlink_sock {
67 /* struct sock has to be the first member of netlink_sock */ 68 /* struct sock has to be the first member of netlink_sock */
@@ -314,10 +315,12 @@ netlink_update_listeners(struct sock *sk)
314 unsigned long mask; 315 unsigned long mask;
315 unsigned int i; 316 unsigned int i;
316 317
317 for (i = 0; i < NLGRPSZ(tbl->groups)/sizeof(unsigned long); i++) { 318 for (i = 0; i < NLGRPLONGS(tbl->groups); i++) {
318 mask = 0; 319 mask = 0;
319 sk_for_each_bound(sk, node, &tbl->mc_list) 320 sk_for_each_bound(sk, node, &tbl->mc_list) {
320 mask |= nlk_sk(sk)->groups[i]; 321 if (i < NLGRPLONGS(nlk_sk(sk)->ngroups))
322 mask |= nlk_sk(sk)->groups[i];
323 }
321 tbl->listeners[i] = mask; 324 tbl->listeners[i] = mask;
322 } 325 }
323 /* this function is only called with the netlink table "grabbed", which 326 /* this function is only called with the netlink table "grabbed", which
@@ -555,26 +558,37 @@ netlink_update_subscriptions(struct sock *sk, unsigned int subscriptions)
555 nlk->subscriptions = subscriptions; 558 nlk->subscriptions = subscriptions;
556} 559}
557 560
558static int netlink_alloc_groups(struct sock *sk) 561static int netlink_realloc_groups(struct sock *sk)
559{ 562{
560 struct netlink_sock *nlk = nlk_sk(sk); 563 struct netlink_sock *nlk = nlk_sk(sk);
561 unsigned int groups; 564 unsigned int groups;
565 unsigned long *new_groups;
562 int err = 0; 566 int err = 0;
563 567
564 netlink_lock_table(); 568 netlink_table_grab();
569
565 groups = nl_table[sk->sk_protocol].groups; 570 groups = nl_table[sk->sk_protocol].groups;
566 if (!nl_table[sk->sk_protocol].registered) 571 if (!nl_table[sk->sk_protocol].registered) {
567 err = -ENOENT; 572 err = -ENOENT;
568 netlink_unlock_table(); 573 goto out_unlock;
574 }
569 575
570 if (err) 576 if (nlk->ngroups >= groups)
571 return err; 577 goto out_unlock;
572 578
573 nlk->groups = kzalloc(NLGRPSZ(groups), GFP_KERNEL); 579 new_groups = krealloc(nlk->groups, NLGRPSZ(groups), GFP_ATOMIC);
574 if (nlk->groups == NULL) 580 if (new_groups == NULL) {
575 return -ENOMEM; 581 err = -ENOMEM;
582 goto out_unlock;
583 }
584 memset((char*)new_groups + NLGRPSZ(nlk->ngroups), 0,
585 NLGRPSZ(groups) - NLGRPSZ(nlk->ngroups));
586
587 nlk->groups = new_groups;
576 nlk->ngroups = groups; 588 nlk->ngroups = groups;
577 return 0; 589 out_unlock:
590 netlink_table_ungrab();
591 return err;
578} 592}
579 593
580static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len) 594static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
@@ -591,11 +605,9 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len
591 if (nladdr->nl_groups) { 605 if (nladdr->nl_groups) {
592 if (!netlink_capable(sock, NL_NONROOT_RECV)) 606 if (!netlink_capable(sock, NL_NONROOT_RECV))
593 return -EPERM; 607 return -EPERM;
594 if (nlk->groups == NULL) { 608 err = netlink_realloc_groups(sk);
595 err = netlink_alloc_groups(sk); 609 if (err)
596 if (err) 610 return err;
597 return err;
598 }
599 } 611 }
600 612
601 if (nlk->pid) { 613 if (nlk->pid) {
@@ -839,10 +851,18 @@ retry:
839int netlink_has_listeners(struct sock *sk, unsigned int group) 851int netlink_has_listeners(struct sock *sk, unsigned int group)
840{ 852{
841 int res = 0; 853 int res = 0;
854 unsigned long *listeners;
842 855
843 BUG_ON(!(nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET)); 856 BUG_ON(!(nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET));
857
858 rcu_read_lock();
859 listeners = rcu_dereference(nl_table[sk->sk_protocol].listeners);
860
844 if (group - 1 < nl_table[sk->sk_protocol].groups) 861 if (group - 1 < nl_table[sk->sk_protocol].groups)
845 res = test_bit(group - 1, nl_table[sk->sk_protocol].listeners); 862 res = test_bit(group - 1, listeners);
863
864 rcu_read_unlock();
865
846 return res; 866 return res;
847} 867}
848EXPORT_SYMBOL_GPL(netlink_has_listeners); 868EXPORT_SYMBOL_GPL(netlink_has_listeners);
@@ -1037,11 +1057,9 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
1037 1057
1038 if (!netlink_capable(sock, NL_NONROOT_RECV)) 1058 if (!netlink_capable(sock, NL_NONROOT_RECV))
1039 return -EPERM; 1059 return -EPERM;
1040 if (nlk->groups == NULL) { 1060 err = netlink_realloc_groups(sk);
1041 err = netlink_alloc_groups(sk); 1061 if (err)
1042 if (err) 1062 return err;
1043 return err;
1044 }
1045 if (!val || val - 1 >= nlk->ngroups) 1063 if (!val || val - 1 >= nlk->ngroups)
1046 return -EINVAL; 1064 return -EINVAL;
1047 netlink_table_grab(); 1065 netlink_table_grab();
@@ -1328,6 +1346,46 @@ out_sock_release:
1328 return NULL; 1346 return NULL;
1329} 1347}
1330 1348
1349/**
1350 * netlink_change_ngroups - change number of multicast groups
1351 *
1352 * This changes the number of multicast groups that are available
1353 * on a certain netlink family. Note that it is not possible to
1354 * change the number of groups to below 32.
1355 *
1356 * @sk: The kernel netlink socket, as returned by netlink_kernel_create().
1357 * @groups: The new number of groups.
1358 */
1359int netlink_change_ngroups(struct sock *sk, unsigned int groups)
1360{
1361 unsigned long *listeners, *old = NULL;
1362 struct netlink_table *tbl = &nl_table[sk->sk_protocol];
1363 int err = 0;
1364
1365 if (groups < 32)
1366 groups = 32;
1367
1368 netlink_table_grab();
1369 if (NLGRPSZ(tbl->groups) < NLGRPSZ(groups)) {
1370 listeners = kzalloc(NLGRPSZ(groups), GFP_ATOMIC);
1371 if (!listeners) {
1372 err = -ENOMEM;
1373 goto out_ungrab;
1374 }
1375 old = tbl->listeners;
1376 memcpy(listeners, old, NLGRPSZ(tbl->groups));
1377 rcu_assign_pointer(tbl->listeners, listeners);
1378 }
1379 tbl->groups = groups;
1380
1381 out_ungrab:
1382 netlink_table_ungrab();
1383 synchronize_rcu();
1384 kfree(old);
1385 return err;
1386}
1387EXPORT_SYMBOL(netlink_change_ngroups);
1388
1331void netlink_set_nonroot(int protocol, unsigned int flags) 1389void netlink_set_nonroot(int protocol, unsigned int flags)
1332{ 1390{
1333 if ((unsigned int)protocol < MAX_LINKS) 1391 if ((unsigned int)protocol < MAX_LINKS)