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) |
