Skip to content

Commit 7797b93

Browse files
Toshiaki Makitadavem330
authored andcommitted
veth: Free queues on link delete
David Ahern reported memory leak in veth. ======================================================================= $ cat /sys/kernel/debug/kmemleak unreferenced object 0xffff8800354d5c00 (size 1024): comm "ip", pid 836, jiffies 4294722952 (age 25.904s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<(____ptrval____)>] kmemleak_alloc+0x70/0x94 [<(____ptrval____)>] slab_post_alloc_hook+0x42/0x52 [<(____ptrval____)>] __kmalloc+0x101/0x142 [<(____ptrval____)>] kmalloc_array.constprop.20+0x1e/0x26 [veth] [<(____ptrval____)>] veth_newlink+0x147/0x3ac [veth] ... unreferenced object 0xffff88002e009c00 (size 1024): comm "ip", pid 836, jiffies 4294722958 (age 25.898s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<(____ptrval____)>] kmemleak_alloc+0x70/0x94 [<(____ptrval____)>] slab_post_alloc_hook+0x42/0x52 [<(____ptrval____)>] __kmalloc+0x101/0x142 [<(____ptrval____)>] kmalloc_array.constprop.20+0x1e/0x26 [veth] [<(____ptrval____)>] veth_newlink+0x219/0x3ac [veth] ======================================================================= veth_rq allocated in veth_newlink() was not freed on dellink. We need to free up them after veth_close() so that any packets will not reference the queues afterwards. Thus free them in veth_dev_free() in the same way as freeing stats structure (vstats). Also move queues allocation to veth_dev_init() to be in line with stats allocation. Fixes: 638264d ("veth: Support per queue XDP ring") Reported-by: David Ahern <[email protected]> Signed-off-by: Toshiaki Makita <[email protected]> Reviewed-by: David Ahern <[email protected]> Tested-by: David Ahern <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent ff93bca commit 7797b93

File tree

1 file changed

+33
-37
lines changed

1 file changed

+33
-37
lines changed

drivers/net/veth.c

Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -789,16 +789,48 @@ static int is_valid_veth_mtu(int mtu)
789789
return mtu >= ETH_MIN_MTU && mtu <= ETH_MAX_MTU;
790790
}
791791

792+
static int veth_alloc_queues(struct net_device *dev)
793+
{
794+
struct veth_priv *priv = netdev_priv(dev);
795+
int i;
796+
797+
priv->rq = kcalloc(dev->num_rx_queues, sizeof(*priv->rq), GFP_KERNEL);
798+
if (!priv->rq)
799+
return -ENOMEM;
800+
801+
for (i = 0; i < dev->num_rx_queues; i++)
802+
priv->rq[i].dev = dev;
803+
804+
return 0;
805+
}
806+
807+
static void veth_free_queues(struct net_device *dev)
808+
{
809+
struct veth_priv *priv = netdev_priv(dev);
810+
811+
kfree(priv->rq);
812+
}
813+
792814
static int veth_dev_init(struct net_device *dev)
793815
{
816+
int err;
817+
794818
dev->vstats = netdev_alloc_pcpu_stats(struct pcpu_vstats);
795819
if (!dev->vstats)
796820
return -ENOMEM;
821+
822+
err = veth_alloc_queues(dev);
823+
if (err) {
824+
free_percpu(dev->vstats);
825+
return err;
826+
}
827+
797828
return 0;
798829
}
799830

800831
static void veth_dev_free(struct net_device *dev)
801832
{
833+
veth_free_queues(dev);
802834
free_percpu(dev->vstats);
803835
}
804836

@@ -1040,31 +1072,13 @@ static int veth_validate(struct nlattr *tb[], struct nlattr *data[],
10401072
return 0;
10411073
}
10421074

1043-
static int veth_alloc_queues(struct net_device *dev)
1044-
{
1045-
struct veth_priv *priv = netdev_priv(dev);
1046-
1047-
priv->rq = kcalloc(dev->num_rx_queues, sizeof(*priv->rq), GFP_KERNEL);
1048-
if (!priv->rq)
1049-
return -ENOMEM;
1050-
1051-
return 0;
1052-
}
1053-
1054-
static void veth_free_queues(struct net_device *dev)
1055-
{
1056-
struct veth_priv *priv = netdev_priv(dev);
1057-
1058-
kfree(priv->rq);
1059-
}
1060-
10611075
static struct rtnl_link_ops veth_link_ops;
10621076

10631077
static int veth_newlink(struct net *src_net, struct net_device *dev,
10641078
struct nlattr *tb[], struct nlattr *data[],
10651079
struct netlink_ext_ack *extack)
10661080
{
1067-
int err, i;
1081+
int err;
10681082
struct net_device *peer;
10691083
struct veth_priv *priv;
10701084
char ifname[IFNAMSIZ];
@@ -1117,12 +1131,6 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
11171131
return PTR_ERR(peer);
11181132
}
11191133

1120-
err = veth_alloc_queues(peer);
1121-
if (err) {
1122-
put_net(net);
1123-
goto err_peer_alloc_queues;
1124-
}
1125-
11261134
if (!ifmp || !tbp[IFLA_ADDRESS])
11271135
eth_hw_addr_random(peer);
11281136

@@ -1151,10 +1159,6 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
11511159
* should be re-allocated
11521160
*/
11531161

1154-
err = veth_alloc_queues(dev);
1155-
if (err)
1156-
goto err_alloc_queues;
1157-
11581162
if (tb[IFLA_ADDRESS] == NULL)
11591163
eth_hw_addr_random(dev);
11601164

@@ -1174,28 +1178,20 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
11741178
*/
11751179

11761180
priv = netdev_priv(dev);
1177-
for (i = 0; i < dev->real_num_rx_queues; i++)
1178-
priv->rq[i].dev = dev;
11791181
rcu_assign_pointer(priv->peer, peer);
11801182

11811183
priv = netdev_priv(peer);
1182-
for (i = 0; i < peer->real_num_rx_queues; i++)
1183-
priv->rq[i].dev = peer;
11841184
rcu_assign_pointer(priv->peer, dev);
11851185

11861186
return 0;
11871187

11881188
err_register_dev:
1189-
veth_free_queues(dev);
1190-
err_alloc_queues:
11911189
/* nothing to do */
11921190
err_configure_peer:
11931191
unregister_netdevice(peer);
11941192
return err;
11951193

11961194
err_register_peer:
1197-
veth_free_queues(peer);
1198-
err_peer_alloc_queues:
11991195
free_netdev(peer);
12001196
return err;
12011197
}

0 commit comments

Comments
 (0)