tun: reserve extra headroom only when XDP is set
authorJason Wang <jasowang@redhat.com>
Mon, 4 Sep 2017 03:36:08 +0000 (11:36 +0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 5 Sep 2017 21:26:41 +0000 (14:26 -0700)
We reserve headroom unconditionally which could cause unnecessary
stress on socket memory accounting because of increased trusesize. Fix
this by only reserve extra headroom when XDP is set.

Cc: Jakub Kicinski <kubakici@wp.pl>
Signed-off-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/tun.c

index 06e8f0bb2dab07b01c8d10737fc9cf30c489bd56..80ac18f8b55f20ee0a8601dec03b4f0a2d07576d 100644 (file)
@@ -108,7 +108,7 @@ do {                                                                \
 #endif
 
 #define TUN_HEADROOM 256
-#define TUN_RX_PAD (NET_IP_ALIGN + NET_SKB_PAD + TUN_HEADROOM)
+#define TUN_RX_PAD (NET_IP_ALIGN + NET_SKB_PAD)
 
 /* TUN device flags */
 
@@ -1272,25 +1272,35 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun,
        struct page_frag *alloc_frag = &current->task_frag;
        struct sk_buff *skb;
        struct bpf_prog *xdp_prog;
-       int buflen = SKB_DATA_ALIGN(len + TUN_RX_PAD) +
-                    SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+       int buflen = SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
        unsigned int delta = 0;
        char *buf;
        size_t copied;
        bool xdp_xmit = false;
-       int err;
+       int err, pad = TUN_RX_PAD;
+
+       rcu_read_lock();
+       xdp_prog = rcu_dereference(tun->xdp_prog);
+       if (xdp_prog)
+               pad += TUN_HEADROOM;
+       buflen += SKB_DATA_ALIGN(len + pad);
+       rcu_read_unlock();
 
        if (unlikely(!skb_page_frag_refill(buflen, alloc_frag, GFP_KERNEL)))
                return ERR_PTR(-ENOMEM);
 
        buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset;
        copied = copy_page_from_iter(alloc_frag->page,
-                                    alloc_frag->offset + TUN_RX_PAD,
+                                    alloc_frag->offset + pad,
                                     len, from);
        if (copied != len)
                return ERR_PTR(-EFAULT);
 
-       if (hdr->gso_type)
+       /* There's a small window that XDP may be set after the check
+        * of xdp_prog above, this should be rare and for simplicity
+        * we do XDP on skb in case the headroom is not enough.
+        */
+       if (hdr->gso_type || !xdp_prog)
                *generic_xdp = 1;
        else
                *generic_xdp = 0;
@@ -1303,7 +1313,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun,
                u32 act;
 
                xdp.data_hard_start = buf;
-               xdp.data = buf + TUN_RX_PAD;
+               xdp.data = buf + pad;
                xdp.data_end = xdp.data + len;
                orig_data = xdp.data;
                act = bpf_prog_run_xdp(xdp_prog, &xdp);
@@ -1339,7 +1349,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun,
                return ERR_PTR(-ENOMEM);
        }
 
-       skb_reserve(skb, TUN_RX_PAD - delta);
+       skb_reserve(skb, pad - delta);
        skb_put(skb, len + delta);
        get_page(alloc_frag->page);
        alloc_frag->offset += buflen;