All of lore.kernel.org
 help / color / mirror / Atom feed
* virtio: console: Return -EFAULT on copy_xx_user errors, allow larger writes
@ 2010-01-29 13:42 Amit Shah
  2010-01-29 13:42 ` [PATCH 19/31] virtio: console: Introduce a send_buf function for a common path for sending data to host Amit Shah
  2010-01-30  4:55 ` virtio: console: Return -EFAULT on copy_xx_user errors, allow larger writes Rusty Russell
  0 siblings, 2 replies; 10+ messages in thread
From: Amit Shah @ 2010-01-29 13:42 UTC (permalink / raw)
  To: rusty; +Cc: virtualization

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:

^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2010-02-01  5:08 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-01-29 13:42 virtio: console: Return -EFAULT on copy_xx_user errors, allow larger writes Amit Shah
2010-01-29 13:42 ` [PATCH 19/31] virtio: console: Introduce a send_buf function for a common path for sending data to host Amit Shah
2010-01-29 13:42   ` [PATCH 20/31] virtio: console: Add a new MULTIPORT feature, support for generic ports Amit Shah
2010-01-29 13:42     ` [PATCH 21/31] virtio: console: Prepare for writing to userspace buffers Amit Shah
2010-01-29 13:42       ` [PATCH 22/31] virtio: console: Associate each port with a char device Amit Shah
2010-01-29 13:42         ` [PATCH 23/31] virtio: console: Add file operations to ports for open/read/write/poll Amit Shah
2010-01-29 13:42           ` [PATCH 29/31] virtio: console: Add ability to hot-unplug ports Amit Shah
2010-02-01  0:19       ` [PATCH 21/31] virtio: console: Prepare for writing to userspace buffers Rusty Russell
2010-02-01  5:08         ` Amit Shah
2010-01-30  4:55 ` virtio: console: Return -EFAULT on copy_xx_user errors, allow larger writes Rusty Russell

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.