]> git.hungrycats.org Git - linux/commitdiff
[PATCH] 2.5.x TUN/TAP driver readv/writev support
authorMaksim Krasnyanskiy <maxk@qualcomm.com>
Tue, 30 Apr 2002 07:51:37 +0000 (00:51 -0700)
committerLinus Torvalds <torvalds@penguin.transmeta.com>
Tue, 30 Apr 2002 07:51:37 +0000 (00:51 -0700)
This adds proper support for readv/writev in the TUN/TAP driver.

drivers/net/tun.c

index 80352ea210b0db9595a11da7e5b4ee15ccc3b68e..00d67044060e027b80cca1b6a5a198268df5679d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  TUN - Universal TUN/TAP device driver.
- *  Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
+ *  Copyright (C) 1999-2002 Maxim Krasnyansky <maxk@qualcomm.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -12,7 +12,7 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  *  GNU General Public License for more details.
  *
- *  $Id: tun.c,v 1.12 2001/03/08 03:29:27 maxk Exp $
+ *  $Id: tun.c,v 1.15 2002/03/01 02:44:24 maxk Exp $
  */
 
 /*
@@ -20,7 +20,7 @@
  *    Modifications for 2.3.99-pre5 kernel.
  */
 
-#define TUN_VER "1.4"
+#define TUN_VER "1.5"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -181,50 +181,26 @@ static unsigned int tun_chr_poll(struct file *file, poll_table * wait)
 }
 
 /* Get packet from user space buffer(already verified) */
-static __inline__ ssize_t
-tun_get_user(struct tun_struct *tun, const struct iovec *iov,
-            unsigned long count, size_t total)
+static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, size_t count)
 {
        struct tun_pi pi = { 0, __constant_htons(ETH_P_IP) };
-       const struct iovec *vector = &iov[0];
-       register const char *ptr = vector->iov_base;
-       register int len = vector->iov_len;
        struct sk_buff *skb;
-       ssize_t sent = 0;
+       size_t len = count;
 
        if (!(tun->flags & TUN_NO_PI)) {
                if ((len -= sizeof(pi)) < 0)
                        return -EINVAL;
 
-               copy_from_user(&pi, ptr, sizeof(pi));
-               ptr += sizeof(pi);
-
-               if (len == 0) {
-                       vector++;
-                       count--;
-
-                       ptr = vector->iov_base;
-                       len = vector->iov_len;
-               }
+               memcpy_fromiovec((void *)&pi, iv, sizeof(pi));
        }
  
-       if (!(skb = alloc_skb(total + 2, GFP_KERNEL))) {
+       if (!(skb = alloc_skb(len + 2, GFP_KERNEL))) {
                tun->stats.rx_dropped++;
                return -ENOMEM;
        }
 
        skb_reserve(skb, 2);
-
-       while (count > 0) {
-               copy_from_user(skb_put(skb, len), ptr, len); 
-               sent += len;
-
-               vector++;
-               count--;
-
-               ptr = vector->iov_base;
-               len = vector->iov_len;
-       }
+       memcpy_fromiovec(skb_put(skb, len), iv, len);
 
        skb->dev = &tun->dev;
        switch (tun->flags & TUN_TYPE_MASK) {
@@ -243,60 +219,48 @@ tun_get_user(struct tun_struct *tun, const struct iovec *iov,
        netif_rx_ni(skb);
    
        tun->stats.rx_packets++;
-       tun->stats.rx_bytes += sent;
+       tun->stats.rx_bytes += len;
 
-       return total;
+       return count;
 } 
 
-/* Write */
-static ssize_t tun_chr_write(struct file * file, const char * buf, 
-                            size_t count, loff_t *pos)
-{
-       struct tun_struct *tun = (struct tun_struct *)file->private_data;
-       struct iovec iov;
-
-       if (!tun)
-               return -EBADFD;
-
-       DBG(KERN_INFO "%s: tun_chr_write %d\n", tun->name, count);
-
-       if (verify_area(VERIFY_READ, buf, count))
-               return -EFAULT;
-
-       iov.iov_base = (char *) buf;
-       iov.iov_len = count;
-
-       return tun_get_user(tun, &iov, 1, count);
-}
-
 /* Writev */
-static ssize_t tun_chr_writev(struct file * file, const struct iovec *iov,
+static ssize_t tun_chr_writev(struct file * file, const struct iovec *iv, 
                              unsigned long count, loff_t *pos)
 {
        struct tun_struct *tun = (struct tun_struct *)file->private_data;
-       size_t total = 0;
        unsigned long i;
+       size_t len;
 
        if (!tun)
                return -EBADFD;
 
-       for (i = 0; i < count; i++) {
-               if (verify_area(VERIFY_READ, iov[i].iov_base, iov[i].iov_len))
+       DBG(KERN_INFO "%s: tun_chr_write %d\n", tun->name, count);
+
+       for (i = 0, len = 0; i < count; i++) {
+               if (verify_area(VERIFY_READ, iv[i].iov_base, iv[i].iov_len))
                        return -EFAULT;
-               total += iov[i].iov_len;
+               len += iv[i].iov_len;
        }
 
-       return tun_get_user(tun, iov, count, total);
+       return tun_get_user(tun, (struct iovec *) iv, len);
+}
+
+/* Write */
+static ssize_t tun_chr_write(struct file * file, const char * buf, 
+                            size_t count, loff_t *pos)
+{
+       struct iovec iv = { (void *) buf, count };
+       return tun_chr_writev(file, &iv, 1, pos);
 }
 
-/* Put packet to user space buffer(already verified) */
+/* Put packet to the user space buffer (already verified) */
 static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
                                       struct sk_buff *skb,
-                                      char *buf, int count)
+                                      struct iovec *iv, int len)
 {
        struct tun_pi pi = { 0, skb->protocol };
-       int len = count, total = 0;
-       char *ptr = buf;
+       ssize_t total = 0;
 
        if (!(tun->flags & TUN_NO_PI)) {
                if ((len -= sizeof(pi)) < 0)
@@ -307,14 +271,13 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
                        pi.flags |= TUN_PKT_STRIP;
                }
  
-               copy_to_user(ptr, &pi, sizeof(pi));
-
+               memcpy_toiovec(iv, (void *) &pi, sizeof(pi));
                total += sizeof(pi);
-               ptr += sizeof(pi);
        }       
 
-       len = MIN(skb->len, len); 
-       copy_to_user(ptr, skb->data, len); 
+       len = MIN(skb->len, len);
+
+       skb_copy_datagram_iovec(skb, 0, iv, len);
        total += len;
 
        tun->stats.tx_packets++;
@@ -323,22 +286,29 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
        return total;
 }
 
-/* Read */
-static ssize_t tun_chr_read(struct file * file, char * buf, 
-                           size_t count, loff_t *pos)
+/* Readv */
+static ssize_t tun_chr_readv(struct file *file, const struct iovec *iv,
+                           unsigned long count, loff_t *pos)
 {
        struct tun_struct *tun = (struct tun_struct *)file->private_data;
        DECLARE_WAITQUEUE(wait, current);
        struct sk_buff *skb;
-       ssize_t ret = 0;
+       ssize_t len, ret = 0;
+       unsigned long i;
 
        if (!tun)
                return -EBADFD;
 
        DBG(KERN_INFO "%s: tun_chr_read\n", tun->name);
 
+       for (i = 0, len = 0; i < count; i++) {
+               if (verify_area(VERIFY_WRITE, iv[i].iov_base, iv[i].iov_len))
+                       return -EFAULT;
+               len += iv[i].iov_len;
+       }
+
        add_wait_queue(&tun->read_wait, &wait);
-       while (count) {
+       while (len) {
                current->state = TASK_INTERRUPTIBLE;
 
                /* Read frames from the queue */
@@ -358,10 +328,7 @@ static ssize_t tun_chr_read(struct file * file, char * buf,
                }
                netif_start_queue(&tun->dev);
 
-               if (!verify_area(VERIFY_WRITE, buf, count))
-                       ret = tun_put_user(tun, skb, buf, count);
-               else
-                       ret = -EFAULT;
+               ret = tun_put_user(tun, skb, (struct iovec *) iv, len);
 
                kfree_skb(skb);
                break;
@@ -373,6 +340,14 @@ static ssize_t tun_chr_read(struct file * file, char * buf,
        return ret;
 }
 
+/* Read */
+static ssize_t tun_chr_read(struct file * file, char * buf, 
+                           size_t count, loff_t *pos)
+{
+       struct iovec iv = { buf, count };
+       return tun_chr_readv(file, &iv, 1, pos);
+}
+
 static int tun_set_iff(struct file *file, struct ifreq *ifr)
 {
        struct tun_struct *tun;
@@ -592,6 +567,7 @@ static struct file_operations tun_fops = {
        owner:  THIS_MODULE,    
        llseek: no_llseek,
        read:   tun_chr_read,
+       readv:  tun_chr_readv,
        write:  tun_chr_write,
        writev: tun_chr_writev,
        poll:   tun_chr_poll,
@@ -611,7 +587,7 @@ static struct miscdevice tun_miscdev=
 int __init tun_init(void)
 {
        printk(KERN_INFO "Universal TUN/TAP device driver %s " 
-              "(C)1999-2001 Maxim Krasnyansky\n", TUN_VER);
+              "(C)1999-2002 Maxim Krasnyansky\n", TUN_VER);
 
        if (misc_register(&tun_miscdev)) {
                printk(KERN_ERR "tun: Can't register misc device %d\n", TUN_MINOR);