diff options
Diffstat (limited to 'drivers/net/tun.c')
| -rw-r--r-- | drivers/net/tun.c | 51 | 
1 files changed, 39 insertions, 12 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 43265207d46..6b150c072a4 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -109,6 +109,9 @@ struct tun_struct {  	struct tap_filter       txflt;  	struct socket		socket; +	struct socket_wq	wq; + +	int			vnet_hdr_sz;  #ifdef TUN_DEBUG  	int debug; @@ -323,7 +326,7 @@ static void tun_net_uninit(struct net_device *dev)  	/* Inform the methods they need to stop using the dev.  	 */  	if (tfile) { -		wake_up_all(&tun->socket.wait); +		wake_up_all(&tun->wq.wait);  		if (atomic_dec_and_test(&tfile->count))  			__tun_detach(tun);  	} @@ -398,7 +401,7 @@ static netdev_tx_t 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_poll(&tun->socket.wait, POLLIN | +	wake_up_interruptible_poll(&tun->wq.wait, POLLIN |  				   POLLRDNORM | POLLRDBAND);  	return NETDEV_TX_OK; @@ -498,7 +501,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->socket.wait, wait); +	poll_wait(file, &tun->wq.wait, wait);  	if (!skb_queue_empty(&sk->sk_receive_queue))  		mask |= POLLIN | POLLRDNORM; @@ -563,7 +566,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun,  	}  	if (tun->flags & TUN_VNET_HDR) { -		if ((len -= sizeof(gso)) > count) +		if ((len -= tun->vnet_hdr_sz) > count)  			return -EINVAL;  		if (memcpy_fromiovecend((void *)&gso, iv, offset, sizeof(gso))) @@ -575,7 +578,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun,  		if (gso.hdr_len > len)  			return -EINVAL; -		offset += sizeof(gso); +		offset += tun->vnet_hdr_sz;  	}  	if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) { @@ -718,7 +721,7 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,  	if (tun->flags & TUN_VNET_HDR) {  		struct virtio_net_hdr gso = { 0 }; /* no info leak */ -		if ((len -= sizeof(gso)) < 0) +		if ((len -= tun->vnet_hdr_sz) < 0)  			return -EINVAL;  		if (skb_is_gso(skb)) { @@ -749,7 +752,7 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,  		if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total,  					       sizeof(gso))))  			return -EFAULT; -		total += sizeof(gso); +		total += tun->vnet_hdr_sz;  	}  	len = min_t(int, skb->len, len); @@ -773,7 +776,7 @@ static ssize_t tun_do_read(struct tun_struct *tun,  	DBG(KERN_INFO "%s: tun_chr_read\n", tun->dev->name); -	add_wait_queue(&tun->socket.wait, &wait); +	add_wait_queue(&tun->wq.wait, &wait);  	while (len) {  		current->state = TASK_INTERRUPTIBLE; @@ -804,7 +807,7 @@ static ssize_t tun_do_read(struct tun_struct *tun,  	}  	current->state = TASK_RUNNING; -	remove_wait_queue(&tun->socket.wait, &wait); +	remove_wait_queue(&tun->wq.wait, &wait);  	return ret;  } @@ -861,6 +864,7 @@ static struct rtnl_link_ops tun_link_ops __read_mostly = {  static void tun_sock_write_space(struct sock *sk)  {  	struct tun_struct *tun; +	wait_queue_head_t *wqueue;  	if (!sock_writeable(sk))  		return; @@ -868,8 +872,9 @@ static void tun_sock_write_space(struct sock *sk)  	if (!test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags))  		return; -	if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) -		wake_up_interruptible_sync_poll(sk->sk_sleep, POLLOUT | +	wqueue = sk_sleep(sk); +	if (wqueue && waitqueue_active(wqueue)) +		wake_up_interruptible_sync_poll(wqueue, POLLOUT |  						POLLWRNORM | POLLWRBAND);  	tun = tun_sk(sk)->tun; @@ -1033,13 +1038,15 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)  		tun->dev = dev;  		tun->flags = flags;  		tun->txflt.count = 0; +		tun->vnet_hdr_sz = sizeof(struct virtio_net_hdr);  		err = -ENOMEM;  		sk = sk_alloc(net, AF_UNSPEC, GFP_KERNEL, &tun_proto);  		if (!sk)  			goto err_free_dev; -		init_waitqueue_head(&tun->socket.wait); +		tun->socket.wq = &tun->wq; +		init_waitqueue_head(&tun->wq.wait);  		tun->socket.ops = &tun_socket_ops;  		sock_init_data(&tun->socket, sk);  		sk->sk_write_space = tun_sock_write_space; @@ -1174,6 +1181,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,  	struct sock_fprog fprog;  	struct ifreq ifr;  	int sndbuf; +	int vnet_hdr_sz;  	int ret;  	if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) @@ -1319,6 +1327,25 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,  		tun->socket.sk->sk_sndbuf = sndbuf;  		break; +	case TUNGETVNETHDRSZ: +		vnet_hdr_sz = tun->vnet_hdr_sz; +		if (copy_to_user(argp, &vnet_hdr_sz, sizeof(vnet_hdr_sz))) +			ret = -EFAULT; +		break; + +	case TUNSETVNETHDRSZ: +		if (copy_from_user(&vnet_hdr_sz, argp, sizeof(vnet_hdr_sz))) { +			ret = -EFAULT; +			break; +		} +		if (vnet_hdr_sz < (int)sizeof(struct virtio_net_hdr)) { +			ret = -EINVAL; +			break; +		} + +		tun->vnet_hdr_sz = vnet_hdr_sz; +		break; +  	case TUNATTACHFILTER:  		/* Can be set only for TAPs */  		ret = -EINVAL;  |