diff options
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r-- | drivers/net/tun.c | 87 |
1 files changed, 64 insertions, 23 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 40b426edc9e6..504f7f1cad94 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
@@ -138,6 +138,8 @@ struct tun_file { | |||
138 | /* only used for fasnyc */ | 138 | /* only used for fasnyc */ |
139 | unsigned int flags; | 139 | unsigned int flags; |
140 | u16 queue_index; | 140 | u16 queue_index; |
141 | struct list_head next; | ||
142 | struct tun_struct *detached; | ||
141 | }; | 143 | }; |
142 | 144 | ||
143 | struct tun_flow_entry { | 145 | struct tun_flow_entry { |
@@ -182,6 +184,8 @@ struct tun_struct { | |||
182 | struct hlist_head flows[TUN_NUM_FLOW_ENTRIES]; | 184 | struct hlist_head flows[TUN_NUM_FLOW_ENTRIES]; |
183 | struct timer_list flow_gc_timer; | 185 | struct timer_list flow_gc_timer; |
184 | unsigned long ageing_time; | 186 | unsigned long ageing_time; |
187 | unsigned int numdisabled; | ||
188 | struct list_head disabled; | ||
185 | }; | 189 | }; |
186 | 190 | ||
187 | static inline u32 tun_hashfn(u32 rxhash) | 191 | static inline u32 tun_hashfn(u32 rxhash) |
@@ -385,6 +389,23 @@ static void tun_set_real_num_queues(struct tun_struct *tun) | |||
385 | netif_set_real_num_rx_queues(tun->dev, tun->numqueues); | 389 | netif_set_real_num_rx_queues(tun->dev, tun->numqueues); |
386 | } | 390 | } |
387 | 391 | ||
392 | static void tun_disable_queue(struct tun_struct *tun, struct tun_file *tfile) | ||
393 | { | ||
394 | tfile->detached = tun; | ||
395 | list_add_tail(&tfile->next, &tun->disabled); | ||
396 | ++tun->numdisabled; | ||
397 | } | ||
398 | |||
399 | static struct tun_struct *tun_enable_queue(struct tun_file *tfile) | ||
400 | { | ||
401 | struct tun_struct *tun = tfile->detached; | ||
402 | |||
403 | tfile->detached = NULL; | ||
404 | list_del_init(&tfile->next); | ||
405 | --tun->numdisabled; | ||
406 | return tun; | ||
407 | } | ||
408 | |||
388 | static void __tun_detach(struct tun_file *tfile, bool clean) | 409 | static void __tun_detach(struct tun_file *tfile, bool clean) |
389 | { | 410 | { |
390 | struct tun_file *ntfile; | 411 | struct tun_file *ntfile; |
@@ -406,20 +427,25 @@ static void __tun_detach(struct tun_file *tfile, bool clean) | |||
406 | ntfile->queue_index = index; | 427 | ntfile->queue_index = index; |
407 | 428 | ||
408 | --tun->numqueues; | 429 | --tun->numqueues; |
409 | sock_put(&tfile->sk); | 430 | if (clean) |
431 | sock_put(&tfile->sk); | ||
432 | else | ||
433 | tun_disable_queue(tun, tfile); | ||
410 | 434 | ||
411 | synchronize_net(); | 435 | synchronize_net(); |
412 | tun_flow_delete_by_queue(tun, tun->numqueues + 1); | 436 | tun_flow_delete_by_queue(tun, tun->numqueues + 1); |
413 | /* Drop read queue */ | 437 | /* Drop read queue */ |
414 | skb_queue_purge(&tfile->sk.sk_receive_queue); | 438 | skb_queue_purge(&tfile->sk.sk_receive_queue); |
415 | tun_set_real_num_queues(tun); | 439 | tun_set_real_num_queues(tun); |
416 | 440 | } else if (tfile->detached && clean) | |
417 | if (tun->numqueues == 0 && !(tun->flags & TUN_PERSIST)) | 441 | tun = tun_enable_queue(tfile); |
418 | if (dev->reg_state == NETREG_REGISTERED) | ||
419 | unregister_netdevice(dev); | ||
420 | } | ||
421 | 442 | ||
422 | if (clean) { | 443 | if (clean) { |
444 | if (tun && tun->numqueues == 0 && tun->numdisabled == 0 && | ||
445 | !(tun->flags & TUN_PERSIST)) | ||
446 | if (tun->dev->reg_state == NETREG_REGISTERED) | ||
447 | unregister_netdevice(tun->dev); | ||
448 | |||
423 | BUG_ON(!test_bit(SOCK_EXTERNALLY_ALLOCATED, | 449 | BUG_ON(!test_bit(SOCK_EXTERNALLY_ALLOCATED, |
424 | &tfile->socket.flags)); | 450 | &tfile->socket.flags)); |
425 | sk_release_kernel(&tfile->sk); | 451 | sk_release_kernel(&tfile->sk); |
@@ -436,7 +462,7 @@ static void tun_detach(struct tun_file *tfile, bool clean) | |||
436 | static void tun_detach_all(struct net_device *dev) | 462 | static void tun_detach_all(struct net_device *dev) |
437 | { | 463 | { |
438 | struct tun_struct *tun = netdev_priv(dev); | 464 | struct tun_struct *tun = netdev_priv(dev); |
439 | struct tun_file *tfile; | 465 | struct tun_file *tfile, *tmp; |
440 | int i, n = tun->numqueues; | 466 | int i, n = tun->numqueues; |
441 | 467 | ||
442 | for (i = 0; i < n; i++) { | 468 | for (i = 0; i < n; i++) { |
@@ -457,6 +483,12 @@ static void tun_detach_all(struct net_device *dev) | |||
457 | skb_queue_purge(&tfile->sk.sk_receive_queue); | 483 | skb_queue_purge(&tfile->sk.sk_receive_queue); |
458 | sock_put(&tfile->sk); | 484 | sock_put(&tfile->sk); |
459 | } | 485 | } |
486 | list_for_each_entry_safe(tfile, tmp, &tun->disabled, next) { | ||
487 | tun_enable_queue(tfile); | ||
488 | skb_queue_purge(&tfile->sk.sk_receive_queue); | ||
489 | sock_put(&tfile->sk); | ||
490 | } | ||
491 | BUG_ON(tun->numdisabled != 0); | ||
460 | } | 492 | } |
461 | 493 | ||
462 | static int tun_attach(struct tun_struct *tun, struct file *file) | 494 | static int tun_attach(struct tun_struct *tun, struct file *file) |
@@ -473,7 +505,8 @@ static int tun_attach(struct tun_struct *tun, struct file *file) | |||
473 | goto out; | 505 | goto out; |
474 | 506 | ||
475 | err = -E2BIG; | 507 | err = -E2BIG; |
476 | if (tun->numqueues == MAX_TAP_QUEUES) | 508 | if (!tfile->detached && |
509 | tun->numqueues + tun->numdisabled == MAX_TAP_QUEUES) | ||
477 | goto out; | 510 | goto out; |
478 | 511 | ||
479 | err = 0; | 512 | err = 0; |
@@ -487,9 +520,13 @@ static int tun_attach(struct tun_struct *tun, struct file *file) | |||
487 | tfile->queue_index = tun->numqueues; | 520 | tfile->queue_index = tun->numqueues; |
488 | rcu_assign_pointer(tfile->tun, tun); | 521 | rcu_assign_pointer(tfile->tun, tun); |
489 | rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile); | 522 | rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile); |
490 | sock_hold(&tfile->sk); | ||
491 | tun->numqueues++; | 523 | tun->numqueues++; |
492 | 524 | ||
525 | if (tfile->detached) | ||
526 | tun_enable_queue(tfile); | ||
527 | else | ||
528 | sock_hold(&tfile->sk); | ||
529 | |||
493 | tun_set_real_num_queues(tun); | 530 | tun_set_real_num_queues(tun); |
494 | 531 | ||
495 | /* device is allowed to go away first, so no need to hold extra | 532 | /* device is allowed to go away first, so no need to hold extra |
@@ -1162,6 +1199,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, | |||
1162 | skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY; | 1199 | skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY; |
1163 | } | 1200 | } |
1164 | 1201 | ||
1202 | skb_reset_network_header(skb); | ||
1165 | rxhash = skb_get_rxhash(skb); | 1203 | rxhash = skb_get_rxhash(skb); |
1166 | netif_rx_ni(skb); | 1204 | netif_rx_ni(skb); |
1167 | 1205 | ||
@@ -1349,6 +1387,7 @@ static void tun_free_netdev(struct net_device *dev) | |||
1349 | { | 1387 | { |
1350 | struct tun_struct *tun = netdev_priv(dev); | 1388 | struct tun_struct *tun = netdev_priv(dev); |
1351 | 1389 | ||
1390 | BUG_ON(!(list_empty(&tun->disabled))); | ||
1352 | tun_flow_uninit(tun); | 1391 | tun_flow_uninit(tun); |
1353 | free_netdev(dev); | 1392 | free_netdev(dev); |
1354 | } | 1393 | } |
@@ -1543,6 +1582,10 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) | |||
1543 | err = tun_attach(tun, file); | 1582 | err = tun_attach(tun, file); |
1544 | if (err < 0) | 1583 | if (err < 0) |
1545 | return err; | 1584 | return err; |
1585 | |||
1586 | if (tun->flags & TUN_TAP_MQ && | ||
1587 | (tun->numqueues + tun->numdisabled > 1)) | ||
1588 | return err; | ||
1546 | } | 1589 | } |
1547 | else { | 1590 | else { |
1548 | char *name; | 1591 | char *name; |
@@ -1601,6 +1644,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) | |||
1601 | TUN_USER_FEATURES; | 1644 | TUN_USER_FEATURES; |
1602 | dev->features = dev->hw_features; | 1645 | dev->features = dev->hw_features; |
1603 | 1646 | ||
1647 | INIT_LIST_HEAD(&tun->disabled); | ||
1604 | err = tun_attach(tun, file); | 1648 | err = tun_attach(tun, file); |
1605 | if (err < 0) | 1649 | if (err < 0) |
1606 | goto err_free_dev; | 1650 | goto err_free_dev; |
@@ -1755,32 +1799,28 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr) | |||
1755 | { | 1799 | { |
1756 | struct tun_file *tfile = file->private_data; | 1800 | struct tun_file *tfile = file->private_data; |
1757 | struct tun_struct *tun; | 1801 | struct tun_struct *tun; |
1758 | struct net_device *dev; | ||
1759 | int ret = 0; | 1802 | int ret = 0; |
1760 | 1803 | ||
1761 | rtnl_lock(); | 1804 | rtnl_lock(); |
1762 | 1805 | ||
1763 | if (ifr->ifr_flags & IFF_ATTACH_QUEUE) { | 1806 | if (ifr->ifr_flags & IFF_ATTACH_QUEUE) { |
1764 | dev = __dev_get_by_name(tfile->net, ifr->ifr_name); | 1807 | tun = tfile->detached; |
1765 | if (!dev) { | 1808 | if (!tun) |
1766 | ret = -EINVAL; | ||
1767 | goto unlock; | ||
1768 | } | ||
1769 | |||
1770 | tun = netdev_priv(dev); | ||
1771 | if (dev->netdev_ops != &tap_netdev_ops && | ||
1772 | dev->netdev_ops != &tun_netdev_ops) | ||
1773 | ret = -EINVAL; | 1809 | ret = -EINVAL; |
1774 | else if (tun_not_capable(tun)) | 1810 | else if (tun_not_capable(tun)) |
1775 | ret = -EPERM; | 1811 | ret = -EPERM; |
1776 | else | 1812 | else |
1777 | ret = tun_attach(tun, file); | 1813 | ret = tun_attach(tun, file); |
1778 | } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) | 1814 | } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { |
1779 | __tun_detach(tfile, false); | 1815 | tun = rcu_dereference_protected(tfile->tun, |
1780 | else | 1816 | lockdep_rtnl_is_held()); |
1817 | if (!tun || !(tun->flags & TUN_TAP_MQ)) | ||
1818 | ret = -EINVAL; | ||
1819 | else | ||
1820 | __tun_detach(tfile, false); | ||
1821 | } else | ||
1781 | ret = -EINVAL; | 1822 | ret = -EINVAL; |
1782 | 1823 | ||
1783 | unlock: | ||
1784 | rtnl_unlock(); | 1824 | rtnl_unlock(); |
1785 | return ret; | 1825 | return ret; |
1786 | } | 1826 | } |
@@ -2092,6 +2132,7 @@ static int tun_chr_open(struct inode *inode, struct file * file) | |||
2092 | 2132 | ||
2093 | file->private_data = tfile; | 2133 | file->private_data = tfile; |
2094 | set_bit(SOCK_EXTERNALLY_ALLOCATED, &tfile->socket.flags); | 2134 | set_bit(SOCK_EXTERNALLY_ALLOCATED, &tfile->socket.flags); |
2135 | INIT_LIST_HEAD(&tfile->next); | ||
2095 | 2136 | ||
2096 | return 0; | 2137 | return 0; |
2097 | } | 2138 | } |