net: Fix inconsistent teardown and release of private netdev state.
authorDavid S. Miller <davem@davemloft.net>
Mon, 8 May 2017 16:52:56 +0000 (12:52 -0400)
committerDavid S. Miller <davem@davemloft.net>
Wed, 7 Jun 2017 19:53:24 +0000 (15:53 -0400)
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 <davem@davemloft.net>
62 files changed:
drivers/net/bonding/bond_main.c
drivers/net/caif/caif_hsi.c
drivers/net/caif/caif_serial.c
drivers/net/caif/caif_spi.c
drivers/net/caif/caif_virtio.c
drivers/net/can/slcan.c
drivers/net/can/vcan.c
drivers/net/can/vxcan.c
drivers/net/dummy.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/geneve.c
drivers/net/gtp.c
drivers/net/hamradio/6pack.c
drivers/net/hamradio/bpqether.c
drivers/net/ifb.c
drivers/net/ipvlan/ipvlan_main.c
drivers/net/loopback.c
drivers/net/macsec.c
drivers/net/macvlan.c
drivers/net/nlmon.c
drivers/net/slip/slip.c
drivers/net/team/team.c
drivers/net/tun.c
drivers/net/usb/cdc-phonet.c
drivers/net/usb/qmi_wwan.c
drivers/net/veth.c
drivers/net/vrf.c
drivers/net/vsockmon.c
drivers/net/vxlan.c
drivers/net/wan/dlci.c
drivers/net/wan/hdlc_fr.c
drivers/net/wan/lapbether.c
drivers/net/wireless/ath/ath6kl/main.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
drivers/net/wireless/intersil/hostap/hostap_main.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/marvell/mwifiex/main.c
drivers/staging/rtl8188eu/os_dep/mon.c
drivers/usb/gadget/function/f_phonet.c
include/linux/netdevice.h
net/8021q/vlan_dev.c
net/batman-adv/soft-interface.c
net/bluetooth/6lowpan.c
net/bridge/br_device.c
net/caif/chnl_net.c
net/core/dev.c
net/hsr/hsr_device.c
net/ieee802154/6lowpan/core.c
net/ipv4/ip_tunnel.c
net/ipv4/ipmr.c
net/ipv6/ip6_gre.c
net/ipv6/ip6_tunnel.c
net/ipv6/ip6_vti.c
net/ipv6/ip6mr.c
net/ipv6/sit.c
net/irda/irlan/irlan_eth.c
net/l2tp/l2tp_eth.c
net/mac80211/iface.c
net/mac802154/iface.c
net/openvswitch/vport-internal_dev.c
net/phonet/pep-gprs.c

index 2359478b977f0e008335e51dc8f63adc2dc35087..8ab6bdbe16820dd3e56ec517838903bedda515de 100644 (file)
@@ -4192,7 +4192,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)
@@ -4212,7 +4211,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);
 
@@ -4736,7 +4736,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;
 }
 
index ddabce7594565232a5e903c23248ea884995c149..71a7c3b44fdde3c3a7ede652a7dab681d577e6ce 100644 (file)
@@ -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]);
index c2dea4916e5d720bb29814153f302ec364fe4f61..76e1d3545105e8abb9e2ef644e03fab0d2f5b243 100644 (file)
@@ -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;
index 3a529fbe539fb6ee098f0b85515a49bdc1b64d90..fc21afe852b9817a2bdd242fc371834c515213dd 100644 (file)
@@ -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;
index 6122768c86444ec5b9c5fa67d7a63aa2690ba2f5..1794ea0420b794e76c75fafdede7e1a73ca86868 100644 (file)
@@ -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 */
index eb7173713bbcb0c339ae6ccbf99fc8014fb17133..6a6e896e52fa09415ae72715602fd4713e28f899 100644 (file)
@@ -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 const struct net_device_ops slc_netdev_ops = {
 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 running\n",
                               dev->name);
-                       /* Intentionally leak the control block. */
-                       dev->destructor = NULL;
                }
 
                unregister_netdev(dev);
index facca33d53e9fd24b996dc6dfd38c925b8128fdb..0eda1b308583ec474d2b420f746426381b50bc1e 100644 (file)
@@ -163,7 +163,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 = {
index 7fbb2479568160b3b57fedb187429618937d2248..30cf2368becfb6ea4896a4152ec2fc957c679b75 100644 (file)
@@ -156,7 +156,7 @@ static void vxcan_setup(struct net_device *dev)
        dev->tx_queue_len       = 0;
        dev->flags              = (IFF_NOARP|IFF_ECHO);
        dev->netdev_ops         = &vxcan_netdev_ops;
-       dev->destructor         = free_netdev;
+       dev->needs_free_netdev  = true;
 }
 
 /* forward declaration for rtnl_create_link() */
index 149244aac20aa765551b93ca25d78018b28f17f9..9905b52fe293c221b1ae9852d72016191ebbadda 100644 (file)
@@ -328,7 +328,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)
@@ -338,7 +337,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;
index 77ed2f628f9ca23854ae8b062ff919ce6d2e3425..ea1bfcf1870afbc5806e61dd6416669216f38ce7 100644 (file)
@@ -4525,7 +4525,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)
index 6ebb0f559a427fdb4d27d9b668b46d7151650043..199459bd69612478e596dc97248d2a40defd6db8 100644 (file)
@@ -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);
 
index 7b652bb7ebe407b35c054009b521b173fd9fa361..ca110cd2a4e42cdebed760bdad103627b556e2f7 100644 (file)
@@ -611,7 +611,7 @@ static const struct net_device_ops gtp_netdev_ops = {
 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;
index 922bf440e9f1cb2d20ba2d2419f73972b09714c3..021a8ec411ab8316d6080592e2469e37c912bc31 100644 (file)
@@ -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;
index f62e7f325cf92edeca25c40a1d11e53166017049..78a6414c5fd994445c7e530065bd6cfa7e5ad213 100644 (file)
@@ -476,7 +476,7 @@ static const struct net_device_ops bpq_netdev_ops = {
 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);
index 312fce7302d3252903282599223063e7d97bb863..144ea5ae8ab4abda588cb6b23292c9f3ccf06c9c 100644 (file)
@@ -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)
index 618ed88fad0fc1d4e227f0e84fde74462b2bc496..7c7680c8f0e32e149167f7943dc02bdc9646da23 100644 (file)
@@ -632,7 +632,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;
 }
index 224f65cb576bbf106a4779ef5f60b75f34903b1c..30612497643c08caa8a3bf352b13f784f729f725 100644 (file)
@@ -159,7 +159,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 = {
@@ -196,7 +195,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. */
index cdc347be68f23196d6ba820d21475260bc5bae3b..79411675f0e66376ecf2fa640b51c7172d8085d9 100644 (file)
@@ -2996,7 +2996,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)
@@ -3006,7 +3005,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);
index 346ad2ff39989d3da4cacc6ec1965ca882c475cd..67bf7ebae5c6dba9b9ea28c0c9c0801093f55eb7 100644 (file)
@@ -1092,7 +1092,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;
 }
index b91603835d2680aa08911da7870ea176c5dd0792..c4b3362da4a2e33184b02dff0df3082b4d7e45e4 100644 (file)
@@ -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;
index 1da31dc47f863845d50823b69e51f2abab5bf3f0..74b907206aa749d894531005716a0c6dd3d0ec1f 100644 (file)
@@ -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 const struct net_device_ops sl_netdev_ops = {
 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 running\n",
                               dev->name);
-                       /* Intentionally leak the control block. */
-                       dev->destructor = NULL;
                }
 
                unregister_netdev(dev);
index 6c5d5ef46f75aa9a9089ac80bbee30a7f579b016..fba8c136aa7c1513b288e641cfe8cd6dca304ccd 100644 (file)
@@ -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;
index bbd707b9ef7a6a305804ed0d56c3fc0e1db7d565..9ee7d4275640919f2293182cb47dca41412975ba 100644 (file)
@@ -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;
 }
index eb52de8205f0d48044bcb6c1302ce7bda779128e..c7a350bbaaa7c881a831ec8ed7b4b59dd83c6de9 100644 (file)
@@ -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;
 }
 
 /*
index 8f923a147fa93117296312c59fdc6761fef50f3c..949671ce403989015bdde0c54a3921c0ff0508e6 100644 (file)
@@ -123,7 +123,7 @@ static void qmimux_setup(struct net_device *dev)
        dev->addr_len        = 0;
        dev->flags           = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
        dev->netdev_ops      = &qmimux_netdev_ops;
-       dev->destructor      = free_netdev;
+       dev->needs_free_netdev = true;
 }
 
 static struct net_device *qmimux_find_dev(struct usbnet *dev, u8 mux_id)
index 38f0f03a29c8898131110a620e73da4776cb7020..0156fe8cac172a909cfe4ed5b9567572132b888d 100644 (file)
@@ -222,7 +222,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
@@ -317,7 +316,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;
index db882493875cd97d30ac5a2b26a764a5b65d9e2e..d38f11d833fe0f207fc3a588539fb49090dfd02e 100644 (file)
@@ -1348,7 +1348,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);
index 7f0136f2dd9d6167acc9b125fb03d8c2c5f9524d..c28bdce14fd5e32b287419227d7d33bf7835c409 100644 (file)
@@ -135,7 +135,7 @@ static void vsockmon_setup(struct net_device *dev)
 
        dev->netdev_ops = &vsockmon_ops;
        dev->ethtool_ops = &vsockmon_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;
index a6b5052c1d36bb99260dd4232842fa9e8df2621c..5fa798a5c9a695ac3796d2ee4a3c1eae59a1cf9f 100644 (file)
@@ -2611,7 +2611,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;
index 65ee2a6f248cfcbd2761272431c37627eafaced5..a0d76f70c4289d03e7b8c25700c58b218a934bd0 100644 (file)
@@ -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;
 
index eb915281197efc98a0fae96cc9dcdd2691a21ee3..78596e42a3f3f27623284c86a0d791a2bf4a65b6 100644 (file)
@@ -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;
index 9df9ed62beff0cb3b2c0be7dbe1f15e646da74de..63f749078a1f1051f3f46bec13f1f60c7c64ea5f 100644 (file)
@@ -306,7 +306,7 @@ static const struct net_device_ops lapbeth_netdev_ops = {
 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;
index 91ee542de3d79ec0903d56f2d45463af83b01a95..b90c77ef792ef8908173aa2f762d15089dce55fd 100644 (file)
@@ -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;
index cd1d6730eab73d514db5a1b88d442607c7834e3d..617199c0e5a0e644576b3c73be0808878d4c102b 100644 (file)
@@ -5225,7 +5225,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)
index a3d82368f1a9f9722e62920e44ef52f7ee8602ef..511d190c6cca1e864e50c7b1b6ae9caa45307e00 100644 (file)
@@ -624,7 +624,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 */
index 544fc09dcb62435dc7316b2d8463c76d386ed91e..1372b20f931e0cca990a158e0ef8fdfc71fd7f88 100644 (file)
@@ -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)
index 002b25cff5b65e18460a2da08637886274bfdfa5..c854a557998b4266c5d63c55c0463497e8dde715 100644 (file)
@@ -2861,7 +2861,7 @@ static const struct net_device_ops hwsim_netdev_ops = {
 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;
index dd87b9ff64c371911a74308be55e93e3a46cdd43..39b6b5e3f6e0e4e9ec6eb81e458250485e94c487 100644 (file)
@@ -1280,7 +1280,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;
index cfe37eb026d6d4418664b767c7b61473d79b1020..859d0d6051cdf6748d298a8f547aff141e5c55c6 100644 (file)
@@ -152,7 +152,7 @@ static const struct net_device_ops mon_netdev_ops = {
 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;
index b4058f0000e4878efae4a475f834d06f16679fdd..6a1ce6a551587f232207612404df80807af215cd 100644 (file)
@@ -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;
 }
 
index 3f39d27decf4d72e734734edec4403b00ca95657..ab7ca3fdc495de8e4c88f62d4f81a63f40ed96f3 100644 (file)
@@ -1596,8 +1596,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
  *
@@ -1858,7 +1858,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;
index 953b6728bd00c8ca7a4a20f2d2036c6f8f27f8e3..abc5f400fc71f2f57f3a029d1c196890ee4e39e0 100644 (file)
@@ -813,7 +813,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)
@@ -826,7 +825,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;
index b25789abf7b9e10aec7af1dfc41a5c9ff805284a..10f7edfb176ebd49c680ff4132db87aa00d3f04e 100644 (file)
@@ -1034,8 +1034,6 @@ static void batadv_softif_free(struct net_device *dev)
         * netdev and its private data (bat_priv)
         */
        rcu_barrier();
-
-       free_netdev(dev);
 }
 
 /**
@@ -1047,7 +1045,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;
 
index 608959989f8eddbfc9b97279a7fba61fd7381d04..ab3b654b05cc87e19a965d0419efb7d45bd4b94e 100644 (file)
@@ -598,7 +598,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 = {
index 430b53e7d941def09220a1c97a2e82d288304595..f0f3447e8aa48ff02c3f11f6da0e65e79cb28e90 100644 (file)
@@ -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;
index 1816fc9f1ee779f85874e886a9ba6db721c60680..fe3c53efb949ef29a3dc1f6278d9abc05b50f579 100644 (file)
@@ -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;
index 84e1e86a4bce93579dbcfa6e79bcd8c13f1852fc..4c15466305c3a032c12eebb183e8992ce69ac2b7 100644 (file)
@@ -7502,6 +7502,8 @@ out:
 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);
@@ -7709,8 +7711,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();
index c73160fb11e7c666f8ac8ed4103b7312ff769a58..0a0a392dc2bd64b8c4202cc1361d828f9f984dd1 100644 (file)
@@ -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 |
index d7efbf0dad20f8c4d3c42d039eff528c678bc04c..0a866f3322901e357091955bf24ac16fc5f6b94f 100644 (file)
@@ -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;
 }
 
index b878ecbc0608fb433ae858b70e6e0101aa20fc4e..b436d077563174c22b48a81a6a856f30dd831a5e 100644 (file)
@@ -967,7 +967,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)
@@ -1155,7 +1154,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;
index 551de4d023a8edbf74835b43cb32d9173eedae36..b4f9622ee9f59d9fdc75bbfbf4b0ac489aa5fee0 100644 (file)
@@ -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;
 }
 
index 0c5b4caa19491eb04bc755032611c76f03008acb..64eea3962733a323fbae25b3c7b6de658a8cfdea 100644 (file)
@@ -991,13 +991,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;
 
@@ -1148,7 +1148,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;
 }
@@ -1300,7 +1300,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;
index 9b37f9747fc6a6fbabb0740188bc98b5c95c41c4..c3581973f5d7265a574ae69416a516526ed64e44 100644 (file)
@@ -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);
 }
@@ -1777,7 +1776,8 @@ static const struct net_device_ops ip6_tnl_netdev_ops = {
 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;
@@ -2224,7 +2224,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;
 }
index d67ef56454b25a088768e88fcd1f878a8c498f12..837ea1eefe7f8cc85924a9604d58cd702af94667 100644 (file)
@@ -180,7 +180,6 @@ vti6_tnl_unlink(struct vti6_net *ip6n, struct ip6_tnl *t)
 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;
 }
@@ -842,7 +841,8 @@ static const struct net_device_ops vti6_netdev_ops = {
 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);
@@ -1100,7 +1100,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;
 }
index 374997d26488ea38db7ed42ff7e6a55ede249021..2ecb39b943b5002e63bfa5b045919fbfd9fd64f6 100644 (file)
@@ -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;
 }
 
index 61e5902f068732b10f734c7937c7539d418820d7..2378503577b0c8823049b7d17f857466481077b3 100644 (file)
@@ -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;
 }
@@ -1336,7 +1336,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          | \
@@ -1351,7 +1350,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;
index 74d09f91709e6121ed80b8f089283615a5ffce5e..3be852808a9d1f7a2767f482b334279a95e90642 100644 (file)
@@ -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;
 
index 8b21af7321b928b4dcc5d7af3a6667380e9a949a..f7c54ece3733fab8c3538cbc5065e05854ebe012 100644 (file)
@@ -141,7 +141,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)
index 8fae1a72e6a7c7ea4f71ec3a3beb215b987a715f..915d7e1b45455b1de2b895c063f0c08c760721fc 100644 (file)
@@ -1213,7 +1213,6 @@ static const struct net_device_ops ieee80211_monitorif_ops = {
 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)
@@ -1221,7 +1220,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)
@@ -1905,7 +1905,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;
                }
        }
index 06019dba4b10e3e0d079ee617707ae5412269144..bd88a9b80773e20e5de1f5a66ae7a3471387135e 100644 (file)
@@ -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 @@ ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
                                        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 @@ ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
 
                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;
index 89193a634da45bb78498ecd28ca39c455e32f2e4..04a3128adcf0adcc2603e45c9054cdd2fd0e7a0b 100644 (file)
@@ -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;
 
index 21c28b51be9439b20369b48077ac8392db7c3150..2c9337946e3038f130d198bb51d061a648ef1ed4 100644 (file)
@@ -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;
 }
 
 /*