]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/net/tun.c
net: pppoe - stop using proc internals
[mirror_ubuntu-bionic-kernel.git] / drivers / net / tun.c
index fa93160bf52244fbf71db7812bf7dccd86cc9278..e9bcbdfe015a6a593ea275f303c31fa0adea5417 100644 (file)
@@ -63,6 +63,7 @@
 #include <linux/virtio_net.h>
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
+#include <net/rtnetlink.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -88,8 +89,10 @@ struct tap_filter {
 };
 
 struct tun_file {
+       atomic_t count;
        struct tun_struct *tun;
        struct net *net;
+       wait_queue_head_t       read_wait;
 };
 
 struct tun_struct {
@@ -98,7 +101,6 @@ struct tun_struct {
        uid_t                   owner;
        gid_t                   group;
 
-       wait_queue_head_t       read_wait;
        struct sk_buff_head     readq;
 
        struct net_device       *dev;
@@ -138,6 +140,8 @@ static int tun_attach(struct tun_struct *tun, struct file *file)
        err = 0;
        tfile->tun = tun;
        tun->tfile = tfile;
+       dev_hold(tun->dev);
+       atomic_inc(&tfile->count);
 
 out:
        netif_tx_unlock_bh(tun->dev);
@@ -156,11 +160,26 @@ static void __tun_detach(struct tun_struct *tun)
 
        /* Drop read queue */
        skb_queue_purge(&tun->readq);
+
+       /* Drop the extra count on the net device */
+       dev_put(tun->dev);
+}
+
+static void tun_detach(struct tun_struct *tun)
+{
+       rtnl_lock();
+       __tun_detach(tun);
+       rtnl_unlock();
 }
 
 static struct tun_struct *__tun_get(struct tun_file *tfile)
 {
-       return tfile->tun;
+       struct tun_struct *tun = NULL;
+
+       if (atomic_inc_not_zero(&tfile->count))
+               tun = tfile->tun;
+
+       return tun;
 }
 
 static struct tun_struct *tun_get(struct file *file)
@@ -170,7 +189,10 @@ static struct tun_struct *tun_get(struct file *file)
 
 static void tun_put(struct tun_struct *tun)
 {
-       /* Noop for now */
+       struct tun_file *tfile = tun->tfile;
+
+       if (atomic_dec_and_test(&tfile->count))
+               tun_detach(tfile->tun);
 }
 
 /* TAP filterting */
@@ -281,6 +303,21 @@ static int check_filter(struct tap_filter *filter, const struct sk_buff *skb)
 
 static const struct ethtool_ops tun_ethtool_ops;
 
+/* Net device detach from fd. */
+static void tun_net_uninit(struct net_device *dev)
+{
+       struct tun_struct *tun = netdev_priv(dev);
+       struct tun_file *tfile = tun->tfile;
+
+       /* Inform the methods they need to stop using the dev.
+        */
+       if (tfile) {
+               wake_up_all(&tfile->read_wait);
+               if (atomic_dec_and_test(&tfile->count))
+                       __tun_detach(tun);
+       }
+}
+
 /* Net device open. */
 static int tun_net_open(struct net_device *dev)
 {
@@ -335,7 +372,7 @@ static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
        /* Notify and wake up reader process */
        if (tun->flags & TUN_FASYNC)
                kill_fasync(&tun->fasync, SIGIO, POLL_IN);
-       wake_up_interruptible(&tun->read_wait);
+       wake_up_interruptible(&tun->tfile->read_wait);
        return 0;
 
 drop:
@@ -367,6 +404,7 @@ tun_net_change_mtu(struct net_device *dev, int new_mtu)
 }
 
 static const struct net_device_ops tun_netdev_ops = {
+       .ndo_uninit             = tun_net_uninit,
        .ndo_open               = tun_net_open,
        .ndo_stop               = tun_net_close,
        .ndo_start_xmit         = tun_net_xmit,
@@ -374,6 +412,7 @@ static const struct net_device_ops tun_netdev_ops = {
 };
 
 static const struct net_device_ops tap_netdev_ops = {
+       .ndo_uninit             = tun_net_uninit,
        .ndo_open               = tun_net_open,
        .ndo_stop               = tun_net_close,
        .ndo_start_xmit         = tun_net_xmit,
@@ -420,7 +459,8 @@ static void tun_net_init(struct net_device *dev)
 /* Poll */
 static unsigned int tun_chr_poll(struct file *file, poll_table * wait)
 {
-       struct tun_struct *tun = tun_get(file);
+       struct tun_file *tfile = file->private_data;
+       struct tun_struct *tun = __tun_get(tfile);
        unsigned int mask = POLLOUT | POLLWRNORM;
 
        if (!tun)
@@ -428,11 +468,14 @@ static unsigned int tun_chr_poll(struct file *file, poll_table * wait)
 
        DBG(KERN_INFO "%s: tun_chr_poll\n", tun->dev->name);
 
-       poll_wait(file, &tun->read_wait, wait);
+       poll_wait(file, &tfile->read_wait, wait);
 
        if (!skb_queue_empty(&tun->readq))
                mask |= POLLIN | POLLRDNORM;
 
+       if (tun->dev->reg_state != NETREG_REGISTERED)
+               mask = POLLERR;
+
        tun_put(tun);
        return mask;
 }
@@ -702,7 +745,8 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
                            unsigned long count, loff_t pos)
 {
        struct file *file = iocb->ki_filp;
-       struct tun_struct *tun = tun_get(file);
+       struct tun_file *tfile = file->private_data;
+       struct tun_struct *tun = __tun_get(tfile);
        DECLARE_WAITQUEUE(wait, current);
        struct sk_buff *skb;
        ssize_t len, ret = 0;
@@ -718,7 +762,7 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
                goto out;
        }
 
-       add_wait_queue(&tun->read_wait, &wait);
+       add_wait_queue(&tfile->read_wait, &wait);
        while (len) {
                current->state = TASK_INTERRUPTIBLE;
 
@@ -732,6 +776,10 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
                                ret = -ERESTARTSYS;
                                break;
                        }
+                       if (tun->dev->reg_state != NETREG_REGISTERED) {
+                               ret = -EIO;
+                               break;
+                       }
 
                        /* Nothing to read, let's sleep */
                        schedule();
@@ -745,7 +793,7 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
        }
 
        current->state = TASK_RUNNING;
-       remove_wait_queue(&tun->read_wait, &wait);
+       remove_wait_queue(&tfile->read_wait, &wait);
 
 out:
        tun_put(tun);
@@ -757,16 +805,30 @@ static void tun_setup(struct net_device *dev)
        struct tun_struct *tun = netdev_priv(dev);
 
        skb_queue_head_init(&tun->readq);
-       init_waitqueue_head(&tun->read_wait);
 
        tun->owner = -1;
        tun->group = -1;
 
        dev->ethtool_ops = &tun_ethtool_ops;
        dev->destructor = free_netdev;
-       dev->features |= NETIF_F_NETNS_LOCAL;
 }
 
+/* Trivial set of netlink ops to allow deleting tun or tap
+ * device with netlink.
+ */
+static int tun_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+       return -EINVAL;
+}
+
+static struct rtnl_link_ops tun_link_ops __read_mostly = {
+       .kind           = DRV_NAME,
+       .priv_size      = sizeof(struct tun_struct),
+       .setup          = tun_setup,
+       .validate       = tun_validate,
+};
+
+
 static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
 {
        struct tun_struct *tun;
@@ -816,6 +878,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
                        return -ENOMEM;
 
                dev_net_set(dev, net);
+               dev->rtnl_link_ops = &tun_link_ops;
 
                tun = netdev_priv(dev);
                tun->dev = dev;
@@ -1134,8 +1197,10 @@ static int tun_chr_open(struct inode *inode, struct file * file)
        tfile = kmalloc(sizeof(*tfile), GFP_KERNEL);
        if (!tfile)
                return -ENOMEM;
+       atomic_set(&tfile->count, 0);
        tfile->tun = NULL;
        tfile->net = get_net(current->nsproxy->net_ns);
+       init_waitqueue_head(&tfile->read_wait);
        file->private_data = tfile;
        return 0;
 }
@@ -1270,29 +1335,6 @@ static const struct ethtool_ops tun_ethtool_ops = {
        .set_rx_csum    = tun_set_rx_csum
 };
 
-static int tun_init_net(struct net *net)
-{
-       return 0;
-}
-
-static void tun_exit_net(struct net *net)
-{
-       struct net_device *dev, *next;
-
-       rtnl_lock();
-       for_each_netdev_safe(net, dev, next) {
-               if (dev->ethtool_ops != &tun_ethtool_ops)
-                       continue;
-               DBG(KERN_INFO "%s cleaned up\n", dev->name);
-               unregister_netdevice(dev);
-       }
-       rtnl_unlock();
-}
-
-static struct pernet_operations tun_net_ops = {
-       .init = tun_init_net,
-       .exit = tun_exit_net,
-};
 
 static int __init tun_init(void)
 {
@@ -1301,10 +1343,10 @@ static int __init tun_init(void)
        printk(KERN_INFO "tun: %s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
        printk(KERN_INFO "tun: %s\n", DRV_COPYRIGHT);
 
-       ret = register_pernet_device(&tun_net_ops);
+       ret = rtnl_link_register(&tun_link_ops);
        if (ret) {
-               printk(KERN_ERR "tun: Can't register pernet ops\n");
-               goto err_pernet;
+               printk(KERN_ERR "tun: Can't register link_ops\n");
+               goto err_linkops;
        }
 
        ret = misc_register(&tun_miscdev);
@@ -1312,18 +1354,17 @@ static int __init tun_init(void)
                printk(KERN_ERR "tun: Can't register misc device %d\n", TUN_MINOR);
                goto err_misc;
        }
-       return 0;
-
+       return  0;
 err_misc:
-       unregister_pernet_device(&tun_net_ops);
-err_pernet:
+       rtnl_link_unregister(&tun_link_ops);
+err_linkops:
        return ret;
 }
 
 static void tun_cleanup(void)
 {
        misc_deregister(&tun_miscdev);
-       unregister_pernet_device(&tun_net_ops);
+       rtnl_link_unregister(&tun_link_ops);
 }
 
 module_init(tun_init);