[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>
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 59dc7d1..d00a903 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -106,6 +106,7 @@
 struct netlink_table {
 	struct nl_pid_hash hash;
 	struct hlist_head mc_list;
+	unsigned long *listeners;
 	unsigned int nl_nonroot;
 	unsigned int groups;
 	struct module *module;
@@ -296,6 +297,24 @@
 
 static const struct proto_ops netlink_ops;
 
+static void
+netlink_update_listeners(struct sock *sk)
+{
+	struct netlink_table *tbl = &nl_table[sk->sk_protocol];
+	struct hlist_node *node;
+	unsigned long mask;
+	unsigned int i;
+
+	for (i = 0; i < NLGRPSZ(tbl->groups)/sizeof(unsigned long); i++) {
+		mask = 0;
+		sk_for_each_bound(sk, node, &tbl->mc_list)
+			mask |= nlk_sk(sk)->groups[i];
+		tbl->listeners[i] = mask;
+	}
+	/* this function is only called with the netlink table "grabbed", which
+	 * makes sure updates are visible before bind or setsockopt return. */
+}
+
 static int netlink_insert(struct sock *sk, u32 pid)
 {
 	struct nl_pid_hash *hash = &nl_table[sk->sk_protocol].hash;
@@ -456,12 +475,14 @@
 	if (nlk->module)
 		module_put(nlk->module);
 
+	netlink_table_grab();
 	if (nlk->flags & NETLINK_KERNEL_SOCKET) {
-		netlink_table_grab();
+		kfree(nl_table[sk->sk_protocol].listeners);
 		nl_table[sk->sk_protocol].module = NULL;
 		nl_table[sk->sk_protocol].registered = 0;
-		netlink_table_ungrab();
-	}
+	} else if (nlk->subscriptions)
+		netlink_update_listeners(sk);
+	netlink_table_ungrab();
 
 	kfree(nlk->groups);
 	nlk->groups = NULL;
@@ -589,6 +610,7 @@
 	                                 hweight32(nladdr->nl_groups) -
 	                                 hweight32(nlk->groups[0]));
 	nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups; 
+	netlink_update_listeners(sk);
 	netlink_table_ungrab();
 
 	return 0;
@@ -807,6 +829,17 @@
 	return netlink_sendskb(sk, skb, ssk->sk_protocol);
 }
 
+int netlink_has_listeners(struct sock *sk, unsigned int group)
+{
+	int res = 0;
+
+	BUG_ON(!(nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET));
+	if (group - 1 < nl_table[sk->sk_protocol].groups)
+		res = test_bit(group - 1, nl_table[sk->sk_protocol].listeners);
+	return res;
+}
+EXPORT_SYMBOL_GPL(netlink_has_listeners);
+
 static __inline__ int netlink_broadcast_deliver(struct sock *sk, struct sk_buff *skb)
 {
 	struct netlink_sock *nlk = nlk_sk(sk);
@@ -1011,6 +1044,7 @@
 		else
 			__clear_bit(val - 1, nlk->groups);
 		netlink_update_subscriptions(sk, subscriptions);
+		netlink_update_listeners(sk);
 		netlink_table_ungrab();
 		err = 0;
 		break;
@@ -1237,6 +1271,7 @@
 	struct socket *sock;
 	struct sock *sk;
 	struct netlink_sock *nlk;
+	unsigned long *listeners = NULL;
 
 	if (!nl_table)
 		return NULL;
@@ -1250,6 +1285,13 @@
 	if (__netlink_create(sock, unit) < 0)
 		goto out_sock_release;
 
+	if (groups < 32)
+		groups = 32;
+
+	listeners = kzalloc(NLGRPSZ(groups), GFP_KERNEL);
+	if (!listeners)
+		goto out_sock_release;
+
 	sk = sock->sk;
 	sk->sk_data_ready = netlink_data_ready;
 	if (input)
@@ -1262,7 +1304,8 @@
 	nlk->flags |= NETLINK_KERNEL_SOCKET;
 
 	netlink_table_grab();
-	nl_table[unit].groups = groups < 32 ? 32 : groups;
+	nl_table[unit].groups = groups;
+	nl_table[unit].listeners = listeners;
 	nl_table[unit].module = module;
 	nl_table[unit].registered = 1;
 	netlink_table_ungrab();
@@ -1270,6 +1313,7 @@
 	return sk;
 
 out_sock_release:
+	kfree(listeners);
 	sock_release(sock);
 	return NULL;
 }