diff options
Diffstat (limited to 'drivers/net/tun.c')
| -rw-r--r-- | drivers/net/tun.c | 47 | 
1 files changed, 25 insertions, 22 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 16716aef184..735bf41c654 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -93,7 +93,6 @@ struct tun_file {  	atomic_t count;  	struct tun_struct *tun;  	struct net *net; -	wait_queue_head_t	read_wait;  };  struct tun_sock; @@ -156,6 +155,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file)  	tfile->tun = tun;  	tun->tfile = tfile;  	dev_hold(tun->dev); +	sock_hold(tun->sk);  	atomic_inc(&tfile->count);  out: @@ -165,11 +165,8 @@ out:  static void __tun_detach(struct tun_struct *tun)  { -	struct tun_file *tfile = tun->tfile; -  	/* Detach from net device */  	netif_tx_lock_bh(tun->dev); -	tfile->tun = NULL;  	tun->tfile = NULL;  	netif_tx_unlock_bh(tun->dev); @@ -333,12 +330,19 @@ static void tun_net_uninit(struct net_device *dev)  	/* Inform the methods they need to stop using the dev.  	 */  	if (tfile) { -		wake_up_all(&tfile->read_wait); +		wake_up_all(&tun->socket.wait);  		if (atomic_dec_and_test(&tfile->count))  			__tun_detach(tun);  	}  } +static void tun_free_netdev(struct net_device *dev) +{ +	struct tun_struct *tun = netdev_priv(dev); + +	sock_put(tun->sk); +} +  /* Net device open. */  static int tun_net_open(struct net_device *dev)  { @@ -393,7 +397,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->tfile->read_wait); +	wake_up_interruptible(&tun->socket.wait);  	return 0;  drop: @@ -490,7 +494,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, &tfile->read_wait, wait); +	poll_wait(file, &tun->socket.wait, wait);  	if (!skb_queue_empty(&tun->readq))  		mask |= POLLIN | POLLRDNORM; @@ -763,7 +767,7 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,  		goto out;  	} -	add_wait_queue(&tfile->read_wait, &wait); +	add_wait_queue(&tun->socket.wait, &wait);  	while (len) {  		current->state = TASK_INTERRUPTIBLE; @@ -794,7 +798,7 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,  	}  	current->state = TASK_RUNNING; -	remove_wait_queue(&tfile->read_wait, &wait); +	remove_wait_queue(&tun->socket.wait, &wait);  out:  	tun_put(tun); @@ -811,7 +815,7 @@ static void tun_setup(struct net_device *dev)  	tun->group = -1;  	dev->ethtool_ops = &tun_ethtool_ops; -	dev->destructor = free_netdev; +	dev->destructor = tun_free_netdev;  }  /* Trivial set of netlink ops to allow deleting tun or tap @@ -848,7 +852,7 @@ static void tun_sock_write_space(struct sock *sk)  static void tun_sock_destruct(struct sock *sk)  { -	dev_put(container_of(sk, struct tun_sock, sk)->tun->dev); +	free_netdev(container_of(sk, struct tun_sock, sk)->tun->dev);  }  static struct proto tun_proto = { @@ -862,7 +866,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)  	struct sock *sk;  	struct tun_struct *tun;  	struct net_device *dev; -	struct tun_file *tfile = file->private_data;  	int err;  	dev = __dev_get_by_name(net, ifr->ifr_name); @@ -920,13 +923,10 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)  		if (!sk)  			goto err_free_dev; -		/* This ref count is for tun->sk. */ -		dev_hold(dev); +		init_waitqueue_head(&tun->socket.wait);  		sock_init_data(&tun->socket, sk);  		sk->sk_write_space = tun_sock_write_space; -		sk->sk_destruct = tun_sock_destruct;  		sk->sk_sndbuf = INT_MAX; -		sk->sk_sleep = &tfile->read_wait;  		tun->sk = sk;  		container_of(sk, struct tun_sock, sk)->tun = tun; @@ -942,11 +942,13 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)  		err = -EINVAL;  		err = register_netdevice(tun->dev);  		if (err < 0) -			goto err_free_dev; +			goto err_free_sk; + +		sk->sk_destruct = tun_sock_destruct;  		err = tun_attach(tun, file);  		if (err < 0) -			goto err_free_dev; +			goto failed;  	}  	DBG(KERN_INFO "%s: tun_set_iff\n", tun->dev->name); @@ -1266,7 +1268,6 @@ static int tun_chr_open(struct inode *inode, struct file * file)  	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;  } @@ -1284,14 +1285,16 @@ static int tun_chr_close(struct inode *inode, struct file *file)  		__tun_detach(tun);  		/* If desireable, unregister the netdevice. */ -		if (!(tun->flags & TUN_PERSIST)) { -			sock_put(tun->sk); +		if (!(tun->flags & TUN_PERSIST))  			unregister_netdevice(tun->dev); -		}  		rtnl_unlock();  	} +	tun = tfile->tun; +	if (tun) +		sock_put(tun->sk); +  	put_net(tfile->net);  	kfree(tfile);  |