diff options
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r-- | drivers/net/tun.c | 59 |
1 files changed, 44 insertions, 15 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 96c39bddc78..97b25533e5f 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); } @@ -387,14 +390,17 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) } } + /* Orphan the skb - required as we might hang on to it + * for indefinite time. */ + skb_orphan(skb); + /* Enqueue packet */ skb_queue_tail(&tun->socket.sk->sk_receive_queue, skb); - dev->trans_start = jiffies; /* 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; @@ -411,7 +417,6 @@ static void tun_net_mclist(struct net_device *dev) * _rx_ path and has nothing to do with the _tx_ path. * In rx path we always accept everything userspace gives us. */ - return; } #define MIN_MTU 68 @@ -494,7 +499,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; @@ -559,7 +564,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))) @@ -571,7 +576,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) { @@ -714,7 +719,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)) { @@ -745,7 +750,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); @@ -769,7 +774,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; @@ -800,7 +805,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; } @@ -857,6 +862,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; @@ -864,8 +870,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; @@ -1029,13 +1036,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; @@ -1170,6 +1179,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) @@ -1315,6 +1325,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; @@ -1338,7 +1367,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, default: ret = -EINVAL; break; - }; + } unlock: rtnl_unlock(); |