Hey Rusty,

These updated patches in the series return -EFAULT on copy_xx_user
errors and also move the copy_from_user into fops_write() instead of it
being in send_buf. This enables send_buf to just read from kernel
buffers, making it simpler.

This also allows write()s to write more to the host in one go,
removingthe 4k limitation. I do limit the writes to 32k at once to not
put too much pressure on the GFP_KERNEL area.

Both of these were pointed out by Marcelo.

I'm including the relative diff between the version that's in linux-next
and the new version below.

Please apply,
Thanks.
                Amit.


diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index b923b5c..2c2de35 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -162,9 +162,6 @@ struct port {
         */
        spinlock_t inbuf_lock;
 
-       /* Buffer that's used to pass data from the guest to the host */
-       struct port_buffer *outbuf;
-
        /* The IO vqs for this port */
        struct virtqueue *in_vq, *out_vq;
 
@@ -399,43 +396,23 @@ static ssize_t send_control_msg(struct port *port, 
unsigned int event,
        return 0;
 }
 
-static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count,
-                       bool from_user)
+static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count)
 {
        struct scatterlist sg[1];
        struct virtqueue *out_vq;
-       struct port_buffer *buf;
        ssize_t ret;
-       unsigned int tmplen;
+       unsigned int len;
 
        out_vq = port->out_vq;
-       buf = port->outbuf;
 
-       if (in_count > buf->size)
-               in_count = buf->size;
-
-       if (from_user) {
-               ret = copy_from_user(buf->buf, in_buf, in_count);
-       } else {
-               /*
-                * Since we're not sure when the host will actually
-                * consume the data and tell us about it, we have to
-                * copy the data here in case the caller frees the
-                * in_buf.
-                */
-               memcpy(buf->buf, in_buf, in_count);
-               ret = 0; /* Emulate copy_from_user behaviour */
-       }
-       buf->len = in_count - ret;
-
-       sg_init_one(sg, buf->buf, buf->len);
-       ret = out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, buf);
+       sg_init_one(sg, in_buf, in_count);
+       ret = out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, in_buf);
 
        /* Tell Host to go! */
        out_vq->vq_ops->kick(out_vq);
 
        if (ret < 0) {
-               buf->len = 0;
+               len = 0;
                goto fail;
        }
 
@@ -444,13 +421,11 @@ static ssize_t send_buf(struct port *port, const char 
*in_buf, size_t in_count,
         * sent. Also ensure we return to userspace the number of
         * bytes that were successfully consumed by the host.
         */
-       while (!out_vq->vq_ops->get_buf(out_vq, &tmplen))
+       while (!out_vq->vq_ops->get_buf(out_vq, &len))
                cpu_relax();
-
-       buf->len = tmplen;
 fail:
        /* We're expected to return the amount of data we wrote */
-       return buf->len;
+       return len;
 }
 
 /*
@@ -461,26 +436,25 @@ static ssize_t fill_readbuf(struct port *port, char 
*out_buf, size_t out_count,
                            bool to_user)
 {
        struct port_buffer *buf;
-       ssize_t ret;
        unsigned long flags;
 
        if (!out_count || !port_has_data(port))
                return 0;
 
        buf = port->inbuf;
-       if (out_count > buf->len - buf->offset)
-               out_count = buf->len - buf->offset;
+       out_count = min(out_count, buf->len - buf->offset);
 
        if (to_user) {
+               ssize_t ret;
+
                ret = copy_to_user(out_buf, buf->buf + buf->offset, out_count);
+               if (ret)
+                       return -EFAULT;
        } else {
                memcpy(out_buf, buf->buf + buf->offset, out_count);
-               ret = 0; /* Emulate copy_to_user behaviour */
        }
 
-       /* Return the number of bytes actually copied */
-       ret = out_count - ret;
-       buf->offset += ret;
+       buf->offset += out_count;
 
        if (buf->offset == buf->len) {
                /*
@@ -495,7 +469,8 @@ static ssize_t fill_readbuf(struct port *port, char 
*out_buf, size_t out_count,
 
                spin_unlock_irqrestore(&port->inbuf_lock, flags);
        }
-       return ret;
+       /* Return the number of bytes actually copied */
+       return out_count;
 }
 
 /* The condition that must be true for polling to end */
@@ -548,10 +523,27 @@ static ssize_t port_fops_write(struct file *filp, const 
char __user *ubuf,
                               size_t count, loff_t *offp)
 {
        struct port *port;
+       char *buf;
+       ssize_t ret;
 
        port = filp->private_data;
 
-       return send_buf(port, ubuf, count, true);
+       count = min((size_t)(32 * 1024), count);
+
+       buf = kmalloc(count, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = copy_from_user(buf, ubuf, count);
+       if (ret) {
+               ret = -EFAULT;
+               goto free_buf;
+       }
+
+       ret = send_buf(port, buf, count);
+free_buf:
+       kfree(buf);
+       return ret;
 }
 
 static unsigned int port_fops_poll(struct file *filp, poll_table *wait)
@@ -657,7 +649,7 @@ static int put_chars(u32 vtermno, const char *buf, int 
count)
        if (unlikely(early_put_chars))
                return early_put_chars(vtermno, buf, count);
 
-       return send_buf(port, buf, count, false);
+       return send_buf(port, (void *)buf, count);
 }
 
 /*
@@ -874,7 +866,6 @@ static int remove_port(struct port *port)
        cdev_del(&port->cdev);
 
        discard_port_data(port);
-       free_buf(port->outbuf);
        kfree(port->name);
 
        debugfs_remove(port->debugfs_file);
@@ -1143,11 +1134,6 @@ static int add_port(struct ports_device *portdev, u32 id)
                err = -ENOMEM;
                goto free_device;
        }
-       port->outbuf = alloc_buf(PAGE_SIZE);
-       if (!port->outbuf) {
-               err = -ENOMEM;
-               goto free_inbuf;
-       }
 
        /* Register the input buffer the first time. */
        add_inbuf(port->in_vq, inbuf);
@@ -1158,7 +1144,7 @@ static int add_port(struct ports_device *portdev, u32 id)
        if (!use_multiport(port->portdev)) {
                err = init_port_console(port);
                if (err)
-                       goto free_outbuf;
+                       goto free_inbuf;
        }
 
        spin_lock_irq(&portdev->ports_lock);
@@ -1186,8 +1172,6 @@ static int add_port(struct ports_device *portdev, u32 id)
        }
        return 0;
 
-free_outbuf:
-       free_buf(port->outbuf);
 free_inbuf:
        free_buf(inbuf);
 free_device:

_______________________________________________
Virtualization mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/virtualization

Reply via email to