summaryrefslogtreecommitdiff
path: root/drivers/net/tun.c
diff options
context:
space:
mode:
authorJason Wang <jasowang@redhat.com>2017-09-04 06:36:08 +0300
committerDavid S. Miller <davem@davemloft.net>2017-09-06 00:26:41 +0300
commit7df13219d757054c49b62417479a3724621ca8ae (patch)
tree2129c92584e1e80b460bb6ee7f9691d4bfe3b4f8 /drivers/net/tun.c
parent9e776f225dcf741ff7e91d866be752d1660d2073 (diff)
downloadlinux-7df13219d757054c49b62417479a3724621ca8ae.tar.xz
tun: reserve extra headroom only when XDP is set
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>
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r--drivers/net/tun.c26
1 files changed, 18 insertions, 8 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 06e8f0bb2dab..80ac18f8b55f 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -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;