From: Jason Wang Date: Thu, 9 May 2019 03:20:18 +0000 (-0400) Subject: tuntap: synchronize through tfiles array instead of tun->numqueues X-Git-Tag: v5.2-rc1~102^2~13 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=9871a9e47a2646fe30ae7fd2e67668a8d30912f6;p=uclinux-h8%2Flinux.git tuntap: synchronize through tfiles array instead of tun->numqueues When a queue(tfile) is detached through __tun_detach(), we move the last enabled tfile to the position where detached one sit but don't NULL out last position. We expect to synchronize the datapath through tun->numqueues. Unfortunately, this won't work since we're lacking sufficient mechanism to order or synchronize the access to tun->numqueues. To fix this, NULL out the last position during detaching and check RCU protected tfile against NULL instead of checking tun->numqueues in datapath. Cc: YueHaibing Cc: Cong Wang Cc: weiyongjun (A) Cc: Eric Dumazet Fixes: c8d68e6be1c3b ("tuntap: multiqueue support") Signed-off-by: Jason Wang Reviewed-by: Wei Yongjun Signed-off-by: David S. Miller --- diff --git a/drivers/net/tun.c b/drivers/net/tun.c index a52bd47e7aa2..abae165dcca5 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -704,6 +704,8 @@ static void __tun_detach(struct tun_file *tfile, bool clean) tun->tfiles[tun->numqueues - 1]); ntfile = rtnl_dereference(tun->tfiles[index]); ntfile->queue_index = index; + rcu_assign_pointer(tun->tfiles[tun->numqueues - 1], + NULL); --tun->numqueues; if (clean) { @@ -1086,7 +1088,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) tfile = rcu_dereference(tun->tfiles[txq]); /* Drop packet if interface is not attached */ - if (txq >= tun->numqueues) + if (!tfile) goto drop; if (!rcu_dereference(tun->steering_prog)) @@ -1309,6 +1311,7 @@ static int tun_xdp_xmit(struct net_device *dev, int n, rcu_read_lock(); +resample: numqueues = READ_ONCE(tun->numqueues); if (!numqueues) { rcu_read_unlock(); @@ -1317,6 +1320,8 @@ static int tun_xdp_xmit(struct net_device *dev, int n, tfile = rcu_dereference(tun->tfiles[smp_processor_id() % numqueues]); + if (unlikely(!tfile)) + goto resample; spin_lock(&tfile->tx_ring.producer_lock); for (i = 0; i < n; i++) {