* [patch 1/3] netlink: allocate group bitmaps dynamically
2007-07-17 12:27 [patch 0/3] dynamic generic netlink multicast Johannes Berg
@ 2007-07-17 12:27 ` Johannes Berg
2007-07-17 15:45 ` Patrick McHardy
2007-07-17 12:27 ` [patch 2/3] netlink: allow removing multicast groups Johannes Berg
` (5 subsequent siblings)
6 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2007-07-17 12:27 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Patrick McHardy, Zhang Rui, jamal
[-- Attachment #1: 020-netlink-groups.patch --]
[-- Type: text/plain, Size: 5234 bytes --]
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>
---
include/linux/netlink.h | 1
net/netlink/af_netlink.c | 86 +++++++++++++++++++++++++++++++++++------------
2 files changed, 66 insertions(+), 21 deletions(-)
--- wireless-dev.orig/net/netlink/af_netlink.c 2007-07-17 14:05:30.210964463 +0200
+++ wireless-dev/net/netlink/af_netlink.c 2007-07-17 14:05:30.720964463 +0200
@@ -62,6 +62,7 @@
#include <net/netlink.h>
#define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8)
+#define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long))
struct netlink_sock {
/* struct sock has to be the first member of netlink_sock */
@@ -314,10 +315,12 @@ netlink_update_listeners(struct sock *sk
unsigned long mask;
unsigned int i;
- for (i = 0; i < NLGRPSZ(tbl->groups)/sizeof(unsigned long); i++) {
+ for (i = 0; i < NLGRPLONGS(tbl->groups); i++) {
mask = 0;
- sk_for_each_bound(sk, node, &tbl->mc_list)
- mask |= nlk_sk(sk)->groups[i];
+ sk_for_each_bound(sk, node, &tbl->mc_list) {
+ if (i < NLGRPLONGS(nlk_sk(sk)->ngroups))
+ mask |= nlk_sk(sk)->groups[i];
+ }
tbl->listeners[i] = mask;
}
/* this function is only called with the netlink table "grabbed", which
@@ -555,26 +558,37 @@ netlink_update_subscriptions(struct sock
nlk->subscriptions = subscriptions;
}
-static int netlink_alloc_groups(struct sock *sk)
+static int netlink_realloc_groups(struct sock *sk)
{
struct netlink_sock *nlk = nlk_sk(sk);
unsigned int groups;
+ unsigned long *new_groups;
int err = 0;
netlink_lock_table();
groups = nl_table[sk->sk_protocol].groups;
if (!nl_table[sk->sk_protocol].registered)
err = -ENOENT;
- netlink_unlock_table();
if (err)
- return err;
+ goto out_unlock;
- nlk->groups = kzalloc(NLGRPSZ(groups), GFP_KERNEL);
- if (nlk->groups == NULL)
- return -ENOMEM;
+ if (nlk->ngroups >= groups)
+ goto out_unlock;
+
+ new_groups = krealloc(nlk->groups, NLGRPSZ(groups), GFP_KERNEL);
+ if (new_groups == NULL) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+ memset((char*)new_groups + NLGRPSZ(nlk->ngroups), 0,
+ NLGRPSZ(groups) - NLGRPSZ(nlk->ngroups));
+
+ nlk->groups = new_groups;
nlk->ngroups = groups;
- return 0;
+ out_unlock:
+ netlink_unlock_table();
+ return err;
}
static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
@@ -591,11 +605,9 @@ static int netlink_bind(struct socket *s
if (nladdr->nl_groups) {
if (!netlink_capable(sock, NL_NONROOT_RECV))
return -EPERM;
- if (nlk->groups == NULL) {
- err = netlink_alloc_groups(sk);
- if (err)
- return err;
- }
+ err = netlink_realloc_groups(sk);
+ if (err)
+ return err;
}
if (nlk->pid) {
@@ -839,10 +851,18 @@ retry:
int netlink_has_listeners(struct sock *sk, unsigned int group)
{
int res = 0;
+ unsigned long *listeners;
BUG_ON(!(nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET));
+
+ rcu_read_lock();
+ listeners = rcu_dereference(nl_table[sk->sk_protocol].listeners);
+
if (group - 1 < nl_table[sk->sk_protocol].groups)
- res = test_bit(group - 1, nl_table[sk->sk_protocol].listeners);
+ res = test_bit(group - 1, listeners);
+
+ rcu_read_unlock();
+
return res;
}
EXPORT_SYMBOL_GPL(netlink_has_listeners);
@@ -1037,11 +1057,9 @@ static int netlink_setsockopt(struct soc
if (!netlink_capable(sock, NL_NONROOT_RECV))
return -EPERM;
- if (nlk->groups == NULL) {
- err = netlink_alloc_groups(sk);
- if (err)
- return err;
- }
+ err = netlink_realloc_groups(sk);
+ if (err)
+ return err;
if (!val || val - 1 >= nlk->ngroups)
return -EINVAL;
netlink_table_grab();
@@ -1328,6 +1346,32 @@ out_sock_release:
return NULL;
}
+int netlink_change_ngroups(int unit, unsigned int groups)
+{
+ unsigned long *listeners, old = NULL;
+ int err = 0;
+
+ netlink_table_grab();
+ if (NLGRPSZ(nl_table[unit].groups) < NLGRPSZ(groups)) {
+ listeners = kzalloc(NLGRPSZ(groups), GFP_ATOMIC);
+ if (!listeners) {
+ err = -ENOMEM;
+ goto out_ungrab;
+ }
+ old = nl_table[unit].listeners;
+ memcpy(listeners, old, NLGRPSZ(nl_table[unit].groups));
+ rcu_assign_pointer(nl_table[unit].listeners, listeners);
+ }
+ nl_table[unit].groups = groups;
+
+ out_ungrab:
+ netlink_table_ungrab();
+ synchronize_rcu();
+ kfree(old);
+ return err;
+}
+EXPORT_SYMBOL(netlink_change_ngroups);
+
void netlink_set_nonroot(int protocol, unsigned int flags)
{
if ((unsigned int)protocol < MAX_LINKS)
--- wireless-dev.orig/include/linux/netlink.h 2007-07-17 14:05:14.400964463 +0200
+++ wireless-dev/include/linux/netlink.h 2007-07-17 14:05:30.720964463 +0200
@@ -161,6 +161,7 @@ extern struct sock *netlink_kernel_creat
void (*input)(struct sock *sk, int len),
struct mutex *cb_mutex,
struct module *module);
+extern int netlink_change_ngroups(int unit, unsigned int groups);
extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);
extern int netlink_has_listeners(struct sock *sk, unsigned int group);
extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock);
--
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch 1/3] netlink: allocate group bitmaps dynamically
2007-07-17 12:27 ` [patch 1/3] netlink: allocate group bitmaps dynamically Johannes Berg
@ 2007-07-17 15:45 ` Patrick McHardy
2007-07-18 13:34 ` Johannes Berg
0 siblings, 1 reply; 16+ messages in thread
From: Patrick McHardy @ 2007-07-17 15:45 UTC (permalink / raw)
To: Johannes Berg; +Cc: David Miller, netdev, Zhang Rui, jamal
Johannes Berg wrote:
> 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>
>
> ---
> include/linux/netlink.h | 1
> net/netlink/af_netlink.c | 86 +++++++++++++++++++++++++++++++++++------------
> 2 files changed, 66 insertions(+), 21 deletions(-)
>
> --- wireless-dev.orig/net/netlink/af_netlink.c 2007-07-17 14:05:30.210964463 +0200
> +++ wireless-dev/net/netlink/af_netlink.c 2007-07-17 14:05:30.720964463 +0200
> -static int netlink_alloc_groups(struct sock *sk)
> +static int netlink_realloc_groups(struct sock *sk)
> {
> struct netlink_sock *nlk = nlk_sk(sk);
> unsigned int groups;
> + unsigned long *new_groups;
> int err = 0;
>
> netlink_lock_table();
This is actually a bug in the current code I think, netlink_lock_table
is a reader lock.
> groups = nl_table[sk->sk_protocol].groups;
> if (!nl_table[sk->sk_protocol].registered)
> err = -ENOENT;
> - netlink_unlock_table();
>
> if (err)
> - return err;
> + goto out_unlock;
>
> - nlk->groups = kzalloc(NLGRPSZ(groups), GFP_KERNEL);
> - if (nlk->groups == NULL)
> - return -ENOMEM;
> + if (nlk->ngroups >= groups)
> + goto out_unlock;
> +
> + new_groups = krealloc(nlk->groups, NLGRPSZ(groups), GFP_KERNEL);
> + if (new_groups == NULL) {
> + err = -ENOMEM;
> + goto out_unlock;
> + }
> + memset((char*)new_groups + NLGRPSZ(nlk->ngroups), 0,
> + NLGRPSZ(groups) - NLGRPSZ(nlk->ngroups));
> +
> + nlk->groups = new_groups;
> nlk->ngroups = groups;
> - return 0;
> + out_unlock:
> + netlink_unlock_table();
> + return err;
> }
> +int netlink_change_ngroups(int unit, unsigned int groups)
I think it would be more consistent to pass the kernel socket
instead of the unit.
> +{
> + unsigned long *listeners, old = NULL;
> + int err = 0;
> +
> + netlink_table_grab();
> + if (NLGRPSZ(nl_table[unit].groups) < NLGRPSZ(groups)) {
> + listeners = kzalloc(NLGRPSZ(groups), GFP_ATOMIC);
> + if (!listeners) {
> + err = -ENOMEM;
> + goto out_ungrab;
> + }
> + old = nl_table[unit].listeners;
> + memcpy(listeners, old, NLGRPSZ(nl_table[unit].groups));
> + rcu_assign_pointer(nl_table[unit].listeners, listeners);
> + }
> + nl_table[unit].groups = groups;
This might set the group to a value < 32. I don't expect it matters,
but when I changed to old code to support > 32 groups I enforced
a minimum of 32 so anything outside the kernel multicasting on them
would still work (even though its a really stupid idea). So for
consistency this should probably also use a minimum of 32.
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch 1/3] netlink: allocate group bitmaps dynamically
2007-07-17 15:45 ` Patrick McHardy
@ 2007-07-18 13:34 ` Johannes Berg
0 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2007-07-18 13:34 UTC (permalink / raw)
To: Patrick McHardy; +Cc: David Miller, netdev, Zhang Rui, jamal
[-- Attachment #1: Type: text/plain, Size: 1292 bytes --]
On Tue, 2007-07-17 at 17:45 +0200, Patrick McHardy wrote:
> > --- wireless-dev.orig/net/netlink/af_netlink.c 2007-07-17 14:05:30.210964463 +0200
> > +++ wireless-dev/net/netlink/af_netlink.c 2007-07-17 14:05:30.720964463 +0200
> > -static int netlink_alloc_groups(struct sock *sk)
> > +static int netlink_realloc_groups(struct sock *sk)
> > {
> > struct netlink_sock *nlk = nlk_sk(sk);
> > unsigned int groups;
> > + unsigned long *new_groups;
> > int err = 0;
> >
> > netlink_lock_table();
>
>
> This is actually a bug in the current code I think, netlink_lock_table
> is a reader lock.
Looks like, changed to netlink_table_grab()
> > +int netlink_change_ngroups(int unit, unsigned int groups)
>
>
> I think it would be more consistent to pass the kernel socket
> instead of the unit.
Alright, changed. Also added kernel doc for the function.
> This might set the group to a value < 32. I don't expect it matters,
> but when I changed to old code to support > 32 groups I enforced
> a minimum of 32 so anything outside the kernel multicasting on them
> would still work (even though its a really stupid idea). So for
> consistency this should probably also use a minimum of 32.
Huh ok. Fixed, and added to documentation.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 2/3] netlink: allow removing multicast groups
2007-07-17 12:27 [patch 0/3] dynamic generic netlink multicast Johannes Berg
2007-07-17 12:27 ` [patch 1/3] netlink: allocate group bitmaps dynamically Johannes Berg
@ 2007-07-17 12:27 ` Johannes Berg
2007-07-17 15:50 ` Patrick McHardy
2007-07-17 12:27 ` [patch 3/3] generic netlink: dynamic " Johannes Berg
` (4 subsequent siblings)
6 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2007-07-17 12:27 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Patrick McHardy, Zhang Rui, jamal
[-- Attachment #1: 021-netlink-mc-groups.patch --]
[-- Type: text/plain, Size: 3370 bytes --]
Allow kicking listeners out of a multicast group when necessary
(for example if that group is going to be removed.)
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
include/linux/netlink.h | 1 +
net/netlink/af_netlink.c | 47 ++++++++++++++++++++++++++++++++++-------------
2 files changed, 35 insertions(+), 13 deletions(-)
--- wireless-dev.orig/include/linux/netlink.h 2007-07-17 14:05:30.720964463 +0200
+++ wireless-dev/include/linux/netlink.h 2007-07-17 14:05:31.250964463 +0200
@@ -162,6 +162,7 @@ extern struct sock *netlink_kernel_creat
struct mutex *cb_mutex,
struct module *module);
extern int netlink_change_ngroups(int unit, unsigned int groups);
+extern void netlink_clear_multicast_users(int unit, unsigned int group);
extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);
extern int netlink_has_listeners(struct sock *sk, unsigned int group);
extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock);
--- wireless-dev.orig/net/netlink/af_netlink.c 2007-07-17 14:05:30.720964463 +0200
+++ wireless-dev/net/netlink/af_netlink.c 2007-07-17 14:05:31.250964463 +0200
@@ -1027,6 +1027,24 @@ void netlink_set_err(struct sock *ssk, u
read_unlock(&nl_table_lock);
}
+static void netlink_update_socket_mc(struct netlink_sock *nlk,
+ unsigned int group,
+ int is_new)
+{
+ int old, new = !!is_new, subscriptions;
+
+ netlink_table_grab();
+ old = test_bit(group - 1, nlk->groups);
+ subscriptions = nlk->subscriptions - old + new;
+ if (new)
+ __set_bit(group - 1, nlk->groups);
+ else
+ __clear_bit(group - 1, nlk->groups);
+ netlink_update_subscriptions(&nlk->sk, subscriptions);
+ netlink_update_listeners(&nlk->sk);
+ netlink_table_ungrab();
+}
+
static int netlink_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, int optlen)
{
@@ -1052,9 +1070,6 @@ static int netlink_setsockopt(struct soc
break;
case NETLINK_ADD_MEMBERSHIP:
case NETLINK_DROP_MEMBERSHIP: {
- unsigned int subscriptions;
- int old, new = optname == NETLINK_ADD_MEMBERSHIP ? 1 : 0;
-
if (!netlink_capable(sock, NL_NONROOT_RECV))
return -EPERM;
err = netlink_realloc_groups(sk);
@@ -1062,16 +1077,8 @@ static int netlink_setsockopt(struct soc
return err;
if (!val || val - 1 >= nlk->ngroups)
return -EINVAL;
- netlink_table_grab();
- old = test_bit(val - 1, nlk->groups);
- subscriptions = nlk->subscriptions - old + new;
- if (new)
- __set_bit(val - 1, nlk->groups);
- else
- __clear_bit(val - 1, nlk->groups);
- netlink_update_subscriptions(sk, subscriptions);
- netlink_update_listeners(sk);
- netlink_table_ungrab();
+ netlink_update_socket_mc(nlk, val,
+ optname == NETLINK_ADD_MEMBERSHIP);
err = 0;
break;
}
@@ -1372,6 +1379,20 @@ int netlink_change_ngroups(int unit, uns
}
EXPORT_SYMBOL(netlink_change_ngroups);
+void netlink_clear_multicast_users(int unit, unsigned int group)
+{
+ struct sock *sk;
+ struct hlist_node *node;
+
+ read_lock(&nl_table_lock);
+
+ sk_for_each_bound(sk, node, &nl_table[unit].mc_list)
+ netlink_update_socket_mc(nlk_sk(sk), group, 0);
+
+ read_unlock(&nl_table_lock);
+}
+EXPORT_SYMBOL(netlink_clear_multicast_users);
+
void netlink_set_nonroot(int protocol, unsigned int flags)
{
if ((unsigned int)protocol < MAX_LINKS)
--
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch 2/3] netlink: allow removing multicast groups
2007-07-17 12:27 ` [patch 2/3] netlink: allow removing multicast groups Johannes Berg
@ 2007-07-17 15:50 ` Patrick McHardy
2007-07-18 13:35 ` Johannes Berg
0 siblings, 1 reply; 16+ messages in thread
From: Patrick McHardy @ 2007-07-17 15:50 UTC (permalink / raw)
To: Johannes Berg; +Cc: David Miller, netdev, Zhang Rui, jamal
Johannes Berg wrote:
> +static void netlink_update_socket_mc(struct netlink_sock *nlk,
> + unsigned int group,
> + int is_new)
> +{
> + int old, new = !!is_new, subscriptions;
> +
> + netlink_table_grab();
Having the caller lock the table would save lots of atomic operation
in case of netlink_clear_multicast_users.
> + old = test_bit(group - 1, nlk->groups);
> + subscriptions = nlk->subscriptions - old + new;
> + if (new)
> + __set_bit(group - 1, nlk->groups);
> + else
> + __clear_bit(group - 1, nlk->groups);
> + netlink_update_subscriptions(&nlk->sk, subscriptions);
> + netlink_update_listeners(&nlk->sk);
> + netlink_table_ungrab();
> +}
> +
> +void netlink_clear_multicast_users(int unit, unsigned int group)
Same as in the last patch, passing the kernel socket would be nicer IMO.
> +{
> + struct sock *sk;
> + struct hlist_node *node;
> +
> + read_lock(&nl_table_lock);
Won't this deadlock? netlink_table_grab takes a write-lock.
> +
> + sk_for_each_bound(sk, node, &nl_table[unit].mc_list)
> + netlink_update_socket_mc(nlk_sk(sk), group, 0);
> +
> + read_unlock(&nl_table_lock);
> +}
> +EXPORT_SYMBOL(netlink_clear_multicast_users);
> +
> void netlink_set_nonroot(int protocol, unsigned int flags)
> {
> if ((unsigned int)protocol < MAX_LINKS)
>
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch 2/3] netlink: allow removing multicast groups
2007-07-17 15:50 ` Patrick McHardy
@ 2007-07-18 13:35 ` Johannes Berg
0 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2007-07-18 13:35 UTC (permalink / raw)
To: Patrick McHardy; +Cc: David Miller, netdev, Zhang Rui, jamal
[-- Attachment #1: Type: text/plain, Size: 675 bytes --]
On Tue, 2007-07-17 at 17:50 +0200, Patrick McHardy wrote:
> Having the caller lock the table would save lots of atomic operation
> in case of netlink_clear_multicast_users.
Good point.
> > +void netlink_clear_multicast_users(int unit, unsigned int group)
>
> Same as in the last patch, passing the kernel socket would be nicer IMO.
Changed.
> > + read_lock(&nl_table_lock);
>
> Won't this deadlock? netlink_table_grab takes a write-lock.
I guess it's valid to update a read lock to a write lock? Or I was just
lucky on UP. But moving the lock out of netlink_clear_multicast_users()
made this obvious and I just use the write lock now.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread
* [patch 3/3] generic netlink: dynamic multicast groups
2007-07-17 12:27 [patch 0/3] dynamic generic netlink multicast Johannes Berg
2007-07-17 12:27 ` [patch 1/3] netlink: allocate group bitmaps dynamically Johannes Berg
2007-07-17 12:27 ` [patch 2/3] netlink: allow removing multicast groups Johannes Berg
@ 2007-07-17 12:27 ` Johannes Berg
2007-07-18 13:37 ` [PATCH v2 2/3] netlink: allow removing " Johannes Berg
` (3 subsequent siblings)
6 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2007-07-17 12:27 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Patrick McHardy, Zhang Rui, jamal
[-- Attachment #1: 022-genl-multicast-groups.patch --]
[-- Type: text/plain, Size: 10370 bytes --]
Introduce API to dynamically register and unregister multicast groups.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
include/linux/genetlink.h | 13 +++
include/net/genetlink.h | 22 +++++
net/netlink/genetlink.c | 190 ++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 218 insertions(+), 7 deletions(-)
--- wireless-dev.orig/include/net/genetlink.h 2007-07-17 14:05:13.760964463 +0200
+++ wireless-dev/include/net/genetlink.h 2007-07-17 14:05:31.780964463 +0200
@@ -5,6 +5,22 @@
#include <net/netlink.h>
/**
+ * struct genl_multicast_group - generic netlink multicast group
+ * @name: name of the multicast group, names are per-family
+ * @id: multicast group ID, assigned by the core, to use with
+ * genlmsg_multicast().
+ * @list: list entry for linking
+ * @family: pointer to family, need not be set before registering
+ */
+struct genl_multicast_group
+{
+ struct genl_family *family; /* private */
+ struct list_head list; /* private */
+ char name[GENL_NAMSIZ];
+ u32 id;
+};
+
+/**
* struct genl_family - generic netlink family
* @id: protocol family idenfitier
* @hdrsize: length of user specific header in bytes
@@ -14,6 +30,7 @@
* @attrbuf: buffer to store parsed attributes
* @ops_list: list of all assigned operations
* @family_list: family list
+ * @mcast_groups: multicast groups list
*/
struct genl_family
{
@@ -25,6 +42,7 @@ struct genl_family
struct nlattr ** attrbuf; /* private */
struct list_head ops_list; /* private */
struct list_head family_list; /* private */
+ struct list_head mcast_groups; /* private */
};
/**
@@ -73,6 +91,10 @@ extern int genl_register_family(struct g
extern int genl_unregister_family(struct genl_family *family);
extern int genl_register_ops(struct genl_family *, struct genl_ops *ops);
extern int genl_unregister_ops(struct genl_family *, struct genl_ops *ops);
+extern int genl_register_mc_group(struct genl_family *family,
+ struct genl_multicast_group *grp);
+extern void genl_unregister_mc_group(struct genl_family *family,
+ struct genl_multicast_group *grp);
extern struct sock *genl_sock;
--- wireless-dev.orig/net/netlink/genetlink.c 2007-07-17 14:05:13.860964463 +0200
+++ wireless-dev/net/netlink/genetlink.c 2007-07-17 14:05:31.780964463 +0200
@@ -3,6 +3,7 @@
*
* Authors: Jamal Hadi Salim
* Thomas Graf <tgraf@suug.ch>
+ * Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/module.h>
@@ -13,6 +14,7 @@
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/mutex.h>
+#include <linux/bitmap.h>
#include <net/sock.h>
#include <net/genetlink.h>
@@ -42,6 +44,17 @@ static void genl_unlock(void)
#define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1)
static struct list_head family_ht[GENL_FAM_TAB_SIZE];
+/*
+ * Bitmap of multicast groups that are currently in use.
+ *
+ * To avoid an allocation at boot of just one unsigned long,
+ * declare it global instead.
+ * Bit 0 is marked as already used since group 0 is invalid,
+ * bit 1 is marked as already used since group 1 is the controller group.
+ */
+static unsigned long mc_group_start = 0x3;
+static unsigned long *mc_groups = &mc_group_start;
+static unsigned long mc_groups_longs = 1;
static int genl_ctrl_event(int event, void *data);
@@ -116,6 +129,77 @@ static inline u16 genl_generate_id(void)
return id_gen_idx;
}
+int genl_register_mc_group(struct genl_family *family,
+ struct genl_multicast_group *grp)
+{
+ int id = find_first_zero_bit(mc_groups,
+ mc_groups_longs * BITS_PER_LONG);
+ unsigned long *new_groups;
+ size_t nlen = (mc_groups_longs + 1) * sizeof(unsigned long);
+ int err;
+
+ genl_lock();
+
+ if (id >= mc_groups_longs * BITS_PER_LONG) {
+ if (mc_groups == &mc_group_start) {
+ new_groups = kzalloc(nlen, GFP_KERNEL);
+ if (!mc_groups) {
+ err = -ENOMEM;
+ goto out;
+ }
+ mc_groups = new_groups;
+ *mc_groups = mc_group_start;
+ } else {
+ new_groups = krealloc(mc_groups, nlen, GFP_KERNEL);
+ if (!new_groups) {
+ err = -ENOMEM;
+ goto out;
+ }
+ mc_groups = new_groups;
+ mc_groups[mc_groups_longs] = 0;
+ }
+ mc_groups_longs++;
+ }
+
+ err = netlink_change_ngroups(NETLINK_GENERIC,
+ sizeof(unsigned long) * NETLINK_GENERIC);
+ if (err)
+ goto out;
+
+ grp->id = id;
+ set_bit(id, mc_groups);
+ list_add_tail(&grp->list, &family->mcast_groups);
+ grp->family = family;
+
+ genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, grp);
+ out:
+ genl_unlock();
+ return 0;
+}
+EXPORT_SYMBOL(genl_register_mc_group);
+
+void genl_unregister_mc_group(struct genl_family *family,
+ struct genl_multicast_group *grp)
+{
+ BUG_ON(grp->family != family);
+ genl_lock();
+ netlink_clear_multicast_users(NETLINK_GENERIC, grp->id);
+ clear_bit(grp->id, mc_groups);
+ list_del(&grp->list);
+ genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, grp);
+ grp->id = 0;
+ grp->family = NULL;
+ genl_unlock();
+}
+
+static void genl_unregister_mc_groups(struct genl_family *family)
+{
+ struct genl_multicast_group *grp, *tmp;
+
+ list_for_each_entry_safe(grp, tmp, &family->mcast_groups, list)
+ genl_unregister_mc_group(family, grp);
+}
+
/**
* genl_register_ops - register generic netlink operations
* @family: generic netlink family
@@ -216,6 +300,7 @@ int genl_register_family(struct genl_fam
goto errout;
INIT_LIST_HEAD(&family->ops_list);
+ INIT_LIST_HEAD(&family->mcast_groups);
genl_lock();
@@ -275,6 +360,8 @@ int genl_unregister_family(struct genl_f
{
struct genl_family *rc;
+ genl_unregister_mc_groups(family);
+
genl_lock();
list_for_each_entry(rc, genl_family_chain(family->id), family_list) {
@@ -410,6 +497,67 @@ static int ctrl_fill_info(struct genl_fa
nla_nest_end(skb, nla_ops);
}
+ if (!list_empty(&family->mcast_groups)) {
+ struct genl_multicast_group *grp;
+ struct nlattr *nla_grps;
+ int idx = 1;
+
+ nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS);
+ if (nla_grps == NULL)
+ goto nla_put_failure;
+
+ list_for_each_entry(grp, &family->mcast_groups, list) {
+ struct nlattr *nest;
+
+ nest = nla_nest_start(skb, idx++);
+ if (nest == NULL)
+ goto nla_put_failure;
+
+ NLA_PUT_U32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id);
+ NLA_PUT_STRING(skb, CTRL_ATTR_MCAST_GRP_NAME,
+ grp->name);
+
+ nla_nest_end(skb, nest);
+ }
+ nla_nest_end(skb, nla_grps);
+ }
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ return genlmsg_cancel(skb, hdr);
+}
+
+static int ctrl_fill_mcgrp_info(struct genl_multicast_group *grp, u32 pid,
+ u32 seq, u32 flags, struct sk_buff *skb,
+ u8 cmd)
+{
+ void *hdr;
+ struct nlattr *nla_grps;
+ struct nlattr *nest;
+
+ hdr = genlmsg_put(skb, pid, seq, &genl_ctrl, flags, cmd);
+ if (hdr == NULL)
+ return -1;
+
+ NLA_PUT_STRING(skb, CTRL_ATTR_FAMILY_NAME, grp->family->name);
+ NLA_PUT_U16(skb, CTRL_ATTR_FAMILY_ID, grp->family->id);
+
+ nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS);
+ if (nla_grps == NULL)
+ goto nla_put_failure;
+
+ nest = nla_nest_start(skb, 1);
+ if (nest == NULL)
+ goto nla_put_failure;
+
+ NLA_PUT_U32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id);
+ NLA_PUT_STRING(skb, CTRL_ATTR_MCAST_GRP_NAME,
+ grp->name);
+
+ nla_nest_end(skb, nest);
+ nla_nest_end(skb, nla_grps);
+
return genlmsg_end(skb, hdr);
nla_put_failure:
@@ -453,8 +601,8 @@ errout:
return skb->len;
}
-static struct sk_buff *ctrl_build_msg(struct genl_family *family, u32 pid,
- int seq, u8 cmd)
+static struct sk_buff *ctrl_build_family_msg(struct genl_family *family,
+ u32 pid, int seq, u8 cmd)
{
struct sk_buff *skb;
int err;
@@ -472,6 +620,25 @@ static struct sk_buff *ctrl_build_msg(st
return skb;
}
+static struct sk_buff *ctrl_build_mcgrp_msg(struct genl_multicast_group *grp,
+ u32 pid, int seq, u8 cmd)
+{
+ struct sk_buff *skb;
+ int err;
+
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (skb == NULL)
+ return ERR_PTR(-ENOBUFS);
+
+ err = ctrl_fill_mcgrp_info(grp, pid, seq, 0, skb, cmd);
+ if (err < 0) {
+ nlmsg_free(skb);
+ return ERR_PTR(err);
+ }
+
+ return skb;
+}
+
static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
[CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 },
[CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING,
@@ -501,8 +668,8 @@ static int ctrl_getfamily(struct sk_buff
goto errout;
}
- msg = ctrl_build_msg(res, info->snd_pid, info->snd_seq,
- CTRL_CMD_NEWFAMILY);
+ msg = ctrl_build_family_msg(res, info->snd_pid, info->snd_seq,
+ CTRL_CMD_NEWFAMILY);
if (IS_ERR(msg)) {
err = PTR_ERR(msg);
goto errout;
@@ -523,7 +690,15 @@ static int genl_ctrl_event(int event, vo
switch (event) {
case CTRL_CMD_NEWFAMILY:
case CTRL_CMD_DELFAMILY:
- msg = ctrl_build_msg(data, 0, 0, event);
+ msg = ctrl_build_family_msg(data, 0, 0, event);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL);
+ break;
+ case CTRL_CMD_NEWMCAST_GRP:
+ case CTRL_CMD_DELMCAST_GRP:
+ msg = ctrl_build_mcgrp_msg(data, 0, 0, event);
if (IS_ERR(msg))
return PTR_ERR(msg);
@@ -557,8 +732,9 @@ static int __init genl_init(void)
goto errout_register;
netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV);
- genl_sock = netlink_kernel_create(NETLINK_GENERIC, GENL_MAX_ID,
- genl_rcv, NULL, THIS_MODULE);
+ /* only one static multicast group */
+ genl_sock = netlink_kernel_create(NETLINK_GENERIC, 1, genl_rcv,
+ NULL, THIS_MODULE);
if (genl_sock == NULL)
panic("GENL: Cannot initialize generic netlink\n");
--- wireless-dev.orig/include/linux/genetlink.h 2007-07-17 14:05:13.800964463 +0200
+++ wireless-dev/include/linux/genetlink.h 2007-07-17 14:05:31.780964463 +0200
@@ -39,6 +39,9 @@ enum {
CTRL_CMD_NEWOPS,
CTRL_CMD_DELOPS,
CTRL_CMD_GETOPS,
+ CTRL_CMD_NEWMCAST_GRP,
+ CTRL_CMD_DELMCAST_GRP,
+ CTRL_CMD_GETMCAST_GRP, /* unused */
__CTRL_CMD_MAX,
};
@@ -52,6 +55,7 @@ enum {
CTRL_ATTR_HDRSIZE,
CTRL_ATTR_MAXATTR,
CTRL_ATTR_OPS,
+ CTRL_ATTR_MCAST_GROUPS,
__CTRL_ATTR_MAX,
};
@@ -66,4 +70,13 @@ enum {
#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1)
+enum {
+ CTRL_ATTR_MCAST_GRP_UNSPEC,
+ CTRL_ATTR_MCAST_GRP_NAME,
+ CTRL_ATTR_MCAST_GRP_ID,
+ __CTRL_ATTR_MCAST_GRP_MAX,
+};
+
+#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)
+
#endif /* __LINUX_GENERIC_NETLINK_H */
--
^ permalink raw reply [flat|nested] 16+ messages in thread* [PATCH v2 2/3] netlink: allow removing multicast groups
2007-07-17 12:27 [patch 0/3] dynamic generic netlink multicast Johannes Berg
` (2 preceding siblings ...)
2007-07-17 12:27 ` [patch 3/3] generic netlink: dynamic " Johannes Berg
@ 2007-07-18 13:37 ` Johannes Berg
2007-07-18 22:30 ` David Miller
2007-07-18 14:34 ` [PATCH v2 1/3] netlink: allocate group bitmaps dynamically Johannes Berg
` (2 subsequent siblings)
6 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2007-07-18 13:37 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Patrick McHardy, Zhang Rui, jamal
Allow kicking listeners out of a multicast group when necessary
(for example if that group is going to be removed.)
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
Changes: incorporate Patrick's comments, add kernel-doc.
include/linux/netlink.h | 1
net/netlink/af_netlink.c | 57 +++++++++++++++++++++++++++++++++++++----------
2 files changed, 46 insertions(+), 12 deletions(-)
--- wireless-dev.orig/include/linux/netlink.h 2007-07-18 15:01:06.042900849 +0200
+++ wireless-dev/include/linux/netlink.h 2007-07-18 15:01:22.542900849 +0200
@@ -162,6 +162,7 @@ extern struct sock *netlink_kernel_creat
struct mutex *cb_mutex,
struct module *module);
extern int netlink_change_ngroups(struct sock *sk, unsigned int groups);
+extern void netlink_clear_multicast_users(struct sock *sk, unsigned int group);
extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);
extern int netlink_has_listeners(struct sock *sk, unsigned int group);
extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock);
--- wireless-dev.orig/net/netlink/af_netlink.c 2007-07-18 15:01:10.482900849 +0200
+++ wireless-dev/net/netlink/af_netlink.c 2007-07-18 15:02:09.642900849 +0200
@@ -1027,6 +1027,23 @@ void netlink_set_err(struct sock *ssk, u
read_unlock(&nl_table_lock);
}
+/* must be called with netlink table grabbed */
+static void netlink_update_socket_mc(struct netlink_sock *nlk,
+ unsigned int group,
+ int is_new)
+{
+ int old, new = !!is_new, subscriptions;
+
+ old = test_bit(group - 1, nlk->groups);
+ subscriptions = nlk->subscriptions - old + new;
+ if (new)
+ __set_bit(group - 1, nlk->groups);
+ else
+ __clear_bit(group - 1, nlk->groups);
+ netlink_update_subscriptions(&nlk->sk, subscriptions);
+ netlink_update_listeners(&nlk->sk);
+}
+
static int netlink_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, int optlen)
{
@@ -1052,9 +1069,6 @@ static int netlink_setsockopt(struct soc
break;
case NETLINK_ADD_MEMBERSHIP:
case NETLINK_DROP_MEMBERSHIP: {
- unsigned int subscriptions;
- int old, new = optname == NETLINK_ADD_MEMBERSHIP ? 1 : 0;
-
if (!netlink_capable(sock, NL_NONROOT_RECV))
return -EPERM;
err = netlink_realloc_groups(sk);
@@ -1063,14 +1077,8 @@ static int netlink_setsockopt(struct soc
if (!val || val - 1 >= nlk->ngroups)
return -EINVAL;
netlink_table_grab();
- old = test_bit(val - 1, nlk->groups);
- subscriptions = nlk->subscriptions - old + new;
- if (new)
- __set_bit(val - 1, nlk->groups);
- else
- __clear_bit(val - 1, nlk->groups);
- netlink_update_subscriptions(sk, subscriptions);
- netlink_update_listeners(sk);
+ netlink_update_socket_mc(nlk, val,
+ optname == NETLINK_ADD_MEMBERSHIP);
netlink_table_ungrab();
err = 0;
break;
@@ -1351,7 +1359,9 @@ out_sock_release:
*
* This changes the number of multicast groups that are available
* on a certain netlink family. Note that it is not possible to
- * change the number of groups to below 32.
+ * change the number of groups to below 32. Also note that it does
+ * not implicitly call netlink_clear_multicast_users() when the
+ * number of groups is reduced.
*
* @sk: The kernel netlink socket, as returned by netlink_kernel_create().
* @groups: The new number of groups.
@@ -1386,6 +1396,29 @@ int netlink_change_ngroups(struct sock *
}
EXPORT_SYMBOL(netlink_change_ngroups);
+/**
+ * netlink_clear_multicast_users - kick off multicast listeners
+ *
+ * This function removes all listeners from the given group.
+ * @ksk: The kernel netlink socket, as returned by
+ * netlink_kernel_create().
+ * @group: The multicast group to clear.
+ */
+void netlink_clear_multicast_users(struct sock *ksk, unsigned int group)
+{
+ struct sock *sk;
+ struct hlist_node *node;
+ struct netlink_table *tbl = &nl_table[ksk->sk_protocol];
+
+ netlink_table_grab();
+
+ sk_for_each_bound(sk, node, &tbl->mc_list)
+ netlink_update_socket_mc(nlk_sk(sk), group, 0);
+
+ netlink_table_ungrab();
+}
+EXPORT_SYMBOL(netlink_clear_multicast_users);
+
void netlink_set_nonroot(int protocol, unsigned int flags)
{
if ((unsigned int)protocol < MAX_LINKS)
^ permalink raw reply [flat|nested] 16+ messages in thread* [PATCH v2 1/3] netlink: allocate group bitmaps dynamically
2007-07-17 12:27 [patch 0/3] dynamic generic netlink multicast Johannes Berg
` (3 preceding siblings ...)
2007-07-18 13:37 ` [PATCH v2 2/3] netlink: allow removing " Johannes Berg
@ 2007-07-18 14:34 ` Johannes Berg
2007-07-18 16:30 ` Patrick McHardy
2007-07-18 15:05 ` [PATCH v2 3/3] generic netlink: dynamic multicast groups Johannes Berg
2007-07-19 10:39 ` [patch 0/3] dynamic generic netlink multicast Johannes Berg
6 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2007-07-18 14:34 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Patrick McHardy, Zhang Rui, jamal
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>
---
Changes: incorporate Patrick's comments, add kernel-doc
include/linux/netlink.h | 1
net/netlink/af_netlink.c | 106 ++++++++++++++++++++++++++++++++++++-----------
2 files changed, 83 insertions(+), 24 deletions(-)
--- wireless-dev.orig/net/netlink/af_netlink.c 2007-07-18 15:09:02.952900849 +0200
+++ wireless-dev/net/netlink/af_netlink.c 2007-07-18 16:33:33.691236687 +0200
@@ -62,6 +62,7 @@
#include <net/netlink.h>
#define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8)
+#define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long))
struct netlink_sock {
/* struct sock has to be the first member of netlink_sock */
@@ -314,10 +315,12 @@ netlink_update_listeners(struct sock *sk
unsigned long mask;
unsigned int i;
- for (i = 0; i < NLGRPSZ(tbl->groups)/sizeof(unsigned long); i++) {
+ for (i = 0; i < NLGRPLONGS(tbl->groups); i++) {
mask = 0;
- sk_for_each_bound(sk, node, &tbl->mc_list)
- mask |= nlk_sk(sk)->groups[i];
+ sk_for_each_bound(sk, node, &tbl->mc_list) {
+ if (i < NLGRPLONGS(nlk_sk(sk)->ngroups))
+ mask |= nlk_sk(sk)->groups[i];
+ }
tbl->listeners[i] = mask;
}
/* this function is only called with the netlink table "grabbed", which
@@ -555,26 +558,37 @@ netlink_update_subscriptions(struct sock
nlk->subscriptions = subscriptions;
}
-static int netlink_alloc_groups(struct sock *sk)
+static int netlink_realloc_groups(struct sock *sk)
{
struct netlink_sock *nlk = nlk_sk(sk);
unsigned int groups;
+ unsigned long *new_groups;
int err = 0;
- netlink_lock_table();
+ netlink_table_grab();
+
groups = nl_table[sk->sk_protocol].groups;
- if (!nl_table[sk->sk_protocol].registered)
+ if (!nl_table[sk->sk_protocol].registered) {
err = -ENOENT;
- netlink_unlock_table();
+ goto out_unlock;
+ }
- if (err)
- return err;
+ if (nlk->ngroups >= groups)
+ goto out_unlock;
- nlk->groups = kzalloc(NLGRPSZ(groups), GFP_KERNEL);
- if (nlk->groups == NULL)
- return -ENOMEM;
+ new_groups = krealloc(nlk->groups, NLGRPSZ(groups), GFP_ATOMIC);
+ if (new_groups == NULL) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+ memset((char*)new_groups + NLGRPSZ(nlk->ngroups), 0,
+ NLGRPSZ(groups) - NLGRPSZ(nlk->ngroups));
+
+ nlk->groups = new_groups;
nlk->ngroups = groups;
- return 0;
+ out_unlock:
+ netlink_table_ungrab();
+ return err;
}
static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
@@ -591,11 +605,9 @@ static int netlink_bind(struct socket *s
if (nladdr->nl_groups) {
if (!netlink_capable(sock, NL_NONROOT_RECV))
return -EPERM;
- if (nlk->groups == NULL) {
- err = netlink_alloc_groups(sk);
- if (err)
- return err;
- }
+ err = netlink_realloc_groups(sk);
+ if (err)
+ return err;
}
if (nlk->pid) {
@@ -839,10 +851,18 @@ retry:
int netlink_has_listeners(struct sock *sk, unsigned int group)
{
int res = 0;
+ unsigned long *listeners;
BUG_ON(!(nlk_sk(sk)->flags & NETLINK_KERNEL_SOCKET));
+
+ rcu_read_lock();
+ listeners = rcu_dereference(nl_table[sk->sk_protocol].listeners);
+
if (group - 1 < nl_table[sk->sk_protocol].groups)
- res = test_bit(group - 1, nl_table[sk->sk_protocol].listeners);
+ res = test_bit(group - 1, listeners);
+
+ rcu_read_unlock();
+
return res;
}
EXPORT_SYMBOL_GPL(netlink_has_listeners);
@@ -1037,11 +1057,9 @@ static int netlink_setsockopt(struct soc
if (!netlink_capable(sock, NL_NONROOT_RECV))
return -EPERM;
- if (nlk->groups == NULL) {
- err = netlink_alloc_groups(sk);
- if (err)
- return err;
- }
+ err = netlink_realloc_groups(sk);
+ if (err)
+ return err;
if (!val || val - 1 >= nlk->ngroups)
return -EINVAL;
netlink_table_grab();
@@ -1328,6 +1346,46 @@ out_sock_release:
return NULL;
}
+/**
+ * netlink_change_ngroups - change number of multicast groups
+ *
+ * This changes the number of multicast groups that are available
+ * on a certain netlink family. Note that it is not possible to
+ * change the number of groups to below 32.
+ *
+ * @sk: The kernel netlink socket, as returned by netlink_kernel_create().
+ * @groups: The new number of groups.
+ */
+int netlink_change_ngroups(struct sock *sk, unsigned int groups)
+{
+ unsigned long *listeners, *old = NULL;
+ struct netlink_table *tbl = &nl_table[sk->sk_protocol];
+ int err = 0;
+
+ if (groups < 32)
+ groups = 32;
+
+ netlink_table_grab();
+ if (NLGRPSZ(tbl->groups) < NLGRPSZ(groups)) {
+ listeners = kzalloc(NLGRPSZ(groups), GFP_ATOMIC);
+ if (!listeners) {
+ err = -ENOMEM;
+ goto out_ungrab;
+ }
+ old = tbl->listeners;
+ memcpy(listeners, old, NLGRPSZ(tbl->groups));
+ rcu_assign_pointer(tbl->listeners, listeners);
+ }
+ tbl->groups = groups;
+
+ out_ungrab:
+ netlink_table_ungrab();
+ synchronize_rcu();
+ kfree(old);
+ return err;
+}
+EXPORT_SYMBOL(netlink_change_ngroups);
+
void netlink_set_nonroot(int protocol, unsigned int flags)
{
if ((unsigned int)protocol < MAX_LINKS)
--- wireless-dev.orig/include/linux/netlink.h 2007-07-18 15:08:48.132900849 +0200
+++ wireless-dev/include/linux/netlink.h 2007-07-18 16:31:43.061236687 +0200
@@ -161,6 +161,7 @@ extern struct sock *netlink_kernel_creat
void (*input)(struct sock *sk, int len),
struct mutex *cb_mutex,
struct module *module);
+extern int netlink_change_ngroups(struct sock *sk, unsigned int groups);
extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);
extern int netlink_has_listeners(struct sock *sk, unsigned int group);
extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock);
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH v2 1/3] netlink: allocate group bitmaps dynamically
2007-07-18 14:34 ` [PATCH v2 1/3] netlink: allocate group bitmaps dynamically Johannes Berg
@ 2007-07-18 16:30 ` Patrick McHardy
2007-07-18 22:34 ` David Miller
0 siblings, 1 reply; 16+ messages in thread
From: Patrick McHardy @ 2007-07-18 16:30 UTC (permalink / raw)
To: Johannes Berg; +Cc: David Miller, netdev, Zhang Rui, jamal
Johannes Berg wrote:
> 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>
>
> ---
> Changes: incorporate Patrick's comments, add kernel-doc
All three patches look good to me.
Acked-by: Patrick McHardy <kaber@trash.net>
and for Jamal again ..
Acked-by: Jamal Hadi Salim <hadi@cyberus.ca>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 1/3] netlink: allocate group bitmaps dynamically
2007-07-18 16:30 ` Patrick McHardy
@ 2007-07-18 22:34 ` David Miller
2007-07-19 10:08 ` Johannes Berg
0 siblings, 1 reply; 16+ messages in thread
From: David Miller @ 2007-07-18 22:34 UTC (permalink / raw)
To: kaber; +Cc: johannes, netdev, rui.zhang, hadi
From: Patrick McHardy <kaber@trash.net>
Date: Wed, 18 Jul 2007 18:30:04 +0200
> Johannes Berg wrote:
> > 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>
> >
> > ---
> > Changes: incorporate Patrick's comments, add kernel-doc
>
>
> All three patches look good to me.
>
> Acked-by: Patrick McHardy <kaber@trash.net>
>
> and for Jamal again ..
>
> Acked-by: Jamal Hadi Salim <hadi@cyberus.ca>
Aha, Johannes did resubmit all 3 patches, they arrived out
of order here which confused me :-)
I'll apply these, thanks.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 1/3] netlink: allocate group bitmaps dynamically
2007-07-18 22:34 ` David Miller
@ 2007-07-19 10:08 ` Johannes Berg
0 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2007-07-19 10:08 UTC (permalink / raw)
To: David Miller; +Cc: kaber, netdev, rui.zhang, hadi
[-- Attachment #1: Type: text/plain, Size: 532 bytes --]
On Wed, 2007-07-18 at 15:34 -0700, David Miller wrote:
> > All three patches look good to me.
> >
> > Acked-by: Patrick McHardy <kaber@trash.net>
> >
> > and for Jamal again ..
> >
> > Acked-by: Jamal Hadi Salim <hadi@cyberus.ca>
>
> Aha, Johannes did resubmit all 3 patches, they arrived out
> of order here which confused me :-)
>
> I'll apply these, thanks.
Thanks. And sorry about the out-of-order, my mistake because I'd noticed
a small bug in one patch and didn't rewrite all three emails.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 3/3] generic netlink: dynamic multicast groups
2007-07-17 12:27 [patch 0/3] dynamic generic netlink multicast Johannes Berg
` (4 preceding siblings ...)
2007-07-18 14:34 ` [PATCH v2 1/3] netlink: allocate group bitmaps dynamically Johannes Berg
@ 2007-07-18 15:05 ` Johannes Berg
2007-07-19 10:39 ` [patch 0/3] dynamic generic netlink multicast Johannes Berg
6 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2007-07-18 15:05 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Patrick McHardy, Zhang Rui, jamal
Introduce API to dynamically register and unregister multicast groups.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
Changes: update for slightly changed API from v2 of patch 1 and 2, add
kernel-doc, properly register the generic netlink group as well, fix bug
with that group (it isn't ID 1 but ID 0x10)
include/linux/genetlink.h | 13 ++
include/net/genetlink.h | 22 ++++
net/netlink/genetlink.c | 235 ++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 263 insertions(+), 7 deletions(-)
--- wireless-dev.orig/include/net/genetlink.h 2007-07-18 16:52:31.366081353 +0200
+++ wireless-dev/include/net/genetlink.h 2007-07-18 16:54:30.716081353 +0200
@@ -5,6 +5,22 @@
#include <net/netlink.h>
/**
+ * struct genl_multicast_group - generic netlink multicast group
+ * @name: name of the multicast group, names are per-family
+ * @id: multicast group ID, assigned by the core, to use with
+ * genlmsg_multicast().
+ * @list: list entry for linking
+ * @family: pointer to family, need not be set before registering
+ */
+struct genl_multicast_group
+{
+ struct genl_family *family; /* private */
+ struct list_head list; /* private */
+ char name[GENL_NAMSIZ];
+ u32 id;
+};
+
+/**
* struct genl_family - generic netlink family
* @id: protocol family idenfitier
* @hdrsize: length of user specific header in bytes
@@ -14,6 +30,7 @@
* @attrbuf: buffer to store parsed attributes
* @ops_list: list of all assigned operations
* @family_list: family list
+ * @mcast_groups: multicast groups list
*/
struct genl_family
{
@@ -25,6 +42,7 @@ struct genl_family
struct nlattr ** attrbuf; /* private */
struct list_head ops_list; /* private */
struct list_head family_list; /* private */
+ struct list_head mcast_groups; /* private */
};
/**
@@ -73,6 +91,10 @@ extern int genl_register_family(struct g
extern int genl_unregister_family(struct genl_family *family);
extern int genl_register_ops(struct genl_family *, struct genl_ops *ops);
extern int genl_unregister_ops(struct genl_family *, struct genl_ops *ops);
+extern int genl_register_mc_group(struct genl_family *family,
+ struct genl_multicast_group *grp);
+extern void genl_unregister_mc_group(struct genl_family *family,
+ struct genl_multicast_group *grp);
extern struct sock *genl_sock;
--- wireless-dev.orig/net/netlink/genetlink.c 2007-07-18 16:52:31.456081353 +0200
+++ wireless-dev/net/netlink/genetlink.c 2007-07-18 17:04:45.706081353 +0200
@@ -3,6 +3,7 @@
*
* Authors: Jamal Hadi Salim
* Thomas Graf <tgraf@suug.ch>
+ * Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/module.h>
@@ -13,6 +14,7 @@
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/mutex.h>
+#include <linux/bitmap.h>
#include <net/sock.h>
#include <net/genetlink.h>
@@ -42,6 +44,16 @@ static void genl_unlock(void)
#define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1)
static struct list_head family_ht[GENL_FAM_TAB_SIZE];
+/*
+ * Bitmap of multicast groups that are currently in use.
+ *
+ * To avoid an allocation at boot of just one unsigned long,
+ * declare it global instead.
+ * Bit 0 is marked as already used since group 0 is invalid.
+ */
+static unsigned long mc_group_start = 0x1;
+static unsigned long *mc_groups = &mc_group_start;
+static unsigned long mc_groups_longs = 1;
static int genl_ctrl_event(int event, void *data);
@@ -116,6 +128,114 @@ static inline u16 genl_generate_id(void)
return id_gen_idx;
}
+static struct genl_multicast_group notify_grp;
+
+/**
+ * genl_register_mc_group - register a multicast group
+ *
+ * Registers the specified multicast group and notifies userspace
+ * about the new group.
+ *
+ * Returns 0 on success or a negative error code.
+ *
+ * @family: The generic netlink family the group shall be registered for.
+ * @grp: The group to register, must have a name.
+ */
+int genl_register_mc_group(struct genl_family *family,
+ struct genl_multicast_group *grp)
+{
+ int id;
+ unsigned long *new_groups;
+ int err;
+
+ BUG_ON(grp->name[0] == '\0');
+
+ genl_lock();
+
+ /* special-case our own group */
+ if (grp == ¬ify_grp)
+ id = GENL_ID_CTRL;
+ else
+ id = find_first_zero_bit(mc_groups,
+ mc_groups_longs * BITS_PER_LONG);
+
+
+ if (id >= mc_groups_longs * BITS_PER_LONG) {
+ size_t nlen = (mc_groups_longs + 1) * sizeof(unsigned long);
+
+ if (mc_groups == &mc_group_start) {
+ new_groups = kzalloc(nlen, GFP_KERNEL);
+ if (!new_groups) {
+ err = -ENOMEM;
+ goto out;
+ }
+ mc_groups = new_groups;
+ *mc_groups = mc_group_start;
+ } else {
+ new_groups = krealloc(mc_groups, nlen, GFP_KERNEL);
+ if (!new_groups) {
+ err = -ENOMEM;
+ goto out;
+ }
+ mc_groups = new_groups;
+ mc_groups[mc_groups_longs] = 0;
+ }
+ mc_groups_longs++;
+ }
+
+ err = netlink_change_ngroups(genl_sock,
+ sizeof(unsigned long) * NETLINK_GENERIC);
+ if (err)
+ goto out;
+
+ grp->id = id;
+ set_bit(id, mc_groups);
+ list_add_tail(&grp->list, &family->mcast_groups);
+ grp->family = family;
+
+ genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, grp);
+ out:
+ genl_unlock();
+ return 0;
+}
+EXPORT_SYMBOL(genl_register_mc_group);
+
+/**
+ * genl_unregister_mc_group - unregister a multicast group
+ *
+ * Unregisters the specified multicast group and notifies userspace
+ * about it. All current listeners on the group are removed.
+ *
+ * Note: It is not necessary to unregister all multicast groups before
+ * unregistering the family, unregistering the family will cause
+ * all assigned multicast groups to be unregistered automatically.
+ *
+ * @family: Generic netlink family the group belongs to.
+ * @grp: The group to unregister, must have been registered successfully
+ * previously.
+ */
+void genl_unregister_mc_group(struct genl_family *family,
+ struct genl_multicast_group *grp)
+{
+ BUG_ON(grp->family != family);
+ genl_lock();
+ netlink_clear_multicast_users(genl_sock, grp->id);
+ clear_bit(grp->id, mc_groups);
+ list_del(&grp->list);
+ genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, grp);
+ grp->id = 0;
+ grp->family = NULL;
+ genl_unlock();
+}
+
+static void genl_unregister_mc_groups(struct genl_family *family)
+{
+ struct genl_multicast_group *grp, *tmp;
+
+ list_for_each_entry_safe(grp, tmp, &family->mcast_groups, list)
+ genl_unregister_mc_group(family, grp);
+}
+
/**
* genl_register_ops - register generic netlink operations
* @family: generic netlink family
@@ -216,6 +336,7 @@ int genl_register_family(struct genl_fam
goto errout;
INIT_LIST_HEAD(&family->ops_list);
+ INIT_LIST_HEAD(&family->mcast_groups);
genl_lock();
@@ -275,6 +396,8 @@ int genl_unregister_family(struct genl_f
{
struct genl_family *rc;
+ genl_unregister_mc_groups(family);
+
genl_lock();
list_for_each_entry(rc, genl_family_chain(family->id), family_list) {
@@ -410,6 +533,67 @@ static int ctrl_fill_info(struct genl_fa
nla_nest_end(skb, nla_ops);
}
+ if (!list_empty(&family->mcast_groups)) {
+ struct genl_multicast_group *grp;
+ struct nlattr *nla_grps;
+ int idx = 1;
+
+ nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS);
+ if (nla_grps == NULL)
+ goto nla_put_failure;
+
+ list_for_each_entry(grp, &family->mcast_groups, list) {
+ struct nlattr *nest;
+
+ nest = nla_nest_start(skb, idx++);
+ if (nest == NULL)
+ goto nla_put_failure;
+
+ NLA_PUT_U32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id);
+ NLA_PUT_STRING(skb, CTRL_ATTR_MCAST_GRP_NAME,
+ grp->name);
+
+ nla_nest_end(skb, nest);
+ }
+ nla_nest_end(skb, nla_grps);
+ }
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ return genlmsg_cancel(skb, hdr);
+}
+
+static int ctrl_fill_mcgrp_info(struct genl_multicast_group *grp, u32 pid,
+ u32 seq, u32 flags, struct sk_buff *skb,
+ u8 cmd)
+{
+ void *hdr;
+ struct nlattr *nla_grps;
+ struct nlattr *nest;
+
+ hdr = genlmsg_put(skb, pid, seq, &genl_ctrl, flags, cmd);
+ if (hdr == NULL)
+ return -1;
+
+ NLA_PUT_STRING(skb, CTRL_ATTR_FAMILY_NAME, grp->family->name);
+ NLA_PUT_U16(skb, CTRL_ATTR_FAMILY_ID, grp->family->id);
+
+ nla_grps = nla_nest_start(skb, CTRL_ATTR_MCAST_GROUPS);
+ if (nla_grps == NULL)
+ goto nla_put_failure;
+
+ nest = nla_nest_start(skb, 1);
+ if (nest == NULL)
+ goto nla_put_failure;
+
+ NLA_PUT_U32(skb, CTRL_ATTR_MCAST_GRP_ID, grp->id);
+ NLA_PUT_STRING(skb, CTRL_ATTR_MCAST_GRP_NAME,
+ grp->name);
+
+ nla_nest_end(skb, nest);
+ nla_nest_end(skb, nla_grps);
+
return genlmsg_end(skb, hdr);
nla_put_failure:
@@ -453,8 +637,8 @@ errout:
return skb->len;
}
-static struct sk_buff *ctrl_build_msg(struct genl_family *family, u32 pid,
- int seq, u8 cmd)
+static struct sk_buff *ctrl_build_family_msg(struct genl_family *family,
+ u32 pid, int seq, u8 cmd)
{
struct sk_buff *skb;
int err;
@@ -472,6 +656,25 @@ static struct sk_buff *ctrl_build_msg(st
return skb;
}
+static struct sk_buff *ctrl_build_mcgrp_msg(struct genl_multicast_group *grp,
+ u32 pid, int seq, u8 cmd)
+{
+ struct sk_buff *skb;
+ int err;
+
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (skb == NULL)
+ return ERR_PTR(-ENOBUFS);
+
+ err = ctrl_fill_mcgrp_info(grp, pid, seq, 0, skb, cmd);
+ if (err < 0) {
+ nlmsg_free(skb);
+ return ERR_PTR(err);
+ }
+
+ return skb;
+}
+
static const struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
[CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 },
[CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING,
@@ -501,8 +704,8 @@ static int ctrl_getfamily(struct sk_buff
goto errout;
}
- msg = ctrl_build_msg(res, info->snd_pid, info->snd_seq,
- CTRL_CMD_NEWFAMILY);
+ msg = ctrl_build_family_msg(res, info->snd_pid, info->snd_seq,
+ CTRL_CMD_NEWFAMILY);
if (IS_ERR(msg)) {
err = PTR_ERR(msg);
goto errout;
@@ -523,7 +726,15 @@ static int genl_ctrl_event(int event, vo
switch (event) {
case CTRL_CMD_NEWFAMILY:
case CTRL_CMD_DELFAMILY:
- msg = ctrl_build_msg(data, 0, 0, event);
+ msg = ctrl_build_family_msg(data, 0, 0, event);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL);
+ break;
+ case CTRL_CMD_NEWMCAST_GRP:
+ case CTRL_CMD_DELMCAST_GRP:
+ msg = ctrl_build_mcgrp_msg(data, 0, 0, event);
if (IS_ERR(msg))
return PTR_ERR(msg);
@@ -541,6 +752,10 @@ static struct genl_ops genl_ctrl_ops = {
.policy = ctrl_policy,
};
+static struct genl_multicast_group notify_grp = {
+ .name = "notify",
+};
+
static int __init genl_init(void)
{
int i, err;
@@ -557,11 +772,17 @@ static int __init genl_init(void)
goto errout_register;
netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV);
- genl_sock = netlink_kernel_create(NETLINK_GENERIC, GENL_MAX_ID,
- genl_rcv, NULL, THIS_MODULE);
+
+ /* we'll bump the group number right afterwards */
+ genl_sock = netlink_kernel_create(NETLINK_GENERIC, 0, genl_rcv,
+ NULL, THIS_MODULE);
if (genl_sock == NULL)
panic("GENL: Cannot initialize generic netlink\n");
+ err = genl_register_mc_group(&genl_ctrl, ¬ify_grp);
+ if (err < 0)
+ goto errout_register;
+
return 0;
errout_register:
--- wireless-dev.orig/include/linux/genetlink.h 2007-07-18 16:52:31.416081353 +0200
+++ wireless-dev/include/linux/genetlink.h 2007-07-18 16:54:30.716081353 +0200
@@ -39,6 +39,9 @@ enum {
CTRL_CMD_NEWOPS,
CTRL_CMD_DELOPS,
CTRL_CMD_GETOPS,
+ CTRL_CMD_NEWMCAST_GRP,
+ CTRL_CMD_DELMCAST_GRP,
+ CTRL_CMD_GETMCAST_GRP, /* unused */
__CTRL_CMD_MAX,
};
@@ -52,6 +55,7 @@ enum {
CTRL_ATTR_HDRSIZE,
CTRL_ATTR_MAXATTR,
CTRL_ATTR_OPS,
+ CTRL_ATTR_MCAST_GROUPS,
__CTRL_ATTR_MAX,
};
@@ -66,4 +70,13 @@ enum {
#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1)
+enum {
+ CTRL_ATTR_MCAST_GRP_UNSPEC,
+ CTRL_ATTR_MCAST_GRP_NAME,
+ CTRL_ATTR_MCAST_GRP_ID,
+ __CTRL_ATTR_MCAST_GRP_MAX,
+};
+
+#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)
+
#endif /* __LINUX_GENERIC_NETLINK_H */
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [patch 0/3] dynamic generic netlink multicast
2007-07-17 12:27 [patch 0/3] dynamic generic netlink multicast Johannes Berg
` (5 preceding siblings ...)
2007-07-18 15:05 ` [PATCH v2 3/3] generic netlink: dynamic multicast groups Johannes Berg
@ 2007-07-19 10:39 ` Johannes Berg
6 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2007-07-19 10:39 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Patrick McHardy, Zhang Rui, jamal
[-- Attachment #1: Type: text/plain, Size: 270 bytes --]
Oh, this patchset triggers a current bug in slub, see:
http://article.gmane.org/gmane.linux.kernel/556887
I forgot mentioning that yesterday, I hope it's not causing any trouble.
Also, I had expected it to be fixed soonish, but can't see that it is.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 190 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread