Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: extend net_device allocation to vmalloc()

Joby Poriyath provided a xen-netback patch to reduce the size of
xenvif structure as some netdev allocation could fail under
memory pressure/fragmentation.

This patch is handling the problem at the core level, allowing
any netdev structures to use vmalloc() if kmalloc() failed.

As vmalloc() adds overhead on a critical network path, add __GFP_REPEAT
to kzalloc() flags to do this fallback only when really needed.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Joby Poriyath <joby.poriyath@citrix.com>
Cc: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
74d332c1 b397f999

+24 -11
+5 -5
Documentation/networking/netdevices.txt
··· 10 10 struct net_device allocation rules 11 11 ================================== 12 12 Network device structures need to persist even after module is unloaded and 13 - must be allocated with kmalloc. If device has registered successfully, 14 - it will be freed on last use by free_netdev. This is required to handle the 15 - pathologic case cleanly (example: rmmod mydriver </sys/class/net/myeth/mtu ) 13 + must be allocated with alloc_netdev_mqs() and friends. 14 + If device has registered successfully, it will be freed on last use 15 + by free_netdev(). This is required to handle the pathologic case cleanly 16 + (example: rmmod mydriver </sys/class/net/myeth/mtu ) 16 17 17 - There are routines in net_init.c to handle the common cases of 18 - alloc_etherdev, alloc_netdev. These reserve extra space for driver 18 + alloc_netdev_mqs()/alloc_netdev() reserve extra space for driver 19 19 private data which gets freed when the network device is freed. If 20 20 separately allocated data is attached to the network device 21 21 (netdev_priv(dev)) then it is up to the module exit handler to free that.
+1
include/linux/netdevice.h
··· 1800 1800 1801 1801 int netdev_refcnt_read(const struct net_device *dev); 1802 1802 void free_netdev(struct net_device *dev); 1803 + void netdev_freemem(struct net_device *dev); 1803 1804 void synchronize_net(void); 1804 1805 int init_dummy_netdev(struct net_device *dev); 1805 1806
+17 -5
net/core/dev.c
··· 6196 6196 } 6197 6197 EXPORT_SYMBOL_GPL(netdev_set_default_ethtool_ops); 6198 6198 6199 + void netdev_freemem(struct net_device *dev) 6200 + { 6201 + char *addr = (char *)dev - dev->padded; 6202 + 6203 + if (is_vmalloc_addr(addr)) 6204 + vfree(addr); 6205 + else 6206 + kfree(addr); 6207 + } 6208 + 6199 6209 /** 6200 6210 * alloc_netdev_mqs - allocate network device 6201 6211 * @sizeof_priv: size of private data to allocate space for ··· 6249 6239 /* ensure 32-byte alignment of whole construct */ 6250 6240 alloc_size += NETDEV_ALIGN - 1; 6251 6241 6252 - p = kzalloc(alloc_size, GFP_KERNEL); 6242 + p = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT); 6243 + if (!p) 6244 + p = vzalloc(alloc_size); 6253 6245 if (!p) 6254 6246 return NULL; 6255 6247 ··· 6260 6248 6261 6249 dev->pcpu_refcnt = alloc_percpu(int); 6262 6250 if (!dev->pcpu_refcnt) 6263 - goto free_p; 6251 + goto free_dev; 6264 6252 6265 6253 if (dev_addr_init(dev)) 6266 6254 goto free_pcpu; ··· 6313 6301 kfree(dev->_rx); 6314 6302 #endif 6315 6303 6316 - free_p: 6317 - kfree(p); 6304 + free_dev: 6305 + netdev_freemem(dev); 6318 6306 return NULL; 6319 6307 } 6320 6308 EXPORT_SYMBOL(alloc_netdev_mqs); ··· 6351 6339 6352 6340 /* Compatibility with error handling in drivers */ 6353 6341 if (dev->reg_state == NETREG_UNINITIALIZED) { 6354 - kfree((char *)dev - dev->padded); 6342 + netdev_freemem(dev); 6355 6343 return; 6356 6344 } 6357 6345
+1 -1
net/core/net-sysfs.c
··· 1263 1263 BUG_ON(dev->reg_state != NETREG_RELEASED); 1264 1264 1265 1265 kfree(dev->ifalias); 1266 - kfree((char *)dev - dev->padded); 1266 + netdev_freemem(dev); 1267 1267 } 1268 1268 1269 1269 static const void *net_namespace(struct device *d)