net: Fix inconsistent teardown and release of private netdev state. [Linux 4.11.9]

This Linux kernel change, a part of the Linux 4.11.9 release, is about net: Fix inconsistent teardown and release of private netdev state. The change is by David S. Miller <davem [at] davemloft.net> on Mon May 8 12:52:56 2017 -0400. Commit for this change in Linux stable tree is 9587685 (patch) from upstream commit cf124db. The same Linux upstream change may be applied to various maintained Linux releases. Find all Linux releases changes from cf124db.

net: Fix inconsistent teardown and release of private netdev state.

[ Upstream commit cf124db566e6b036b8bcbe8decbed740bdfac8c6 ]

Network devices can allocate reasources and private memory using
netdev_ops->ndo_init().  However, the release of these resources
can occur in one of two different places.

Either netdev_ops->ndo_uninit() or netdev->destructor().

The decision of which operation frees the resources depends upon
whether it is necessary for all netdev refs to be released before it
is safe to perform the freeing.

netdev_ops->ndo_uninit() presumably can occur right after the
NETDEV_UNREGISTER notifier completes and the unicast and multicast
address lists are flushed.

netdev->destructor(), on the other hand, does not run until the
netdev references all go away.

Further complicating the situation is that netdev->destructor()
almost universally does also a free_netdev().

This creates a problem for the logic in register_netdevice().
Because all callers of register_netdevice() manage the freeing
of the netdev, and invoke free_netdev(dev) if register_netdevice()
fails.

If netdev_ops->ndo_init() succeeds, but something else fails inside
of register_netdevice(), it does call ndo_ops->ndo_uninit().  But
it is not able to invoke netdev->destructor().

This is because netdev->destructor() will do a free_netdev() and
then the caller of register_netdevice() will do the same.

However, this means that the resources that would normally be released
by netdev->destructor() will not be.

Over the years drivers have added local hacks to deal with this, by
invoking their destructor parts by hand when register_netdevice()
fails.

Many drivers do not try to deal with this, and instead we have leaks.

Let's close this hole by formalizing the distinction between what
private things need to be freed up by netdev->destructor() and whether
the driver needs unregister_netdevice() to perform the free_netdev().

netdev->priv_destructor() performs all actions to free up the private
resources that used to be freed by netdev->destructor(), except for
free_netdev().

netdev->needs_free_netdev is a boolean that indicates whether
free_netdev() should be done at the end of unregister_netdevice().

Now, register_netdevice() can sanely release all resources after
ndo_ops->ndo_init() succeeds, by invoking both ndo_ops->ndo_uninit()
and netdev->priv_destructor().

And at the end of unregister_netdevice(), we invoke
netdev->priv_destructor() and optionally call free_netdev().

Signed-off-by: David S. Miller <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>

There are 202 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 drivers/net/bonding/bond_main.c                             | 6 +++---
 drivers/net/caif/caif_hsi.c                                 | 2 +-
 drivers/net/caif/caif_serial.c                              | 2 +-
 drivers/net/caif/caif_spi.c                                 | 2 +-
 drivers/net/caif/caif_virtio.c                              | 2 +-
 drivers/net/can/slcan.c                                     | 7 +++----
 drivers/net/can/vcan.c                                      | 2 +-
 drivers/net/dummy.c                                         | 4 ++--
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c             | 2 +-
 drivers/net/geneve.c                                        | 2 +-
 drivers/net/gtp.c                                           | 2 +-
 drivers/net/hamradio/6pack.c                                | 2 +-
 drivers/net/hamradio/bpqether.c                             | 2 +-
 drivers/net/ifb.c                                           | 4 ++--
 drivers/net/ipvlan/ipvlan_main.c                            | 2 +-
 drivers/net/loopback.c                                      | 4 ++--
 drivers/net/macsec.c                                        | 4 ++--
 drivers/net/macvlan.c                                       | 2 +-
 drivers/net/nlmon.c                                         | 2 +-
 drivers/net/slip/slip.c                                     | 7 +++----
 drivers/net/team/team.c                                     | 4 ++--
 drivers/net/tun.c                                           | 4 ++--
 drivers/net/usb/cdc-phonet.c                                | 2 +-
 drivers/net/veth.c                                          | 4 ++--
 drivers/net/vrf.c                                           | 2 +-
 drivers/net/vxlan.c                                         | 2 +-
 drivers/net/wan/dlci.c                                      | 2 +-
 drivers/net/wan/hdlc_fr.c                                   | 2 +-
 drivers/net/wan/lapbether.c                                 | 2 +-
 drivers/net/wireless/ath/ath6kl/main.c                      | 2 +-
 drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 1 -
 drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c     | 3 ++-
 drivers/net/wireless/intersil/hostap/hostap_main.c          | 2 +-
 drivers/net/wireless/mac80211_hwsim.c                       | 2 +-
 drivers/net/wireless/marvell/mwifiex/main.c                 | 2 +-
 drivers/staging/rtl8188eu/os_dep/mon.c                      | 2 +-
 drivers/usb/gadget/function/f_phonet.c                      | 2 +-
 include/linux/netdevice.h                                   | 7 ++++---
 net/8021q/vlan_dev.c                                        | 4 ++--
 net/batman-adv/soft-interface.c                             | 5 ++---
 net/bluetooth/6lowpan.c                                     | 2 +-
 net/bridge/br_device.c                                      | 2 +-
 net/caif/chnl_net.c                                         | 4 ++--
 net/core/dev.c                                              | 8 ++++++--
 net/hsr/hsr_device.c                                        | 4 ++--
 net/ieee802154/6lowpan/core.c                               | 2 +-
 net/ipv4/ip_tunnel.c                                        | 4 ++--
 net/ipv4/ipmr.c                                             | 2 +-
 net/ipv6/ip6_gre.c                                          | 9 +++++----
 net/ipv6/ip6_tunnel.c                                       | 8 ++++----
 net/ipv6/ip6_vti.c                                          | 8 ++++----
 net/ipv6/ip6mr.c                                            | 2 +-
 net/ipv6/sit.c                                              | 6 +++---
 net/irda/irlan/irlan_eth.c                                  | 2 +-
 net/l2tp/l2tp_eth.c                                         | 2 +-
 net/mac80211/iface.c                                        | 6 +++---
 net/mac802154/iface.c                                       | 7 +++----
 net/openvswitch/vport-internal_dev.c                        | 4 ++--
 net/phonet/pep-gprs.c                                       | 2 +-
 59 files changed, 102 insertions(+), 100 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 34481c9..6d80067 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -4163,7 +4163,6 @@ static void bond_destructor(struct net_device *bond_dev)
    struct bonding *bond = netdev_priv(bond_dev);
    if (bond->wq)
        destroy_workqueue(bond->wq);
-   free_netdev(bond_dev);
 }

 void bond_setup(struct net_device *bond_dev)
@@ -4183,7 +4182,8 @@ void bond_setup(struct net_device *bond_dev)
    bond_dev->netdev_ops = &bond_netdev_ops;
    bond_dev->ethtool_ops = &bond_ethtool_ops;

-   bond_dev->destructor = bond_destructor;
+   bond_dev->needs_free_netdev = true;
+   bond_dev->priv_destructor = bond_destructor;

    SET_NETDEV_DEVTYPE(bond_dev, &bond_type);

@@ -4689,7 +4689,7 @@ int bond_create(struct net *net, const char *name)

    rtnl_unlock();
    if (res < 0)
-       bond_destructor(bond_dev);
+       free_netdev(bond_dev);
    return res;
 }

diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index ddabce7..71a7c3b 100644
--- a/drivers/net/caif/caif_hsi.c
+++ b/drivers/net/caif/caif_hsi.c
@@ -1121,7 +1121,7 @@ static void cfhsi_setup(struct net_device *dev)
    dev->flags = IFF_POINTOPOINT | IFF_NOARP;
    dev->mtu = CFHSI_MAX_CAIF_FRAME_SZ;
    dev->priv_flags |= IFF_NO_QUEUE;
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;
    dev->netdev_ops = &cfhsi_netdevops;
    for (i = 0; i < CFHSI_PRIO_LAST; ++i)
        skb_queue_head_init(&cfhsi->qhead[i]);
diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c
index c2dea49..76e1d35 100644
--- a/drivers/net/caif/caif_serial.c
+++ b/drivers/net/caif/caif_serial.c
@@ -428,7 +428,7 @@ static void caifdev_setup(struct net_device *dev)
    dev->flags = IFF_POINTOPOINT | IFF_NOARP;
    dev->mtu = CAIF_MAX_MTU;
    dev->priv_flags |= IFF_NO_QUEUE;
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;
    skb_queue_head_init(&serdev->head);
    serdev->common.link_select = CAIF_LINK_LOW_LATENCY;
    serdev->common.use_frag = true;
diff --git a/drivers/net/caif/caif_spi.c b/drivers/net/caif/caif_spi.c
index 3a529fb..fc21afe 100644
--- a/drivers/net/caif/caif_spi.c
+++ b/drivers/net/caif/caif_spi.c
@@ -712,7 +712,7 @@ static void cfspi_setup(struct net_device *dev)
    dev->flags = IFF_NOARP | IFF_POINTOPOINT;
    dev->priv_flags |= IFF_NO_QUEUE;
    dev->mtu = SPI_MAX_PAYLOAD_SIZE;
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;
    skb_queue_head_init(&cfspi->qhead);
    skb_queue_head_init(&cfspi->chead);
    cfspi->cfdev.link_select = CAIF_LINK_HIGH_BANDW;
diff --git a/drivers/net/caif/caif_virtio.c b/drivers/net/caif/caif_virtio.c
index bc0eb47..8bffd25 100644
--- a/drivers/net/caif/caif_virtio.c
+++ b/drivers/net/caif/caif_virtio.c
@@ -617,7 +617,7 @@ static void cfv_netdev_setup(struct net_device *netdev)
    netdev->tx_queue_len = 100;
    netdev->flags = IFF_POINTOPOINT | IFF_NOARP;
    netdev->mtu = CFV_DEF_MTU_SIZE;
-   netdev->destructor = free_netdev;
+   netdev->needs_free_netdev = true;
 }

 /* Create debugfs counters for the device */
diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c
index eb71737..6a6e896 100644
--- a/drivers/net/can/slcan.c
+++ b/drivers/net/can/slcan.c
@@ -417,7 +417,7 @@ static int slc_open(struct net_device *dev)
 static void slc_free_netdev(struct net_device *dev)
 {
    int i = dev->base_addr;
-   free_netdev(dev);
+
    slcan_devs[i] = NULL;
 }

@@ -436,7 +436,8 @@ static int slcan_change_mtu(struct net_device *dev, int new_mtu)
 static void slc_setup(struct net_device *dev)
 {
    dev->netdev_ops     = &slc_netdev_ops;
-   dev->destructor     = slc_free_netdev;
+   dev->needs_free_netdev  = true;
+   dev->priv_destructor    = slc_free_netdev;

    dev->hard_header_len    = 0;
    dev->addr_len       = 0;
@@ -761,8 +762,6 @@ static void __exit slcan_exit(void)
        if (sl->tty) {
            printk(KERN_ERR "%s: tty discipline still runningn",
                   dev->name);
-           /* Intentionally leak the control block. */
-           dev->destructor = NULL;
        }

        unregister_netdev(dev);
diff --git a/drivers/net/can/vcan.c b/drivers/net/can/vcan.c
index 674f367..25d1209 100644
--- a/drivers/net/can/vcan.c
+++ b/drivers/net/can/vcan.c
@@ -160,7 +160,7 @@ static void vcan_setup(struct net_device *dev)
        dev->flags |= IFF_ECHO;

    dev->netdev_ops     = &vcan_netdev_ops;
-   dev->destructor     = free_netdev;
+   dev->needs_free_netdev  = true;
 }

 static struct rtnl_link_ops vcan_link_ops __read_mostly = {
diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c
index 2c80611..b66fbe5 100644
--- a/drivers/net/dummy.c
+++ b/drivers/net/dummy.c
@@ -313,7 +313,6 @@ static void dummy_free_netdev(struct net_device *dev)
    struct dummy_priv *priv = netdev_priv(dev);

    kfree(priv->vfinfo);
-   free_netdev(dev);
 }

 static void dummy_setup(struct net_device *dev)
@@ -323,7 +322,8 @@ static void dummy_setup(struct net_device *dev)
    /* Initialize the device structure. */
    dev->netdev_ops = &dummy_netdev_ops;
    dev->ethtool_ops = &dummy_ethtool_ops;
-   dev->destructor = dummy_free_netdev;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = dummy_free_netdev;

    /* Fill in device structure with ethernet-generic values. */
    dev->flags |= IFF_NOARP;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 1238c4e..fb09519 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -4530,7 +4530,7 @@ static void dummy_setup(struct net_device *dev)
    /* Initialize the device structure. */
    dev->netdev_ops = &cxgb4_mgmt_netdev_ops;
    dev->ethtool_ops = &cxgb4_mgmt_ethtool_ops;
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;
 }

 static int config_mgmt_dev(struct pci_dev *pdev)
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 6ebb0f5..199459b 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -1007,7 +1007,7 @@ static void geneve_setup(struct net_device *dev)

    dev->netdev_ops = &geneve_netdev_ops;
    dev->ethtool_ops = &geneve_ethtool_ops;
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;

    SET_NETDEV_DEVTYPE(dev, &geneve_type);

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 8969874..4293870 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -618,7 +618,7 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 static void gtp_link_setup(struct net_device *dev)
 {
    dev->netdev_ops     = &gtp_netdev_ops;
-   dev->destructor     = free_netdev;
+   dev->needs_free_netdev  = true;

    dev->hard_header_len = 0;
    dev->addr_len = 0;
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
index 922bf44..021a8ec 100644
--- a/drivers/net/hamradio/6pack.c
+++ b/drivers/net/hamradio/6pack.c
@@ -311,7 +311,7 @@ static void sp_setup(struct net_device *dev)
 {
    /* Finish setting up the DEVICE info. */
    dev->netdev_ops     = &sp_netdev_ops;
-   dev->destructor     = free_netdev;
+   dev->needs_free_netdev  = true;
    dev->mtu        = SIXP_MTU;
    dev->hard_header_len    = AX25_MAX_HEADER_LEN;
    dev->header_ops     = &ax25_header_ops;
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index f62e7f3..78a6414 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -476,7 +476,7 @@ static int bpq_info_open(struct inode *inode, struct file *file)
 static void bpq_setup(struct net_device *dev)
 {
    dev->netdev_ops      = &bpq_netdev_ops;
-   dev->destructor      = free_netdev;
+   dev->needs_free_netdev = true;

    memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN);
    memcpy(dev->dev_addr,  &ax25_defaddr, AX25_ADDR_LEN);
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index 312fce7..144ea5a 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -207,7 +207,6 @@ static void ifb_dev_free(struct net_device *dev)
        __skb_queue_purge(&txp->tq);
    }
    kfree(dp->tx_private);
-   free_netdev(dev);
 }

 static void ifb_setup(struct net_device *dev)
@@ -230,7 +229,8 @@ static void ifb_setup(struct net_device *dev)
    dev->priv_flags &= ~IFF_TX_SKB_SHARING;
    netif_keep_dst(dev);
    eth_hw_addr_random(dev);
-   dev->destructor = ifb_dev_free;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = ifb_dev_free;
 }

 static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
index aa8575c..48e6c4e 100644
--- a/drivers/net/ipvlan/ipvlan_main.c
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -621,7 +621,7 @@ void ipvlan_link_setup(struct net_device *dev)
    dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
    dev->priv_flags |= IFF_UNICAST_FLT | IFF_NO_QUEUE;
    dev->netdev_ops = &ipvlan_netdev_ops;
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;
    dev->header_ops = &ipvlan_header_ops;
    dev->ethtool_ops = &ipvlan_ethtool_ops;
 }
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index b23b719..80e3ace 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -145,7 +145,6 @@ static void loopback_dev_free(struct net_device *dev)
 {
    dev_net(dev)->loopback_dev = NULL;
    free_percpu(dev->lstats);
-   free_netdev(dev);
 }

 static const struct net_device_ops loopback_ops = {
@@ -183,7 +182,8 @@ static void loopback_setup(struct net_device *dev)
    dev->ethtool_ops    = &loopback_ethtool_ops;
    dev->header_ops     = &eth_header_ops;
    dev->netdev_ops     = &loopback_ops;
-   dev->destructor     = loopback_dev_free;
+   dev->needs_free_netdev  = true;
+   dev->priv_destructor    = loopback_dev_free;
 }

 /* Setup and register the loopback device. */
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index 49ce4e9..43f3e1b 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -2994,7 +2994,6 @@ static void macsec_free_netdev(struct net_device *dev)
    free_percpu(macsec->secy.tx_sc.stats);

    dev_put(real_dev);
-   free_netdev(dev);
 }

 static void macsec_setup(struct net_device *dev)
@@ -3004,7 +3003,8 @@ static void macsec_setup(struct net_device *dev)
    dev->max_mtu = ETH_MAX_MTU;
    dev->priv_flags |= IFF_NO_QUEUE;
    dev->netdev_ops = &macsec_netdev_ops;
-   dev->destructor = macsec_free_netdev;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = macsec_free_netdev;
    SET_NETDEV_DEVTYPE(dev, &macsec_type);

    eth_zero_addr(dev->broadcast);
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index b34eaaa..b8cec52 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -1089,7 +1089,7 @@ void macvlan_common_setup(struct net_device *dev)
    netif_keep_dst(dev);
    dev->priv_flags        |= IFF_UNICAST_FLT;
    dev->netdev_ops     = &macvlan_netdev_ops;
-   dev->destructor     = free_netdev;
+   dev->needs_free_netdev  = true;
    dev->header_ops     = &macvlan_hard_header_ops;
    dev->ethtool_ops    = &macvlan_ethtool_ops;
 }
diff --git a/drivers/net/nlmon.c b/drivers/net/nlmon.c
index b916038..c4b3362 100644
--- a/drivers/net/nlmon.c
+++ b/drivers/net/nlmon.c
@@ -113,7 +113,7 @@ static void nlmon_setup(struct net_device *dev)

    dev->netdev_ops = &nlmon_ops;
    dev->ethtool_ops = &nlmon_ethtool_ops;
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;

    dev->features = NETIF_F_SG | NETIF_F_FRAGLIST |
            NETIF_F_HIGHDMA | NETIF_F_LLTX;
diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c
index 1da31dc..74b9072 100644
--- a/drivers/net/slip/slip.c
+++ b/drivers/net/slip/slip.c
@@ -629,7 +629,7 @@ static void sl_uninit(struct net_device *dev)
 static void sl_free_netdev(struct net_device *dev)
 {
    int i = dev->base_addr;
-   free_netdev(dev);
+
    slip_devs[i] = NULL;
 }

@@ -651,7 +651,8 @@ static void sl_free_netdev(struct net_device *dev)
 static void sl_setup(struct net_device *dev)
 {
    dev->netdev_ops     = &sl_netdev_ops;
-   dev->destructor     = sl_free_netdev;
+   dev->needs_free_netdev  = true;
+   dev->priv_destructor    = sl_free_netdev;

    dev->hard_header_len    = 0;
    dev->addr_len       = 0;
@@ -1369,8 +1370,6 @@ static void __exit slip_exit(void)
        if (sl->tty) {
            printk(KERN_ERR "%s: tty discipline still runningn",
                   dev->name);
-           /* Intentionally leak the control block. */
-           dev->destructor = NULL;
        }

        unregister_netdev(dev);
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 85c0124..eaa6ee7 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -1643,7 +1643,6 @@ static void team_destructor(struct net_device *dev)
    struct team *team = netdev_priv(dev);

    free_percpu(team->pcpu_stats);
-   free_netdev(dev);
 }

 static int team_open(struct net_device *dev)
@@ -2079,7 +2078,8 @@ static void team_setup(struct net_device *dev)

    dev->netdev_ops = &team_netdev_ops;
    dev->ethtool_ops = &team_ethtool_ops;
-   dev->destructor = team_destructor;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = team_destructor;
    dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
    dev->priv_flags |= IFF_NO_QUEUE;
    dev->priv_flags |= IFF_TEAM;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index cc88cd7..7f3db49 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1560,7 +1560,6 @@ static void tun_free_netdev(struct net_device *dev)
    free_percpu(tun->pcpu_stats);
    tun_flow_uninit(tun);
    security_tun_dev_free_security(tun->security);
-   free_netdev(dev);
 }

 static void tun_setup(struct net_device *dev)
@@ -1571,7 +1570,8 @@ static void tun_setup(struct net_device *dev)
    tun->group = INVALID_GID;

    dev->ethtool_ops = &tun_ethtool_ops;
-   dev->destructor = tun_free_netdev;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = tun_free_netdev;
    /* We prefer our own queue length */
    dev->tx_queue_len = TUN_READQ_SIZE;
 }
diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c
index eb52de8..c7a350b 100644
--- a/drivers/net/usb/cdc-phonet.c
+++ b/drivers/net/usb/cdc-phonet.c
@@ -298,7 +298,7 @@ static void usbpn_setup(struct net_device *dev)
    dev->addr_len       = 1;
    dev->tx_queue_len   = 3;

-   dev->destructor     = free_netdev;
+   dev->needs_free_netdev  = true;
 }

 /*
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 8c39d6d..2efbe98 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -227,7 +227,6 @@ static int veth_dev_init(struct net_device *dev)
 static void veth_dev_free(struct net_device *dev)
 {
    free_percpu(dev->vstats);
-   free_netdev(dev);
 }

 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -322,7 +321,8 @@ static void veth_setup(struct net_device *dev)
                   NETIF_F_HW_VLAN_STAG_TX |
                   NETIF_F_HW_VLAN_CTAG_RX |
                   NETIF_F_HW_VLAN_STAG_RX);
-   dev->destructor = veth_dev_free;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = veth_dev_free;
    dev->max_mtu = ETH_MAX_MTU;

    dev->hw_features = VETH_FEATURES;
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index df74efc..e669372 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -1206,7 +1206,7 @@ static void vrf_setup(struct net_device *dev)
    dev->netdev_ops = &vrf_netdev_ops;
    dev->l3mdev_ops = &vrf_l3mdev_ops;
    dev->ethtool_ops = &vrf_ethtool_ops;
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;

    /* Fill in device structure with ethernet-generic values. */
    eth_hw_addr_random(dev);
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 70dbd5a..4574b95 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -2607,7 +2607,7 @@ static void vxlan_setup(struct net_device *dev)
    eth_hw_addr_random(dev);
    ether_setup(dev);

-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;
    SET_NETDEV_DEVTYPE(dev, &vxlan_type);

    dev->features   |= NETIF_F_LLTX;
diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c
index 65ee2a6..a0d76f7 100644
--- a/drivers/net/wan/dlci.c
+++ b/drivers/net/wan/dlci.c
@@ -475,7 +475,7 @@ static void dlci_setup(struct net_device *dev)
    dev->flags      = 0;
    dev->header_ops     = &dlci_header_ops;
    dev->netdev_ops     = &dlci_netdev_ops;
-   dev->destructor     = free_netdev;
+   dev->needs_free_netdev  = true;

    dlp->receive        = dlci_receive;

diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index eb91528..78596e4 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -1106,7 +1106,7 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type)
        return -EIO;
    }

-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;
    *get_dev_p(pvc, type) = dev;
    if (!used) {
        state(hdlc)->dce_changed = 1;
diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c
index 9df9ed6..63f7490 100644
--- a/drivers/net/wan/lapbether.c
+++ b/drivers/net/wan/lapbether.c
@@ -306,7 +306,7 @@ static int lapbeth_close(struct net_device *dev)
 static void lapbeth_setup(struct net_device *dev)
 {
    dev->netdev_ops      = &lapbeth_netdev_ops;
-   dev->destructor      = free_netdev;
+   dev->needs_free_netdev = true;
    dev->type            = ARPHRD_X25;
    dev->hard_header_len = 3;
    dev->mtu             = 1000;
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
index 91ee542..b90c77e 100644
--- a/drivers/net/wireless/ath/ath6kl/main.c
+++ b/drivers/net/wireless/ath/ath6kl/main.c
@@ -1287,7 +1287,7 @@ void init_netdev(struct net_device *dev)
    struct ath6kl *ar = ath6kl_priv(dev);

    dev->netdev_ops = &ath6kl_netdev_ops;
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;
    dev->watchdog_timeo = ATH6KL_TX_TIMEOUT;

    dev->needed_headroom = ETH_HLEN;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 944b83c..017e20b 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -5209,7 +5209,6 @@ void brcmf_cfg80211_free_netdev(struct net_device *ndev)

    if (vif)
        brcmf_free_vif(vif);
-   free_netdev(ndev);
 }

 static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index f6b17fb..d02e7f6 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -652,7 +652,8 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
        if (!ndev)
            return ERR_PTR(-ENOMEM);

-       ndev->destructor = brcmf_cfg80211_free_netdev;
+       ndev->needs_free_netdev = true;
+       ndev->priv_destructor = brcmf_cfg80211_free_netdev;
        ifp = netdev_priv(ndev);
        ifp->ndev = ndev;
        /* store mapping ifidx to bsscfgidx */
diff --git a/drivers/net/wireless/intersil/hostap/hostap_main.c b/drivers/net/wireless/intersil/hostap/hostap_main.c
index 544fc09..1372b20 100644
--- a/drivers/net/wireless/intersil/hostap/hostap_main.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_main.c
@@ -73,7 +73,7 @@ struct net_device * hostap_add_interface(struct local_info *local,
    dev->mem_end = mdev->mem_end;

    hostap_setup_dev(dev, local, type);
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;

    sprintf(dev->name, "%s%s", prefix, name);
    if (!rtnl_locked)
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 50c219f..2aa5cc6 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -2807,7 +2807,7 @@ static void mac80211_hwsim_free(void)
 static void hwsim_mon_setup(struct net_device *dev)
 {
    dev->netdev_ops = &hwsim_netdev_ops;
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;
    ether_setup(dev);
    dev->priv_flags |= IFF_NO_QUEUE;
    dev->type = ARPHRD_IEEE80211_RADIOTAP;
diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c
index b62e03d..46073bb 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.c
+++ b/drivers/net/wireless/marvell/mwifiex/main.c
@@ -1277,7 +1277,7 @@ void mwifiex_init_priv_params(struct mwifiex_private *priv,
                  struct net_device *dev)
 {
    dev->netdev_ops = &mwifiex_netdev_ops;
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;
    /* Initialize private structure */
    priv->current_key_index = 0;
    priv->media_connected = false;
diff --git a/drivers/staging/rtl8188eu/os_dep/mon.c b/drivers/staging/rtl8188eu/os_dep/mon.c
index cfe37eb..859d0d6 100644
--- a/drivers/staging/rtl8188eu/os_dep/mon.c
+++ b/drivers/staging/rtl8188eu/os_dep/mon.c
@@ -152,7 +152,7 @@ static netdev_tx_t mon_xmit(struct sk_buff *skb, struct net_device *dev)
 static void mon_setup(struct net_device *dev)
 {
    dev->netdev_ops = &mon_netdev_ops;
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;
    ether_setup(dev);
    dev->priv_flags |= IFF_NO_QUEUE;
    dev->type = ARPHRD_IEEE80211;
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index b4058f0..6a1ce6a 100644
--- a/drivers/usb/gadget/function/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
@@ -281,7 +281,7 @@ static void pn_net_setup(struct net_device *dev)
    dev->tx_queue_len   = 1;

    dev->netdev_ops     = &pn_netdev_ops;
-   dev->destructor     = free_netdev;
+   dev->needs_free_netdev  = true;
    dev->header_ops     = &phonet_header_ops;
 }

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 97456b25..3ccabb4 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1581,8 +1581,8 @@ enum netdev_priv_flags {
  * @rtnl_link_state:   This enum represents the phases of creating
  *             a new link
  *
- * @destructor:        Called from unregister,
- *             can be used to call free_netdev
+ * @needs_free_netdev: Should unregister perform free_netdev?
+ * @priv_destructor:   Called from unregister
  * @npinfo:        XXX: need comments on this one
  *     @nd_net:        Network namespace this network device is inside
  *
@@ -1838,7 +1838,8 @@ struct net_device {
        RTNL_LINK_INITIALIZING,
    } rtnl_link_state:16;

-   void (*destructor)(struct net_device *dev);
+   bool needs_free_netdev;
+   void (*priv_destructor)(struct net_device *dev);

 #ifdef CONFIG_NETPOLL
    struct netpoll_info __rcu   *npinfo;
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index e97ab82..0064dfd 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -807,7 +807,6 @@ static void vlan_dev_free(struct net_device *dev)

    free_percpu(vlan->vlan_pcpu_stats);
    vlan->vlan_pcpu_stats = NULL;
-   free_netdev(dev);
 }

 void vlan_setup(struct net_device *dev)
@@ -820,7 +819,8 @@ void vlan_setup(struct net_device *dev)
    netif_keep_dst(dev);

    dev->netdev_ops     = &vlan_netdev_ops;
-   dev->destructor     = vlan_dev_free;
+   dev->needs_free_netdev  = true;
+   dev->priv_destructor    = vlan_dev_free;
    dev->ethtool_ops    = &vlan_ethtool_ops;

    dev->min_mtu        = 0;
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index d042c99..07ca0e8 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -961,8 +961,6 @@ static void batadv_softif_free(struct net_device *dev)
     * netdev and its private data (bat_priv)
     */
    rcu_barrier();
-
-   free_netdev(dev);
 }

 /**
@@ -976,7 +974,8 @@ static void batadv_softif_init_early(struct net_device *dev)
    ether_setup(dev);

    dev->netdev_ops = &batadv_netdev_ops;
-   dev->destructor = batadv_softif_free;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = batadv_softif_free;
    dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_NETNS_LOCAL;
    dev->priv_flags |= IFF_NO_QUEUE;

diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index d491529..7ffeca7 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -653,7 +653,7 @@ static void netdev_setup(struct net_device *dev)

    dev->netdev_ops     = &netdev_ops;
    dev->header_ops     = &header_ops;
-   dev->destructor     = free_netdev;
+   dev->needs_free_netdev  = true;
 }

 static struct device_type bt_type = {
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 430b53e..f0f3447 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -379,7 +379,7 @@ void br_dev_setup(struct net_device *dev)
    ether_setup(dev);

    dev->netdev_ops = &br_netdev_ops;
-   dev->destructor = free_netdev;
+   dev->needs_free_netdev = true;
    dev->ethtool_ops = &br_ethtool_ops;
    SET_NETDEV_DEVTYPE(dev, &br_type);
    dev->priv_flags = IFF_EBRIDGE | IFF_NO_QUEUE;
diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c
index 1816fc9..fe3c53e 100644
--- a/net/caif/chnl_net.c
+++ b/net/caif/chnl_net.c
@@ -392,14 +392,14 @@ static void chnl_net_destructor(struct net_device *dev)
 {
    struct chnl_net *priv = netdev_priv(dev);
    caif_free_client(&priv->chnl);
-   free_netdev(dev);
 }

 static void ipcaif_net_setup(struct net_device *dev)
 {
    struct chnl_net *priv;
    dev->netdev_ops = &netdev_ops;
-   dev->destructor = chnl_net_destructor;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = chnl_net_destructor;
    dev->flags |= IFF_NOARP;
    dev->flags |= IFF_POINTOPOINT;
    dev->mtu = GPRS_PDP_MTU;
diff --git a/net/core/dev.c b/net/core/dev.c
index 85c626b..9debc1b 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -7346,6 +7346,8 @@ int register_netdevice(struct net_device *dev)
 err_uninit:
    if (dev->netdev_ops->ndo_uninit)
        dev->netdev_ops->ndo_uninit(dev);
+   if (dev->priv_destructor)
+       dev->priv_destructor(dev);
    goto out;
 }
 EXPORT_SYMBOL(register_netdevice);
@@ -7553,8 +7555,10 @@ void netdev_run_todo(void)
        WARN_ON(rcu_access_pointer(dev->ip6_ptr));
        WARN_ON(dev->dn_ptr);

-       if (dev->destructor)
-           dev->destructor(dev);
+       if (dev->priv_destructor)
+           dev->priv_destructor(dev);
+       if (dev->needs_free_netdev)
+           free_netdev(dev);

        /* Report a network device has been unregistered */
        rtnl_lock();
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
index c73160f..0a0a392 100644
--- a/net/hsr/hsr_device.c
+++ b/net/hsr/hsr_device.c
@@ -378,7 +378,6 @@ static void hsr_dev_destroy(struct net_device *hsr_dev)
    del_timer_sync(&hsr->announce_timer);

    synchronize_rcu();
-   free_netdev(hsr_dev);
 }

 static const struct net_device_ops hsr_device_ops = {
@@ -404,7 +403,8 @@ void hsr_dev_setup(struct net_device *dev)
    SET_NETDEV_DEVTYPE(dev, &hsr_type);
    dev->priv_flags |= IFF_NO_QUEUE;

-   dev->destructor = hsr_dev_destroy;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = hsr_dev_destroy;

    dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
               NETIF_F_GSO_MASK | NETIF_F_HW_CSUM |
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index d7efbf0..0a866f33 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -107,7 +107,7 @@ static void lowpan_setup(struct net_device *ldev)

    ldev->netdev_ops    = &lowpan_netdev_ops;
    ldev->header_ops    = &lowpan_header_ops;
-   ldev->destructor    = free_netdev;
+   ldev->needs_free_netdev = true;
    ldev->features      |= NETIF_F_NETNS_LOCAL;
 }

diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index 823abae..b2d1be5 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -961,7 +961,6 @@ static void ip_tunnel_dev_free(struct net_device *dev)
    gro_cells_destroy(&tunnel->gro_cells);
    dst_cache_destroy(&tunnel->dst_cache);
    free_percpu(dev->tstats);
-   free_netdev(dev);
 }

 void ip_tunnel_dellink(struct net_device *dev, struct list_head *head)
@@ -1148,7 +1147,8 @@ int ip_tunnel_init(struct net_device *dev)
    struct iphdr *iph = &tunnel->parms.iph;
    int err;

-   dev->destructor = ip_tunnel_dev_free;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = ip_tunnel_dev_free;
    dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
    if (!dev->tstats)
        return -ENOMEM;
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index b036e85..96c5681 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -501,7 +501,7 @@ static void reg_vif_setup(struct net_device *dev)
    dev->mtu        = ETH_DATA_LEN - sizeof(struct iphdr) - 8;
    dev->flags      = IFF_NOARP;
    dev->netdev_ops     = &reg_vif_netdev_ops;
-   dev->destructor     = free_netdev;
+   dev->needs_free_netdev  = true;
    dev->features       |= NETIF_F_NETNS_LOCAL;
 }

diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 4d60164..0f1c249 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -986,13 +986,13 @@ static void ip6gre_dev_free(struct net_device *dev)

    dst_cache_destroy(&t->dst_cache);
    free_percpu(dev->tstats);
-   free_netdev(dev);
 }

 static void ip6gre_tunnel_setup(struct net_device *dev)
 {
    dev->netdev_ops = &ip6gre_netdev_ops;
-   dev->destructor = ip6gre_dev_free;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = ip6gre_dev_free;

    dev->type = ARPHRD_IP6GRE;

@@ -1143,7 +1143,7 @@ static int __net_init ip6gre_init_net(struct net *net)
    return 0;

 err_reg_dev:
-   ip6gre_dev_free(ign->fb_tunnel_dev);
+   free_netdev(ign->fb_tunnel_dev);
 err_alloc_dev:
    return err;
 }
@@ -1292,7 +1292,8 @@ static void ip6gre_tap_setup(struct net_device *dev)
    ether_setup(dev);

    dev->netdev_ops = &ip6gre_tap_netdev_ops;
-   dev->destructor = ip6gre_dev_free;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = ip6gre_dev_free;

    dev->features |= NETIF_F_NETNS_LOCAL;
    dev->priv_flags &= ~IFF_TX_SKB_SHARING;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index e2d7867..292f24c 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -254,7 +254,6 @@ static void ip6_dev_free(struct net_device *dev)
    gro_cells_destroy(&t->gro_cells);
    dst_cache_destroy(&t->dst_cache);
    free_percpu(dev->tstats);
-   free_netdev(dev);
 }

 static int ip6_tnl_create2(struct net_device *dev)
@@ -322,7 +321,7 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p)
    return t;

 failed_free:
-   ip6_dev_free(dev);
+   free_netdev(dev);
 failed:
    return ERR_PTR(err);
 }
@@ -1772,7 +1771,8 @@ int ip6_tnl_encap_setup(struct ip6_tnl *t,
 static void ip6_tnl_dev_setup(struct net_device *dev)
 {
    dev->netdev_ops = &ip6_tnl_netdev_ops;
-   dev->destructor = ip6_dev_free;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = ip6_dev_free;

    dev->type = ARPHRD_TUNNEL6;
    dev->flags |= IFF_NOARP;
@@ -2211,7 +2211,7 @@ static int __net_init ip6_tnl_init_net(struct net *net)
    return 0;

 err_register:
-   ip6_dev_free(ip6n->fb_tnl_dev);
+   free_netdev(ip6n->fb_tnl_dev);
 err_alloc_dev:
    return err;
 }
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index 3d8a3b6..5659f4a 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -180,7 +180,6 @@ struct vti6_net {
 static void vti6_dev_free(struct net_device *dev)
 {
    free_percpu(dev->tstats);
-   free_netdev(dev);
 }

 static int vti6_tnl_create2(struct net_device *dev)
@@ -235,7 +234,7 @@ static struct ip6_tnl *vti6_tnl_create(struct net *net, struct __ip6_tnl_parm *p
    return t;

 failed_free:
-   vti6_dev_free(dev);
+   free_netdev(dev);
 failed:
    return NULL;
 }
@@ -841,7 +840,8 @@ static int vti6_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p)
 static void vti6_dev_setup(struct net_device *dev)
 {
    dev->netdev_ops = &vti6_netdev_ops;
-   dev->destructor = vti6_dev_free;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = vti6_dev_free;

    dev->type = ARPHRD_TUNNEL6;
    dev->hard_header_len = LL_MAX_HEADER + sizeof(struct ipv6hdr);
@@ -1092,7 +1092,7 @@ static int __net_init vti6_init_net(struct net *net)
    return 0;

 err_register:
-   vti6_dev_free(ip6n->fb_tnl_dev);
+   free_netdev(ip6n->fb_tnl_dev);
 err_alloc_dev:
    return err;
 }
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index bf34d09..576fd93 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -733,7 +733,7 @@ static void reg_vif_setup(struct net_device *dev)
    dev->mtu        = 1500 - sizeof(struct ipv6hdr) - 8;
    dev->flags      = IFF_NOARP;
    dev->netdev_ops     = &reg_vif_netdev_ops;
-   dev->destructor     = free_netdev;
+   dev->needs_free_netdev  = true;
    dev->features       |= NETIF_F_NETNS_LOCAL;
 }

diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 99853c6..4f07a21 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -265,7 +265,7 @@ static struct ip_tunnel *ipip6_tunnel_locate(struct net *net,
    return nt;

 failed_free:
-   ipip6_dev_free(dev);
+   free_netdev(dev);
 failed:
    return NULL;
 }
@@ -1333,7 +1333,6 @@ static void ipip6_dev_free(struct net_device *dev)

    dst_cache_destroy(&tunnel->dst_cache);
    free_percpu(dev->tstats);
-   free_netdev(dev);
 }

 #define SIT_FEATURES (NETIF_F_SG      | 
@@ -1348,7 +1347,8 @@ static void ipip6_tunnel_setup(struct net_device *dev)
    int t_hlen = tunnel->hlen + sizeof(struct iphdr);

    dev->netdev_ops     = &ipip6_netdev_ops;
-   dev->destructor     = ipip6_dev_free;
+   dev->needs_free_netdev  = true;
+   dev->priv_destructor    = ipip6_dev_free;

    dev->type       = ARPHRD_SIT;
    dev->hard_header_len    = LL_MAX_HEADER + t_hlen;
diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c
index 74d09f9..3be8528 100644
--- a/net/irda/irlan/irlan_eth.c
+++ b/net/irda/irlan/irlan_eth.c
@@ -65,7 +65,7 @@ static void irlan_eth_setup(struct net_device *dev)
    ether_setup(dev);

    dev->netdev_ops     = &irlan_eth_netdev_ops;
-   dev->destructor     = free_netdev;
+   dev->needs_free_netdev  = true;
    dev->min_mtu        = 0;
    dev->max_mtu        = ETH_MAX_MTU;

diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c
index 6fd41d7..cafb667 100644
--- a/net/l2tp/l2tp_eth.c
+++ b/net/l2tp/l2tp_eth.c
@@ -133,7 +133,7 @@ static void l2tp_eth_dev_setup(struct net_device *dev)
    dev->priv_flags     &= ~IFF_TX_SKB_SHARING;
    dev->features       |= NETIF_F_LLTX;
    dev->netdev_ops     = &l2tp_eth_netdev_ops;
-   dev->destructor     = free_netdev;
+   dev->needs_free_netdev  = true;
 }

 static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb, int data_len)
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 5bb0c50..6d23427 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1198,7 +1198,6 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev,
 static void ieee80211_if_free(struct net_device *dev)
 {
    free_percpu(dev->tstats);
-   free_netdev(dev);
 }

 static void ieee80211_if_setup(struct net_device *dev)
@@ -1206,7 +1205,8 @@ static void ieee80211_if_setup(struct net_device *dev)
    ether_setup(dev);
    dev->priv_flags &= ~IFF_TX_SKB_SHARING;
    dev->netdev_ops = &ieee80211_dataif_ops;
-   dev->destructor = ieee80211_if_free;
+   dev->needs_free_netdev = true;
+   dev->priv_destructor = ieee80211_if_free;
 }

 static void ieee80211_if_setup_no_queue(struct net_device *dev)
@@ -1899,7 +1899,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,

        ret = register_netdevice(ndev);
        if (ret) {
-           ieee80211_if_free(ndev);
+           free_netdev(ndev);
            return ret;
        }
    }
diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c
index 06019db..bd88a9b 100644
--- a/net/mac802154/iface.c
+++ b/net/mac802154/iface.c
@@ -526,8 +526,6 @@ static void mac802154_wpan_free(struct net_device *dev)
    struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);

    mac802154_llsec_destroy(&sdata->sec);
-
-   free_netdev(dev);
 }

 static void ieee802154_if_setup(struct net_device *dev)
@@ -593,7 +591,8 @@ static void ieee802154_if_setup(struct net_device *dev)
                    sdata->dev->dev_addr);

        sdata->dev->header_ops = &mac802154_header_ops;
-       sdata->dev->destructor = mac802154_wpan_free;
+       sdata->dev->needs_free_netdev = true;
+       sdata->dev->priv_destructor = mac802154_wpan_free;
        sdata->dev->netdev_ops = &mac802154_wpan_ops;
        sdata->dev->ml_priv = &mac802154_mlme_wpan;
        wpan_dev->promiscuous_mode = false;
@@ -608,7 +607,7 @@ static void ieee802154_if_setup(struct net_device *dev)

        break;
    case NL802154_IFTYPE_MONITOR:
-       sdata->dev->destructor = free_netdev;
+       sdata->dev->needs_free_netdev = true;
        sdata->dev->netdev_ops = &mac802154_monitor_ops;
        wpan_dev->promiscuous_mode = true;
        break;
diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c
index 89193a6..04a3128 100644
--- a/net/openvswitch/vport-internal_dev.c
+++ b/net/openvswitch/vport-internal_dev.c
@@ -94,7 +94,6 @@ static void internal_dev_destructor(struct net_device *dev)
    struct vport *vport = ovs_internal_dev_get_vport(dev);

    ovs_vport_free(vport);
-   free_netdev(dev);
 }

 static void
@@ -156,7 +155,8 @@ static void do_setup(struct net_device *netdev)
    netdev->priv_flags &= ~IFF_TX_SKB_SHARING;
    netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_OPENVSWITCH |
                  IFF_PHONY_HEADROOM | IFF_NO_QUEUE;
-   netdev->destructor = internal_dev_destructor;
+   netdev->needs_free_netdev = true;
+   netdev->priv_destructor = internal_dev_destructor;
    netdev->ethtool_ops = &internal_dev_ethtool_ops;
    netdev->rtnl_link_ops = &internal_dev_link_ops;

diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c
index 21c28b5..2c93379 100644
--- a/net/phonet/pep-gprs.c
+++ b/net/phonet/pep-gprs.c
@@ -236,7 +236,7 @@ static void gprs_setup(struct net_device *dev)
    dev->tx_queue_len   = 10;

    dev->netdev_ops     = &gprs_netdev_ops;
-   dev->destructor     = free_netdev;
+   dev->needs_free_netdev  = true;
 }

 /*

Leave a Reply

Your email address will not be published. Required fields are marked *