diff options
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r-- | drivers/net/tun.c | 16 |
1 files changed, 11 insertions, 5 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index db16d7a13e00..aab0be40d443 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
@@ -787,7 +787,8 @@ static void tun_detach_all(struct net_device *dev) | |||
787 | } | 787 | } |
788 | 788 | ||
789 | static int tun_attach(struct tun_struct *tun, struct file *file, | 789 | static int tun_attach(struct tun_struct *tun, struct file *file, |
790 | bool skip_filter, bool napi, bool napi_frags) | 790 | bool skip_filter, bool napi, bool napi_frags, |
791 | bool publish_tun) | ||
791 | { | 792 | { |
792 | struct tun_file *tfile = file->private_data; | 793 | struct tun_file *tfile = file->private_data; |
793 | struct net_device *dev = tun->dev; | 794 | struct net_device *dev = tun->dev; |
@@ -870,7 +871,8 @@ static int tun_attach(struct tun_struct *tun, struct file *file, | |||
870 | * initialized tfile; otherwise we risk using half-initialized | 871 | * initialized tfile; otherwise we risk using half-initialized |
871 | * object. | 872 | * object. |
872 | */ | 873 | */ |
873 | rcu_assign_pointer(tfile->tun, tun); | 874 | if (publish_tun) |
875 | rcu_assign_pointer(tfile->tun, tun); | ||
874 | rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile); | 876 | rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile); |
875 | tun->numqueues++; | 877 | tun->numqueues++; |
876 | tun_set_real_num_queues(tun); | 878 | tun_set_real_num_queues(tun); |
@@ -2730,7 +2732,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) | |||
2730 | 2732 | ||
2731 | err = tun_attach(tun, file, ifr->ifr_flags & IFF_NOFILTER, | 2733 | err = tun_attach(tun, file, ifr->ifr_flags & IFF_NOFILTER, |
2732 | ifr->ifr_flags & IFF_NAPI, | 2734 | ifr->ifr_flags & IFF_NAPI, |
2733 | ifr->ifr_flags & IFF_NAPI_FRAGS); | 2735 | ifr->ifr_flags & IFF_NAPI_FRAGS, true); |
2734 | if (err < 0) | 2736 | if (err < 0) |
2735 | return err; | 2737 | return err; |
2736 | 2738 | ||
@@ -2829,13 +2831,17 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) | |||
2829 | 2831 | ||
2830 | INIT_LIST_HEAD(&tun->disabled); | 2832 | INIT_LIST_HEAD(&tun->disabled); |
2831 | err = tun_attach(tun, file, false, ifr->ifr_flags & IFF_NAPI, | 2833 | err = tun_attach(tun, file, false, ifr->ifr_flags & IFF_NAPI, |
2832 | ifr->ifr_flags & IFF_NAPI_FRAGS); | 2834 | ifr->ifr_flags & IFF_NAPI_FRAGS, false); |
2833 | if (err < 0) | 2835 | if (err < 0) |
2834 | goto err_free_flow; | 2836 | goto err_free_flow; |
2835 | 2837 | ||
2836 | err = register_netdevice(tun->dev); | 2838 | err = register_netdevice(tun->dev); |
2837 | if (err < 0) | 2839 | if (err < 0) |
2838 | goto err_detach; | 2840 | goto err_detach; |
2841 | /* free_netdev() won't check refcnt, to aovid race | ||
2842 | * with dev_put() we need publish tun after registration. | ||
2843 | */ | ||
2844 | rcu_assign_pointer(tfile->tun, tun); | ||
2839 | } | 2845 | } |
2840 | 2846 | ||
2841 | netif_carrier_on(tun->dev); | 2847 | netif_carrier_on(tun->dev); |
@@ -2978,7 +2984,7 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr) | |||
2978 | if (ret < 0) | 2984 | if (ret < 0) |
2979 | goto unlock; | 2985 | goto unlock; |
2980 | ret = tun_attach(tun, file, false, tun->flags & IFF_NAPI, | 2986 | ret = tun_attach(tun, file, false, tun->flags & IFF_NAPI, |
2981 | tun->flags & IFF_NAPI_FRAGS); | 2987 | tun->flags & IFF_NAPI_FRAGS, true); |
2982 | } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { | 2988 | } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { |
2983 | tun = rtnl_dereference(tfile->tun); | 2989 | tun = rtnl_dereference(tfile->tun); |
2984 | if (!tun || !(tun->flags & IFF_MULTI_QUEUE) || tfile->detached) | 2990 | if (!tun || !(tun->flags & IFF_MULTI_QUEUE) || tfile->detached) |