diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2009-01-20 12:03:21 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-01-22 01:00:46 +0100 |
commit | b2430de37ef0bc0799ffba7b5219d38ca417eb76 (patch) | |
tree | 19a5a5103ed2227f230aa06f87dc7807e871d5b5 /drivers/net/tun.c | |
parent | tun: Make tun_net_xmit atomic wrt tun_attach && tun_detach (diff) | |
download | linux-b2430de37ef0bc0799ffba7b5219d38ca417eb76.tar.xz linux-b2430de37ef0bc0799ffba7b5219d38ca417eb76.zip |
tun: Move read_wait into tun_file
The poll interface requires that the waitqueue exist while the struct
file is open. In the rare case when a tun device disappears before
the tun file closes we fail to provide this property, so move
read_wait.
This is safe now that tun_net_xmit is atomic with tun_detach.
Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r-- | drivers/net/tun.c | 18 |
1 files changed, 10 insertions, 8 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index fa93160bf522..030d9858bb68 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -90,6 +90,7 @@ struct tap_filter { struct tun_file { struct tun_struct *tun; struct net *net; + wait_queue_head_t read_wait; }; struct tun_struct { @@ -98,7 +99,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; @@ -335,7 +335,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: @@ -420,7 +420,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,7 +429,7 @@ 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; @@ -702,7 +703,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 +720,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; @@ -745,7 +747,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,7 +759,6 @@ 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; @@ -1136,6 +1137,7 @@ static int tun_chr_open(struct inode *inode, struct file * file) return -ENOMEM; tfile->tun = NULL; tfile->net = get_net(current->nsproxy->net_ns); + init_waitqueue_head(&tfile->read_wait); file->private_data = tfile; return 0; } |