diff options
author | Patrick McHardy <kaber@trash.net> | 2006-03-20 21:52:01 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-03-20 21:52:01 -0500 |
commit | 4277a083ecd2c8771058641132bcecea04ca6608 (patch) | |
tree | c35d659c63fa4d64e2569e5eebd76e1b3fba9a15 | |
parent | a24276924875802853b5bdc12c56d29f1c1bbc79 (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.h | 1 | ||||
-rw-r--r-- | net/netlink/af_netlink.c | 52 |
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 | ||
152 | extern struct sock *netlink_kernel_create(int unit, unsigned int groups, void (*input)(struct sock *sk, int len), struct module *module); | 152 | extern struct sock *netlink_kernel_create(int unit, unsigned int groups, void (*input)(struct sock *sk, int len), struct module *module); |
153 | extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); | 153 | extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); |
154 | extern int netlink_has_listeners(struct sock *sk, unsigned int group); | ||
154 | extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock); | 155 | extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock); |
155 | extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid, | 156 | extern 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 { | |||
106 | struct netlink_table { | 106 | struct 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 | ||
297 | static const struct proto_ops netlink_ops; | 298 | static const struct proto_ops netlink_ops; |
298 | 299 | ||
300 | static void | ||
301 | netlink_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 | |||
299 | static int netlink_insert(struct sock *sk, u32 pid) | 318 | static 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 | ||
832 | int 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 | } | ||
841 | EXPORT_SYMBOL_GPL(netlink_has_listeners); | ||
842 | |||
810 | static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb) | 843 | static __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 | ||
1272 | out_sock_release: | 1315 | out_sock_release: |
1316 | kfree(listeners); | ||
1273 | sock_release(sock); | 1317 | sock_release(sock); |
1274 | return NULL; | 1318 | return NULL; |
1275 | } | 1319 | } |