aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/tun.c25
1 files changed, 16 insertions, 9 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index ffdb84474c47..2917a86f4c43 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -298,11 +298,12 @@ static void tun_flow_cleanup(unsigned long data)
298} 298}
299 299
300static void tun_flow_update(struct tun_struct *tun, u32 rxhash, 300static void tun_flow_update(struct tun_struct *tun, u32 rxhash,
301 u16 queue_index) 301 struct tun_file *tfile)
302{ 302{
303 struct hlist_head *head; 303 struct hlist_head *head;
304 struct tun_flow_entry *e; 304 struct tun_flow_entry *e;
305 unsigned long delay = tun->ageing_time; 305 unsigned long delay = tun->ageing_time;
306 u16 queue_index = tfile->queue_index;
306 307
307 if (!rxhash) 308 if (!rxhash)
308 return; 309 return;
@@ -311,7 +312,9 @@ static void tun_flow_update(struct tun_struct *tun, u32 rxhash,
311 312
312 rcu_read_lock(); 313 rcu_read_lock();
313 314
314 if (tun->numqueues == 1) 315 /* We may get a very small possibility of OOO during switching, not
316 * worth to optimize.*/
317 if (tun->numqueues == 1 || tfile->detached)
315 goto unlock; 318 goto unlock;
316 319
317 e = tun_flow_find(head, rxhash); 320 e = tun_flow_find(head, rxhash);
@@ -411,21 +414,21 @@ static void __tun_detach(struct tun_file *tfile, bool clean)
411 414
412 tun = rtnl_dereference(tfile->tun); 415 tun = rtnl_dereference(tfile->tun);
413 416
414 if (tun) { 417 if (tun && !tfile->detached) {
415 u16 index = tfile->queue_index; 418 u16 index = tfile->queue_index;
416 BUG_ON(index >= tun->numqueues); 419 BUG_ON(index >= tun->numqueues);
417 dev = tun->dev; 420 dev = tun->dev;
418 421
419 rcu_assign_pointer(tun->tfiles[index], 422 rcu_assign_pointer(tun->tfiles[index],
420 tun->tfiles[tun->numqueues - 1]); 423 tun->tfiles[tun->numqueues - 1]);
421 rcu_assign_pointer(tfile->tun, NULL);
422 ntfile = rtnl_dereference(tun->tfiles[index]); 424 ntfile = rtnl_dereference(tun->tfiles[index]);
423 ntfile->queue_index = index; 425 ntfile->queue_index = index;
424 426
425 --tun->numqueues; 427 --tun->numqueues;
426 if (clean) 428 if (clean) {
429 rcu_assign_pointer(tfile->tun, NULL);
427 sock_put(&tfile->sk); 430 sock_put(&tfile->sk);
428 else 431 } else
429 tun_disable_queue(tun, tfile); 432 tun_disable_queue(tun, tfile);
430 433
431 synchronize_net(); 434 synchronize_net();
@@ -473,6 +476,10 @@ static void tun_detach_all(struct net_device *dev)
473 rcu_assign_pointer(tfile->tun, NULL); 476 rcu_assign_pointer(tfile->tun, NULL);
474 --tun->numqueues; 477 --tun->numqueues;
475 } 478 }
479 list_for_each_entry(tfile, &tun->disabled, next) {
480 wake_up_all(&tfile->wq.wait);
481 rcu_assign_pointer(tfile->tun, NULL);
482 }
476 BUG_ON(tun->numqueues != 0); 483 BUG_ON(tun->numqueues != 0);
477 484
478 synchronize_net(); 485 synchronize_net();
@@ -503,7 +510,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file)
503 goto out; 510 goto out;
504 511
505 err = -EINVAL; 512 err = -EINVAL;
506 if (rtnl_dereference(tfile->tun)) 513 if (rtnl_dereference(tfile->tun) && !tfile->detached)
507 goto out; 514 goto out;
508 515
509 err = -EBUSY; 516 err = -EBUSY;
@@ -1202,7 +1209,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
1202 tun->dev->stats.rx_packets++; 1209 tun->dev->stats.rx_packets++;
1203 tun->dev->stats.rx_bytes += len; 1210 tun->dev->stats.rx_bytes += len;
1204 1211
1205 tun_flow_update(tun, rxhash, tfile->queue_index); 1212 tun_flow_update(tun, rxhash, tfile);
1206 return total_len; 1213 return total_len;
1207} 1214}
1208 1215
@@ -1816,7 +1823,7 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr)
1816 ret = tun_attach(tun, file); 1823 ret = tun_attach(tun, file);
1817 } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { 1824 } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) {
1818 tun = rtnl_dereference(tfile->tun); 1825 tun = rtnl_dereference(tfile->tun);
1819 if (!tun || !(tun->flags & TUN_TAP_MQ)) 1826 if (!tun || !(tun->flags & TUN_TAP_MQ) || tfile->detached)
1820 ret = -EINVAL; 1827 ret = -EINVAL;
1821 else 1828 else
1822 __tun_detach(tfile, false); 1829 __tun_detach(tfile, false);