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>
This commit is contained in:
parent
38231b7a8d
commit
b2430de37e
@ -90,6 +90,7 @@ struct tap_filter {
|
|||||||
struct tun_file {
|
struct tun_file {
|
||||||
struct tun_struct *tun;
|
struct tun_struct *tun;
|
||||||
struct net *net;
|
struct net *net;
|
||||||
|
wait_queue_head_t read_wait;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tun_struct {
|
struct tun_struct {
|
||||||
@ -98,7 +99,6 @@ struct tun_struct {
|
|||||||
uid_t owner;
|
uid_t owner;
|
||||||
gid_t group;
|
gid_t group;
|
||||||
|
|
||||||
wait_queue_head_t read_wait;
|
|
||||||
struct sk_buff_head readq;
|
struct sk_buff_head readq;
|
||||||
|
|
||||||
struct net_device *dev;
|
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 */
|
/* Notify and wake up reader process */
|
||||||
if (tun->flags & TUN_FASYNC)
|
if (tun->flags & TUN_FASYNC)
|
||||||
kill_fasync(&tun->fasync, SIGIO, POLL_IN);
|
kill_fasync(&tun->fasync, SIGIO, POLL_IN);
|
||||||
wake_up_interruptible(&tun->read_wait);
|
wake_up_interruptible(&tun->tfile->read_wait);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
drop:
|
drop:
|
||||||
@ -420,7 +420,8 @@ static void tun_net_init(struct net_device *dev)
|
|||||||
/* Poll */
|
/* Poll */
|
||||||
static unsigned int tun_chr_poll(struct file *file, poll_table * wait)
|
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;
|
unsigned int mask = POLLOUT | POLLWRNORM;
|
||||||
|
|
||||||
if (!tun)
|
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);
|
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))
|
if (!skb_queue_empty(&tun->readq))
|
||||||
mask |= POLLIN | POLLRDNORM;
|
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)
|
unsigned long count, loff_t pos)
|
||||||
{
|
{
|
||||||
struct file *file = iocb->ki_filp;
|
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);
|
DECLARE_WAITQUEUE(wait, current);
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
ssize_t len, ret = 0;
|
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;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_wait_queue(&tun->read_wait, &wait);
|
add_wait_queue(&tfile->read_wait, &wait);
|
||||||
while (len) {
|
while (len) {
|
||||||
current->state = TASK_INTERRUPTIBLE;
|
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;
|
current->state = TASK_RUNNING;
|
||||||
remove_wait_queue(&tun->read_wait, &wait);
|
remove_wait_queue(&tfile->read_wait, &wait);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
tun_put(tun);
|
tun_put(tun);
|
||||||
@ -757,7 +759,6 @@ static void tun_setup(struct net_device *dev)
|
|||||||
struct tun_struct *tun = netdev_priv(dev);
|
struct tun_struct *tun = netdev_priv(dev);
|
||||||
|
|
||||||
skb_queue_head_init(&tun->readq);
|
skb_queue_head_init(&tun->readq);
|
||||||
init_waitqueue_head(&tun->read_wait);
|
|
||||||
|
|
||||||
tun->owner = -1;
|
tun->owner = -1;
|
||||||
tun->group = -1;
|
tun->group = -1;
|
||||||
@ -1136,6 +1137,7 @@ static int tun_chr_open(struct inode *inode, struct file * file)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
tfile->tun = NULL;
|
tfile->tun = NULL;
|
||||||
tfile->net = get_net(current->nsproxy->net_ns);
|
tfile->net = get_net(current->nsproxy->net_ns);
|
||||||
|
init_waitqueue_head(&tfile->read_wait);
|
||||||
file->private_data = tfile;
|
file->private_data = tfile;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user