net: add NETIF_F_ALLOC_PSKB to allow netdev_alloc_skb to give paged skbs.

Should probably have been a flag to netdev_alloc_skb(), but let's not add
YA allthegoodnamesweretaken_alloc_skb().

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 include/linux/netdevice.h |    1 +
 net/core/skbuff.c         |   26 ++++++++++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -521,6 +521,7 @@ struct net_device
 #define NETIF_F_LLTX		4096	/* LockLess TX - deprecated. Please */
 					/* do not use LLTX in new drivers */
 #define NETIF_F_NETNS_LOCAL	8192	/* Does not change network namespaces */
+#define NETIF_F_ALLOC_PSKB	16384	/* netdev_alloc_skb can ret paged skb */
 #define NETIF_F_LRO		32768	/* large receive offload */
 
 	/* Segmentation offload features */
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -247,6 +247,8 @@ nodata:
  *	the headroom they think they need without accounting for the
  *	built in space. The built in space is used for optimisations.
  *
+ *	If dev->features includes NETIF_F_ALLOC_PSKB, there may be skb->frags.
+ *
  *	%NULL is returned if there is no free memory.
  */
 struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
@@ -254,11 +256,35 @@ struct sk_buff *__netdev_alloc_skb(struc
 {
 	int node = dev->dev.parent ? dev_to_node(dev->dev.parent) : -1;
 	struct sk_buff *skb;
+	unsigned int pages = 0;
+
+	if (dev->features & NETIF_F_ALLOC_PSKB) {
+		if (length + NET_SKB_PAD > PAGE_SIZE) {
+			pages = (length - PAGE_SIZE + NET_SKB_PAD) / PAGE_SIZE;
+			length = PAGE_SIZE - NET_SKB_PAD;
+		}
+	}
 
 	skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, 0, node);
 	if (likely(skb)) {
+		struct skb_shared_info *shinfo = skb_shinfo(skb);
+
 		skb_reserve(skb, NET_SKB_PAD);
 		skb->dev = dev;
+
+		while (pages) {
+			shinfo->frags[shinfo->nr_frags].page =
+				__netdev_alloc_page(dev, gfp_mask);
+			if (unlikely(!shinfo->frags[shinfo->nr_frags].page)) {
+				kfree_skb(skb);
+				skb = NULL;
+				break;
+			}
+			shinfo->frags[shinfo->nr_frags].page_offset = 0;
+			shinfo->frags[shinfo->nr_frags].size = PAGE_SIZE;
+			shinfo->nr_frags++;
+			pages--;
+		}
 	}
 	return skb;
 }
