Commit 74d332c1 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

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: default avatarEric Dumazet <edumazet@google.com>
Reported-by: default avatarJoby Poriyath <joby.poriyath@citrix.com>
Cc: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b397f999
...@@ -10,12 +10,12 @@ network devices. ...@@ -10,12 +10,12 @@ network devices.
struct net_device allocation rules struct net_device allocation rules
================================== ==================================
Network device structures need to persist even after module is unloaded and Network device structures need to persist even after module is unloaded and
must be allocated with kmalloc. If device has registered successfully, must be allocated with alloc_netdev_mqs() and friends.
it will be freed on last use by free_netdev. This is required to handle the If device has registered successfully, it will be freed on last use
pathologic case cleanly (example: rmmod mydriver </sys/class/net/myeth/mtu ) by free_netdev(). This is required to handle the pathologic case cleanly
(example: rmmod mydriver </sys/class/net/myeth/mtu )
There are routines in net_init.c to handle the common cases of alloc_netdev_mqs()/alloc_netdev() reserve extra space for driver
alloc_etherdev, alloc_netdev. These reserve extra space for driver
private data which gets freed when the network device is freed. If private data which gets freed when the network device is freed. If
separately allocated data is attached to the network device separately allocated data is attached to the network device
(netdev_priv(dev)) then it is up to the module exit handler to free that. (netdev_priv(dev)) then it is up to the module exit handler to free that.
......
...@@ -1800,6 +1800,7 @@ static inline void unregister_netdevice(struct net_device *dev) ...@@ -1800,6 +1800,7 @@ static inline void unregister_netdevice(struct net_device *dev)
int netdev_refcnt_read(const struct net_device *dev); int netdev_refcnt_read(const struct net_device *dev);
void free_netdev(struct net_device *dev); void free_netdev(struct net_device *dev);
void netdev_freemem(struct net_device *dev);
void synchronize_net(void); void synchronize_net(void);
int init_dummy_netdev(struct net_device *dev); int init_dummy_netdev(struct net_device *dev);
......
...@@ -6196,6 +6196,16 @@ void netdev_set_default_ethtool_ops(struct net_device *dev, ...@@ -6196,6 +6196,16 @@ void netdev_set_default_ethtool_ops(struct net_device *dev,
} }
EXPORT_SYMBOL_GPL(netdev_set_default_ethtool_ops); EXPORT_SYMBOL_GPL(netdev_set_default_ethtool_ops);
void netdev_freemem(struct net_device *dev)
{
char *addr = (char *)dev - dev->padded;
if (is_vmalloc_addr(addr))
vfree(addr);
else
kfree(addr);
}
/** /**
* alloc_netdev_mqs - allocate network device * alloc_netdev_mqs - allocate network device
* @sizeof_priv: size of private data to allocate space for * @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,7 +6249,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
/* ensure 32-byte alignment of whole construct */ /* ensure 32-byte alignment of whole construct */
alloc_size += NETDEV_ALIGN - 1; alloc_size += NETDEV_ALIGN - 1;
p = kzalloc(alloc_size, GFP_KERNEL); p = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
if (!p)
p = vzalloc(alloc_size);
if (!p) if (!p)
return NULL; return NULL;
...@@ -6248,7 +6260,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, ...@@ -6248,7 +6260,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
dev->pcpu_refcnt = alloc_percpu(int); dev->pcpu_refcnt = alloc_percpu(int);
if (!dev->pcpu_refcnt) if (!dev->pcpu_refcnt)
goto free_p; goto free_dev;
if (dev_addr_init(dev)) if (dev_addr_init(dev))
goto free_pcpu; goto free_pcpu;
...@@ -6301,8 +6313,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, ...@@ -6301,8 +6313,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
kfree(dev->_rx); kfree(dev->_rx);
#endif #endif
free_p: free_dev:
kfree(p); netdev_freemem(dev);
return NULL; return NULL;
} }
EXPORT_SYMBOL(alloc_netdev_mqs); EXPORT_SYMBOL(alloc_netdev_mqs);
...@@ -6339,7 +6351,7 @@ void free_netdev(struct net_device *dev) ...@@ -6339,7 +6351,7 @@ void free_netdev(struct net_device *dev)
/* Compatibility with error handling in drivers */ /* Compatibility with error handling in drivers */
if (dev->reg_state == NETREG_UNINITIALIZED) { if (dev->reg_state == NETREG_UNINITIALIZED) {
kfree((char *)dev - dev->padded); netdev_freemem(dev);
return; return;
} }
......
...@@ -1263,7 +1263,7 @@ static void netdev_release(struct device *d) ...@@ -1263,7 +1263,7 @@ static void netdev_release(struct device *d)
BUG_ON(dev->reg_state != NETREG_RELEASED); BUG_ON(dev->reg_state != NETREG_RELEASED);
kfree(dev->ifalias); kfree(dev->ifalias);
kfree((char *)dev - dev->padded); netdev_freemem(dev);
} }
static const void *net_namespace(struct device *d) static const void *net_namespace(struct device *d)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment