aboutsummaryrefslogtreecommitdiffstats
path: root/net/netlink/genetlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/netlink/genetlink.c')
-rw-r--r--net/netlink/genetlink.c64
1 files changed, 61 insertions, 3 deletions
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index 76393f2f4b22..ee57459fc258 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -23,6 +23,9 @@
23static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */ 23static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */
24static DECLARE_RWSEM(cb_lock); 24static DECLARE_RWSEM(cb_lock);
25 25
26atomic_t genl_sk_destructing_cnt = ATOMIC_INIT(0);
27DECLARE_WAIT_QUEUE_HEAD(genl_sk_destructing_waitq);
28
26void genl_lock(void) 29void 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);
@@ -983,11 +989,63 @@ static struct genl_multicast_group genl_ctrl_groups[] = {
983 { .name = "notify", }, 989 { .name = "notify", },
984}; 990};
985 991
992static int genl_bind(struct net *net, int group)
993{
994 int i, err = -ENOENT;
995
996 down_read(&cb_lock);
997 for (i = 0; i < GENL_FAM_TAB_SIZE; i++) {
998 struct genl_family *f;
999
1000 list_for_each_entry(f, genl_family_chain(i), family_list) {
1001 if (group >= f->mcgrp_offset &&
1002 group < f->mcgrp_offset + f->n_mcgrps) {
1003 int fam_grp = group - f->mcgrp_offset;
1004
1005 if (!f->netnsok && net != &init_net)
1006 err = -ENOENT;
1007 else if (f->mcast_bind)
1008 err = f->mcast_bind(net, fam_grp);
1009 else
1010 err = 0;
1011 break;
1012 }
1013 }
1014 }
1015 up_read(&cb_lock);
1016
1017 return err;
1018}
1019
1020static void genl_unbind(struct net *net, int group)
1021{
1022 int i;
1023
1024 down_read(&cb_lock);
1025 for (i = 0; i < GENL_FAM_TAB_SIZE; i++) {
1026 struct genl_family *f;
1027
1028 list_for_each_entry(f, genl_family_chain(i), family_list) {
1029 if (group >= f->mcgrp_offset &&
1030 group < f->mcgrp_offset + f->n_mcgrps) {
1031 int fam_grp = group - f->mcgrp_offset;
1032
1033 if (f->mcast_unbind)
1034 f->mcast_unbind(net, fam_grp);
1035 break;
1036 }
1037 }
1038 }
1039 up_read(&cb_lock);
1040}
1041
986static int __net_init genl_pernet_init(struct net *net) 1042static int __net_init genl_pernet_init(struct net *net)
987{ 1043{
988 struct netlink_kernel_cfg cfg = { 1044 struct netlink_kernel_cfg cfg = {
989 .input = genl_rcv, 1045 .input = genl_rcv,
990 .flags = NL_CFG_F_NONROOT_RECV, 1046 .flags = NL_CFG_F_NONROOT_RECV,
1047 .bind = genl_bind,
1048 .unbind = genl_unbind,
991 }; 1049 };
992 1050
993 /* we'll bump the group number right afterwards */ 1051 /* we'll bump the group number right afterwards */