diff options
author | Eric Dumazet <edumazet@google.com> | 2013-10-30 16:10:44 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-11-03 23:19:00 -0500 |
commit | 74d332c13b2148ae934ea94dac1745ae92efe8e5 (patch) | |
tree | 4f8f287774deaba28fa4c85d085d587ae7515aaf | |
parent | b397f99921827e114d7f5600447e172a99c50165 (diff) |
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>
-rw-r--r-- | Documentation/networking/netdevices.txt | 10 | ||||
-rw-r--r-- | include/linux/netdevice.h | 1 | ||||
-rw-r--r-- | net/core/dev.c | 22 | ||||
-rw-r--r-- | net/core/net-sysfs.c | 2 |
4 files changed, 24 insertions, 11 deletions
diff --git a/Documentation/networking/netdevices.txt b/Documentation/networking/netdevices.txt index c7ecc7080494..0b1cf6b2a592 100644 --- a/Documentation/networking/netdevices.txt +++ b/Documentation/networking/netdevices.txt | |||
@@ -10,12 +10,12 @@ network devices. | |||
10 | struct net_device allocation rules | 10 | struct net_device allocation rules |
11 | ================================== | 11 | ================================== |
12 | Network device structures need to persist even after module is unloaded and | 12 | Network device structures need to persist even after module is unloaded and |
13 | must be allocated with kmalloc. If device has registered successfully, | 13 | must be allocated with alloc_netdev_mqs() and friends. |
14 | it will be freed on last use by free_netdev. This is required to handle the | 14 | If device has registered successfully, it will be freed on last use |
15 | pathologic case cleanly (example: rmmod mydriver </sys/class/net/myeth/mtu ) | 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_netdev_mqs()/alloc_netdev() reserve extra space for driver |
18 | alloc_etherdev, alloc_netdev. These reserve extra space for driver | ||
19 | private data which gets freed when the network device is freed. If | 19 | private data which gets freed when the network device is freed. If |
20 | separately allocated data is attached to the network device | 20 | separately allocated data is attached to the network device |
21 | (netdev_priv(dev)) then it is up to the module exit handler to free that. | 21 | (netdev_priv(dev)) then it is up to the module exit handler to free that. |
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index cb1d918ecdf1..e6353cafbf05 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -1800,6 +1800,7 @@ static inline void unregister_netdevice(struct net_device *dev) | |||
1800 | 1800 | ||
1801 | int netdev_refcnt_read(const struct net_device *dev); | 1801 | int netdev_refcnt_read(const struct net_device *dev); |
1802 | void free_netdev(struct net_device *dev); | 1802 | void free_netdev(struct net_device *dev); |
1803 | void netdev_freemem(struct net_device *dev); | ||
1803 | void synchronize_net(void); | 1804 | void synchronize_net(void); |
1804 | int init_dummy_netdev(struct net_device *dev); | 1805 | int init_dummy_netdev(struct net_device *dev); |
1805 | 1806 | ||
diff --git a/net/core/dev.c b/net/core/dev.c index 0054c8c75f50..0e6136546a8c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -6196,6 +6196,16 @@ void netdev_set_default_ethtool_ops(struct net_device *dev, | |||
6196 | } | 6196 | } |
6197 | EXPORT_SYMBOL_GPL(netdev_set_default_ethtool_ops); | 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 | * alloc_netdev_mqs - allocate network device | 6210 | * alloc_netdev_mqs - allocate network device |
6201 | * @sizeof_priv: size of private data to allocate space for | 6211 | * @sizeof_priv: size of private data to allocate space for |
@@ -6239,7 +6249,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, | |||
6239 | /* ensure 32-byte alignment of whole construct */ | 6249 | /* ensure 32-byte alignment of whole construct */ |
6240 | alloc_size += NETDEV_ALIGN - 1; | 6250 | alloc_size += NETDEV_ALIGN - 1; |
6241 | 6251 | ||
6242 | p = kzalloc(alloc_size, GFP_KERNEL); | 6252 | p = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT); |
6253 | if (!p) | ||
6254 | p = vzalloc(alloc_size); | ||
6243 | if (!p) | 6255 | if (!p) |
6244 | return NULL; | 6256 | return NULL; |
6245 | 6257 | ||
@@ -6248,7 +6260,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, | |||
6248 | 6260 | ||
6249 | dev->pcpu_refcnt = alloc_percpu(int); | 6261 | dev->pcpu_refcnt = alloc_percpu(int); |
6250 | if (!dev->pcpu_refcnt) | 6262 | if (!dev->pcpu_refcnt) |
6251 | goto free_p; | 6263 | goto free_dev; |
6252 | 6264 | ||
6253 | if (dev_addr_init(dev)) | 6265 | if (dev_addr_init(dev)) |
6254 | goto free_pcpu; | 6266 | goto free_pcpu; |
@@ -6301,8 +6313,8 @@ free_pcpu: | |||
6301 | kfree(dev->_rx); | 6313 | kfree(dev->_rx); |
6302 | #endif | 6314 | #endif |
6303 | 6315 | ||
6304 | free_p: | 6316 | free_dev: |
6305 | kfree(p); | 6317 | netdev_freemem(dev); |
6306 | return NULL; | 6318 | return NULL; |
6307 | } | 6319 | } |
6308 | EXPORT_SYMBOL(alloc_netdev_mqs); | 6320 | EXPORT_SYMBOL(alloc_netdev_mqs); |
@@ -6339,7 +6351,7 @@ void free_netdev(struct net_device *dev) | |||
6339 | 6351 | ||
6340 | /* Compatibility with error handling in drivers */ | 6352 | /* Compatibility with error handling in drivers */ |
6341 | if (dev->reg_state == NETREG_UNINITIALIZED) { | 6353 | if (dev->reg_state == NETREG_UNINITIALIZED) { |
6342 | kfree((char *)dev - dev->padded); | 6354 | netdev_freemem(dev); |
6343 | return; | 6355 | return; |
6344 | } | 6356 | } |
6345 | 6357 | ||
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index d954b56b4e47..d03f2c9750fa 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c | |||
@@ -1263,7 +1263,7 @@ static void netdev_release(struct device *d) | |||
1263 | BUG_ON(dev->reg_state != NETREG_RELEASED); | 1263 | BUG_ON(dev->reg_state != NETREG_RELEASED); |
1264 | 1264 | ||
1265 | kfree(dev->ifalias); | 1265 | kfree(dev->ifalias); |
1266 | kfree((char *)dev - dev->padded); | 1266 | netdev_freemem(dev); |
1267 | } | 1267 | } |
1268 | 1268 | ||
1269 | static const void *net_namespace(struct device *d) | 1269 | static const void *net_namespace(struct device *d) |