net: introduce per-netns netdevice notifiers
authorJiri Pirko <jiri@mellanox.com>
Mon, 30 Sep 2019 08:15:10 +0000 (10:15 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 2 Oct 2019 15:48:44 +0000 (11:48 -0400)
Often the code for example in drivers is interested in getting notifier
call only from certain network namespace. In addition to the existing
global netdevice notifier chain introduce per-netns chains and allow
users to register to that. Eventually this would eliminate unnecessary
overhead in case there are many netdevices in many network namespaces.

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netdevice.h
include/net/net_namespace.h
net/core/dev.c

index 7b183f724fc4f601e726e2da7f9052a487abce9a..fe45b2c72315f422dfa1b1e74a033777dd83fa21 100644 (file)
@@ -2504,6 +2504,9 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd);
 
 int register_netdevice_notifier(struct notifier_block *nb);
 int unregister_netdevice_notifier(struct notifier_block *nb);
+int register_netdevice_notifier_net(struct net *net, struct notifier_block *nb);
+int unregister_netdevice_notifier_net(struct net *net,
+                                     struct notifier_block *nb);
 
 struct netdev_notifier_info {
        struct net_device       *dev;
index c5a98e03591dfadea98d83e8e09ae3f20ebef687..5ac2bb16d4b3fb866848291bc8d21cfb7bccbb3d 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/ns_common.h>
 #include <linux/idr.h>
 #include <linux/skbuff.h>
+#include <linux/notifier.h>
 
 struct user_namespace;
 struct proc_dir_entry;
@@ -96,6 +97,8 @@ struct net {
        struct list_head        dev_base_head;
        struct hlist_head       *dev_name_head;
        struct hlist_head       *dev_index_head;
+       struct raw_notifier_head        netdev_chain;
+
        unsigned int            dev_base_seq;   /* protected by rtnl_mutex */
        int                     ifindex;
        unsigned int            dev_unreg_count;
index a8b70cb6c732a9dfb509c20a80d973ded6a78008..c680225e0da844bbbd66c9044851880fa7885117 100644 (file)
@@ -1874,6 +1874,80 @@ unlock:
 }
 EXPORT_SYMBOL(unregister_netdevice_notifier);
 
+/**
+ * register_netdevice_notifier_net - register a per-netns network notifier block
+ * @net: network namespace
+ * @nb: notifier
+ *
+ * Register a notifier to be called when network device events occur.
+ * The notifier passed is linked into the kernel structures and must
+ * not be reused until it has been unregistered. A negative errno code
+ * is returned on a failure.
+ *
+ * When registered all registration and up events are replayed
+ * to the new notifier to allow device to have a race free
+ * view of the network device list.
+ */
+
+int register_netdevice_notifier_net(struct net *net, struct notifier_block *nb)
+{
+       int err;
+
+       rtnl_lock();
+       err = raw_notifier_chain_register(&net->netdev_chain, nb);
+       if (err)
+               goto unlock;
+       if (dev_boot_phase)
+               goto unlock;
+
+       err = call_netdevice_register_net_notifiers(nb, net);
+       if (err)
+               goto chain_unregister;
+
+unlock:
+       rtnl_unlock();
+       return err;
+
+chain_unregister:
+       raw_notifier_chain_unregister(&netdev_chain, nb);
+       goto unlock;
+}
+EXPORT_SYMBOL(register_netdevice_notifier_net);
+
+/**
+ * unregister_netdevice_notifier_net - unregister a per-netns
+ *                                     network notifier block
+ * @net: network namespace
+ * @nb: notifier
+ *
+ * Unregister a notifier previously registered by
+ * register_netdevice_notifier(). The notifier is unlinked into the
+ * kernel structures and may then be reused. A negative errno code
+ * is returned on a failure.
+ *
+ * After unregistering unregister and down device events are synthesized
+ * for all devices on the device list to the removed notifier to remove
+ * the need for special case cleanup code.
+ */
+
+int unregister_netdevice_notifier_net(struct net *net,
+                                     struct notifier_block *nb)
+{
+       int err;
+
+       rtnl_lock();
+       err = raw_notifier_chain_unregister(&net->netdev_chain, nb);
+       if (err)
+               goto unlock;
+
+       call_netdevice_unregister_net_notifiers(nb, net);
+
+unlock:
+       rtnl_unlock();
+       return err;
+}
+EXPORT_SYMBOL(unregister_netdevice_notifier_net);
+
 /**
  *     call_netdevice_notifiers_info - call all network notifier blocks
  *     @val: value passed unmodified to notifier function
@@ -1886,7 +1960,18 @@ EXPORT_SYMBOL(unregister_netdevice_notifier);
 static int call_netdevice_notifiers_info(unsigned long val,
                                         struct netdev_notifier_info *info)
 {
+       struct net *net = dev_net(info->dev);
+       int ret;
+
        ASSERT_RTNL();
+
+       /* Run per-netns notifier block chain first, then run the global one.
+        * Hopefully, one day, the global one is going to be removed after
+        * all notifier block registrators get converted to be per-netns.
+        */
+       ret = raw_notifier_call_chain(&net->netdev_chain, val, info);
+       if (ret & NOTIFY_STOP_MASK)
+               return ret;
        return raw_notifier_call_chain(&netdev_chain, val, info);
 }
 
@@ -9785,6 +9870,8 @@ static int __net_init netdev_init(struct net *net)
        if (net->dev_index_head == NULL)
                goto err_idx;
 
+       RAW_INIT_NOTIFIER_HEAD(&net->netdev_chain);
+
        return 0;
 
 err_idx: