aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorecd <ecd>2002-02-10 15:09:20 +0000
committerecd <ecd>2002-02-10 15:09:20 +0000
commit34ff509e70eb55d1f956bbf52451eee524ed1783 (patch)
tree1c378d356ee41111822e6888497d91c20fda958b
parente217dc913da0c57cbf302b3c6bb430eff496dc1c (diff)
downloadnetdev-vger-cvs-34ff509e70eb55d1f956bbf52451eee524ed1783.tar.gz
implement writev() support (patch also sent to maintainer)
-rw-r--r--drivers/net/tun.c63
1 files changed, 55 insertions, 8 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index fc552d28d..80352ea21 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -181,12 +181,16 @@ 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 char *buf, size_t count)
+static __inline__ ssize_t
+tun_get_user(struct tun_struct *tun, const struct iovec *iov,
+ unsigned long count, size_t total)
{
struct tun_pi pi = { 0, __constant_htons(ETH_P_IP) };
- register const char *ptr = buf;
- register int len = count;
+ 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;
if (!(tun->flags & TUN_NO_PI)) {
if ((len -= sizeof(pi)) < 0)
@@ -194,15 +198,33 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, const char *buf,
copy_from_user(&pi, ptr, sizeof(pi));
ptr += sizeof(pi);
+
+ if (len == 0) {
+ vector++;
+ count--;
+
+ ptr = vector->iov_base;
+ len = vector->iov_len;
+ }
}
- if (!(skb = alloc_skb(len + 2, GFP_KERNEL))) {
+ if (!(skb = alloc_skb(total + 2, GFP_KERNEL))) {
tun->stats.rx_dropped++;
return -ENOMEM;
}
skb_reserve(skb, 2);
- copy_from_user(skb_put(skb, len), ptr, len);
+
+ while (count > 0) {
+ copy_from_user(skb_put(skb, len), ptr, len);
+ sent += len;
+
+ vector++;
+ count--;
+
+ ptr = vector->iov_base;
+ len = vector->iov_len;
+ }
skb->dev = &tun->dev;
switch (tun->flags & TUN_TYPE_MASK) {
@@ -221,9 +243,9 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, const char *buf,
netif_rx_ni(skb);
tun->stats.rx_packets++;
- tun->stats.rx_bytes += len;
+ tun->stats.rx_bytes += sent;
- return count;
+ return total;
}
/* Write */
@@ -231,6 +253,7 @@ 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;
@@ -240,7 +263,30 @@ static ssize_t tun_chr_write(struct file * file, const char * buf,
if (verify_area(VERIFY_READ, buf, count))
return -EFAULT;
- return tun_get_user(tun, buf, count);
+ 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,
+ unsigned long count, loff_t *pos)
+{
+ struct tun_struct *tun = (struct tun_struct *)file->private_data;
+ size_t total = 0;
+ unsigned long i;
+
+ if (!tun)
+ return -EBADFD;
+
+ for (i = 0; i < count; i++) {
+ if (verify_area(VERIFY_READ, iov[i].iov_base, iov[i].iov_len))
+ return -EFAULT;
+ total += iov[i].iov_len;
+ }
+
+ return tun_get_user(tun, iov, count, total);
}
/* Put packet to user space buffer(already verified) */
@@ -547,6 +593,7 @@ static struct file_operations tun_fops = {
llseek: no_llseek,
read: tun_chr_read,
write: tun_chr_write,
+ writev: tun_chr_writev,
poll: tun_chr_poll,
ioctl: tun_chr_ioctl,
open: tun_chr_open,