net: Fix inconsistent teardown and release of private netdev state.
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>
This commit is contained in:
parent
7005cade1b
commit
cf124db566
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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() */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 = >p_netdev_ops;
|
||||
dev->destructor = free_netdev;
|
||||
dev->needs_free_netdev = true;
|
||||
|
||||
dev->hard_header_len = 0;
|
||||
dev->addr_len = 0;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 = ð_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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -7502,6 +7502,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);
|
||||
|
@ -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();
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = ®_vif_netdev_ops;
|
||||
dev->destructor = free_netdev;
|
||||
dev->needs_free_netdev = true;
|
||||
dev->features |= NETIF_F_NETNS_LOCAL;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 = ®_vif_netdev_ops;
|
||||
dev->destructor = free_netdev;
|
||||
dev->needs_free_netdev = true;
|
||||
dev->features |= NETIF_F_NETNS_LOCAL;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue