#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 */
 
        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;
                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);
                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;