aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2006-03-20 21:52:01 -0500
committerDavid S. Miller <davem@davemloft.net>2006-03-20 21:52:01 -0500
commit4277a083ecd2c8771058641132bcecea04ca6608 (patch)
treec35d659c63fa4d64e2569e5eebd76e1b3fba9a15
parenta24276924875802853b5bdc12c56d29f1c1bbc79 (diff)
[NETLINK]: Add netlink_has_listeners for avoiding unneccessary event message generation
Keep a bitmask of multicast groups with subscribed listeners to let netlink users check for listeners before generating multicast messages. Queries don't perform any locking, which may result in false positives, it is guaranteed however that any new subscriptions are visible before bind() or setsockopt() return. Signed-off-by: Patrick McHardy <kaber@trash.net> ACKed-by: Jamal Hadi Salim<hadi@cyberus.ca> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/netlink.h1
-rw-r--r--net/netlink/af_netlink.c52
2 files changed, 49 insertions, 4 deletions
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index c256ebe2a7b4..f8f3d1c927f8 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -151,6 +151,7 @@ struct netlink_skb_parms
151 151
152extern struct sock *netlink_kernel_create(int unit, unsigned int groups, void (*input)(struct sock *sk, int len), struct module *module); 152extern struct sock *netlink_kernel_create(int unit, unsigned int groups, void (*input)(struct sock *sk, int len), struct module *module);
153extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); 153extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);
154extern int netlink_has_listeners(struct sock *sk, unsigned int group);
154extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock); 155extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock);
155extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid, 156extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid,
156 __u32 group, gfp_t allocation); 157 __u32 group, gfp_t allocation);
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 59dc7d140600..d00a9034cb5f 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -106,6 +106,7 @@ struct nl_pid_hash {
106struct netlink_table { 106struct netlink_table {
107 struct nl_pid_hash hash; 107 struct nl_pid_hash hash;
108 struct hlist_head mc_list; 108 struct hlist_head mc_list;
109 unsigned long *listeners;
109 unsigned int nl_nonroot; 110 unsigned int nl_nonroot;
110 unsigned int groups; 111 unsigned int groups;
111 struct module *module; 112 struct module *module;
@@ -296,6 +297,24 @@ static inline int nl_pid_hash_dilute(struct nl_pid_hash *hash, int len)
296 297
297static const struct proto_ops netlink_ops; 298static const struct proto_ops netlink_ops;
298 299
300static void
301netlink_update_listeners(struct sock *sk)
302{
303 struct netlink_table *tbl = &nl_table[sk->sk_protocol];
304 struct hlist_node *node;
305 unsigned long mask;
306 unsigned int i;
307
308 for (i = 0; i < NLGRPSZ(tbl->groups)/sizeof(unsigned long); i++) {
309 mask = 0;
310 sk_for_each_bound(sk, node, &tbl->mc_list)
311 mask |= nlk_sk(sk)->groups[i];
312 tbl->listeners[i] = mask;
313 }
314 /* this function is only called with the netlink table "grabbed", which
315 * makes sure updates are visible before bind or setsockopt return. */
316}
317
299static int netlink_insert(struct sock *sk, u32 pid) 318static int netlink_insert(struct sock *sk, u32 pid)
300{ 319{
301 struct nl_pid_hash *hash = &nl_table[sk->sk_protocol].hash; 320 struct nl_pid_hash *hash = &nl_table[sk->sk_protocol].hash;
@@ -456,12 +475,14 @@ static int netlink_release(struct socket *sock)
456 if (nlk->module) 475 if (nlk->module)
457 module_put(nlk->module); 476 module_put(nlk->module);
458 477
478 netlink_table_grab();
459 if (nlk->flags & NETLINK_KERNEL_SOCKET) { 479 if (nlk->flags & NETLINK_KERNEL_SOCKET) {
460 netlink_table_grab(); 480 kfree(nl_table[sk->sk_protocol].listeners);
461 nl_table[sk->sk_protocol].module = NULL; 481 nl_table[sk->sk_protocol].module = NULL;
462 nl_table[sk->sk_protocol].registered = 0; 482 nl_table[sk->sk_protocol].registered = 0;
463 netlink_table_ungrab(); 483 } else if (nlk->subscriptions)
464 } 484 netlink_update_listeners(sk);
485 netlink_table_ungrab();
465 486
466 kfree(nlk->groups); 487 kfree(nlk->groups);
467 nlk->groups = NULL; 488 nlk->groups = NULL;
@@ -589,6 +610,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len
589 hweight32(nladdr->nl_groups) - 610 hweight32(nladdr->nl_groups) -
590 hweight32(nlk->groups[0])); 611 hweight32(nlk->groups[0]));
591 nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups; 612 nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups;
613 netlink_update_listeners(sk);
592 netlink_table_ungrab(); 614 netlink_table_ungrab();
593 615
594 return 0; 616 return 0;
@@ -807,6 +829,17 @@ retry:
807 return netlink_sendskb(sk, skb, ssk->sk_protocol); 829 return netlink_sendskb(sk, skb, ssk->sk_protocol);
808} 830}
809 831
832int netlink_has_listeners(struct sock *sk, unsigned int group)
833{
834 int res = 0;
835
836 BUG_ON(!(nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET));
837 if (group - 1 < nl_table[sk->sk_protocol].groups)
838 res = test_bit(group - 1, nl_table[sk->sk_protocol].listeners);
839 return res;
840}
841EXPORT_SYMBOL_GPL(netlink_has_listeners);
842
810static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb) 843static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb)
811{ 844{
812 struct netlink_sock *nlk = nlk_sk(sk); 845 struct netlink_sock *nlk = nlk_sk(sk);
@@ -1011,6 +1044,7 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
1011 else 1044 else
1012 __clear_bit(val - 1, nlk->groups); 1045 __clear_bit(val - 1, nlk->groups);
1013 netlink_update_subscriptions(sk, subscriptions); 1046 netlink_update_subscriptions(sk, subscriptions);
1047 netlink_update_listeners(sk);
1014 netlink_table_ungrab(); 1048 netlink_table_ungrab();
1015 err = 0; 1049 err = 0;
1016 break; 1050 break;
@@ -1237,6 +1271,7 @@ netlink_kernel_create(int unit, unsigned int groups,
1237 struct socket *sock; 1271 struct socket *sock;
1238 struct sock *sk; 1272 struct sock *sk;
1239 struct netlink_sock *nlk; 1273 struct netlink_sock *nlk;
1274 unsigned long *listeners = NULL;
1240 1275
1241 if (!nl_table) 1276 if (!nl_table)
1242 return NULL; 1277 return NULL;
@@ -1250,6 +1285,13 @@ netlink_kernel_create(int unit, unsigned int groups,
1250 if (__netlink_create(sock, unit) < 0) 1285 if (__netlink_create(sock, unit) < 0)
1251 goto out_sock_release; 1286 goto out_sock_release;
1252 1287
1288 if (groups < 32)
1289 groups = 32;
1290
1291 listeners = kzalloc(NLGRPSZ(groups), GFP_KERNEL);
1292 if (!listeners)
1293 goto out_sock_release;
1294
1253 sk = sock->sk; 1295 sk = sock->sk;
1254 sk->sk_data_ready = netlink_data_ready; 1296 sk->sk_data_ready = netlink_data_ready;
1255 if (input) 1297 if (input)
@@ -1262,7 +1304,8 @@ netlink_kernel_create(int unit, unsigned int groups,
1262 nlk->flags |= NETLINK_KERNEL_SOCKET; 1304 nlk->flags |= NETLINK_KERNEL_SOCKET;
1263 1305
1264 netlink_table_grab(); 1306 netlink_table_grab();
1265 nl_table[unit].groups = groups < 32 ? 32 : groups; 1307 nl_table[unit].groups = groups;
1308 nl_table[unit].listeners = listeners;
1266 nl_table[unit].module = module; 1309 nl_table[unit].module = module;
1267 nl_table[unit].registered = 1; 1310 nl_table[unit].registered = 1;
1268 netlink_table_ungrab(); 1311 netlink_table_ungrab();
@@ -1270,6 +1313,7 @@ netlink_kernel_create(int unit, unsigned int groups,
1270 return sk; 1313 return sk;
1271 1314
1272out_sock_release: 1315out_sock_release:
1316 kfree(listeners);
1273 sock_release(sock); 1317 sock_release(sock);
1274 return NULL; 1318 return NULL;
1275} 1319}