[PATCH] tun: thread safe tun_build_skb()

From: Jason Wang
Date: Wed Aug 16 2017 - 01:48:11 EST


Signed-off-by: Jason Wang <jasowang@xxxxxxxxxx>
---
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


--------------2397F0CFBC84A5FEBD698946
Content-Type: text/x-patch;
name="0001-tun-serialize-page-frag-allocation.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="0001-tun-serialize-page-frag-allocation.patch"