diff options
-rw-r--r-- | include/linux/genetlink.h | 4 | ||||
-rw-r--r-- | include/net/genetlink.h | 5 | ||||
-rw-r--r-- | net/netlink/af_netlink.c | 24 | ||||
-rw-r--r-- | net/netlink/af_netlink.h | 1 | ||||
-rw-r--r-- | net/netlink/genetlink.c | 16 |
5 files changed, 35 insertions, 15 deletions
diff --git a/include/linux/genetlink.h b/include/linux/genetlink.h index 55b685719d52..09460d6d6682 100644 --- a/include/linux/genetlink.h +++ b/include/linux/genetlink.h | |||
@@ -11,6 +11,10 @@ extern void genl_unlock(void); | |||
11 | extern int lockdep_genl_is_held(void); | 11 | extern int lockdep_genl_is_held(void); |
12 | #endif | 12 | #endif |
13 | 13 | ||
14 | /* for synchronisation between af_netlink and genetlink */ | ||
15 | extern atomic_t genl_sk_destructing_cnt; | ||
16 | extern wait_queue_head_t genl_sk_destructing_waitq; | ||
17 | |||
14 | /** | 18 | /** |
15 | * rcu_dereference_genl - rcu_dereference with debug checking | 19 | * rcu_dereference_genl - rcu_dereference with debug checking |
16 | * @p: The pointer to read, prior to dereferencing | 20 | * @p: The pointer to read, prior to dereferencing |
diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 2ea2c55bdc87..6c92415311ca 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h | |||
@@ -35,7 +35,10 @@ struct genl_info; | |||
35 | * undo operations done by pre_doit, for example release locks | 35 | * undo operations done by pre_doit, for example release locks |
36 | * @mcast_bind: a socket bound to the given multicast group (which | 36 | * @mcast_bind: a socket bound to the given multicast group (which |
37 | * is given as the offset into the groups array) | 37 | * is given as the offset into the groups array) |
38 | * @mcast_unbind: a socket was unbound from the given multicast group | 38 | * @mcast_unbind: a socket was unbound from the given multicast group. |
39 | * Note that unbind() will not be called symmetrically if the | ||
40 | * generic netlink family is removed while there are still open | ||
41 | * sockets. | ||
39 | * @attrbuf: buffer to store parsed attributes | 42 | * @attrbuf: buffer to store parsed attributes |
40 | * @family_list: family list | 43 | * @family_list: family list |
41 | * @mcgrps: multicast groups used by this family (private) | 44 | * @mcgrps: multicast groups used by this family (private) |
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 84ea76ca3f1f..02fdde28dada 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -61,6 +61,7 @@ | |||
61 | #include <linux/rhashtable.h> | 61 | #include <linux/rhashtable.h> |
62 | #include <asm/cacheflush.h> | 62 | #include <asm/cacheflush.h> |
63 | #include <linux/hash.h> | 63 | #include <linux/hash.h> |
64 | #include <linux/genetlink.h> | ||
64 | 65 | ||
65 | #include <net/net_namespace.h> | 66 | #include <net/net_namespace.h> |
66 | #include <net/sock.h> | 67 | #include <net/sock.h> |
@@ -1095,6 +1096,8 @@ static void netlink_remove(struct sock *sk) | |||
1095 | __sk_del_bind_node(sk); | 1096 | __sk_del_bind_node(sk); |
1096 | netlink_update_listeners(sk); | 1097 | netlink_update_listeners(sk); |
1097 | } | 1098 | } |
1099 | if (sk->sk_protocol == NETLINK_GENERIC) | ||
1100 | atomic_inc(&genl_sk_destructing_cnt); | ||
1098 | netlink_table_ungrab(); | 1101 | netlink_table_ungrab(); |
1099 | } | 1102 | } |
1100 | 1103 | ||
@@ -1211,6 +1214,20 @@ static int netlink_release(struct socket *sock) | |||
1211 | * will be purged. | 1214 | * will be purged. |
1212 | */ | 1215 | */ |
1213 | 1216 | ||
1217 | /* must not acquire netlink_table_lock in any way again before unbind | ||
1218 | * and notifying genetlink is done as otherwise it might deadlock | ||
1219 | */ | ||
1220 | if (nlk->netlink_unbind) { | ||
1221 | int i; | ||
1222 | |||
1223 | for (i = 0; i < nlk->ngroups; i++) | ||
1224 | if (test_bit(i, nlk->groups)) | ||
1225 | nlk->netlink_unbind(sock_net(sk), i + 1); | ||
1226 | } | ||
1227 | if (sk->sk_protocol == NETLINK_GENERIC && | ||
1228 | atomic_dec_return(&genl_sk_destructing_cnt) == 0) | ||
1229 | wake_up(&genl_sk_destructing_waitq); | ||
1230 | |||
1214 | sock->sk = NULL; | 1231 | sock->sk = NULL; |
1215 | wake_up_interruptible_all(&nlk->wait); | 1232 | wake_up_interruptible_all(&nlk->wait); |
1216 | 1233 | ||
@@ -1246,13 +1263,6 @@ static int netlink_release(struct socket *sock) | |||
1246 | netlink_table_ungrab(); | 1263 | netlink_table_ungrab(); |
1247 | } | 1264 | } |
1248 | 1265 | ||
1249 | if (nlk->netlink_unbind) { | ||
1250 | int i; | ||
1251 | |||
1252 | for (i = 0; i < nlk->ngroups; i++) | ||
1253 | if (test_bit(i, nlk->groups)) | ||
1254 | nlk->netlink_unbind(sock_net(sk), i + 1); | ||
1255 | } | ||
1256 | kfree(nlk->groups); | 1266 | kfree(nlk->groups); |
1257 | nlk->groups = NULL; | 1267 | nlk->groups = NULL; |
1258 | 1268 | ||
diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h index f123a88496f8..f1c31b39aa3e 100644 --- a/net/netlink/af_netlink.h +++ b/net/netlink/af_netlink.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define _AF_NETLINK_H | 2 | #define _AF_NETLINK_H |
3 | 3 | ||
4 | #include <linux/rhashtable.h> | 4 | #include <linux/rhashtable.h> |
5 | #include <linux/atomic.h> | ||
5 | #include <net/sock.h> | 6 | #include <net/sock.h> |
6 | 7 | ||
7 | #define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8) | 8 | #define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8) |
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index c18d3f5624b2..ee57459fc258 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c | |||
@@ -23,6 +23,9 @@ | |||
23 | static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */ | 23 | static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */ |
24 | static DECLARE_RWSEM(cb_lock); | 24 | static DECLARE_RWSEM(cb_lock); |
25 | 25 | ||
26 | atomic_t genl_sk_destructing_cnt = ATOMIC_INIT(0); | ||
27 | DECLARE_WAIT_QUEUE_HEAD(genl_sk_destructing_waitq); | ||
28 | |||
26 | void genl_lock(void) | 29 | void genl_lock(void) |
27 | { | 30 | { |
28 | mutex_lock(&genl_mutex); | 31 | mutex_lock(&genl_mutex); |
@@ -435,15 +438,18 @@ int genl_unregister_family(struct genl_family *family) | |||
435 | 438 | ||
436 | genl_lock_all(); | 439 | genl_lock_all(); |
437 | 440 | ||
438 | genl_unregister_mc_groups(family); | ||
439 | |||
440 | list_for_each_entry(rc, genl_family_chain(family->id), family_list) { | 441 | list_for_each_entry(rc, genl_family_chain(family->id), family_list) { |
441 | if (family->id != rc->id || strcmp(rc->name, family->name)) | 442 | if (family->id != rc->id || strcmp(rc->name, family->name)) |
442 | continue; | 443 | continue; |
443 | 444 | ||
445 | genl_unregister_mc_groups(family); | ||
446 | |||
444 | list_del(&rc->family_list); | 447 | list_del(&rc->family_list); |
445 | family->n_ops = 0; | 448 | family->n_ops = 0; |
446 | genl_unlock_all(); | 449 | up_write(&cb_lock); |
450 | wait_event(genl_sk_destructing_waitq, | ||
451 | atomic_read(&genl_sk_destructing_cnt) == 0); | ||
452 | genl_unlock(); | ||
447 | 453 | ||
448 | kfree(family->attrbuf); | 454 | kfree(family->attrbuf); |
449 | genl_ctrl_event(CTRL_CMD_DELFAMILY, family, NULL, 0); | 455 | genl_ctrl_event(CTRL_CMD_DELFAMILY, family, NULL, 0); |
@@ -1014,7 +1020,6 @@ static int genl_bind(struct net *net, int group) | |||
1014 | static void genl_unbind(struct net *net, int group) | 1020 | static void genl_unbind(struct net *net, int group) |
1015 | { | 1021 | { |
1016 | int i; | 1022 | int i; |
1017 | bool found = false; | ||
1018 | 1023 | ||
1019 | down_read(&cb_lock); | 1024 | down_read(&cb_lock); |
1020 | for (i = 0; i < GENL_FAM_TAB_SIZE; i++) { | 1025 | for (i = 0; i < GENL_FAM_TAB_SIZE; i++) { |
@@ -1027,14 +1032,11 @@ static void genl_unbind(struct net *net, int group) | |||
1027 | 1032 | ||
1028 | if (f->mcast_unbind) | 1033 | if (f->mcast_unbind) |
1029 | f->mcast_unbind(net, fam_grp); | 1034 | f->mcast_unbind(net, fam_grp); |
1030 | found = true; | ||
1031 | break; | 1035 | break; |
1032 | } | 1036 | } |
1033 | } | 1037 | } |
1034 | } | 1038 | } |
1035 | up_read(&cb_lock); | 1039 | up_read(&cb_lock); |
1036 | |||
1037 | WARN_ON(!found); | ||
1038 | } | 1040 | } |
1039 | 1041 | ||
1040 | static int __net_init genl_pernet_init(struct net *net) | 1042 | static int __net_init genl_pernet_init(struct net *net) |