diff options
-rw-r--r-- | drivers/net/tun.c | 26 |
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 = ¤t->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; |