net: introduce dev_net notifier register/unregister variants
authorJiri Pirko <jiri@mellanox.com>
Sat, 25 Jan 2020 11:17:08 +0000 (12:17 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 27 Jan 2020 10:03:44 +0000 (11:03 +0100)
Introduce dev_net variants of netdev notifier register/unregister functions
and allow per-net notifier to follow the netdevice into the namespace it is
moved to.

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

index 20445f94eb1c81881ae1fc23339f8375fc5a244c..4626188a754bf21be63a318e47c639ec4227c944 100644 (file)
@@ -939,6 +939,11 @@ struct netdev_name_node {
 int netdev_name_node_alt_create(struct net_device *dev, const char *name);
 int netdev_name_node_alt_destroy(struct net_device *dev, const char *name);
 
+struct netdev_net_notifier {
+       struct list_head list;
+       struct notifier_block *nb;
+};
+
 /*
  * This structure defines the management hooks for network devices.
  * The following hooks can be defined; unless noted otherwise, they are
@@ -1793,6 +1798,10 @@ enum netdev_priv_flags {
  *
  *     @wol_enabled:   Wake-on-LAN is enabled
  *
+ *     @net_notifier_list:     List of per-net netdev notifier block
+ *                             that follow this device when it is moved
+ *                             to another network namespace.
+ *
  *     FIXME: cleanup struct net_device such that network protocol info
  *     moves out.
  */
@@ -2085,6 +2094,8 @@ struct net_device {
        struct lock_class_key   addr_list_lock_key;
        bool                    proto_down;
        unsigned                wol_enabled:1;
+
+       struct list_head        net_notifier_list;
 };
 #define to_net_dev(d) container_of(d, struct net_device, dev)
 
@@ -2529,6 +2540,12 @@ 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);
+int register_netdevice_notifier_dev_net(struct net_device *dev,
+                                       struct notifier_block *nb,
+                                       struct netdev_net_notifier *nn);
+int unregister_netdevice_notifier_dev_net(struct net_device *dev,
+                                         struct notifier_block *nb,
+                                         struct netdev_net_notifier *nn);
 
 struct netdev_notifier_info {
        struct net_device       *dev;
index b521b509a6532cda9e934ba514cd6ec8d0b1deef..38bc35da39f7f6065f97ba2418b18662ca7a31ab 100644 (file)
@@ -1874,6 +1874,48 @@ int unregister_netdevice_notifier_net(struct net *net,
 }
 EXPORT_SYMBOL(unregister_netdevice_notifier_net);
 
+int register_netdevice_notifier_dev_net(struct net_device *dev,
+                                       struct notifier_block *nb,
+                                       struct netdev_net_notifier *nn)
+{
+       int err;
+
+       rtnl_lock();
+       err = __register_netdevice_notifier_net(dev_net(dev), nb, false);
+       if (!err) {
+               nn->nb = nb;
+               list_add(&nn->list, &dev->net_notifier_list);
+       }
+       rtnl_unlock();
+       return err;
+}
+EXPORT_SYMBOL(register_netdevice_notifier_dev_net);
+
+int unregister_netdevice_notifier_dev_net(struct net_device *dev,
+                                         struct notifier_block *nb,
+                                         struct netdev_net_notifier *nn)
+{
+       int err;
+
+       rtnl_lock();
+       list_del(&nn->list);
+       err = __unregister_netdevice_notifier_net(dev_net(dev), nb);
+       rtnl_unlock();
+       return err;
+}
+EXPORT_SYMBOL(unregister_netdevice_notifier_dev_net);
+
+static void move_netdevice_notifiers_dev_net(struct net_device *dev,
+                                            struct net *net)
+{
+       struct netdev_net_notifier *nn;
+
+       list_for_each_entry(nn, &dev->net_notifier_list, list) {
+               __unregister_netdevice_notifier_net(dev_net(dev), nn->nb);
+               __register_netdevice_notifier_net(net, nn->nb, true);
+       }
+}
+
 /**
  *     call_netdevice_notifiers_info - call all network notifier blocks
  *     @val: value passed unmodified to notifier function
@@ -9786,6 +9828,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
        INIT_LIST_HEAD(&dev->adj_list.lower);
        INIT_LIST_HEAD(&dev->ptype_all);
        INIT_LIST_HEAD(&dev->ptype_specific);
+       INIT_LIST_HEAD(&dev->net_notifier_list);
 #ifdef CONFIG_NET_SCHED
        hash_init(dev->qdisc_hash);
 #endif
@@ -10049,6 +10092,9 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
        kobject_uevent(&dev->dev.kobj, KOBJ_REMOVE);
        netdev_adjacent_del_links(dev);
 
+       /* Move per-net netdevice notifiers that are following the netdevice */
+       move_netdevice_notifiers_dev_net(dev, net);
+
        /* Actually switch the network namespace */
        dev_net_set(dev, net);
        dev->ifindex = new_ifindex;