Linux virtualization list
 help / color / mirror / Atom feed
* [PATCH v2] virtio-blk physical block size
From: Avi Kivity @ 2009-12-28 16:39 UTC (permalink / raw)
  To: Rusty Russell; +Cc: qemu-devel, kvm, virtualization

This patch adds a physical block size attribute to virtio disks,
corresponding to /sys/devices/.../physical_block_size.  It is defined as
the request alignment which will not trigger RMW cycles.  This can be
important for modern disks which use 4K physical sectors (though they
still support 512 logical sectors), and for file-backed disk images (which
have both the underlying filesystem block size and their own allocation
granularity to consider).

Installers use this to align partitions to physical block boundaries.

Note the spec already defined blk_size as the performance rather than
minimum alignment.  However the driver interpreted this as the logical
block size, so I updated the spec to match the driver assuming the driver
predates the spec and that this is an error.

Signed-off-by: Avi Kivity <avi@redhat.com>

--

v2: feature bit 7 was already used by an undocumented feature; use a new bit

--- virtio-spec-0.8.3.lyx.orig	2009-12-28 17:42:40.000000000 +0200
+++ virtio-spec-0.8.3.lyx	2009-12-28 18:37:39.000000000 +0200
@@ -4138,6 +4138,19 @@
 (10) Maximum total sectors in an I/O.
 \end_layout
 
+\begin_layout Description
+VIRTIO_BLK_F_PHYS_BLK_SIZE(11) Physical block size of disk (writes aligned
+ to this will not trigger read-modify-write cycles) is in 
+\begin_inset Quotes erd
+\end_inset
+
+phys_blk_size
+\begin_inset Quotes erd
+\end_inset
+
+.
+\end_layout
+
 \end_deeper
 \begin_layout Description
 Device
@@ -4214,6 +4227,11 @@
 
 \begin_layout Plain Layout
 
+    u32 phys_blk_size;
+\end_layout
+
+\begin_layout Plain Layout
+
 };
 \end_layout
 
@@ -4241,9 +4259,9 @@
 
 \begin_layout Enumerate
 If the VIRTIO_BLK_F_BLK_SIZE feature is negotiated, the blk_size field can
- be read to determine the optimal sector size for the driver to use.
+ be read to determine the sector size for the driver to use.
  This does not effect the units used in the protocol (always 512 bytes),
- but awareness of the correct value can effect performance.
+ but requests must be aligned to this size.
 \end_layout
 
 \begin_layout Enumerate
@@ -4257,6 +4275,13 @@
  No requests should be submitted which go beyond this limit.
 \end_layout
 
+\begin_layout Enumerate
+If the VIRTIO_BLK_F_PHYS_BLK_SIZE feature is negotiated, the phys_blk_size
+ field should be read to determine the hardware block size.
+ Smaller writes are liable to require read-modify-write cycles on behalf
+ of the underlying hardware, however they are still supported.
+\end_layout
+
 \begin_layout Section*
 Device Operation
 \end_layout

^ permalink raw reply

* [PATCH] virtio-blk physical block size
From: Avi Kivity @ 2009-12-28 16:09 UTC (permalink / raw)
  To: Rusty Russell; +Cc: qemu-devel, kvm, virtualization

This patch adds a physical block size attribute to virtio disks,
corresponding to /sys/devices/.../physical_block_size.  It is defined as
the request alignment which will not trigger RMW cycles.  This can be
important for modern disks which use 4K physical sectors (though they
still support 512 logical sectors), and for file-backed disk images (which
have both the underlying filesystem block size and their own allocation
granularity to consider).

Installers use this to align partitions to physical block boundaries.

Note the spec already defined blk_size as the performance rather than
minimum alignment.  However the driver interpreted this as the logical
block size, so I updated the spec to match the driver assuming the driver
predates the spec and that this is an error.

Signed-off-by: Avi Kivity <avi@redhat.com>


--- virtio-spec-0.8.3.lyx.orig	2009-12-28 17:42:40.000000000 +0200
+++ virtio-spec-0.8.3.lyx	2009-12-28 18:05:17.000000000 +0200
@@ -4131,6 +4131,19 @@
 \end_layout
 
 \begin_layout Description
+VIRTIO_BLK_F_PHYS_BLK_SIZE(7) Physical block size of disk (writes aligned
+ to this will not trigger read-modify-write cycles) is in 
+\begin_inset Quotes erd
+\end_inset
+
+phys_blk_size
+\begin_inset Quotes erd
+\end_inset
+
+.
+\end_layout
+
+\begin_layout Description
 VIRTIO_BLK_F_SECTOR_MAX
 \begin_inset space ~
 \end_inset
@@ -4214,6 +4227,11 @@
 
 \begin_layout Plain Layout
 
+    u32 phys_blk_size;
+\end_layout
+
+\begin_layout Plain Layout
+
 };
 \end_layout
 
@@ -4241,9 +4259,9 @@
 
 \begin_layout Enumerate
 If the VIRTIO_BLK_F_BLK_SIZE feature is negotiated, the blk_size field can
- be read to determine the optimal sector size for the driver to use.
+ be read to determine the sector size for the driver to use.
  This does not effect the units used in the protocol (always 512 bytes),
- but awareness of the correct value can effect performance.
+ but requests must be aligned to this size.
 \end_layout
 
 \begin_layout Enumerate
@@ -4257,6 +4275,13 @@
  No requests should be submitted which go beyond this limit.
 \end_layout
 
+\begin_layout Enumerate
+If the VIRTIO_BLK_F_PHYS_BLK_SIZE feature is negotiated, the phys_blk_size
+ field should be read to determine the hardware block size.
+ Smaller writes are liable to require read-modify-write cycles on behalf
+ of the underlying hardware, however they are still supported.
+\end_layout
+
 \begin_layout Section*
 Device Operation
 \end_layout

^ permalink raw reply

* Re: [PATCH] vhost-net: defer f->private_data until setup succeeds
From: Michael S. Tsirkin @ 2009-12-24  6:06 UTC (permalink / raw)
  To: Chris Wright; +Cc: kvm, virtualization
In-Reply-To: <20091222190228.GB2095@sequoia.sous-sol.org>

On Tue, Dec 22, 2009 at 11:02:28AM -0800, Chris Wright wrote:
> Trivial change, just for readability.  The filp is not installed on
> failure, so the current code is not incorrect (also vhost_dev_init
> currently has no failure case).  This just treats setting f->private_data
> as something with global scope (sure, true only after fd_install).
> 
> Signed-off-by: Chris Wright <chrisw@redhat.com>

Acked-by: Michael S. Tsirkin <mst@redhat.com>

> ---
>  drivers/vhost/net.c |    4 +++-
>  1 files changed, 3 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> index 22d5fef..0697ab2 100644
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
> @@ -326,7 +326,6 @@ static int vhost_net_open(struct inode *inode, struct file *f)
>  	int r;
>  	if (!n)
>  		return -ENOMEM;
> -	f->private_data = n;
>  	n->vqs[VHOST_NET_VQ_TX].handle_kick = handle_tx_kick;
>  	n->vqs[VHOST_NET_VQ_RX].handle_kick = handle_rx_kick;
>  	r = vhost_dev_init(&n->dev, n->vqs, VHOST_NET_VQ_MAX);
> @@ -338,6 +337,9 @@ static int vhost_net_open(struct inode *inode, struct file *f)
>  	vhost_poll_init(n->poll + VHOST_NET_VQ_TX, handle_tx_net, POLLOUT);
>  	vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, POLLIN);
>  	n->tx_poll_state = VHOST_NET_POLL_DISABLED;
> +
> +	f->private_data = n;
> +
>  	return 0;
>  }
>  

^ permalink raw reply

* [PATCH] vhost: access check thinko fixes
From: Michael S. Tsirkin @ 2009-12-24  6:00 UTC (permalink / raw)
  To: Michael S. Tsirkin, rusty, Al Viro, kvm, virtualization

This fixes two issues with recent access checking patch:
1.  if (&d->vqs[i].private_data) -> if (d->vqs[i].private_data)
2.  we can't forbid log base changes while ring is running,
    because host needs to resize log in rare cases
    (e.g. when memory is added with a baloon)
    and in that case it needs to increase log size with realloc,
    which might move the log address.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---

Rusty, this needs to be applied on top of the access_ok patch.
If you want me to rool it in with that one and esend, let me
know please.
Thanks!

 drivers/vhost/vhost.c |   39 +++++++++++++++++++++++----------------
 1 file changed, 23 insertions(+), 16 deletions(-)

diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 2b65d9b..c8c25db 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -230,7 +230,7 @@ static int log_access_ok(void __user *log_base, u64 addr, unsigned long sz)
 }
 
 /* Caller should have vq mutex and device mutex. */
-static int vq_memory_access_ok(struct vhost_virtqueue *vq, struct vhost_memory *mem,
+static int vq_memory_access_ok(void __user *log_base, struct vhost_memory *mem,
 			       int log_all)
 {
 	int i;
@@ -242,7 +242,7 @@ static int vq_memory_access_ok(struct vhost_virtqueue *vq, struct vhost_memory *
 		else if (!access_ok(VERIFY_WRITE, (void __user *)a,
 				    m->memory_size))
 			return 0;
-		else if (log_all && !log_access_ok(vq->log_base,
+		else if (log_all && !log_access_ok(log_base,
 						   m->guest_phys_addr,
 						   m->memory_size))
 			return 0;
@@ -259,9 +259,10 @@ static int memory_access_ok(struct vhost_dev *d, struct vhost_memory *mem,
 	for (i = 0; i < d->nvqs; ++i) {
 		int ok;
 		mutex_lock(&d->vqs[i].mutex);
-		/* If ring is not running, will check when it's enabled. */
-		if (&d->vqs[i].private_data)
-			ok = vq_memory_access_ok(&d->vqs[i], mem, log_all);
+		/* If ring is inactive, will check when it's enabled. */
+		if (d->vqs[i].private_data)
+			ok = vq_memory_access_ok(d->vqs[i].log_base, mem,
+						 log_all);
 		else
 			ok = 1;
 		mutex_unlock(&d->vqs[i].mutex);
@@ -290,18 +291,25 @@ int vhost_log_access_ok(struct vhost_dev *dev)
 	return memory_access_ok(dev, dev->memory, 1);
 }
 
-/* Can we start vq? */
+/* Verify access for write logging. */
 /* Caller should have vq mutex and device mutex */
-int vhost_vq_access_ok(struct vhost_virtqueue *vq)
+static int vq_log_access_ok(struct vhost_virtqueue *vq, void __user *log_base)
 {
-	return vq_access_ok(vq->num, vq->desc, vq->avail, vq->used) &&
-		vq_memory_access_ok(vq, vq->dev->memory,
+	return vq_memory_access_ok(log_base, vq->dev->memory,
 			    vhost_has_feature(vq->dev, VHOST_F_LOG_ALL)) &&
-		(!vq->log_used || log_access_ok(vq->log_base, vq->log_addr,
+		(!vq->log_used || log_access_ok(log_base, vq->log_addr,
 					sizeof *vq->used +
 					vq->num * sizeof *vq->used->ring));
 }
 
+/* Can we start vq? */
+/* Caller should have vq mutex and device mutex */
+int vhost_vq_access_ok(struct vhost_virtqueue *vq)
+{
+	return vq_access_ok(vq->num, vq->desc, vq->avail, vq->used) &&
+		vq_log_access_ok(vq, vq->log_base);
+}
+
 static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
 {
 	struct vhost_memory mem, *newmem, *oldmem;
@@ -564,15 +572,14 @@ long vhost_dev_ioctl(struct vhost_dev *d, unsigned int ioctl, unsigned long arg)
 		}
 		for (i = 0; i < d->nvqs; ++i) {
 			struct vhost_virtqueue *vq;
+			void __user *base = (void __user *)(unsigned long)p;
 			vq = d->vqs + i;
 			mutex_lock(&vq->mutex);
-			/* Moving log base with an active backend?
-			 * You don't want to do that. */
-			if (vq->private_data && (vq->log_used ||
-			     vhost_has_feature(d, VHOST_F_LOG_ALL)))
-				r = -EBUSY;
+			/* If ring is inactive, will check when it's enabled. */
+			if (vq->private_data && !vq_log_access_ok(vq, base))
+				r = -EFAULT;
 			else
-				vq->log_base = (void __user *)(unsigned long)p;
+				vq->log_base = base;
 			mutex_unlock(&vq->mutex);
 		}
 		break;
-- 
1.6.6.rc1.43.gf55cc

^ permalink raw reply related

* CFP: VPACT 2010: Third International Workshop on Virtualization Performance: Analysis, Characterization and Tools
From: Ming Zhao @ 2009-12-23 19:41 UTC (permalink / raw)
  To: virtualization

VPACT 2010  CALL FOR PAPERS

Third International Workshop on
Virtualization Performance: Analysis, Characterization and Tools

March 28, 2010
White Plains, New York
(co-located with IEEE ISPASS 2010)

The International Workshop on Virtualization Performance: Analysis,
Characterization and Tools (VPACT) is a selective venue for reporting
and discussing new initial results in the measurement,
characterization, analysis, and modeling of the performance of
virtualized computer systems, including the tools to support such
work.  VPACT is interested in results at all scales, including
multicore/manycore processors, mobile devices, desktops, servers, data
centers, clusters, parallel supercomputers, and distributed
virtualized computing environments.

VPACT 2010 seeks papers from researchers and practitioners in both
academia and industry.  Topics of interest include, but are not
limited to the following, in the context of virtualized computer
systems:

- Performance measurement, characterization, analysis, and modeling
- Power measurement characterization, analysis, and modeling
- Workload measurement characterization, analysis, and modeling
- Benchmarks and benchmarking
- Evaluation of hardware virtualization features
- Evaluation of virtualization software (VMMs, etc)
- Evaluation of virtualization services (Clouds, etc)
- Evaluation of scalability in virtualized environments
- Evaluation of innovative uses of virtualization
- Interaction of virtualization and multicore/manycore architectures
- Interaction of virtualization and parallel computing
- Interaction of language and OS virtual machines
- Tools and techniques

Papers should be no more than 10 pages in length, should be
formatted according to the ACM SIG Proceedings style
(http://www.acm.org/sigs/publications/proceedings-templates), and must
be in PDF format.  Reviewing is single-blind.  Submission instructions
are available on the workshop web site, http://vpact.org.

DATES

Submission deadline:     February 1, 2010
Notification:            March 1, 2010
Final form:              March 8, 2010
Workshop:                March 28, 2010


ORGANIZATION

General Chair

Mazin Yousif, IBM


Program Chair

Peter Dinda, Northwestern University


Publicity Chair

Ming Zhao, Florida International University


Program Committee

Patrick Bridges, University of New Mexico
Ron Brightwell, Sandia National Labs
Kshitij Doshi, Intel
Renato Figueiredo, University of Florida
Russ Joseph, Northwestern University
John Lange, Northwestern University
Arthur Maccabe, Oak Ridge National Labs
Elmoustapha Ould-ahmed-vall, Intel
Kevin Pedretti, Sandia National Labs
Stephen Scott, Oak Ridge National Labs
Tim Sherwood, UC Santa Barbara
Seetharami Seelam, IBM Research
Karsten Schwan, Georgia Tech
Bhuvan Urgaonkar, Penn State
Peter Varman, Rice University
Dongyan Xu, Purdue
Ming Zhao, Florida International University









-- 
Ming Zhao, Assistant Professor
School of Computing and Information Sciences
Florida International University
Tel: (305) 348-2034, Fax: (305) 348-3549
Web: http://www.cis.fiu.edu/~zhaom

^ permalink raw reply

* Re: [PATCH 00/31] virtio: console: Fixes, multiple devices and generic ports
From: Amit Shah @ 2009-12-23  6:44 UTC (permalink / raw)
  To: Rusty Russell; +Cc: virtualization
In-Reply-To: <200912231709.28442.rusty@rustcorp.com.au>

On (Wed) Dec 23 2009 [17:09:28], Rusty Russell wrote:
> 
> Hi Amit,
> 
>    I've just started a couple of weeks' leave, but I'll try to review these
> in the last week of this year.

Hey Rusty,

No problem; enjoy your vacations! :-)

		Amit

^ permalink raw reply

* Re: [PATCH 00/31] virtio: console: Fixes, multiple devices and generic ports
From: Rusty Russell @ 2009-12-23  6:39 UTC (permalink / raw)
  To: Amit Shah; +Cc: virtualization
In-Reply-To: <1261492481-19817-1-git-send-email-amit.shah@redhat.com>

On Wed, 23 Dec 2009 01:04:10 am Amit Shah wrote:
> Hey Rusty,
> 
> This series adds support for generic ports with each port getting two
> vqs: one for input and one for output. The host notifies us via the
> config space of the max. number of ports that can be added for a
> particular device.

Hi Amit,

   I've just started a couple of weeks' leave, but I'll try to review these
in the last week of this year.

Thanks,
Rusty.

^ permalink raw reply

* Re: [PATCH] vhost: fix high 32 bit in FEATURES ioctls
From: Rusty Russell @ 2009-12-23  6:32 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: David Stevens, kvm, virtualization
In-Reply-To: <20091222113933.GA16103@redhat.com>

On Tue, 22 Dec 2009 10:09:33 pm Michael S. Tsirkin wrote:
> From: David Stevens <dlstevens@us.ibm.com>
> Subject: vhost: fix high 32 bit in FEATURES ioctls

Thanks, applied.

Rusty.

^ permalink raw reply

* Re: [PATCH] vhost-net: comment use of invalid fd when setting vhost backend
From: Michael S. Tsirkin @ 2009-12-22 19:37 UTC (permalink / raw)
  To: Chris Wright; +Cc: kvm, virtualization
In-Reply-To: <20091222181828.GA2095@sequoia.sous-sol.org>

On Tue, Dec 22, 2009 at 10:18:28AM -0800, Chris Wright wrote:
> This looks like an error case, but it's just a special case to shutdown
> the backend.  Clarify with a comment.
> 
> Signed-off-by: Chris Wright <chrisw@redhat.com>

Acked-by: Michael S. Tsirkin <mst@redhat.com>

> ---
>  drivers/vhost/net.c |    1 +
>  1 files changed, 1 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> index 22d5fef..cc92086 100644
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
> @@ -465,6 +465,7 @@ static struct socket *get_tun_socket(int fd)
>  static struct socket *get_socket(int fd)
>  {
>  	struct socket *sock;
> +	/* special case to disable backend */
>  	if (fd == -1)
>  		return NULL;
>  	sock = get_raw_socket(fd);

^ permalink raw reply

* [PATCH] vhost-net: defer f->private_data until setup succeeds
From: Chris Wright @ 2009-12-22 19:02 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: kvm, virtualization

Trivial change, just for readability.  The filp is not installed on
failure, so the current code is not incorrect (also vhost_dev_init
currently has no failure case).  This just treats setting f->private_data
as something with global scope (sure, true only after fd_install).

Signed-off-by: Chris Wright <chrisw@redhat.com>
---
 drivers/vhost/net.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 22d5fef..0697ab2 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -326,7 +326,6 @@ static int vhost_net_open(struct inode *inode, struct file *f)
 	int r;
 	if (!n)
 		return -ENOMEM;
-	f->private_data = n;
 	n->vqs[VHOST_NET_VQ_TX].handle_kick = handle_tx_kick;
 	n->vqs[VHOST_NET_VQ_RX].handle_kick = handle_rx_kick;
 	r = vhost_dev_init(&n->dev, n->vqs, VHOST_NET_VQ_MAX);
@@ -338,6 +337,9 @@ static int vhost_net_open(struct inode *inode, struct file *f)
 	vhost_poll_init(n->poll + VHOST_NET_VQ_TX, handle_tx_net, POLLOUT);
 	vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, POLLIN);
 	n->tx_poll_state = VHOST_NET_POLL_DISABLED;
+
+	f->private_data = n;
+
 	return 0;
 }

^ permalink raw reply related

* [PATCH] vhost-net: comment use of invalid fd when setting vhost backend
From: Chris Wright @ 2009-12-22 18:18 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: kvm, virtualization

This looks like an error case, but it's just a special case to shutdown
the backend.  Clarify with a comment.

Signed-off-by: Chris Wright <chrisw@redhat.com>
---
 drivers/vhost/net.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 22d5fef..cc92086 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -465,6 +465,7 @@ static struct socket *get_tun_socket(int fd)
 static struct socket *get_socket(int fd)
 {
 	struct socket *sock;
+	/* special case to disable backend */
 	if (fd == -1)
 		return NULL;
 	sock = get_raw_socket(fd);

^ permalink raw reply related

* [PATCH 31/31] virtio: console: Add debugfs files for each port to expose debug info
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-31-git-send-email-amit.shah@redhat.com>

This is helpful in examining ports' state.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |   75 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 75 insertions(+), 0 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 48ef259..1b3f532 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -17,6 +17,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #include <linux/cdev.h>
+#include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/fs.h>
@@ -43,6 +44,9 @@ struct ports_driver_data {
 	/* Used for registering chardevs */
 	struct class *class;
 
+	/* Used for exporting per-port information to debugfs */
+	struct dentry *debugfs_dir;
+
 	/* Number of devices this driver is handling */
 	unsigned int index;
 
@@ -164,6 +168,9 @@ struct port {
 	/* The IO vqs for this port */
 	struct virtqueue *in_vq, *out_vq;
 
+	/* File in the debugfs directory that exposes this port's information */
+	struct dentry *debugfs_file;
+
 	/*
 	 * The entries in this struct will be valid if this port is
 	 * hooked up to an hvc console
@@ -833,6 +840,53 @@ static struct attribute_group port_attribute_group = {
 	.attrs = port_sysfs_entries,
 };
 
+static int debugfs_open(struct inode *inode, struct file *filp)
+{
+	filp->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t debugfs_read(struct file *filp, char __user *ubuf,
+			    size_t count, loff_t *offp)
+{
+	struct port *port;
+	char *buf;
+	ssize_t ret, out_offset, out_count;
+
+	out_count = 1024;
+	buf = kmalloc(out_count, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	port = filp->private_data;
+	out_offset = 0;
+	out_offset += snprintf(buf + out_offset, out_count,
+			       "name: %s\n", port->name ? port->name : "");
+	out_offset += snprintf(buf + out_offset, out_count - out_offset,
+			       "guest_connected: %d\n", port->guest_connected);
+	out_offset += snprintf(buf + out_offset, out_count - out_offset,
+			       "cache_buffers: %d\n", port->cache_buffers);
+	out_offset += snprintf(buf + out_offset, out_count - out_offset,
+			       "host_connected: %d\n", port->host_connected);
+	out_offset += snprintf(buf + out_offset, out_count - out_offset,
+			       "host_throttled: %d\n", port->host_throttled);
+	out_offset += snprintf(buf + out_offset, out_count - out_offset,
+			       "is_console: %s\n",
+			       is_console_port(port) ? "yes" : "no");
+	out_offset += snprintf(buf + out_offset, out_count - out_offset,
+			       "console_vtermno: %u\n", port->cons.vtermno);
+
+	ret = simple_read_from_buffer(ubuf, count, offp, buf, out_offset);
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations port_debugfs_ops = {
+	.owner = THIS_MODULE,
+	.open  = debugfs_open,
+	.read  = debugfs_read,
+};
+
 /* Remove port-specific data. */
 static int remove_port_data(struct port *port)
 {
@@ -857,6 +911,8 @@ static int remove_port_data(struct port *port)
 	free_buf(port->outbuf);
 	kfree(port->name);
 
+	debugfs_remove(port->debugfs_file);
+
 	kfree(port);
 	return 0;
 }
@@ -1085,6 +1141,7 @@ static void fill_queue(struct virtqueue *vq, spinlock_t *lock)
 
 static int add_port(struct ports_device *portdev, u32 id)
 {
+	char debugfs_name[16];
 	struct port *port;
 	dev_t devt;
 	int err;
@@ -1158,6 +1215,18 @@ static int add_port(struct ports_device *portdev, u32 id)
 	 */
 	send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
 
+	if (pdrvdata.debugfs_dir) {
+		/*
+		 * Finally, create the debugfs file that we can use to
+		 * inspect a port's state at any time
+		 */
+		sprintf(debugfs_name, "vport%up%u",
+			port->portdev->drv_index, id);
+		port->debugfs_file = debugfs_create_file(debugfs_name, 0444,
+							 pdrvdata.debugfs_dir,
+							 port,
+							 &port_debugfs_ops);
+	}
 	return 0;
 
 free_device:
@@ -1478,6 +1547,12 @@ static int __init init(void)
 		return err;
 	}
 
+	pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL);
+	if (!pdrvdata.debugfs_dir) {
+		pr_warning("Error %ld creating debugfs dir for virtio-ports\n",
+			   PTR_ERR(pdrvdata.debugfs_dir));
+	}
+
 	INIT_LIST_HEAD(&pdrvdata.consoles);
 	return register_virtio_driver(&virtio_console);
 }
-- 
1.6.2.5

^ permalink raw reply related

* [PATCH 30/31] virtio: console: Add ability to hot-unplug ports
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-30-git-send-email-amit.shah@redhat.com>

Remove port data; deregister from the hvc core if it's a console port.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c  |   63 ++++++++++++++++++++++++++++++++++++++-
 include/linux/virtio_console.h |    1 +
 2 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index d964121..48ef259 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -833,6 +833,34 @@ static struct attribute_group port_attribute_group = {
 	.attrs = port_sysfs_entries,
 };
 
+/* Remove port-specific data. */
+static int remove_port_data(struct port *port)
+{
+	spin_lock_irq(&port->portdev->ports_lock);
+	list_del(&port->list);
+	spin_unlock_irq(&port->portdev->ports_lock);
+
+	if (is_console_port(port)) {
+		spin_lock_irq(&pdrvdata_lock);
+		list_del(&port->cons.list);
+		spin_unlock_irq(&pdrvdata_lock);
+		hvc_remove(port->cons.hvc);
+	}
+	if (port->guest_connected)
+		send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
+
+	sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
+	device_destroy(pdrvdata.class, port->dev->devt);
+	cdev_del(&port->cdev);
+
+	flush_port_data(port);
+	free_buf(port->outbuf);
+	kfree(port->name);
+
+	kfree(port);
+	return 0;
+}
+
 /* Any private messages that the Host and Guest want to share */
 static void handle_control_message(struct ports_device *portdev,
 				   struct port_buffer *buf)
@@ -919,6 +947,32 @@ static void handle_control_message(struct ports_device *portdev,
 	case VIRTIO_CONSOLE_CACHE_BUFFERS:
 		port->cache_buffers = cpkt->value;
 		break;
+	case VIRTIO_CONSOLE_PORT_REMOVE:
+		/*
+		 * Hot unplug the port.  We don't decrement nr_ports
+		 * since we don't want to deal with extra complexities
+		 * of using the lowest-available port id: We can just
+		 * pick up the nr_ports number as the id and not have
+		 * userspace send it to us.  This helps us in two
+		 * ways:
+		 *
+		 * - We don't need to have a 'port_id' field in the
+		 *   config space when a port is hot-added.  This is a
+		 *   good thing as we might queue up multiple hotplug
+		 *   requests issued in our workqueue.
+		 *
+		 * - Another way to deal with this would have been to
+		 *   use a bitmap of the active ports and select the
+		 *   lowest non-active port from that map.  That
+		 *   bloats the already tight config space and we
+		 *   would end up artificially limiting the
+		 *   max. number of ports to sizeof(bitmap).  Right
+		 *   now we can support 2^32 ports (as the port id is
+		 *   stored in a u32 type).
+		 *
+		 */
+		remove_port_data(port);
+		break;
 	}
 }
 
@@ -1140,12 +1194,17 @@ static void config_work_handler(struct work_struct *work)
 		/*
 		 * Port 0 got hot-added.  Since we already did all the
 		 * other initialisation for it, just tell the Host
-		 * that the port is ready.
+		 * that the port is ready if we find the port.  In
+		 * case the port was hot-removed earlier, we call
+		 * add_port to add the port.
 		 */
 		struct port *port;
 
 		port = find_port_by_id(portdev, 0);
-		send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
+		if (!port)
+			add_port(portdev, 0);
+		else
+			send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
 		return;
 	}
 	if (virtconconf.nr_ports > portdev->config.max_nr_ports) {
diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h
index 41ef890..cf52b9b 100644
--- a/include/linux/virtio_console.h
+++ b/include/linux/virtio_console.h
@@ -43,6 +43,7 @@ struct virtio_console_control {
 #define VIRTIO_CONSOLE_PORT_NAME	4
 #define VIRTIO_CONSOLE_THROTTLE_PORT	5
 #define VIRTIO_CONSOLE_CACHE_BUFFERS	6
+#define VIRTIO_CONSOLE_PORT_REMOVE	7
 
 /*
  * This struct is put at the start of each data buffer that gets
-- 
1.6.2.5

^ permalink raw reply related

* [PATCH 29/31] virtio: console: Handle port hot-plug
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-29-git-send-email-amit.shah@redhat.com>

If the 'nr_ports' variable in the config space is updated to a higher
value, that means new ports have been hotplugged.

Introduce a new workqueue to handle such updates and create new ports.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |   78 +++++++++++++++++++++++++++++++++++++---
 1 files changed, 72 insertions(+), 6 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 4c9ec04..d964121 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -105,6 +105,7 @@ struct ports_device {
 	 * notification
 	 */
 	struct work_struct control_work;
+	struct work_struct config_work;
 
 	struct list_head ports;
 
@@ -728,11 +729,6 @@ static void resize_console(struct port *port)
 	}
 }
 
-static void virtcons_apply_config(struct virtio_device *vdev)
-{
-	resize_console(find_port_by_vtermno(0));
-}
-
 /* We set the configuration at this point, since we now have a tty */
 static int notifier_add_vio(struct hvc_struct *hp, int data)
 {
@@ -994,6 +990,24 @@ static void control_intr(struct virtqueue *vq)
 	schedule_work(&portdev->control_work);
 }
 
+static void config_intr(struct virtio_device *vdev)
+{
+	struct ports_device *portdev;
+
+	portdev = vdev->priv;
+	if (use_multiport(portdev)) {
+		/* Handle port hot-add */
+		schedule_work(&portdev->config_work);
+	}
+	/*
+	 * We'll use this way of resizing only for legacy support.
+	 * For newer userspace (VIRTIO_CONSOLE_F_MULTPORT+), use
+	 * control messages to indicate console size changes so that
+	 * it can be done per-port
+	 */
+	resize_console(find_port_by_id(portdev, 0));
+}
+
 static void fill_queue(struct virtqueue *vq, spinlock_t *lock)
 {
 	struct port_buffer *buf;
@@ -1102,6 +1116,57 @@ fail:
 	return err;
 }
 
+/*
+ * The workhandler for config-space updates.
+ *
+ * This is called when ports are hot-added.
+ */
+static void config_work_handler(struct work_struct *work)
+{
+	struct virtio_console_config virtconconf;
+	struct ports_device *portdev;
+	struct virtio_device *vdev;
+	int err;
+
+	portdev = container_of(work, struct ports_device, config_work);
+
+	vdev = portdev->vdev;
+	vdev->config->get(vdev,
+			  offsetof(struct virtio_console_config, nr_ports),
+			  &virtconconf.nr_ports,
+			  sizeof(virtconconf.nr_ports));
+
+	if (portdev->config.nr_ports == virtconconf.nr_ports) {
+		/*
+		 * Port 0 got hot-added.  Since we already did all the
+		 * other initialisation for it, just tell the Host
+		 * that the port is ready.
+		 */
+		struct port *port;
+
+		port = find_port_by_id(portdev, 0);
+		send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
+		return;
+	}
+	if (virtconconf.nr_ports > portdev->config.max_nr_ports) {
+		dev_warn(&vdev->dev,
+			 "More ports specified (%u) than allowed (%u)",
+			 portdev->config.nr_ports + 1,
+			 portdev->config.max_nr_ports);
+		return;
+	}
+	if (virtconconf.nr_ports < portdev->config.nr_ports)
+		return;
+
+	/* Hot-add ports */
+	while (virtconconf.nr_ports - portdev->config.nr_ports) {
+		err = add_port(portdev, portdev->config.nr_ports);
+		if (err)
+			break;
+		portdev->config.nr_ports++;
+	}
+}
+
 static int init_vqs(struct ports_device *portdev)
 {
 	vq_callback_t **io_callbacks;
@@ -1293,6 +1358,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
 	if (multiport) {
 		spin_lock_init(&portdev->cvq_lock);
 		INIT_WORK(&portdev->control_work, &control_work_handler);
+		INIT_WORK(&portdev->config_work, &config_work_handler);
 
 		portdev->outbuf = alloc_buf(PAGE_SIZE);
 		if (!portdev->outbuf) {
@@ -1339,7 +1405,7 @@ static struct virtio_driver virtio_console = {
 	.driver.owner =	THIS_MODULE,
 	.id_table =	id_table,
 	.probe =	virtcons_probe,
-	.config_changed = virtcons_apply_config,
+	.config_changed = config_intr,
 };
 
 static int __init init(void)
-- 
1.6.2.5

^ permalink raw reply related

* [PATCH 28/31] virtio: console: Add option to remove cached buffers on port close
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-28-git-send-email-amit.shah@redhat.com>

If caching of buffers upon closing a port is not desired, delete all the
buffers queued up for the port upon port close.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c  |   56 ++++++++++++++++++++++++++++++++++++++++
 include/linux/virtio_console.h |    1 +
 2 files changed, 57 insertions(+), 0 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 2409330..4c9ec04 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -190,6 +190,9 @@ struct port {
 
 	/* Does the Host not want to accept more data currently?  */
 	bool host_throttled;
+
+	/* Does the port want to cache data when disconnected? */
+	bool cache_buffers;
 };
 
 /* This is the very early arch-specified put chars function. */
@@ -326,6 +329,34 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
 	return ret;
 }
 
+/* Discard any unread data this port has. Callers lockers. */
+static void flush_port_data(struct port *port)
+{
+	struct port_buffer *buf;
+	struct virtqueue *vq;
+	unsigned int len;
+	int ret;
+
+	vq = port->in_vq;
+	if (port->inbuf)
+		buf = port->inbuf;
+	else
+		buf = vq->vq_ops->get_buf(vq, &len);
+
+	ret = 0;
+	while (buf) {
+		if (add_inbuf(vq, buf) < 0) {
+			ret++;
+			free_buf(buf);
+		}
+		buf = vq->vq_ops->get_buf(vq, &len);
+	}
+	port->inbuf = NULL;
+	if (ret)
+		dev_warn(port->dev, "Errors adding %d buffers back to vq\n",
+			 ret);
+}
+
 static bool port_has_data(struct port *port)
 {
 	unsigned long flags;
@@ -577,8 +608,18 @@ static int port_fops_release(struct inode *inode, struct file *filp)
 	/* Notify host of port being closed */
 	send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
 
+	spin_lock_irq(&port->inbuf_lock);
 	port->guest_connected = false;
 
+	/*
+	 * If the port doesn't want to cache buffers while closed,
+	 * flush the unread buffers queue
+	 */
+	if (!port->cache_buffers)
+		flush_port_data(port);
+
+	spin_unlock_irq(&port->inbuf_lock);
+
 	return 0;
 }
 
@@ -879,6 +920,9 @@ static void handle_control_message(struct ports_device *portdev,
 		 */
 		port->host_throttled = cpkt->value;
 		break;
+	case VIRTIO_CONSOLE_CACHE_BUFFERS:
+		port->cache_buffers = cpkt->value;
+		break;
 	}
 }
 
@@ -923,6 +967,17 @@ static void in_intr(struct virtqueue *vq)
 	spin_lock_irqsave(&port->inbuf_lock, flags);
 	if (!port->inbuf)
 		port->inbuf = get_inbuf(port);
+
+	/*
+	 * Don't queue up buffers if caching is disabled and port is
+	 * closed.  This condition can be reached when a console port
+	 * is not yet connected (no tty is spawned) and the host sends
+	 * out data to console ports.  For generic serial ports, the
+	 * host won't send data till the guest is connected.
+	 */
+	if (!port->guest_connected && !port->cache_buffers)
+		flush_port_data(port);
+
 	spin_unlock_irqrestore(&port->inbuf_lock, flags);
 
 	wake_up_interruptible(&port->waitqueue);
@@ -981,6 +1036,7 @@ static int add_port(struct ports_device *portdev, u32 id)
 
 	port->host_connected = port->guest_connected = false;
 	port->host_throttled = false;
+	port->cache_buffers = false;
 
 	port->in_vq = portdev->in_vqs[port->id];
 	port->out_vq = portdev->out_vqs[port->id];
diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h
index 4bd57c8..41ef890 100644
--- a/include/linux/virtio_console.h
+++ b/include/linux/virtio_console.h
@@ -42,6 +42,7 @@ struct virtio_console_control {
 #define VIRTIO_CONSOLE_PORT_OPEN	3
 #define VIRTIO_CONSOLE_PORT_NAME	4
 #define VIRTIO_CONSOLE_THROTTLE_PORT	5
+#define VIRTIO_CONSOLE_CACHE_BUFFERS	6
 
 /*
  * This struct is put at the start of each data buffer that gets
-- 
1.6.2.5

^ permalink raw reply related

* [PATCH 27/31] virtio: console: Add throttling support to prevent flooding ports
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-27-git-send-email-amit.shah@redhat.com>

Rogue processes on guests could pump in data to hosts and cause an OOM
condition on the host. The host can indicate when to stop and start
sending data via control messages.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c  |   21 ++++++++++++++++++++-
 include/linux/virtio_console.h |    1 +
 2 files changed, 21 insertions(+), 1 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 4fefa4d..2409330 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -187,6 +187,9 @@ struct port {
 
 	/* We should allow only one process to open a port */
 	bool guest_connected;
+
+	/* Does the Host not want to accept more data currently?  */
+	bool host_throttled;
 };
 
 /* This is the very early arch-specified put chars function. */
@@ -540,6 +543,9 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
 
 	port = filp->private_data;
 
+	if (port->host_throttled)
+		return -ENOSPC;
+
 	return send_buf(port, ubuf, count, true);
 }
 
@@ -554,7 +560,7 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait)
 	ret = 0;
 	if (port->inbuf)
 		ret |= POLLIN | POLLRDNORM;
-	if (port->host_connected)
+	if (port->host_connected && !port->host_throttled)
 		ret |= POLLOUT;
 	if (!port->host_connected)
 		ret |= POLLHUP;
@@ -861,6 +867,18 @@ static void handle_control_message(struct ports_device *portdev,
 				err);
 
 		break;
+	case VIRTIO_CONSOLE_THROTTLE_PORT:
+		/*
+		 * Hosts can govern some policy to disallow rogue
+		 * guest processes writing indefinitely to ports
+		 * leading to OOM situations.  If we receive this
+		 * message here, it means the Host side of the port
+		 * either reached its max. limit to cache data or
+		 * signal to us that the host is ready to accept more
+		 * data.
+		 */
+		port->host_throttled = cpkt->value;
+		break;
 	}
 }
 
@@ -962,6 +980,7 @@ static int add_port(struct ports_device *portdev, u32 id)
 	port->cons.hvc = NULL;
 
 	port->host_connected = port->guest_connected = false;
+	port->host_throttled = false;
 
 	port->in_vq = portdev->in_vqs[port->id];
 	port->out_vq = portdev->out_vqs[port->id];
diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h
index 8e85045..4bd57c8 100644
--- a/include/linux/virtio_console.h
+++ b/include/linux/virtio_console.h
@@ -41,6 +41,7 @@ struct virtio_console_control {
 #define VIRTIO_CONSOLE_RESIZE		2
 #define VIRTIO_CONSOLE_PORT_OPEN	3
 #define VIRTIO_CONSOLE_PORT_NAME	4
+#define VIRTIO_CONSOLE_THROTTLE_PORT	5
 
 /*
  * This struct is put at the start of each data buffer that gets
-- 
1.6.2.5

^ permalink raw reply related

* [PATCH 26/31] virtio: console: Register with sysfs and create a 'name' attribute for ports
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-26-git-send-email-amit.shah@redhat.com>

The host can set a name for ports so that they're easily discoverable
instead of going by the /dev/vportNpn naming. This attribute will be
placed in /sys/class/virtio-ports/vportNpn/name. udev scripts can then
create symlinks to the port using the name.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c  |   57 ++++++++++++++++++++++++++++++++++++++++
 include/linux/virtio_console.h |    1 +
 2 files changed, 58 insertions(+), 0 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index abab15f..4fefa4d 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -176,6 +176,9 @@ struct port {
 	/* A waitqueue for poll() or blocking read operations */
 	wait_queue_head_t waitqueue;
 
+	/* The 'name' of the port that we expose via sysfs properties */
+	char *name;
+
 	/* The 'id' to identify the port with the Host */
 	u32 id;
 
@@ -765,12 +768,36 @@ int init_port_console(struct port *port)
 	return 0;
 }
 
+static ssize_t show_port_name(struct device *dev,
+			      struct device_attribute *attr, char *buffer)
+{
+	struct port *port;
+
+	port = dev_get_drvdata(dev);
+
+	return sprintf(buffer, "%s\n", port->name);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, show_port_name, NULL);
+
+static struct attribute *port_sysfs_entries[] = {
+	&dev_attr_name.attr,
+	NULL
+};
+
+static struct attribute_group port_attribute_group = {
+	.name = NULL,		/* put in device directory */
+	.attrs = port_sysfs_entries,
+};
+
 /* Any private messages that the Host and Guest want to share */
 static void handle_control_message(struct ports_device *portdev,
 				   struct port_buffer *buf)
 {
 	struct virtio_console_control *cpkt;
 	struct port *port;
+	size_t name_size;
+	int err;
 
 	cpkt = (struct virtio_console_control *)(buf->buf + buf->offset);
 
@@ -805,6 +832,35 @@ static void handle_control_message(struct ports_device *portdev,
 		port->host_connected = cpkt->value;
 		wake_up_interruptible(&port->waitqueue);
 		break;
+	case VIRTIO_CONSOLE_PORT_NAME:
+		/*
+		 * Skip the size of the header and the cpkt to get the size
+		 * of the name that was sent
+		 */
+		name_size = buf->len - buf->offset - sizeof(*cpkt) + 1;
+
+		port->name = kmalloc(name_size, GFP_KERNEL);
+		if (!port->name) {
+			dev_err(port->dev,
+				"Not enough space to store port name\n");
+			break;
+		}
+		strncpy(port->name, buf->buf + buf->offset + sizeof(*cpkt),
+			name_size - 1);
+		port->name[name_size - 1] = 0;
+
+		/*
+		 * Since we only have one sysfs attribute, 'name',
+		 * create it only if we have a name for the port.
+		 */
+		err = sysfs_create_group(&port->dev->kobj,
+					 &port_attribute_group);
+		if (err)
+			dev_err(port->dev,
+				"Error %d creating sysfs device attributes\n",
+				err);
+
+		break;
 	}
 }
 
@@ -901,6 +957,7 @@ static int add_port(struct ports_device *portdev, u32 id)
 	port->portdev = portdev;
 	port->id = id;
 
+	port->name = NULL;
 	port->inbuf = NULL;
 	port->cons.hvc = NULL;
 
diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h
index 9952b02..8e85045 100644
--- a/include/linux/virtio_console.h
+++ b/include/linux/virtio_console.h
@@ -40,6 +40,7 @@ struct virtio_console_control {
 #define VIRTIO_CONSOLE_CONSOLE_PORT	1
 #define VIRTIO_CONSOLE_RESIZE		2
 #define VIRTIO_CONSOLE_PORT_OPEN	3
+#define VIRTIO_CONSOLE_PORT_NAME	4
 
 /*
  * This struct is put at the start of each data buffer that gets
-- 
1.6.2.5

^ permalink raw reply related

* [PATCH 25/31] virtio: console: Ensure only one process can have a port open at a time
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-25-git-send-email-amit.shah@redhat.com>

Add a guest_connected field that ensures only one process
can have a port open at a time.

This also ensures we don't have a race when we later add support for
dropping buffers when closing the char dev and buffer caching is turned
off for the particular port.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |   17 ++++++++++++++++-
 1 files changed, 16 insertions(+), 1 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index c949e6c..abab15f 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -181,6 +181,9 @@ struct port {
 
 	/* Is the host device open */
 	bool host_connected;
+
+	/* We should allow only one process to open a port */
+	bool guest_connected;
 };
 
 /* This is the very early arch-specified put chars function. */
@@ -565,6 +568,8 @@ static int port_fops_release(struct inode *inode, struct file *filp)
 	/* Notify host of port being closed */
 	send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
 
+	port->guest_connected = false;
+
 	return 0;
 }
 
@@ -583,6 +588,15 @@ static int port_fops_open(struct inode *inode, struct file *filp)
 	if (is_console_port(port))
 		return -ENXIO;
 
+	/* Allow only one process to open a particular port at a time */
+	spin_lock_irq(&port->inbuf_lock);
+	if (port->guest_connected) {
+		spin_unlock_irq(&port->inbuf_lock);
+		return -EMFILE;
+	}
+	port->guest_connected = true;
+	spin_unlock_irq(&port->inbuf_lock);
+
 	/* Notify host of port being opened */
 	send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1);
 
@@ -746,6 +760,7 @@ int init_port_console(struct port *port)
 	pdrvdata.next_vtermno++;
 	list_add_tail(&port->cons.list, &pdrvdata.consoles);
 	spin_unlock_irq(&pdrvdata_lock);
+	port->guest_connected = true;
 
 	return 0;
 }
@@ -889,7 +904,7 @@ static int add_port(struct ports_device *portdev, u32 id)
 	port->inbuf = NULL;
 	port->cons.hvc = NULL;
 
-	port->host_connected = false;
+	port->host_connected = port->guest_connected = false;
 
 	port->in_vq = portdev->in_vqs[port->id];
 	port->out_vq = portdev->out_vqs[port->id];
-- 
1.6.2.5

^ permalink raw reply related

* [PATCH 24/31] virtio: console: Add file operations to ports for open/read/write/poll
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-24-git-send-email-amit.shah@redhat.com>

Allow guest userspace applications to open, read from, write to, poll
the ports via the char dev interface.

When a port gets opened, a notification is sent to the host via a
control message indicating a connection has been established. Similarly,
on closing of the port, a notification is sent indicating disconnection.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c  |  151 +++++++++++++++++++++++++++++++++++++++-
 include/linux/virtio_console.h |    7 ++-
 2 files changed, 156 insertions(+), 2 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 721ad2f..c949e6c 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -19,11 +19,15 @@
 #include <linux/cdev.h>
 #include <linux/device.h>
 #include <linux/err.h>
+#include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/list.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <linux/virtio.h>
 #include <linux/virtio_console.h>
+#include <linux/wait.h>
 #include <linux/workqueue.h>
 #include "hvc_console.h"
 
@@ -169,8 +173,14 @@ struct port {
 	struct cdev cdev;
 	struct device *dev;
 
+	/* A waitqueue for poll() or blocking read operations */
+	wait_queue_head_t waitqueue;
+
 	/* The 'id' to identify the port with the Host */
 	u32 id;
+
+	/* Is the host device open */
+	bool host_connected;
 };
 
 /* This is the very early arch-specified put chars function. */
@@ -372,6 +382,7 @@ static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count,
 	out_vq = port->out_vq;
 	buf = port->outbuf;
 
+	header.flags = VIRTIO_CONSOLE_HDR_START_DATA;
 	header_len = use_multiport(port->portdev) ? sizeof(header) : 0;
 
 	in_offset = 0; /* offset in the user buffer */
@@ -394,6 +405,9 @@ static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count,
 		}
 		buf->len = header_len + copy_size - ret;
 
+		if (!(in_count - in_offset - (buf->len - header_len)))
+			header.flags |= VIRTIO_CONSOLE_HDR_END_DATA;
+
 		if (header_len)
 			memcpy(buf->buf, &header, header_len);
 
@@ -409,6 +423,9 @@ static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count,
 		in_offset += buf->len - header_len;
 		while (!out_vq->vq_ops->get_buf(out_vq, &tmplen))
 			cpu_relax();
+
+		/* Set the START_DATA flag only for the first buffer. */
+		header.flags &= ~VIRTIO_CONSOLE_HDR_START_DATA;
 	}
 
 	/* We're expected to return the amount of data we wrote */
@@ -464,6 +481,129 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count,
 	return out_offset;
 }
 
+/* The condition that must be true for polling to end */
+static bool wait_is_over(struct port *port)
+{
+	return port_has_data(port) || !port->host_connected;
+}
+
+static ssize_t port_fops_read(struct file *filp, char __user *ubuf,
+			      size_t count, loff_t *offp)
+{
+	struct port *port;
+	ssize_t ret;
+
+	port = filp->private_data;
+
+	if (!port_has_data(port)) {
+		/*
+		 * If nothing's connected on the host just return 0 in
+		 * case of list_empty; this tells the userspace app
+		 * that there's no connection
+		 */
+		if (!port->host_connected)
+			return 0;
+		if (filp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		ret = wait_event_interruptible(port->waitqueue,
+					       wait_is_over(port));
+		if (ret < 0)
+			return ret;
+	}
+	/*
+	 * We could've received a disconnection message while we were
+	 * waiting for more data.
+	 *
+	 * This check is not clubbed in the if() statement above as we
+	 * might receive some data as well as the host could get
+	 * disconnected after we got woken up from our wait.  So we
+	 * really want to give off whatever data we have and only then
+	 * check for host_connected.
+	 */
+	if (!port_has_data(port) && !port->host_connected)
+		return 0;
+
+	return fill_readbuf(port, ubuf, count, true);
+}
+
+static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
+			       size_t count, loff_t *offp)
+{
+	struct port *port;
+
+	port = filp->private_data;
+
+	return send_buf(port, ubuf, count, true);
+}
+
+static unsigned int port_fops_poll(struct file *filp, poll_table *wait)
+{
+	struct port *port;
+	unsigned int ret;
+
+	port = filp->private_data;
+	poll_wait(filp, &port->waitqueue, wait);
+
+	ret = 0;
+	if (port->inbuf)
+		ret |= POLLIN | POLLRDNORM;
+	if (port->host_connected)
+		ret |= POLLOUT;
+	if (!port->host_connected)
+		ret |= POLLHUP;
+
+	return ret;
+}
+
+static int port_fops_release(struct inode *inode, struct file *filp)
+{
+	struct port *port;
+
+	port = filp->private_data;
+
+	/* Notify host of port being closed */
+	send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
+
+	return 0;
+}
+
+static int port_fops_open(struct inode *inode, struct file *filp)
+{
+	struct cdev *cdev = inode->i_cdev;
+	struct port *port;
+
+	port = container_of(cdev, struct port, cdev);
+	filp->private_data = port;
+
+	/*
+	 * Don't allow opening of console port devices -- that's done
+	 * via /dev/hvc
+	 */
+	if (is_console_port(port))
+		return -ENXIO;
+
+	/* Notify host of port being opened */
+	send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1);
+
+	return 0;
+}
+
+/*
+ * The file operations that we support: programs in the guest can open
+ * a console device, read from it, write to it, poll for data and
+ * close it.  The devices are at
+ *   /dev/vport<device number>p<port number>
+ */
+static const struct file_operations port_fops = {
+	.owner = THIS_MODULE,
+	.open  = port_fops_open,
+	.read  = port_fops_read,
+	.write = port_fops_write,
+	.poll  = port_fops_poll,
+	.release = port_fops_release,
+};
+
 /*
  * The put_chars() callback is pretty straightforward.
  *
@@ -646,6 +786,10 @@ static void handle_control_message(struct ports_device *portdev,
 		port->cons.hvc->irq_requested = 1;
 		resize_console(port);
 		break;
+	case VIRTIO_CONSOLE_PORT_OPEN:
+		port->host_connected = cpkt->value;
+		wake_up_interruptible(&port->waitqueue);
+		break;
 	}
 }
 
@@ -692,6 +836,8 @@ static void in_intr(struct virtqueue *vq)
 		port->inbuf = get_inbuf(port);
 	spin_unlock_irqrestore(&port->inbuf_lock, flags);
 
+	wake_up_interruptible(&port->waitqueue);
+
 	if (is_console_port(port) && hvc_poll(port->cons.hvc))
 		hvc_kick();
 }
@@ -743,10 +889,12 @@ static int add_port(struct ports_device *portdev, u32 id)
 	port->inbuf = NULL;
 	port->cons.hvc = NULL;
 
+	port->host_connected = false;
+
 	port->in_vq = portdev->in_vqs[port->id];
 	port->out_vq = portdev->out_vqs[port->id];
 
-	cdev_init(&port->cdev, NULL);
+	cdev_init(&port->cdev, &port_fops);
 
 	devt = MKDEV(portdev->chr_major, id);
 	err = cdev_add(&port->cdev, devt, 1);
@@ -767,6 +915,7 @@ static int add_port(struct ports_device *portdev, u32 id)
 	}
 
 	spin_lock_init(&port->inbuf_lock);
+	init_waitqueue_head(&port->waitqueue);
 
 	/*
 	 * If we're not using multiport support, this has to be a console port
diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h
index b8e5f9f..9952b02 100644
--- a/include/linux/virtio_console.h
+++ b/include/linux/virtio_console.h
@@ -39,15 +39,20 @@ struct virtio_console_control {
 #define VIRTIO_CONSOLE_PORT_READY	0
 #define VIRTIO_CONSOLE_CONSOLE_PORT	1
 #define VIRTIO_CONSOLE_RESIZE		2
+#define VIRTIO_CONSOLE_PORT_OPEN	3
 
 /*
  * This struct is put at the start of each data buffer that gets
  * passed to Host and vice-versa.
  */
 struct virtio_console_header {
-	/* Empty till multiport support is added */
+	u32 flags;		/* Some message between host and guest */
 };
 
+/* Messages between host and guest ('flags' field in the header above) */
+#define VIRTIO_CONSOLE_HDR_START_DATA	(1 << 0)
+#define VIRTIO_CONSOLE_HDR_END_DATA	(1 << 1)
+
 #ifdef __KERNEL__
 int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int));
 #endif /* __KERNEL__ */
-- 
1.6.2.5

^ permalink raw reply related

* [PATCH 23/31] virtio: console: Associate each port with a char device
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-23-git-send-email-amit.shah@redhat.com>

The char device will be used as an interface by applications on the
guest to communicate with apps on the host.

The devices created are placed in /dev/vportNpn where N is the
virtio-console device number and n is the port number for that device.

One dynamic major device number is allocated for each device and minor
numbers are allocated for the ports contained within that device.

The file operation for the char devs will be added in the following
commits.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/Kconfig          |    8 ++++
 drivers/char/virtio_console.c |   78 +++++++++++++++++++++++++++++++++++++++--
 2 files changed, 83 insertions(+), 3 deletions(-)

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 31be3ac..bcedee1 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -666,6 +666,14 @@ config VIRTIO_CONSOLE
 	help
 	  Virtio console for use with lguest and other hypervisors.
 
+	  Also serves as a general-purpose serial device for data
+	  transfer between the guest and host.  Character devices at
+	  /dev/vportNpn will be created when corresponding ports are
+	  found, where N is the device number and n is the port number
+	  within that device.  If specified by the host, a sysfs
+	  attribute called 'name' will be populated with a name for
+	  the port which can be used by udev scripts to create a
+	  symlink to the device.
 
 config HVCS
 	tristate "IBM Hypervisor Virtual Console Server support"
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 2f5ef29..721ad2f 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -16,6 +16,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
+#include <linux/cdev.h>
+#include <linux/device.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/list.h>
@@ -34,6 +36,12 @@
  * across multiple devices and multiple ports per device.
  */
 struct ports_driver_data {
+	/* Used for registering chardevs */
+	struct class *class;
+
+	/* Number of devices this driver is handling */
+	unsigned int index;
+
 	/*
 	 * This is used to keep track of the number of hvc consoles
 	 * spawned by this driver.  This number is given as the first
@@ -119,6 +127,12 @@ struct ports_device {
 
 	/* The control messages to the Host are sent via this buffer */
 	struct port_buffer *outbuf;
+
+	/* Used for numbering devices for sysfs and debugfs */
+	unsigned int drv_index;
+
+	/* Major number for this device.  Ports will be created as minors. */
+	int chr_major;
 };
 
 /* This struct holds the per-port data */
@@ -151,6 +165,10 @@ struct port {
 	 */
 	struct console cons;
 
+	/* Each port associates with a separate char device */
+	struct cdev cdev;
+	struct device *dev;
+
 	/* The 'id' to identify the port with the Host */
 	u32 id;
 };
@@ -710,6 +728,7 @@ static void fill_queue(struct virtqueue *vq, spinlock_t *lock)
 static int add_port(struct ports_device *portdev, u32 id)
 {
 	struct port *port;
+	dev_t devt;
 	int err;
 
 	port = kmalloc(sizeof(*port), GFP_KERNEL);
@@ -727,6 +746,26 @@ static int add_port(struct ports_device *portdev, u32 id)
 	port->in_vq = portdev->in_vqs[port->id];
 	port->out_vq = portdev->out_vqs[port->id];
 
+	cdev_init(&port->cdev, NULL);
+
+	devt = MKDEV(portdev->chr_major, id);
+	err = cdev_add(&port->cdev, devt, 1);
+	if (err < 0) {
+		dev_err(&port->portdev->vdev->dev,
+			"Error %d adding cdev for port %u\n", err, id);
+		goto free_port;
+	}
+	port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev,
+				  devt, port, "vport%up%u",
+				  port->portdev->drv_index, id);
+	if (IS_ERR(port->dev)) {
+		err = PTR_ERR(port->dev);
+		dev_err(&port->portdev->vdev->dev,
+			"Error %d creating device for port %u\n",
+			err, id);
+		goto free_cdev;
+	}
+
 	spin_lock_init(&port->inbuf_lock);
 
 	/*
@@ -735,14 +774,14 @@ static int add_port(struct ports_device *portdev, u32 id)
 	if (!use_multiport(port->portdev)) {
 		err = init_port_console(port);
 		if (err)
-			goto free_port;
+			goto free_device;
 	}
 
 	fill_queue(port->in_vq, &port->inbuf_lock);
 	port->outbuf = alloc_buf(PAGE_SIZE);
 	if (!port->outbuf) {
 		err = -ENOMEM;
-		goto free_port;
+		goto free_device;
 	}
 	spin_lock_irq(&portdev->ports_lock);
 	list_add_tail(&port->list, &port->portdev->ports);
@@ -757,6 +796,10 @@ static int add_port(struct ports_device *portdev, u32 id)
 
 	return 0;
 
+free_device:
+	device_destroy(pdrvdata.class, port->dev->devt);
+free_cdev:
+	cdev_del(&port->cdev);
 free_port:
 	kfree(port);
 fail:
@@ -869,6 +912,10 @@ fail:
 	return err;
 }
 
+static const struct file_operations portdev_fops = {
+	.owner = THIS_MODULE,
+};
+
 /*
  * Once we're further in boot, we get probed like any other virtio
  * device.
@@ -894,6 +941,20 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
 	portdev->vdev = vdev;
 	vdev->priv = portdev;
 
+	spin_lock_irq(&pdrvdata_lock);
+	portdev->drv_index = pdrvdata.index++;
+	spin_unlock_irq(&pdrvdata_lock);
+
+	portdev->chr_major = register_chrdev(0, "virtio-portsdev",
+					     &portdev_fops);
+	if (portdev->chr_major < 0) {
+		dev_err(&vdev->dev,
+			"Error %d registering chrdev for device %u\n",
+			portdev->chr_major, portdev->drv_index);
+		err = portdev->chr_major;
+		goto free;
+	}
+
 	multiport = false;
 	if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) {
 		multiport = true;
@@ -927,7 +988,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
 	err = init_vqs(portdev);
 	if (err < 0) {
 		dev_err(&vdev->dev, "Error %d initializing vqs\n", err);
-		goto free;
+		goto free_chrdev;
 	}
 
 	spin_lock_init(&portdev->ports_lock);
@@ -957,6 +1018,8 @@ free_vqs:
 	vdev->config->del_vqs(vdev);
 	kfree(portdev->in_vqs);
 	kfree(portdev->out_vqs);
+free_chrdev:
+	unregister_chrdev(portdev->chr_major, "virtio-portsdev");
 free:
 	kfree(portdev);
 fail:
@@ -985,6 +1048,15 @@ static struct virtio_driver virtio_console = {
 
 static int __init init(void)
 {
+	int err;
+
+	pdrvdata.class = class_create(THIS_MODULE, "virtio-ports");
+	if (IS_ERR(pdrvdata.class)) {
+		err = PTR_ERR(pdrvdata.class);
+		pr_err("Error %d creating virtio-ports class\n", err);
+		return err;
+	}
+
 	INIT_LIST_HEAD(&pdrvdata.consoles);
 	return register_virtio_driver(&virtio_console);
 }
-- 
1.6.2.5

^ permalink raw reply related

* [PATCH 22/31] virtio: console: Prepare for writing to / reading from userspace buffers
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-22-git-send-email-amit.shah@redhat.com>

When ports get advertised as char devices, the buffers will come from
userspace. Equip the send_buf and fill_readbuf functions with the
ability to write to / read from userspace buffers respectively.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |   48 ++++++++++++++++++++++++++++------------
 1 files changed, 33 insertions(+), 15 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index bb48345..2f5ef29 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -340,7 +340,8 @@ 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)
+static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count,
+			bool from_user)
 {
 	struct scatterlist sg[1];
 	struct virtio_console_header header;
@@ -359,15 +360,21 @@ static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count)
 	while (in_offset < in_count) {
 		copy_size = min(in_count - in_offset + header_len, buf->size);
 		copy_size -= header_len;
-		/*
-		 * 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 + header_len, in_buf + in_offset, copy_size);
-
-		buf->len = header_len + copy_size;
+		if (from_user) {
+			ret = copy_from_user(buf->buf + header_len,
+					     in_buf + in_offset, copy_size);
+		} 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 + header_len,
+			       in_buf + in_offset, copy_size);
+			ret = 0; /* Emulate copy_from_user behaviour */
+		}
+		buf->len = header_len + copy_size - ret;
 
 		if (header_len)
 			memcpy(buf->buf, &header, header_len);
@@ -394,7 +401,8 @@ static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count)
  * Give out the data that's requested from the buffers that we have
  * queued up.
  */
-static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count)
+static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count,
+			    bool to_user)
 {
 	struct port_buffer *buf;
 	ssize_t out_offset, ret;
@@ -409,9 +417,19 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count)
 		if (copy_size > buf->len - buf->offset)
 			copy_size = buf->len - buf->offset;
 
-		memcpy(out_buf + out_offset, buf->buf + buf->offset, copy_size);
+		if (to_user) {
+			ret = copy_to_user(out_buf + out_offset,
+					   buf->buf + buf->offset,
+					   copy_size);
+		} else {
+			memcpy(out_buf + out_offset,
+			       buf->buf + buf->offset,
+			       copy_size);
+			ret = 0; /* Emulate copy_to_user behaviour */
+		}
 
-		ret = copy_size;
+		/* Return the number of bytes actually copied */
+		ret = copy_size - ret;
 		buf->offset += ret;
 		out_offset += ret;
 		out_count -= ret;
@@ -447,7 +465,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);
+	return send_buf(port, buf, count, false);
 }
 
 /*
@@ -468,7 +486,7 @@ static int get_chars(u32 vtermno, char *buf, int count)
 	/* If we don't have an input queue yet, we can't get input. */
 	BUG_ON(!port->in_vq);
 
-	return fill_readbuf(port, buf, count);
+	return fill_readbuf(port, buf, count, false);
 }
 
 static void resize_console(struct port *port)
-- 
1.6.2.5

^ permalink raw reply related

* [PATCH 21/31] virtio: console: Add a new MULTIPORT feature, support for generic ports
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-21-git-send-email-amit.shah@redhat.com>

This commit adds a new feature, MULTIPORT. If the host supports this
feature as well, the config space has the number of ports defined for
that device. New ports are spawned according to this information.

The config space also has the maximum number of ports that can be
spawned for a particular device. This is useful in initializing the
appropriate number of virtqueues in advance, as ports might be
hot-plugged in later.

Using this feature, generic ports can be created which are not tied to
hvc consoles.

We also open up a private channel between the host and the guest via
which some "control" messages are exchanged for the ports, like whether
the port being spawned is a console port, resizing the console window,
etc.

Next commits will add support for hotplugging and presenting char
devices in /dev/ for bi-directional guest-host communication.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c  |  378 +++++++++++++++++++++++++++++++++-------
 include/linux/virtio_console.h |   22 +++
 2 files changed, 340 insertions(+), 60 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 9d55778..bb48345 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation
+ * Copyright (C) 2009, Red Hat, Inc.
  *
  * 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
@@ -21,6 +22,7 @@
 #include <linux/spinlock.h>
 #include <linux/virtio.h>
 #include <linux/virtio_console.h>
+#include <linux/workqueue.h>
 #include "hvc_console.h"
 
 /*
@@ -69,17 +71,6 @@ struct console {
 	u32 vtermno;
 };
 
-/*
- * This is a per-device struct that stores data common to all the
- * ports for that device (vdev->priv).
- */
-struct ports_device {
-	/* Array of per-port IO virtqueues */
-	struct virtqueue **in_vqs, **out_vqs;
-
-	struct virtio_device *vdev;
-};
-
 struct port_buffer {
 	char *buf;
 
@@ -92,8 +83,49 @@ struct port_buffer {
 	size_t offset;
 };
 
+/*
+ * This is a per-device struct that stores data common to all the
+ * ports for that device (vdev->priv).
+ */
+struct ports_device {
+	/*
+	 * Workqueue handlers where we process deferred work after
+	 * notification
+	 */
+	struct work_struct control_work;
+
+	struct list_head ports;
+
+	/* To protect the list of ports */
+	spinlock_t ports_lock;
+
+	/* To protect the vq operations for the control channel */
+	spinlock_t cvq_lock;
+
+	/* The current config space is stored here */
+	struct virtio_console_config config;
+
+	/* The virtio device we're associated with */
+	struct virtio_device *vdev;
+
+	/*
+	 * A couple of virtqueues for the control channel: one for
+	 * guest->host transfers, one for host->guest transfers
+	 */
+	struct virtqueue *c_ivq, *c_ovq;
+
+	/* Array of per-port IO virtqueues */
+	struct virtqueue **in_vqs, **out_vqs;
+
+	/* The control messages to the Host are sent via this buffer */
+	struct port_buffer *outbuf;
+};
+
 /* This struct holds the per-port data */
 struct port {
+	/* Next port in the list, head is in the ports_device */
+	struct list_head list;
+
 	/* Pointer to the parent virtio_console device */
 	struct ports_device *portdev;
 
@@ -118,6 +150,9 @@ struct port {
 	 * hooked up to an hvc console
 	 */
 	struct console cons;
+
+	/* The 'id' to identify the port with the Host */
+	u32 id;
 };
 
 /* This is the very early arch-specified put chars function. */
@@ -142,25 +177,45 @@ out:
 	return port;
 }
 
+static struct port *find_port_by_id(struct ports_device *portdev, u32 id)
+{
+	struct port *port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&portdev->ports_lock, flags);
+	list_for_each_entry(port, &portdev->ports, list)
+		if (port->id == id)
+			goto out;
+	port = NULL;
+out:
+	spin_unlock_irqrestore(&portdev->ports_lock, flags);
+
+	return port;
+}
+
 static struct port *find_port_by_vq(struct ports_device *portdev,
 				    struct virtqueue *vq)
 {
 	struct port *port;
-	struct console *cons;
 	unsigned long flags;
 
-	spin_lock_irqsave(&pdrvdata_lock, flags);
-	list_for_each_entry(cons, &pdrvdata.consoles, list) {
-		port = container_of(cons, struct port, cons);
+	spin_lock_irqsave(&portdev->ports_lock, flags);
+	list_for_each_entry(port, &portdev->ports, list)
 		if (port->in_vq == vq || port->out_vq == vq)
 			goto out;
-	}
 	port = NULL;
 out:
-	spin_unlock_irqrestore(&pdrvdata_lock, flags);
+	spin_unlock_irqrestore(&portdev->ports_lock, flags);
 	return port;
 }
 
+static bool is_console_port(struct port *port)
+{
+	if (port->cons.hvc)
+		return true;
+	return false;
+}
+
 static inline bool use_multiport(struct ports_device *portdev)
 {
 	/*
@@ -169,8 +224,7 @@ static inline bool use_multiport(struct ports_device *portdev)
 	 */
 	if (!portdev->vdev)
 		return 0;
-	/* Check for feature bit once multiport support is added */
-	return 0;
+	return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT);
 }
 
 static void free_buf(struct port_buffer *buf)
@@ -256,6 +310,36 @@ out:
 	return ret;
 }
 
+static ssize_t send_control_msg(struct port *port, unsigned int event,
+				unsigned int value)
+{
+	struct scatterlist sg[1];
+	struct virtio_console_control cpkt;
+	struct virtqueue *vq;
+	struct port_buffer *outbuf;
+	int tmplen;
+
+	if (!use_multiport(port->portdev))
+		return 0;
+
+	cpkt.id = port->id;
+	cpkt.event = event;
+	cpkt.value = value;
+
+	vq = port->portdev->c_ovq;
+	outbuf = port->portdev->outbuf;
+
+	memcpy(outbuf->buf, (void *)&cpkt, sizeof(cpkt));
+
+	sg_init_one(sg, outbuf->buf, sizeof(cpkt));
+	if (vq->vq_ops->add_buf(vq, sg, 1, 0, outbuf) >= 0) {
+		vq->vq_ops->kick(vq);
+		while (!vq->vq_ops->get_buf(vq, &tmplen))
+			cpu_relax();
+	}
+	return 0;
+}
+
 static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count)
 {
 	struct scatterlist sg[1];
@@ -429,25 +513,7 @@ static void notifier_del_vio(struct hvc_struct *hp, int data)
 	hp->irq_requested = 0;
 }
 
-static void hvc_handle_input(struct virtqueue *vq)
-{
-	struct port *port;
-	unsigned long flags;
-
-	port = find_port_by_vq(vq->vdev->priv, vq);
-	if (!port)
-		return;
-
-	spin_lock_irqsave(&port->inbuf_lock, flags);
-	if (!port->inbuf)
-		port->inbuf = get_inbuf(port);
-	spin_unlock_irqrestore(&port->inbuf_lock, flags);
-
-	if (hvc_poll(port->cons.hvc))
-		hvc_kick();
-}
-
-/* The operations for the console. */
+/* The operations for console ports. */
 static const struct hv_ops hv_ops = {
 	.get_chars = get_chars,
 	.put_chars = put_chars,
@@ -471,7 +537,7 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
 	return hvc_instantiate(0, 0, &hv_ops);
 }
 
-int __devinit init_port_console(struct port *port)
+int init_port_console(struct port *port)
 {
 	int ret;
 
@@ -508,6 +574,100 @@ int __devinit init_port_console(struct port *port)
 	return 0;
 }
 
+/* Any private messages that the Host and Guest want to share */
+static void handle_control_message(struct ports_device *portdev,
+				   struct port_buffer *buf)
+{
+	struct virtio_console_control *cpkt;
+	struct port *port;
+
+	cpkt = (struct virtio_console_control *)(buf->buf + buf->offset);
+
+	port = find_port_by_id(portdev, cpkt->id);
+	if (!port) {
+		/* No valid header at start of buffer.  Drop it. */
+		dev_dbg(&portdev->vdev->dev,
+			"Invalid index %u in control packet\n", cpkt->id);
+		return;
+	}
+
+	switch (cpkt->event) {
+	case VIRTIO_CONSOLE_CONSOLE_PORT:
+		if (!cpkt->value)
+			break;
+		if (is_console_port(port))
+			break;
+
+		init_port_console(port);
+		/*
+		 * Could remove the port here in case init fails - but
+		 * have to notify the host first.
+		 */
+		break;
+	case VIRTIO_CONSOLE_RESIZE:
+		if (!is_console_port(port))
+			break;
+		port->cons.hvc->irq_requested = 1;
+		resize_console(port);
+		break;
+	}
+}
+
+static void control_work_handler(struct work_struct *work)
+{
+	struct ports_device *portdev;
+	struct virtqueue *vq;
+	struct port_buffer *buf;
+	unsigned int len;
+
+	portdev = container_of(work, struct ports_device, control_work);
+	vq = portdev->c_ivq;
+
+	spin_lock(&portdev->cvq_lock);
+	while ((buf = vq->vq_ops->get_buf(vq, &len))) {
+		spin_unlock(&portdev->cvq_lock);
+
+		buf->len = len;
+		buf->offset = 0;
+
+		handle_control_message(portdev, buf);
+
+		spin_lock(&portdev->cvq_lock);
+		if (add_inbuf(portdev->c_ivq, buf) < 0) {
+			dev_warn(&portdev->vdev->dev,
+				 "Error adding buffer to queue\n");
+			free_buf(buf);
+		}
+	}
+	spin_unlock(&portdev->cvq_lock);
+}
+
+static void in_intr(struct virtqueue *vq)
+{
+	struct port *port;
+	unsigned long flags;
+
+	port = find_port_by_vq(vq->vdev->priv, vq);
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->inbuf_lock, flags);
+	if (!port->inbuf)
+		port->inbuf = get_inbuf(port);
+	spin_unlock_irqrestore(&port->inbuf_lock, flags);
+
+	if (is_console_port(port) && hvc_poll(port->cons.hvc))
+		hvc_kick();
+}
+
+static void control_intr(struct virtqueue *vq)
+{
+	struct ports_device *portdev;
+
+	portdev = vq->vdev->priv;
+	schedule_work(&portdev->control_work);
+}
+
 static void fill_queue(struct virtqueue *vq, spinlock_t *lock)
 {
 	struct port_buffer *buf;
@@ -529,7 +689,7 @@ static void fill_queue(struct virtqueue *vq, spinlock_t *lock)
 	} while (ret > 0);
 }
 
-static int __devinit add_port(struct ports_device *portdev)
+static int add_port(struct ports_device *portdev, u32 id)
 {
 	struct port *port;
 	int err;
@@ -541,25 +701,41 @@ static int __devinit add_port(struct ports_device *portdev)
 	}
 
 	port->portdev = portdev;
+	port->id = id;
 
 	port->inbuf = NULL;
+	port->cons.hvc = NULL;
 
-	port->in_vq = portdev->in_vqs[0];
-	port->out_vq = portdev->out_vqs[0];
+	port->in_vq = portdev->in_vqs[port->id];
+	port->out_vq = portdev->out_vqs[port->id];
 
 	spin_lock_init(&port->inbuf_lock);
 
-	fill_queue(port->in_vq, &port->inbuf_lock);
+	/*
+	 * If we're not using multiport support, this has to be a console port
+	 */
+	if (!use_multiport(port->portdev)) {
+		err = init_port_console(port);
+		if (err)
+			goto free_port;
+	}
 
+	fill_queue(port->in_vq, &port->inbuf_lock);
 	port->outbuf = alloc_buf(PAGE_SIZE);
 	if (!port->outbuf) {
 		err = -ENOMEM;
 		goto free_port;
 	}
+	spin_lock_irq(&portdev->ports_lock);
+	list_add_tail(&port->list, &port->portdev->ports);
+	spin_unlock_irq(&portdev->ports_lock);
 
-	err = init_port_console(port);
-	if (err)
-		goto free_port;
+	/*
+	 * Tell the Host we're set so that it can send us various
+	 * configuration parameters for this port (eg, port name,
+	 * caching, whether this is a console port, etc.)
+	 */
+	send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
 
 	return 0;
 
@@ -574,12 +750,11 @@ static int init_vqs(struct ports_device *portdev)
 	vq_callback_t **io_callbacks;
 	char **io_names;
 	struct virtqueue **vqs;
-	u32 nr_ports, nr_queues;
+	u32 i, j, nr_ports, nr_queues;
 	int err;
 
-	/* We currently only have one port and two queues for that port */
-	nr_ports = 1;
-	nr_queues = 2;
+	nr_ports = portdev->config.max_nr_ports;
+	nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2;
 
 	vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL);
 	if (!vqs) {
@@ -609,11 +784,32 @@ static int init_vqs(struct ports_device *portdev)
 		goto free_invqs;
 	}
 
-	io_callbacks[0] = hvc_handle_input;
-	io_callbacks[1] = NULL;
-	io_names[0] = "input";
-	io_names[1] = "output";
-
+	/*
+	 * For backward compat (newer host but older guest), the host
+	 * spawns a console port first and also inits the vqs for port
+	 * 0 before others.
+	 */
+	j = 0;
+	io_callbacks[j] = in_intr;
+	io_callbacks[j + 1] = NULL;
+	io_names[j] = "input";
+	io_names[j + 1] = "output";
+	j += 2;
+
+	if (use_multiport(portdev)) {
+		io_callbacks[j] = control_intr;
+		io_callbacks[j + 1] = NULL;
+		io_names[j] = "control-i";
+		io_names[j + 1] = "control-o";
+
+		for (i = 1; i < nr_ports; i++) {
+			j += 2;
+			io_callbacks[j] = in_intr;
+			io_callbacks[j + 1] = NULL;
+			io_names[j] = "input";
+			io_names[j + 1] = "output";
+		}
+	}
 	/* Find the queues. */
 	err = portdev->vdev->config->find_vqs(portdev->vdev, nr_queues, vqs,
 					      io_callbacks,
@@ -621,9 +817,20 @@ static int init_vqs(struct ports_device *portdev)
 	if (err)
 		goto free_outvqs;
 
+	j = 0;
 	portdev->in_vqs[0] = vqs[0];
 	portdev->out_vqs[0] = vqs[1];
-
+	j += 2;
+	if (use_multiport(portdev)) {
+		portdev->c_ivq = vqs[j];
+		portdev->c_ovq = vqs[j + 1];
+
+		for (i = 1; i < nr_ports; i++) {
+			j += 2;
+			portdev->in_vqs[i] = vqs[j];
+			portdev->out_vqs[i] = vqs[j + 1];
+		}
+	}
 	kfree(io_callbacks);
 	kfree(io_names);
 	kfree(vqs);
@@ -647,11 +854,17 @@ fail:
 /*
  * Once we're further in boot, we get probed like any other virtio
  * device.
+ *
+ * If the host also supports multiple console ports, we check the
+ * config space to see how many ports the host has spawned.  We
+ * initialize each port found.
  */
 static int __devinit virtcons_probe(struct virtio_device *vdev)
 {
 	struct ports_device *portdev;
+	u32 i;
 	int err;
+	bool multiport;
 
 	portdev = kmalloc(sizeof(*portdev), GFP_KERNEL);
 	if (!portdev) {
@@ -663,16 +876,60 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
 	portdev->vdev = vdev;
 	vdev->priv = portdev;
 
+	multiport = false;
+	if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) {
+		multiport = true;
+		vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT;
+
+		vdev->config->get(vdev, offsetof(struct virtio_console_config,
+						 nr_ports),
+				  &portdev->config.nr_ports,
+				  sizeof(portdev->config.nr_ports));
+		vdev->config->get(vdev, offsetof(struct virtio_console_config,
+						 max_nr_ports),
+				  &portdev->config.max_nr_ports,
+				  sizeof(portdev->config.max_nr_ports));
+		if (portdev->config.nr_ports > portdev->config.max_nr_ports) {
+			dev_warn(&vdev->dev,
+				 "More ports (%u) specified than allowed (%u). Will init %u ports.",
+				 portdev->config.nr_ports,
+				 portdev->config.max_nr_ports,
+				 portdev->config.max_nr_ports);
+
+			portdev->config.nr_ports = portdev->config.max_nr_ports;
+		}
+	} else {
+		portdev->config.nr_ports = 1;
+		portdev->config.max_nr_ports = 1;
+	}
+
+	/* Let the Host know we support multiple ports.*/
+	vdev->config->finalize_features(vdev);
+
 	err = init_vqs(portdev);
 	if (err < 0) {
 		dev_err(&vdev->dev, "Error %d initializing vqs\n", err);
 		goto free;
 	}
 
-	/* We only have one port. */
-	err = add_port(portdev);
-	if (err)
-		goto free_vqs;
+	spin_lock_init(&portdev->ports_lock);
+	INIT_LIST_HEAD(&portdev->ports);
+
+	if (multiport) {
+		spin_lock_init(&portdev->cvq_lock);
+		INIT_WORK(&portdev->control_work, &control_work_handler);
+
+		portdev->outbuf = alloc_buf(PAGE_SIZE);
+		if (!portdev->outbuf) {
+			err = -ENOMEM;
+			dev_err(&vdev->dev, "OOM for control outbuf\n");
+			goto free_vqs;
+		}
+		fill_queue(portdev->c_ivq, &portdev->cvq_lock);
+	}
+
+	for (i = 0; i < portdev->config.nr_ports; i++)
+		add_port(portdev, i);
 
 	/* Start using the new console output. */
 	early_put_chars = NULL;
@@ -695,6 +952,7 @@ static struct virtio_device_id id_table[] = {
 
 static unsigned int features[] = {
 	VIRTIO_CONSOLE_F_SIZE,
+	VIRTIO_CONSOLE_F_MULTIPORT,
 };
 
 static struct virtio_driver virtio_console = {
diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h
index 7ea5372..b8e5f9f 100644
--- a/include/linux/virtio_console.h
+++ b/include/linux/virtio_console.h
@@ -6,19 +6,41 @@
 /*
  * This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so
  * anyone can use the definitions to implement compatible drivers/servers.
+ *
+ * Copyright (C) Red Hat, Inc., 2009
  */
 
 /* Feature bits */
 #define VIRTIO_CONSOLE_F_SIZE	0	/* Does host provide console size? */
+#define VIRTIO_CONSOLE_F_MULTIPORT 1	/* Does host provide multiple ports? */
 
 struct virtio_console_config {
 	/* colums of the screens */
 	__u16 cols;
 	/* rows of the screens */
 	__u16 rows;
+	/* max. number of ports this device can hold */
+	__u32 max_nr_ports;
+	/* number of ports added so far */
+	__u32 nr_ports;
 } __attribute__((packed));
 
 /*
+ * A message that's passed between the Host and the Guest for a
+ * particular port.
+ */
+struct virtio_console_control {
+	__u32 id;		/* Port number */
+	__u16 event;		/* The kind of control event (see below) */
+	__u16 value;		/* Extra information for the key */
+};
+
+/* Some events for control messages */
+#define VIRTIO_CONSOLE_PORT_READY	0
+#define VIRTIO_CONSOLE_CONSOLE_PORT	1
+#define VIRTIO_CONSOLE_RESIZE		2
+
+/*
  * This struct is put at the start of each data buffer that gets
  * passed to Host and vice-versa.
  */
-- 
1.6.2.5

^ permalink raw reply related

* [PATCH 20/31] virtio: console: Introduce a 'header' for each buffer towards supporting multiport
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-20-git-send-email-amit.shah@redhat.com>

This header can contain flags that give buffers special meaning, for
example the first and last buffers of a single write request. This
enables the host to send out the data to host applications in a single
read request rather than splitting it up, which is how some applications
like their data to be received.

This header is put in by the host as well as the guest for all data
packets.

This header is currently a 0-length field. It will be populated when
multiport support is added.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c  |   32 ++++++++++++++++++++++++++------
 include/linux/virtio_console.h |    7 +++++++
 2 files changed, 33 insertions(+), 6 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 6509e62..9d55778 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -161,6 +161,18 @@ out:
 	return port;
 }
 
+static inline bool use_multiport(struct ports_device *portdev)
+{
+	/*
+	 * This condition can be true when put_chars is called from
+	 * early_init
+	 */
+	if (!portdev->vdev)
+		return 0;
+	/* Check for feature bit once multiport support is added */
+	return 0;
+}
+
 static void free_buf(struct port_buffer *buf)
 {
 	kfree(buf->buf);
@@ -199,7 +211,8 @@ static void *get_inbuf(struct port *port)
 	buf = vq->vq_ops->get_buf(vq, &len);
 	if (buf) {
 		buf->len = len;
-		buf->offset = 0;
+		buf->offset = use_multiport(port->portdev)
+			? sizeof(struct virtio_console_header) : 0;
 	}
 	return buf;
 }
@@ -246,27 +259,34 @@ out:
 static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count)
 {
 	struct scatterlist sg[1];
+	struct virtio_console_header header;
 	struct virtqueue *out_vq;
 	struct port_buffer *buf;
 	size_t in_offset, copy_size;
 	ssize_t ret;
-	unsigned int tmplen;
+	unsigned int header_len, tmplen;
 
 	out_vq = port->out_vq;
 	buf = port->outbuf;
 
+	header_len = use_multiport(port->portdev) ? sizeof(header) : 0;
+
 	in_offset = 0; /* offset in the user buffer */
 	while (in_offset < in_count) {
-		copy_size = min(in_count - in_offset, buf->size);
+		copy_size = min(in_count - in_offset + header_len, buf->size);
+		copy_size -= header_len;
 		/*
 		 * 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_offset, copy_size);
+		memcpy(buf->buf + header_len, in_buf + in_offset, copy_size);
+
+		buf->len = header_len + copy_size;
 
-		buf->len = copy_size;
+		if (header_len)
+			memcpy(buf->buf, &header, header_len);
 
 		sg_init_one(sg, buf->buf, buf->len);
 		ret = out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, buf);
@@ -277,7 +297,7 @@ static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count)
 		if (ret < 0)
 			break;
 
-		in_offset += buf->len;
+		in_offset += buf->len - header_len;
 		while (!out_vq->vq_ops->get_buf(out_vq, &tmplen))
 			cpu_relax();
 	}
diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h
index 9e0da40..7ea5372 100644
--- a/include/linux/virtio_console.h
+++ b/include/linux/virtio_console.h
@@ -18,6 +18,13 @@ struct virtio_console_config {
 	__u16 rows;
 } __attribute__((packed));
 
+/*
+ * This struct is put at the start of each data buffer that gets
+ * passed to Host and vice-versa.
+ */
+struct virtio_console_header {
+	/* Empty till multiport support is added */
+};
 
 #ifdef __KERNEL__
 int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int));
-- 
1.6.2.5

^ permalink raw reply related

* [PATCH 19/31] virtio: console: Introduce a send_buf function for a common path for sending data to host
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-19-git-send-email-amit.shah@redhat.com>

Adding support for generic ports that will write to userspace will need
some code changes.

Consolidate the write routine into send_buf() and put_chars() now just
calls into the new function.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |   70 +++++++++++++++++++++++++++++++----------
 1 files changed, 53 insertions(+), 17 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 2ab15a5..6509e62 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -107,6 +107,9 @@ 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;
 
@@ -240,6 +243,49 @@ out:
 	return ret;
 }
 
+static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count)
+{
+	struct scatterlist sg[1];
+	struct virtqueue *out_vq;
+	struct port_buffer *buf;
+	size_t in_offset, copy_size;
+	ssize_t ret;
+	unsigned int tmplen;
+
+	out_vq = port->out_vq;
+	buf = port->outbuf;
+
+	in_offset = 0; /* offset in the user buffer */
+	while (in_offset < in_count) {
+		copy_size = min(in_count - in_offset, buf->size);
+		/*
+		 * 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_offset, copy_size);
+
+		buf->len = copy_size;
+
+		sg_init_one(sg, buf->buf, buf->len);
+		ret = out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, buf);
+
+		/* Tell Host to go! */
+		out_vq->vq_ops->kick(out_vq);
+
+		if (ret < 0)
+			break;
+
+		in_offset += buf->len;
+		while (!out_vq->vq_ops->get_buf(out_vq, &tmplen))
+			cpu_relax();
+	}
+
+	/* We're expected to return the amount of data we wrote */
+	return in_offset;
+}
+
 /*
  * Give out the data that's requested from the buffers that we have
  * queued up.
@@ -288,10 +334,7 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count)
  */
 static int put_chars(u32 vtermno, const char *buf, int count)
 {
-	struct scatterlist sg[1];
 	struct port *port;
-	struct virtqueue *out_vq;
-	unsigned int len;
 
 	port = find_port_by_vtermno(vtermno);
 	if (!port)
@@ -300,20 +343,7 @@ static int put_chars(u32 vtermno, const char *buf, int count)
 	if (unlikely(early_put_chars))
 		return early_put_chars(vtermno, buf, count);
 
-	out_vq = port->out_vq;
-	/* This is a convenient routine to initialize a single-elem sg list */
-	sg_init_one(sg, buf, count);
-
-	/* This shouldn't fail: if it does, we lose chars. */
-	if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, port) >= 0) {
-		/* Tell Host to go! */
-		out_vq->vq_ops->kick(out_vq);
-		while (!out_vq->vq_ops->get_buf(out_vq, &len))
-			cpu_relax();
-	}
-
-	/* We're expected to return the amount of data we wrote: all of it. */
-	return count;
+	return send_buf(port, buf, count);
 }
 
 /*
@@ -501,6 +531,12 @@ static int __devinit add_port(struct ports_device *portdev)
 
 	fill_queue(port->in_vq, &port->inbuf_lock);
 
+	port->outbuf = alloc_buf(PAGE_SIZE);
+	if (!port->outbuf) {
+		err = -ENOMEM;
+		goto free_port;
+	}
+
 	err = init_port_console(port);
 	if (err)
 		goto free_port;
-- 
1.6.2.5

^ permalink raw reply related

* [PATCH 18/31] virtio: console: Buffer data that comes from the host
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
  To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-18-git-send-email-amit.shah@redhat.com>

The console could be flooded with data from the host; handle this
situation by buffering the data.

We use the virtio queue ring to maintain our buffer. This buffer length
is set by the host while initializing the queue.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |  172 +++++++++++++++++++++++++++++++----------
 1 files changed, 131 insertions(+), 41 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index e4b6a37..2ab15a5 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -100,6 +100,13 @@ struct port {
 	/* The current buffer from which data has to be fed to readers */
 	struct port_buffer *inbuf;
 
+	/*
+	 * To protect the operations on the in_vq associated with this
+	 * port.  Has to be a spinlock because it can be called from
+	 * interrupt context (get_char()).
+	 */
+	spinlock_t inbuf_lock;
+
 	/* The IO vqs for this port */
 	struct virtqueue *in_vq, *out_vq;
 
@@ -132,6 +139,25 @@ out:
 	return port;
 }
 
+static struct port *find_port_by_vq(struct ports_device *portdev,
+				    struct virtqueue *vq)
+{
+	struct port *port;
+	struct console *cons;
+	unsigned long flags;
+
+	spin_lock_irqsave(&pdrvdata_lock, flags);
+	list_for_each_entry(cons, &pdrvdata.consoles, list) {
+		port = container_of(cons, struct port, cons);
+		if (port->in_vq == vq || port->out_vq == vq)
+			goto out;
+	}
+	port = NULL;
+out:
+	spin_unlock_irqrestore(&pdrvdata_lock, flags);
+	return port;
+}
+
 static void free_buf(struct port_buffer *buf)
 {
 	kfree(buf->buf);
@@ -181,15 +207,75 @@ static void *get_inbuf(struct port *port)
  *
  * Callers should take appropriate locks.
  */
-static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
+static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
 {
 	struct scatterlist sg[1];
+	int ret;
 
 	sg_init_one(sg, buf->buf, buf->size);
 
-	if (vq->vq_ops->add_buf(vq, sg, 0, 1, buf) < 0)
-		BUG();
+	ret = vq->vq_ops->add_buf(vq, sg, 0, 1, buf);
 	vq->vq_ops->kick(vq);
+	return ret;
+}
+
+static bool port_has_data(struct port *port)
+{
+	unsigned long flags;
+	bool ret;
+
+	spin_lock_irqsave(&port->inbuf_lock, flags);
+	if (port->inbuf) {
+		ret = true;
+		goto out;
+	}
+	port->inbuf = get_inbuf(port);
+	if (port->inbuf) {
+		ret = true;
+		goto out;
+	}
+	ret = false;
+out:
+	spin_unlock_irqrestore(&port->inbuf_lock, flags);
+	return ret;
+}
+
+/*
+ * Give out the data that's requested from the buffers that we have
+ * queued up.
+ */
+static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count)
+{
+	struct port_buffer *buf;
+	ssize_t out_offset, ret;
+	unsigned long flags;
+
+	out_offset = 0;
+	while (out_count && port_has_data(port)) {
+		size_t copy_size;
+
+		buf = port->inbuf;
+		copy_size = out_count;
+		if (copy_size > buf->len - buf->offset)
+			copy_size = buf->len - buf->offset;
+
+		memcpy(out_buf + out_offset, buf->buf + buf->offset, copy_size);
+
+		ret = copy_size;
+		buf->offset += ret;
+		out_offset += ret;
+		out_count -= ret;
+
+		if (buf->offset == buf->len) {
+			spin_lock_irqsave(&port->inbuf_lock, flags);
+			port->inbuf = NULL;
+
+			if (add_inbuf(port->in_vq, buf) < 0)
+				free_buf(buf);
+			spin_unlock_irqrestore(&port->inbuf_lock, flags);
+		}
+	}
+	return out_offset;
 }
 
 /*
@@ -234,9 +320,8 @@ static int put_chars(u32 vtermno, const char *buf, int count)
  * get_chars() is the callback from the hvc_console infrastructure
  * when an interrupt is received.
  *
- * Most of the code deals with the fact that the hvc_console()
- * infrastructure only asks us for 16 bytes at a time.  We keep
- * in_offset and in_used fields for partially-filled buffers.
+ * We call out to fill_readbuf that gets us the required data from the
+ * buffers that are queued up.
  */
 static int get_chars(u32 vtermno, char *buf, int count)
 {
@@ -249,25 +334,7 @@ static int get_chars(u32 vtermno, char *buf, int count)
 	/* If we don't have an input queue yet, we can't get input. */
 	BUG_ON(!port->in_vq);
 
-	/* No more in buffer?  See if they've (re)used it. */
-	if (port->inbuf->offset == port->inbuf->len) {
-		if (!get_inbuf(port))
-			return 0;
-	}
-
-	/* You want more than we have to give?  Well, try wanting less! */
-	if (port->inbuf->offset + count > port->inbuf->len)
-		count = port->inbuf->len - port->inbuf->offset;
-
-	/* Copy across to their buffer and increment offset. */
-	memcpy(buf, port->inbuf->buf + port->inbuf->offset, count);
-	port->inbuf->offset += count;
-
-	/* Finished?  Re-register buffer so Host will use it again. */
-	if (port->inbuf->offset == port->inbuf->len)
-		add_inbuf(port->in_vq, port->inbuf);
-
-	return count;
+	return fill_readbuf(port, buf, count);
 }
 
 static void resize_console(struct port *port)
@@ -314,13 +381,19 @@ static void notifier_del_vio(struct hvc_struct *hp, int data)
 
 static void hvc_handle_input(struct virtqueue *vq)
 {
-	struct console *cons;
-	bool activity = false;
+	struct port *port;
+	unsigned long flags;
 
-	list_for_each_entry(cons, &pdrvdata.consoles, list)
-		activity |= hvc_poll(cons->hvc);
+	port = find_port_by_vq(vq->vdev->priv, vq);
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&port->inbuf_lock, flags);
+	if (!port->inbuf)
+		port->inbuf = get_inbuf(port);
+	spin_unlock_irqrestore(&port->inbuf_lock, flags);
 
-	if (activity)
+	if (hvc_poll(port->cons.hvc))
 		hvc_kick();
 }
 
@@ -385,6 +458,27 @@ int __devinit init_port_console(struct port *port)
 	return 0;
 }
 
+static void fill_queue(struct virtqueue *vq, spinlock_t *lock)
+{
+	struct port_buffer *buf;
+	int ret;
+
+	do {
+		buf = alloc_buf(PAGE_SIZE);
+		if (!buf)
+			break;
+
+		spin_lock_irq(lock);
+		ret = add_inbuf(vq, buf);
+		if (ret < 0) {
+			spin_unlock_irq(lock);
+			free_buf(buf);
+			break;
+		}
+		spin_unlock_irq(lock);
+	} while (ret > 0);
+}
+
 static int __devinit add_port(struct ports_device *portdev)
 {
 	struct port *port;
@@ -397,26 +491,22 @@ static int __devinit add_port(struct ports_device *portdev)
 	}
 
 	port->portdev = portdev;
+
+	port->inbuf = NULL;
+
 	port->in_vq = portdev->in_vqs[0];
 	port->out_vq = portdev->out_vqs[0];
 
-	port->inbuf = alloc_buf(PAGE_SIZE);
-	if (!port->inbuf) {
-		err = -ENOMEM;
-		goto free_port;
-	}
+	spin_lock_init(&port->inbuf_lock);
+
+	fill_queue(port->in_vq, &port->inbuf_lock);
 
 	err = init_port_console(port);
 	if (err)
-		goto free_inbuf;
-
-	/* Register the input buffer the first time. */
-	add_inbuf(port->in_vq, port->inbuf);
+		goto free_port;
 
 	return 0;
 
-free_inbuf:
-	free_buf(port->inbuf);
 free_port:
 	kfree(port);
 fail:
-- 
1.6.2.5

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox