|
@@ -108,7 +108,7 @@ do { \
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#define TUN_HEADROOM 256
|
|
#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 */
|
|
/* 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 page_frag *alloc_frag = ¤t->task_frag;
|
|
struct sk_buff *skb;
|
|
struct sk_buff *skb;
|
|
struct bpf_prog *xdp_prog;
|
|
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;
|
|
unsigned int delta = 0;
|
|
char *buf;
|
|
char *buf;
|
|
size_t copied;
|
|
size_t copied;
|
|
bool xdp_xmit = false;
|
|
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)))
|
|
if (unlikely(!skb_page_frag_refill(buflen, alloc_frag, GFP_KERNEL)))
|
|
return ERR_PTR(-ENOMEM);
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset;
|
|
buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset;
|
|
copied = copy_page_from_iter(alloc_frag->page,
|
|
copied = copy_page_from_iter(alloc_frag->page,
|
|
- alloc_frag->offset + TUN_RX_PAD,
|
|
|
|
|
|
+ alloc_frag->offset + pad,
|
|
len, from);
|
|
len, from);
|
|
if (copied != len)
|
|
if (copied != len)
|
|
return ERR_PTR(-EFAULT);
|
|
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;
|
|
*generic_xdp = 1;
|
|
else
|
|
else
|
|
*generic_xdp = 0;
|
|
*generic_xdp = 0;
|
|
@@ -1303,7 +1313,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun,
|
|
u32 act;
|
|
u32 act;
|
|
|
|
|
|
xdp.data_hard_start = buf;
|
|
xdp.data_hard_start = buf;
|
|
- xdp.data = buf + TUN_RX_PAD;
|
|
|
|
|
|
+ xdp.data = buf + pad;
|
|
xdp.data_end = xdp.data + len;
|
|
xdp.data_end = xdp.data + len;
|
|
orig_data = xdp.data;
|
|
orig_data = xdp.data;
|
|
act = bpf_prog_run_xdp(xdp_prog, &xdp);
|
|
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);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
}
|
|
|
|
|
|
- skb_reserve(skb, TUN_RX_PAD - delta);
|
|
|
|
|
|
+ skb_reserve(skb, pad - delta);
|
|
skb_put(skb, len + delta);
|
|
skb_put(skb, len + delta);
|
|
get_page(alloc_frag->page);
|
|
get_page(alloc_frag->page);
|
|
alloc_frag->offset += buflen;
|
|
alloc_frag->offset += buflen;
|