Skip to content

Commit

Permalink
tun: Move read_wait into tun_file
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Eric W. Biederman authored and David S. Miller committed Jan 22, 2009
1 parent 38231b7 commit b2430de
Showing 1 changed file with 10 additions and 8 deletions.
18 changes: 10 additions & 8 deletions drivers/net/tun.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -420,15 +420,16 @@ 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)
return POLLERR;

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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit b2430de

Please sign in to comment.