Skip to content

Commit

Permalink
net: extend net_device allocation to vmalloc()
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Eric Dumazet authored and davem330 committed Nov 4, 2013
1 parent b397f99 commit 74d332c
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 11 deletions.
10 changes: 5 additions & 5 deletions Documentation/networking/netdevices.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ network devices.
struct net_device allocation rules
==================================
Network device structures need to persist even after module is unloaded and
must be allocated with kmalloc. If device has registered successfully,
it will be freed on last use by free_netdev. This is required to handle the
pathologic case cleanly (example: rmmod mydriver </sys/class/net/myeth/mtu )
must be allocated with alloc_netdev_mqs() and friends.
If device has registered successfully, it will be freed on last use
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_etherdev, alloc_netdev. These reserve extra space for driver
alloc_netdev_mqs()/alloc_netdev() reserve extra space for driver
private data which gets freed when the network device is freed. If
separately allocated data is attached to the network device
(netdev_priv(dev)) then it is up to the module exit handler to free that.
Expand Down
1 change: 1 addition & 0 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -1800,6 +1800,7 @@ static inline void unregister_netdevice(struct net_device *dev)

int netdev_refcnt_read(const struct net_device *dev);
void free_netdev(struct net_device *dev);
void netdev_freemem(struct net_device *dev);
void synchronize_net(void);
int init_dummy_netdev(struct net_device *dev);

Expand Down
22 changes: 17 additions & 5 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -6196,6 +6196,16 @@ void netdev_set_default_ethtool_ops(struct net_device *dev,
}
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
* @sizeof_priv: size of private data to allocate space for
Expand Down Expand Up @@ -6239,7 +6249,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
/* ensure 32-byte alignment of whole construct */
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)
return NULL;

Expand All @@ -6248,7 +6260,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,

dev->pcpu_refcnt = alloc_percpu(int);
if (!dev->pcpu_refcnt)
goto free_p;
goto free_dev;

if (dev_addr_init(dev))
goto free_pcpu;
Expand Down Expand Up @@ -6301,8 +6313,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
kfree(dev->_rx);
#endif

free_p:
kfree(p);
free_dev:
netdev_freemem(dev);
return NULL;
}
EXPORT_SYMBOL(alloc_netdev_mqs);
Expand Down Expand Up @@ -6339,7 +6351,7 @@ void free_netdev(struct net_device *dev)

/* Compatibility with error handling in drivers */
if (dev->reg_state == NETREG_UNINITIALIZED) {
kfree((char *)dev - dev->padded);
netdev_freemem(dev);
return;
}

Expand Down
2 changes: 1 addition & 1 deletion net/core/net-sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1263,7 +1263,7 @@ static void netdev_release(struct device *d)
BUG_ON(dev->reg_state != NETREG_RELEASED);

kfree(dev->ifalias);
kfree((char *)dev - dev->padded);
netdev_freemem(dev);
}

static const void *net_namespace(struct device *d)
Expand Down

0 comments on commit 74d332c

Please sign in to comment.