ipv6/addrconf: allocate a per netns hash table
authorEric Dumazet <edumazet@google.com>
Tue, 8 Feb 2022 04:50:28 +0000 (20:50 -0800)
committerJakub Kicinski <kuba@kernel.org>
Wed, 9 Feb 2022 04:41:32 +0000 (20:41 -0800)
Add a per netns hash table and a dedicated spinlock,
first step to get rid of the global inet6_addr_lst[] one.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/net/netns/ipv6.h
net/ipv6/addrconf.c

index 30cdfc4..755f120 100644 (file)
@@ -92,6 +92,10 @@ struct netns_ipv6 {
        struct sock             *tcp_sk;
        struct sock             *igmp_sk;
        struct sock             *mc_autojoin_sk;
+
+       struct hlist_head       *inet6_addr_lst;
+       spinlock_t              addrconf_hash_lock;
+
 #ifdef CONFIG_IPV6_MROUTE
 #ifndef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
        struct mr_table         *mrt6;
index ef23e7d..cda9e59 100644 (file)
@@ -7111,6 +7111,13 @@ static int __net_init addrconf_init_net(struct net *net)
        int err = -ENOMEM;
        struct ipv6_devconf *all, *dflt;
 
+       spin_lock_init(&net->ipv6.addrconf_hash_lock);
+       net->ipv6.inet6_addr_lst = kcalloc(IN6_ADDR_HSIZE,
+                                          sizeof(struct hlist_head),
+                                          GFP_KERNEL);
+       if (!net->ipv6.inet6_addr_lst)
+               goto err_alloc_addr;
+
        all = kmemdup(&ipv6_devconf, sizeof(ipv6_devconf), GFP_KERNEL);
        if (!all)
                goto err_alloc_all;
@@ -7172,11 +7179,15 @@ err_reg_all:
 err_alloc_dflt:
        kfree(all);
 err_alloc_all:
+       kfree(net->ipv6.inet6_addr_lst);
+err_alloc_addr:
        return err;
 }
 
 static void __net_exit addrconf_exit_net(struct net *net)
 {
+       int i;
+
 #ifdef CONFIG_SYSCTL
        __addrconf_sysctl_unregister(net, net->ipv6.devconf_dflt,
                                     NETCONFA_IFINDEX_DEFAULT);
@@ -7187,6 +7198,15 @@ static void __net_exit addrconf_exit_net(struct net *net)
        net->ipv6.devconf_dflt = NULL;
        kfree(net->ipv6.devconf_all);
        net->ipv6.devconf_all = NULL;
+
+       /*
+        *      Check hash table, then free it.
+        */
+       for (i = 0; i < IN6_ADDR_HSIZE; i++)
+               WARN_ON_ONCE(!hlist_empty(&net->ipv6.inet6_addr_lst[i]));
+
+       kfree(net->ipv6.inet6_addr_lst);
+       net->ipv6.inet6_addr_lst = NULL;
 }
 
 static struct pernet_operations addrconf_ops = {