Commit 24f626d3 authored by David S. Miller's avatar David S. Miller

Merge branch 'netlink_multicast'

Johannes Berg says:

====================
netlink/genetlink cleanups & multicast improvements

I'm looking at using the multicast group functionality in a way that would
benefit from knowing when there are subscribers to avoid collecting the
required data when there aren't any. During this I noticed that the unbind
for multicast groups doesn't actually work - it's never called when sockets
are closed. Luckily, nobody actually uses the functionality.

While looking at the code trying to find why it's not called and where the
multicast listeners are actually removed, I found the potential cleanup in
patch 3. Patch 2 also has a cleanup for a generic netlink API in this area.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents eb46e221 c380d9a7
...@@ -31,6 +31,9 @@ struct genl_info; ...@@ -31,6 +31,9 @@ struct genl_info;
* do additional, common, filtering and return an error * do additional, common, filtering and return an error
* @post_doit: called after an operation's doit callback, it may * @post_doit: called after an operation's doit callback, it may
* undo operations done by pre_doit, for example release locks * undo operations done by pre_doit, for example release locks
* @mcast_bind: a socket bound to the given multicast group (which
* is given as the offset into the groups array)
* @mcast_unbind: a socket was unbound from the given multicast group
* @attrbuf: buffer to store parsed attributes * @attrbuf: buffer to store parsed attributes
* @family_list: family list * @family_list: family list
* @mcgrps: multicast groups used by this family (private) * @mcgrps: multicast groups used by this family (private)
...@@ -53,6 +56,8 @@ struct genl_family { ...@@ -53,6 +56,8 @@ struct genl_family {
void (*post_doit)(const struct genl_ops *ops, void (*post_doit)(const struct genl_ops *ops,
struct sk_buff *skb, struct sk_buff *skb,
struct genl_info *info); struct genl_info *info);
int (*mcast_bind)(int group);
void (*mcast_unbind)(int group);
struct nlattr ** attrbuf; /* private */ struct nlattr ** attrbuf; /* private */
const struct genl_ops * ops; /* private */ const struct genl_ops * ops; /* private */
const struct genl_multicast_group *mcgrps; /* private */ const struct genl_multicast_group *mcgrps; /* private */
...@@ -395,11 +400,11 @@ static inline int genl_set_err(struct genl_family *family, struct net *net, ...@@ -395,11 +400,11 @@ static inline int genl_set_err(struct genl_family *family, struct net *net,
} }
static inline int genl_has_listeners(struct genl_family *family, static inline int genl_has_listeners(struct genl_family *family,
struct sock *sk, unsigned int group) struct net *net, unsigned int group)
{ {
if (WARN_ON_ONCE(group >= family->n_mcgrps)) if (WARN_ON_ONCE(group >= family->n_mcgrps))
return -EINVAL; return -EINVAL;
group = family->mcgrp_offset + group; group = family->mcgrp_offset + group;
return netlink_has_listeners(sk, group); return netlink_has_listeners(net->genl_sock, group);
} }
#endif /* __NET_GENERIC_NETLINK_H */ #endif /* __NET_GENERIC_NETLINK_H */
...@@ -1091,8 +1091,10 @@ static void netlink_remove(struct sock *sk) ...@@ -1091,8 +1091,10 @@ static void netlink_remove(struct sock *sk)
mutex_unlock(&nl_sk_hash_lock); mutex_unlock(&nl_sk_hash_lock);
netlink_table_grab(); netlink_table_grab();
if (nlk_sk(sk)->subscriptions) if (nlk_sk(sk)->subscriptions) {
__sk_del_bind_node(sk); __sk_del_bind_node(sk);
netlink_update_listeners(sk);
}
netlink_table_ungrab(); netlink_table_ungrab();
} }
...@@ -1226,8 +1228,8 @@ static int netlink_release(struct socket *sock) ...@@ -1226,8 +1228,8 @@ static int netlink_release(struct socket *sock)
module_put(nlk->module); module_put(nlk->module);
netlink_table_grab();
if (netlink_is_kernel(sk)) { if (netlink_is_kernel(sk)) {
netlink_table_grab();
BUG_ON(nl_table[sk->sk_protocol].registered == 0); BUG_ON(nl_table[sk->sk_protocol].registered == 0);
if (--nl_table[sk->sk_protocol].registered == 0) { if (--nl_table[sk->sk_protocol].registered == 0) {
struct listeners *old; struct listeners *old;
...@@ -1241,11 +1243,16 @@ static int netlink_release(struct socket *sock) ...@@ -1241,11 +1243,16 @@ static int netlink_release(struct socket *sock)
nl_table[sk->sk_protocol].flags = 0; nl_table[sk->sk_protocol].flags = 0;
nl_table[sk->sk_protocol].registered = 0; nl_table[sk->sk_protocol].registered = 0;
} }
} else if (nlk->subscriptions) { netlink_table_ungrab();
netlink_update_listeners(sk);
} }
netlink_table_ungrab();
if (nlk->netlink_unbind) {
int i;
for (i = 0; i < nlk->ngroups; i++)
if (test_bit(i, nlk->groups))
nlk->netlink_unbind(i + 1);
}
kfree(nlk->groups); kfree(nlk->groups);
nlk->groups = NULL; nlk->groups = NULL;
...@@ -1410,8 +1417,8 @@ static int netlink_realloc_groups(struct sock *sk) ...@@ -1410,8 +1417,8 @@ static int netlink_realloc_groups(struct sock *sk)
return err; return err;
} }
static void netlink_unbind(int group, long unsigned int groups, static void netlink_undo_bind(int group, long unsigned int groups,
struct netlink_sock *nlk) struct netlink_sock *nlk)
{ {
int undo; int undo;
...@@ -1461,7 +1468,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, ...@@ -1461,7 +1468,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
err = nlk->netlink_bind(group); err = nlk->netlink_bind(group);
if (!err) if (!err)
continue; continue;
netlink_unbind(group, groups, nlk); netlink_undo_bind(group, groups, nlk);
return err; return err;
} }
} }
...@@ -1471,7 +1478,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, ...@@ -1471,7 +1478,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
netlink_insert(sk, net, nladdr->nl_pid) : netlink_insert(sk, net, nladdr->nl_pid) :
netlink_autobind(sock); netlink_autobind(sock);
if (err) { if (err) {
netlink_unbind(nlk->ngroups, groups, nlk); netlink_undo_bind(nlk->ngroups, groups, nlk);
return err; return err;
} }
} }
......
...@@ -983,11 +983,70 @@ static struct genl_multicast_group genl_ctrl_groups[] = { ...@@ -983,11 +983,70 @@ static struct genl_multicast_group genl_ctrl_groups[] = {
{ .name = "notify", }, { .name = "notify", },
}; };
static int genl_bind(int group)
{
int i, err;
bool found = false;
down_read(&cb_lock);
for (i = 0; i < GENL_FAM_TAB_SIZE; i++) {
struct genl_family *f;
list_for_each_entry(f, genl_family_chain(i), family_list) {
if (group >= f->mcgrp_offset &&
group < f->mcgrp_offset + f->n_mcgrps) {
int fam_grp = group - f->mcgrp_offset;
if (f->mcast_bind)
err = f->mcast_bind(fam_grp);
else
err = 0;
found = true;
break;
}
}
}
up_read(&cb_lock);
if (WARN_ON(!found))
err = 0;
return err;
}
static void genl_unbind(int group)
{
int i;
bool found = false;
down_read(&cb_lock);
for (i = 0; i < GENL_FAM_TAB_SIZE; i++) {
struct genl_family *f;
list_for_each_entry(f, genl_family_chain(i), family_list) {
if (group >= f->mcgrp_offset &&
group < f->mcgrp_offset + f->n_mcgrps) {
int fam_grp = group - f->mcgrp_offset;
if (f->mcast_unbind)
f->mcast_unbind(fam_grp);
found = true;
break;
}
}
}
up_read(&cb_lock);
WARN_ON(!found);
}
static int __net_init genl_pernet_init(struct net *net) static int __net_init genl_pernet_init(struct net *net)
{ {
struct netlink_kernel_cfg cfg = { struct netlink_kernel_cfg cfg = {
.input = genl_rcv, .input = genl_rcv,
.flags = NL_CFG_F_NONROOT_RECV, .flags = NL_CFG_F_NONROOT_RECV,
.bind = genl_bind,
.unbind = genl_unbind,
}; };
/* we'll bump the group number right afterwards */ /* we'll bump the group number right afterwards */
......
...@@ -83,8 +83,7 @@ static bool ovs_must_notify(struct genl_family *family, struct genl_info *info, ...@@ -83,8 +83,7 @@ static bool ovs_must_notify(struct genl_family *family, struct genl_info *info,
unsigned int group) unsigned int group)
{ {
return info->nlhdr->nlmsg_flags & NLM_F_ECHO || return info->nlhdr->nlmsg_flags & NLM_F_ECHO ||
genl_has_listeners(family, genl_info_net(info)->genl_sock, genl_has_listeners(family, genl_info_net(info), group);
group);
} }
static void ovs_notify(struct genl_family *family, static void ovs_notify(struct genl_family *family,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment