Skip to content

Commit cf124db

Browse files
committed
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 <[email protected]>
1 parent 7005cad commit cf124db

File tree

62 files changed

+105
-103
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+105
-103
lines changed

drivers/net/bonding/bond_main.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4192,7 +4192,6 @@ static void bond_destructor(struct net_device *bond_dev)
41924192
struct bonding *bond = netdev_priv(bond_dev);
41934193
if (bond->wq)
41944194
destroy_workqueue(bond->wq);
4195-
free_netdev(bond_dev);
41964195
}
41974196

41984197
void bond_setup(struct net_device *bond_dev)
@@ -4212,7 +4211,8 @@ void bond_setup(struct net_device *bond_dev)
42124211
bond_dev->netdev_ops = &bond_netdev_ops;
42134212
bond_dev->ethtool_ops = &bond_ethtool_ops;
42144213

4215-
bond_dev->destructor = bond_destructor;
4214+
bond_dev->needs_free_netdev = true;
4215+
bond_dev->priv_destructor = bond_destructor;
42164216

42174217
SET_NETDEV_DEVTYPE(bond_dev, &bond_type);
42184218

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

47374737
rtnl_unlock();
47384738
if (res < 0)
4739-
bond_destructor(bond_dev);
4739+
free_netdev(bond_dev);
47404740
return res;
47414741
}
47424742

drivers/net/caif/caif_hsi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,7 @@ static void cfhsi_setup(struct net_device *dev)
11211121
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
11221122
dev->mtu = CFHSI_MAX_CAIF_FRAME_SZ;
11231123
dev->priv_flags |= IFF_NO_QUEUE;
1124-
dev->destructor = free_netdev;
1124+
dev->needs_free_netdev = true;
11251125
dev->netdev_ops = &cfhsi_netdevops;
11261126
for (i = 0; i < CFHSI_PRIO_LAST; ++i)
11271127
skb_queue_head_init(&cfhsi->qhead[i]);

drivers/net/caif/caif_serial.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ static void caifdev_setup(struct net_device *dev)
428428
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
429429
dev->mtu = CAIF_MAX_MTU;
430430
dev->priv_flags |= IFF_NO_QUEUE;
431-
dev->destructor = free_netdev;
431+
dev->needs_free_netdev = true;
432432
skb_queue_head_init(&serdev->head);
433433
serdev->common.link_select = CAIF_LINK_LOW_LATENCY;
434434
serdev->common.use_frag = true;

drivers/net/caif/caif_spi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,7 @@ static void cfspi_setup(struct net_device *dev)
712712
dev->flags = IFF_NOARP | IFF_POINTOPOINT;
713713
dev->priv_flags |= IFF_NO_QUEUE;
714714
dev->mtu = SPI_MAX_PAYLOAD_SIZE;
715-
dev->destructor = free_netdev;
715+
dev->needs_free_netdev = true;
716716
skb_queue_head_init(&cfspi->qhead);
717717
skb_queue_head_init(&cfspi->chead);
718718
cfspi->cfdev.link_select = CAIF_LINK_HIGH_BANDW;

drivers/net/caif/caif_virtio.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ static void cfv_netdev_setup(struct net_device *netdev)
617617
netdev->tx_queue_len = 100;
618618
netdev->flags = IFF_POINTOPOINT | IFF_NOARP;
619619
netdev->mtu = CFV_DEF_MTU_SIZE;
620-
netdev->destructor = free_netdev;
620+
netdev->needs_free_netdev = true;
621621
}
622622

623623
/* Create debugfs counters for the device */

drivers/net/can/slcan.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ static int slc_open(struct net_device *dev)
417417
static void slc_free_netdev(struct net_device *dev)
418418
{
419419
int i = dev->base_addr;
420-
free_netdev(dev);
420+
421421
slcan_devs[i] = NULL;
422422
}
423423

@@ -436,7 +436,8 @@ static const struct net_device_ops slc_netdev_ops = {
436436
static void slc_setup(struct net_device *dev)
437437
{
438438
dev->netdev_ops = &slc_netdev_ops;
439-
dev->destructor = slc_free_netdev;
439+
dev->needs_free_netdev = true;
440+
dev->priv_destructor = slc_free_netdev;
440441

441442
dev->hard_header_len = 0;
442443
dev->addr_len = 0;
@@ -761,8 +762,6 @@ static void __exit slcan_exit(void)
761762
if (sl->tty) {
762763
printk(KERN_ERR "%s: tty discipline still running\n",
763764
dev->name);
764-
/* Intentionally leak the control block. */
765-
dev->destructor = NULL;
766765
}
767766

768767
unregister_netdev(dev);

drivers/net/can/vcan.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ static void vcan_setup(struct net_device *dev)
163163
dev->flags |= IFF_ECHO;
164164

165165
dev->netdev_ops = &vcan_netdev_ops;
166-
dev->destructor = free_netdev;
166+
dev->needs_free_netdev = true;
167167
}
168168

169169
static struct rtnl_link_ops vcan_link_ops __read_mostly = {

drivers/net/can/vxcan.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ static void vxcan_setup(struct net_device *dev)
156156
dev->tx_queue_len = 0;
157157
dev->flags = (IFF_NOARP|IFF_ECHO);
158158
dev->netdev_ops = &vxcan_netdev_ops;
159-
dev->destructor = free_netdev;
159+
dev->needs_free_netdev = true;
160160
}
161161

162162
/* forward declaration for rtnl_create_link() */

drivers/net/dummy.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,6 @@ static void dummy_free_netdev(struct net_device *dev)
328328
struct dummy_priv *priv = netdev_priv(dev);
329329

330330
kfree(priv->vfinfo);
331-
free_netdev(dev);
332331
}
333332

334333
static void dummy_setup(struct net_device *dev)
@@ -338,7 +337,8 @@ static void dummy_setup(struct net_device *dev)
338337
/* Initialize the device structure. */
339338
dev->netdev_ops = &dummy_netdev_ops;
340339
dev->ethtool_ops = &dummy_ethtool_ops;
341-
dev->destructor = dummy_free_netdev;
340+
dev->needs_free_netdev = true;
341+
dev->priv_destructor = dummy_free_netdev;
342342

343343
/* Fill in device structure with ethernet-generic values. */
344344
dev->flags |= IFF_NOARP;

drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4525,7 +4525,7 @@ static void dummy_setup(struct net_device *dev)
45254525
/* Initialize the device structure. */
45264526
dev->netdev_ops = &cxgb4_mgmt_netdev_ops;
45274527
dev->ethtool_ops = &cxgb4_mgmt_ethtool_ops;
4528-
dev->destructor = free_netdev;
4528+
dev->needs_free_netdev = true;
45294529
}
45304530

45314531
static int config_mgmt_dev(struct pci_dev *pdev)

drivers/net/geneve.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,7 @@ static void geneve_setup(struct net_device *dev)
10071007

10081008
dev->netdev_ops = &geneve_netdev_ops;
10091009
dev->ethtool_ops = &geneve_ethtool_ops;
1010-
dev->destructor = free_netdev;
1010+
dev->needs_free_netdev = true;
10111011

10121012
SET_NETDEV_DEVTYPE(dev, &geneve_type);
10131013

drivers/net/gtp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ static const struct net_device_ops gtp_netdev_ops = {
611611
static void gtp_link_setup(struct net_device *dev)
612612
{
613613
dev->netdev_ops = &gtp_netdev_ops;
614-
dev->destructor = free_netdev;
614+
dev->needs_free_netdev = true;
615615

616616
dev->hard_header_len = 0;
617617
dev->addr_len = 0;

drivers/net/hamradio/6pack.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ static void sp_setup(struct net_device *dev)
311311
{
312312
/* Finish setting up the DEVICE info. */
313313
dev->netdev_ops = &sp_netdev_ops;
314-
dev->destructor = free_netdev;
314+
dev->needs_free_netdev = true;
315315
dev->mtu = SIXP_MTU;
316316
dev->hard_header_len = AX25_MAX_HEADER_LEN;
317317
dev->header_ops = &ax25_header_ops;

drivers/net/hamradio/bpqether.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ static const struct net_device_ops bpq_netdev_ops = {
476476
static void bpq_setup(struct net_device *dev)
477477
{
478478
dev->netdev_ops = &bpq_netdev_ops;
479-
dev->destructor = free_netdev;
479+
dev->needs_free_netdev = true;
480480

481481
memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN);
482482
memcpy(dev->dev_addr, &ax25_defaddr, AX25_ADDR_LEN);

drivers/net/ifb.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,6 @@ static void ifb_dev_free(struct net_device *dev)
207207
__skb_queue_purge(&txp->tq);
208208
}
209209
kfree(dp->tx_private);
210-
free_netdev(dev);
211210
}
212211

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

236236
static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)

drivers/net/ipvlan/ipvlan_main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ void ipvlan_link_setup(struct net_device *dev)
632632
dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
633633
dev->priv_flags |= IFF_UNICAST_FLT | IFF_NO_QUEUE;
634634
dev->netdev_ops = &ipvlan_netdev_ops;
635-
dev->destructor = free_netdev;
635+
dev->needs_free_netdev = true;
636636
dev->header_ops = &ipvlan_header_ops;
637637
dev->ethtool_ops = &ipvlan_ethtool_ops;
638638
}

drivers/net/loopback.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ static void loopback_dev_free(struct net_device *dev)
159159
{
160160
dev_net(dev)->loopback_dev = NULL;
161161
free_percpu(dev->lstats);
162-
free_netdev(dev);
163162
}
164163

165164
static const struct net_device_ops loopback_ops = {
@@ -196,7 +195,8 @@ static void loopback_setup(struct net_device *dev)
196195
dev->ethtool_ops = &loopback_ethtool_ops;
197196
dev->header_ops = &eth_header_ops;
198197
dev->netdev_ops = &loopback_ops;
199-
dev->destructor = loopback_dev_free;
198+
dev->needs_free_netdev = true;
199+
dev->priv_destructor = loopback_dev_free;
200200
}
201201

202202
/* Setup and register the loopback device. */

drivers/net/macsec.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2996,7 +2996,6 @@ static void macsec_free_netdev(struct net_device *dev)
29962996
free_percpu(macsec->secy.tx_sc.stats);
29972997

29982998
dev_put(real_dev);
2999-
free_netdev(dev);
30002999
}
30013000

30023001
static void macsec_setup(struct net_device *dev)
@@ -3006,7 +3005,8 @@ static void macsec_setup(struct net_device *dev)
30063005
dev->max_mtu = ETH_MAX_MTU;
30073006
dev->priv_flags |= IFF_NO_QUEUE;
30083007
dev->netdev_ops = &macsec_netdev_ops;
3009-
dev->destructor = macsec_free_netdev;
3008+
dev->needs_free_netdev = true;
3009+
dev->priv_destructor = macsec_free_netdev;
30103010
SET_NETDEV_DEVTYPE(dev, &macsec_type);
30113011

30123012
eth_zero_addr(dev->broadcast);

drivers/net/macvlan.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,7 @@ void macvlan_common_setup(struct net_device *dev)
10921092
netif_keep_dst(dev);
10931093
dev->priv_flags |= IFF_UNICAST_FLT;
10941094
dev->netdev_ops = &macvlan_netdev_ops;
1095-
dev->destructor = free_netdev;
1095+
dev->needs_free_netdev = true;
10961096
dev->header_ops = &macvlan_hard_header_ops;
10971097
dev->ethtool_ops = &macvlan_ethtool_ops;
10981098
}

drivers/net/nlmon.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ static void nlmon_setup(struct net_device *dev)
113113

114114
dev->netdev_ops = &nlmon_ops;
115115
dev->ethtool_ops = &nlmon_ethtool_ops;
116-
dev->destructor = free_netdev;
116+
dev->needs_free_netdev = true;
117117

118118
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST |
119119
NETIF_F_HIGHDMA | NETIF_F_LLTX;

drivers/net/slip/slip.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ static void sl_uninit(struct net_device *dev)
629629
static void sl_free_netdev(struct net_device *dev)
630630
{
631631
int i = dev->base_addr;
632-
free_netdev(dev);
632+
633633
slip_devs[i] = NULL;
634634
}
635635

@@ -651,7 +651,8 @@ static const struct net_device_ops sl_netdev_ops = {
651651
static void sl_setup(struct net_device *dev)
652652
{
653653
dev->netdev_ops = &sl_netdev_ops;
654-
dev->destructor = sl_free_netdev;
654+
dev->needs_free_netdev = true;
655+
dev->priv_destructor = sl_free_netdev;
655656

656657
dev->hard_header_len = 0;
657658
dev->addr_len = 0;
@@ -1369,8 +1370,6 @@ static void __exit slip_exit(void)
13691370
if (sl->tty) {
13701371
printk(KERN_ERR "%s: tty discipline still running\n",
13711372
dev->name);
1372-
/* Intentionally leak the control block. */
1373-
dev->destructor = NULL;
13741373
}
13751374

13761375
unregister_netdev(dev);

drivers/net/team/team.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,7 +1643,6 @@ static void team_destructor(struct net_device *dev)
16431643
struct team *team = netdev_priv(dev);
16441644

16451645
free_percpu(team->pcpu_stats);
1646-
free_netdev(dev);
16471646
}
16481647

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

20802079
dev->netdev_ops = &team_netdev_ops;
20812080
dev->ethtool_ops = &team_ethtool_ops;
2082-
dev->destructor = team_destructor;
2081+
dev->needs_free_netdev = true;
2082+
dev->priv_destructor = team_destructor;
20832083
dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
20842084
dev->priv_flags |= IFF_NO_QUEUE;
20852085
dev->priv_flags |= IFF_TEAM;

drivers/net/tun.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,7 +1560,6 @@ static void tun_free_netdev(struct net_device *dev)
15601560
free_percpu(tun->pcpu_stats);
15611561
tun_flow_uninit(tun);
15621562
security_tun_dev_free_security(tun->security);
1563-
free_netdev(dev);
15641563
}
15651564

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

15731572
dev->ethtool_ops = &tun_ethtool_ops;
1574-
dev->destructor = tun_free_netdev;
1573+
dev->needs_free_netdev = true;
1574+
dev->priv_destructor = tun_free_netdev;
15751575
/* We prefer our own queue length */
15761576
dev->tx_queue_len = TUN_READQ_SIZE;
15771577
}

drivers/net/usb/cdc-phonet.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ static void usbpn_setup(struct net_device *dev)
298298
dev->addr_len = 1;
299299
dev->tx_queue_len = 3;
300300

301-
dev->destructor = free_netdev;
301+
dev->needs_free_netdev = true;
302302
}
303303

304304
/*

drivers/net/usb/qmi_wwan.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ static void qmimux_setup(struct net_device *dev)
123123
dev->addr_len = 0;
124124
dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
125125
dev->netdev_ops = &qmimux_netdev_ops;
126-
dev->destructor = free_netdev;
126+
dev->needs_free_netdev = true;
127127
}
128128

129129
static struct net_device *qmimux_find_dev(struct usbnet *dev, u8 mux_id)

drivers/net/veth.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,6 @@ static int veth_dev_init(struct net_device *dev)
222222
static void veth_dev_free(struct net_device *dev)
223223
{
224224
free_percpu(dev->vstats);
225-
free_netdev(dev);
226225
}
227226

228227
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -317,7 +316,8 @@ static void veth_setup(struct net_device *dev)
317316
NETIF_F_HW_VLAN_STAG_TX |
318317
NETIF_F_HW_VLAN_CTAG_RX |
319318
NETIF_F_HW_VLAN_STAG_RX);
320-
dev->destructor = veth_dev_free;
319+
dev->needs_free_netdev = true;
320+
dev->priv_destructor = veth_dev_free;
321321
dev->max_mtu = ETH_MAX_MTU;
322322

323323
dev->hw_features = VETH_FEATURES;

drivers/net/vrf.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1348,7 +1348,7 @@ static void vrf_setup(struct net_device *dev)
13481348
dev->netdev_ops = &vrf_netdev_ops;
13491349
dev->l3mdev_ops = &vrf_l3mdev_ops;
13501350
dev->ethtool_ops = &vrf_ethtool_ops;
1351-
dev->destructor = free_netdev;
1351+
dev->needs_free_netdev = true;
13521352

13531353
/* Fill in device structure with ethernet-generic values. */
13541354
eth_hw_addr_random(dev);

drivers/net/vsockmon.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ static void vsockmon_setup(struct net_device *dev)
135135

136136
dev->netdev_ops = &vsockmon_ops;
137137
dev->ethtool_ops = &vsockmon_ethtool_ops;
138-
dev->destructor = free_netdev;
138+
dev->needs_free_netdev = true;
139139

140140
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST |
141141
NETIF_F_HIGHDMA | NETIF_F_LLTX;

drivers/net/vxlan.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2611,7 +2611,7 @@ static void vxlan_setup(struct net_device *dev)
26112611
eth_hw_addr_random(dev);
26122612
ether_setup(dev);
26132613

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

26172617
dev->features |= NETIF_F_LLTX;

drivers/net/wan/dlci.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ static void dlci_setup(struct net_device *dev)
475475
dev->flags = 0;
476476
dev->header_ops = &dlci_header_ops;
477477
dev->netdev_ops = &dlci_netdev_ops;
478-
dev->destructor = free_netdev;
478+
dev->needs_free_netdev = true;
479479

480480
dlp->receive = dlci_receive;
481481

drivers/net/wan/hdlc_fr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1106,7 +1106,7 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type)
11061106
return -EIO;
11071107
}
11081108

1109-
dev->destructor = free_netdev;
1109+
dev->needs_free_netdev = true;
11101110
*get_dev_p(pvc, type) = dev;
11111111
if (!used) {
11121112
state(hdlc)->dce_changed = 1;

0 commit comments

Comments
 (0)