When ipt_CLUSTERIP target is inserted, lockdep warns about
possible DEADLOCK situation. to avoid deadlock situation
register_netdevice_notifier() should be called by only init routine.

reproduce command is :
   # iptables -A INPUT -p tcp -i enp3s0 -d 192.168.0.5 --dport 80 \
-j CLUSTERIP --new --hashmode sourceip \
--clustermac 01:00:5e:00:00:20 --total-nodes 2 --local-node 1

warning message is :

[  148.751110] WARNING: possible circular locking dependency detected
[  148.758037] 4.13.0-rc1+ #71 Not tainted
[  148.762334] ------------------------------------------------------

[ ... ]

        the existing dependency chain (in reverse order) is:
[  148.816203]
        -> #1 (sk_lock-AF_INET){+.+.+.}:
[  148.822686]        lock_acquire+0x190/0x370
[  148.827401]        lock_sock_nested+0xb8/0x100
[  148.832405]        do_ip_setsockopt.isra.16+0x140/0x24f0
[  148.838380]        ip_setsockopt+0x34/0xb0
[  148.842988]        udp_setsockopt+0x1b/0x30
[  148.847692]        sock_common_setsockopt+0x78/0xf0
[  148.853182]        SyS_setsockopt+0x11c/0x220
[  148.858089]        do_syscall_64+0x187/0x410
[  148.862901]        return_from_SYSCALL_64+0x0/0x7a
[  148.868289]
        -> #0 (rtnl_mutex){+.+.+.}:
[  148.874303]        __lock_acquire+0x4114/0x47c0
[  148.879405]        lock_acquire+0x190/0x370
[  148.884109]        __mutex_lock+0xef/0x1460
[  148.888820]        mutex_lock_nested+0x1b/0x20
[  148.893824]        rtnl_lock+0x17/0x20
[  148.898052]        register_netdevice_notifier+0x6f/0x4f0
[  148.904127]        clusterip_tg_check+0xbf0/0x13e0
[  148.909519]        xt_check_target+0x1f5/0x6c0
[  148.914525]        find_check_entry.isra.7+0x62f/0x960
[  148.920308]        translate_table+0xcf2/0x1830
[  148.925410]        do_ipt_set_ctl+0x1ff/0x3a0
[  148.930320]        nf_setsockopt+0x61/0xc0
[  148.934933]        ip_setsockopt+0x82/0xb0
[  148.939548]        raw_setsockopt+0x73/0xa0
[  148.944260]        sock_common_setsockopt+0x78/0xf0
[  148.949749]        SyS_setsockopt+0x11c/0x220
[  148.954658]        entry_SYSCALL_64_fastpath+0x1c/0xb1
[  148.960435]
        other info that might help us debug this:

[  148.969459]  Possible unsafe locking scenario:

[  148.976124]        CPU0                    CPU1
[  148.981218]        ----                    ----
[  148.986312]   lock(sk_lock-AF_INET);
[  148.990343]                                lock(rtnl_mutex);
[  148.996708]                                lock(sk_lock-AF_INET);
[  149.003559]   lock(rtnl_mutex);
[  149.007103]
*** DEADLOCK ***

[ ... ]

Signed-off-by: Taehee Yoo <ap420...@gmail.com>
---
 net/ipv4/netfilter/ipt_CLUSTERIP.c | 70 +++++++++++++++++++++-----------------
 1 file changed, 39 insertions(+), 31 deletions(-)

diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c 
b/net/ipv4/netfilter/ipt_CLUSTERIP.c
index 6637e8b..c31f188 100644
--- a/net/ipv4/netfilter/ipt_CLUSTERIP.c
+++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c
@@ -59,7 +59,6 @@ struct clusterip_config {
        struct rcu_head rcu;
 
        char ifname[IFNAMSIZ];                  /* device ifname */
-       struct notifier_block notifier;         /* refresh c->ifindex in it */
 };
 
 #ifdef CONFIG_PROC_FS
@@ -73,6 +72,7 @@ struct clusterip_net {
        /* lock protects the configs list */
        spinlock_t lock;
 
+       struct notifier_block notifier;
 #ifdef CONFIG_PROC_FS
        struct proc_dir_entry *procdir;
 #endif
@@ -111,8 +111,6 @@ clusterip_config_entry_put(struct net *net, struct 
clusterip_config *c)
                spin_unlock(&cn->lock);
                local_bh_enable();
 
-               unregister_netdevice_notifier(&c->notifier);
-
                /* In case anyone still accesses the file, the open/close
                 * functions are also incrementing the refcount on their own,
                 * so it's safe to remove the entry even if it's in use. */
@@ -176,32 +174,37 @@ clusterip_netdev_event(struct notifier_block *this, 
unsigned long event,
                       void *ptr)
 {
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       struct net *net = dev_net(dev);
+       struct clusterip_net *cn = net_generic(net, clusterip_net_id);
        struct clusterip_config *c;
 
-       c = container_of(this, struct clusterip_config, notifier);
-       switch (event) {
-       case NETDEV_REGISTER:
-               if (!strcmp(dev->name, c->ifname)) {
-                       c->ifindex = dev->ifindex;
-                       dev_mc_add(dev, c->clustermac);
-               }
-               break;
-       case NETDEV_UNREGISTER:
-               if (dev->ifindex == c->ifindex) {
-                       dev_mc_del(dev, c->clustermac);
-                       c->ifindex = -1;
-               }
-               break;
-       case NETDEV_CHANGENAME:
-               if (!strcmp(dev->name, c->ifname)) {
-                       c->ifindex = dev->ifindex;
-                       dev_mc_add(dev, c->clustermac);
-               } else if (dev->ifindex == c->ifindex) {
-                       dev_mc_del(dev, c->clustermac);
-                       c->ifindex = -1;
+       rcu_read_lock();
+       list_for_each_entry_rcu(c, &cn->configs, list) {
+               switch (event) {
+               case NETDEV_REGISTER:
+                       if (!strcmp(dev->name, c->ifname)) {
+                               c->ifindex = dev->ifindex;
+                               dev_mc_add(dev, c->clustermac);
+                       }
+                       break;
+               case NETDEV_UNREGISTER:
+                       if (dev->ifindex == c->ifindex) {
+                               dev_mc_del(dev, c->clustermac);
+                               c->ifindex = -1;
+                       }
+                       break;
+               case NETDEV_CHANGENAME:
+                       if (!strcmp(dev->name, c->ifname)) {
+                               c->ifindex = dev->ifindex;
+                               dev_mc_add(dev, c->clustermac);
+                       } else if (dev->ifindex == c->ifindex) {
+                               dev_mc_del(dev, c->clustermac);
+                               c->ifindex = -1;
+                       }
+                       break;
                }
-               break;
        }
+       rcu_read_unlock();
 
        return NOTIFY_DONE;
 }
@@ -256,11 +259,7 @@ clusterip_config_init(struct net *net, const struct 
ipt_clusterip_tgt_info *i,
        }
 #endif
 
-       c->notifier.notifier_call = clusterip_netdev_event;
-       err = register_netdevice_notifier(&c->notifier);
-       if (!err)
-               return c;
-
+       return c;
 #ifdef CONFIG_PROC_FS
        proc_remove(c->pde);
 err:
@@ -798,9 +797,17 @@ static int clusterip_net_init(struct net *net)
        if (ret < 0)
                return ret;
 
+       cn->notifier.notifier_call = clusterip_netdev_event;
+       ret = register_netdevice_notifier(&cn->notifier);
+       if (ret) {
+               nf_unregister_net_hook(net, &cip_arp_ops);
+               return ret;
+       }
+
 #ifdef CONFIG_PROC_FS
        cn->procdir = proc_mkdir("ipt_CLUSTERIP", net->proc_net);
        if (!cn->procdir) {
+               unregister_netdevice_notifier(&cn->notifier);
                nf_unregister_net_hook(net, &cip_arp_ops);
                pr_err("Unable to proc dir entry\n");
                return -ENOMEM;
@@ -812,10 +819,11 @@ static int clusterip_net_init(struct net *net)
 
 static void clusterip_net_exit(struct net *net)
 {
-#ifdef CONFIG_PROC_FS
        struct clusterip_net *cn = net_generic(net, clusterip_net_id);
+#ifdef CONFIG_PROC_FS
        proc_remove(cn->procdir);
 #endif
+       unregister_netdevice_notifier(&cn->notifier);
        nf_unregister_net_hook(net, &cip_arp_ops);
 }
 
-- 
2.9.3

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to