diff options
-rw-r--r-- | drivers/net/tun.c | 25 |
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 | ||
300 | static void tun_flow_update(struct tun_struct *tun, u32 rxhash, | 300 | static 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); |