>From 0b9d930e8192466a9c4b85d136193f9c5f01d96a Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 16 Aug 2017 13:48:11 +0800 Subject: [PATCH] tun: thread safe tun_build_skb() Signed-off-by: Jason Wang --- drivers/net/tun.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 5892284..c72c2ea 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1247,6 +1247,8 @@ static void tun_rx_batched(struct tun_struct *tun, struct tun_file *tfile, static bool tun_can_build_skb(struct tun_struct *tun, struct tun_file *tfile, int len, int noblock, bool zerocopy) { + struct bpf_prog *xdp_prog; + if ((tun->flags & TUN_TYPE_MASK) != IFF_TAP) return false; @@ -1263,7 +1265,11 @@ static bool tun_can_build_skb(struct tun_struct *tun, struct tun_file *tfile, SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) > PAGE_SIZE) return false; - return true; + rcu_read_lock(); + xdp_prog = rcu_dereference(tun->xdp_prog); + rcu_read_unlock(); + + return xdp_prog; } static struct sk_buff *tun_build_skb(struct tun_struct *tun, @@ -1272,7 +1278,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, struct virtio_net_hdr *hdr, int len, int *generic_xdp) { - struct page_frag *alloc_frag = &tfile->alloc_frag; + struct page *page = alloc_page(GFP_KERNEL); struct sk_buff *skb; struct bpf_prog *xdp_prog; int buflen = SKB_DATA_ALIGN(len + TUN_RX_PAD) + @@ -1283,15 +1289,15 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, bool xdp_xmit = false; int err; - if (unlikely(!skb_page_frag_refill(buflen, alloc_frag, GFP_KERNEL))) + if (unlikely(!page)) 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, - len, from); - if (copied != len) + buf = (char *)page_address(page); + copied = copy_page_from_iter(page, TUN_RX_PAD, len, from); + if (copied != len) { + put_page(page); return ERR_PTR(-EFAULT); + } if (hdr->gso_type) *generic_xdp = 1; @@ -1313,11 +1319,9 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, switch (act) { case XDP_REDIRECT: - get_page(alloc_frag->page); - alloc_frag->offset += buflen; err = xdp_do_redirect(tun->dev, &xdp, xdp_prog); if (err) - goto err_redirect; + goto err_xdp; return NULL; case XDP_TX: xdp_xmit = true; @@ -1339,13 +1343,13 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, skb = build_skb(buf, buflen); if (!skb) { rcu_read_unlock(); + put_page(page); return ERR_PTR(-ENOMEM); } skb_reserve(skb, TUN_RX_PAD - delta); skb_put(skb, len + delta); - get_page(alloc_frag->page); - alloc_frag->offset += buflen; + get_page(page); if (xdp_xmit) { skb->dev = tun->dev; @@ -1358,9 +1362,8 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, return skb; -err_redirect: - put_page(alloc_frag->page); err_xdp: + put_page(page); rcu_read_unlock(); this_cpu_inc(tun->pcpu_stats->rx_dropped); return NULL; -- 2.7.4