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>
This commit is contained in:
parent
b397f99921
commit
74d332c13b
4 changed files with 24 additions and 11 deletions
|
|
@ -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)
|
||||||
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
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,
|
||||||
/* 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,
|
||||||
|
|
||||||
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 @@ free_pcpu:
|
||||||
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)
|
||||||
|
|
||||||
/* 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)
|
||||||
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)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue