net: extend net_device allocation to vmalloc()
authorEric Dumazet <edumazet@google.com>
Wed, 30 Oct 2013 20:10:44 +0000 (13:10 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 4 Nov 2013 04:19:00 +0000 (23:19 -0500)
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>
Documentation/networking/netdevices.txt
include/linux/netdevice.h
net/core/dev.c
net/core/net-sysfs.c

index c7ecc7080494da43fc469eac5b62b06905c13559..0b1cf6b2a592f4f02f6d7c90f996428ba9e9fead 100644 (file)
@@ -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.
index cb1d918ecdf1d64bb475c21e55e2f75ef8ab526f..e6353cafbf0536212b01241fbca023579643d3af 100644 (file)
@@ -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);
 
index 0054c8c75f50bdee92819c0a6885707413e38c69..0e6136546a8ce378f92be706bea10a3e753e7e33 100644 (file)
@@ -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
@@ -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;
 
@@ -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;
@@ -6301,8 +6313,8 @@ free_pcpu:
        kfree(dev->_rx);
 #endif
 
-free_p:
-       kfree(p);
+free_dev:
+       netdev_freemem(dev);
        return NULL;
 }
 EXPORT_SYMBOL(alloc_netdev_mqs);
@@ -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;
        }
 
index d954b56b4e47976b3b003c41cb7b39758d5d044d..d03f2c9750fabcd9ff7723bafd91bdff4edc7900 100644 (file)
@@ -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)