net: introduce NETDEV_UNREGISTER_PERNET
This new event is called once for each unique net namespace in batched
unregister operations (with the argument set to a random device from
that namespace) and once per device in non-batched unregister
operations.
It allows us to factorize some device unregister work such as clearing the
routing cache.
Signed-off-by: Octavian Purdila <opurdila@ixiacom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/include/linux/notifier.h b/include/linux/notifier.h
index 29714b8..b0c3671 100644
--- a/include/linux/notifier.h
+++ b/include/linux/notifier.h
@@ -202,6 +202,7 @@
#define NETDEV_BONDING_OLDTYPE 0x000E
#define NETDEV_BONDING_NEWTYPE 0x000F
#define NETDEV_POST_INIT 0x0010
+#define NETDEV_UNREGISTER_PERNET 0x0011
#define SYS_DOWN 0x0001 /* Notify of system down */
#define SYS_RESTART SYS_DOWN
diff --git a/net/core/dev.c b/net/core/dev.c
index c3e0578..e25fe5d 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1344,6 +1344,7 @@
nb->notifier_call(nb, NETDEV_DOWN, dev);
}
nb->notifier_call(nb, NETDEV_UNREGISTER, dev);
+ nb->notifier_call(nb, NETDEV_UNREGISTER_PERNET, dev);
}
}
@@ -4721,7 +4722,8 @@
static void rollback_registered_many(struct list_head *head)
{
- struct net_device *dev;
+ struct net_device *dev, *aux, *fdev;
+ LIST_HEAD(pernet_list);
BUG_ON(dev_boot_phase);
ASSERT_RTNL();
@@ -4779,8 +4781,24 @@
synchronize_net();
- list_for_each_entry(dev, head, unreg_list)
+ list_for_each_entry_safe(dev, aux, head, unreg_list) {
+ int new_net = 1;
+ list_for_each_entry(fdev, &pernet_list, unreg_list) {
+ if (dev_net(dev) == dev_net(fdev)) {
+ new_net = 0;
+ dev_put(dev);
+ break;
+ }
+ }
+ if (new_net)
+ list_move(&dev->unreg_list, &pernet_list);
+ }
+
+ list_for_each_entry_safe(dev, aux, &pernet_list, unreg_list) {
+ call_netdevice_notifiers(NETDEV_UNREGISTER_PERNET, dev);
+ list_move(&dev->unreg_list, head);
dev_put(dev);
+ }
}
static void rollback_registered(struct net_device *dev)
@@ -5074,6 +5092,8 @@
/* Rebroadcast unregister notification */
call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
+ /* don't resend NETDEV_UNREGISTER_PERNET, _PERNET users
+ * should have already handle it the first time */
if (test_bit(__LINK_STATE_LINKWATCH_PENDING,
&dev->state)) {
@@ -5385,6 +5405,10 @@
* unregister_netdevice_many - unregister many devices
* @head: list of devices
*
+ * WARNING: Calling this modifies the given list
+ * (in rollback_registered_many). It may change the order of the elements
+ * in the list. However, you can assume it does not add or delete elements
+ * to/from the list.
*/
void unregister_netdevice_many(struct list_head *head)
{
@@ -5504,6 +5528,7 @@
this device. They should clean all the things.
*/
call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
+ call_netdevice_notifiers(NETDEV_UNREGISTER_PERNET, dev);
/*
* Flush the unicast and multicast chains