* [PATCH 00/32] virtio: console: Fixes, multiple devices and generic ports
@ 2010-01-18 13:44 Amit Shah
2010-01-18 13:44 ` [PATCH 01/32] virtio: console: comment cleanup Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:44 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
Hey Rusty,
In this version:
- Assume only one inbuf; no input buffering for ports
- Remove the START/END delimiters for outgoing buffers
- Remove the header that was sent along with each buffer
- Remove unused buffers in the vqs at port remove time
- Send a guest port open message to the host when a console port is
attached
- Remove cached buffers when ports are closed / disconnected
- Send only one buffer in send_buf
As usual, this series is passed through the testsuite that tests for
the functionality given here.
Amit Shah (25):
hvc_console: Remove __devinit annotation from hvc_alloc
virtio: console: We support only one device at a time
virtio: console: encapsulate buffer information in a struct
virtio: console: ensure add_inbuf can work for multiple ports as well
virtio: console: introduce a get_inbuf helper to fetch bufs from
in_vq
virtio: console: don't assume a single console port.
virtio: console: struct ports for multiple ports per device.
virtio: console: ensure console size is updated on hvc open
virtio: console: Separate out console-specific data into a separate
struct
virtio: console: Separate out console init into a new function
virtio: console: Separate out find_vqs operation into a different
function
virtio: console: Introduce function to hand off data from host to
readers
virtio: console: Introduce a send_buf function for a common path for
sending data to host
virtio: console: Add a new MULTIPORT feature, support for generic
ports
virtio: console: Prepare for writing to / reading from userspace
buffers
virtio: console: Associate each port with a char device
virtio: console: Add file operations to ports for
open/read/write/poll
virtio: console: Ensure only one process can have a port open at a
time
virtio: console: Register with sysfs and create a 'name' attribute
for ports
virtio: console: Add throttling support to prevent flooding ports
virtio: console: Remove cached data on port close
virtio: console: Handle port hot-plug
virtio: console: Add ability to hot-unplug ports
virtio: console: Add debugfs files for each port to expose debug info
virtio: console: show error message if hvc_alloc fails for console
ports
Rusty Russell (6):
virtio: console: comment cleanup
virtio: console: statically initialize virtio_cons
hvc_console: make the ops pointer const.
virtio: console: port encapsulation
virtio: console: use vdev->priv to avoid accessing global var.
virtio: console: remove global var
Shirley Ma (1):
virtio: Add ability to detach unused buffers from vrings
drivers/char/Kconfig | 8 +
drivers/char/hvc_beat.c | 2 +-
drivers/char/hvc_console.c | 9 +-
drivers/char/hvc_console.h | 9 +-
drivers/char/hvc_iseries.c | 2 +-
drivers/char/hvc_iucv.c | 2 +-
drivers/char/hvc_rtas.c | 2 +-
drivers/char/hvc_udbg.c | 2 +-
drivers/char/hvc_vio.c | 2 +-
drivers/char/hvc_xen.c | 2 +-
drivers/char/virtio_console.c | 1559 ++++++++++++++++++++++++++++++++++++----
drivers/virtio/virtio_ring.c | 25 +
include/linux/virtio.h | 4 +
include/linux/virtio_console.h | 31 +-
14 files changed, 1496 insertions(+), 163 deletions(-)
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH 01/32] virtio: console: comment cleanup
2010-01-18 13:44 [PATCH 00/32] virtio: console: Fixes, multiple devices and generic ports Amit Shah
@ 2010-01-18 13:44 ` Amit Shah
2010-01-18 13:44 ` [PATCH 02/32] virtio: console: statically initialize virtio_cons Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:44 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
From: Rusty Russell <rusty@rustcorp.com.au>
Remove old lguest-style comments.
[Amit: - wingify comments acc. to kernel style
- indent comments ]
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 108 ++++++++++++++++++++--------------------
include/linux/virtio_console.h | 6 ++-
2 files changed, 58 insertions(+), 56 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index a035ae3..26e238c 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1,18 +1,5 @@
-/*D:300
- * The Guest console driver
- *
- * Writing console drivers is one of the few remaining Dark Arts in Linux.
- * Fortunately for us, the path of virtual consoles has been well-trodden by
- * the PowerPC folks, who wrote "hvc_console.c" to generically support any
- * virtual console. We use that infrastructure which only requires us to write
- * the basic put_chars and get_chars functions and call the right register
- * functions.
- :*/
-
-/*M:002 The console can be flooded: while the Guest is processing input the
- * Host can send more. Buffering in the Host could alleviate this, but it is a
- * difficult problem in general. :*/
-/* Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation
+/*
+ * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation
*
* 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
@@ -34,8 +21,6 @@
#include <linux/virtio_console.h>
#include "hvc_console.h"
-/*D:340 These represent our input and output console queues, and the virtio
- * operations for them. */
static struct virtqueue *in_vq, *out_vq;
static struct virtio_device *vdev;
@@ -49,12 +34,14 @@ static struct hv_ops virtio_cons;
/* The hvc device */
static struct hvc_struct *hvc;
-/*D:310 The put_chars() callback is pretty straightforward.
+/*
+ * The put_chars() callback is pretty straightforward.
*
- * We turn the characters into a scatter-gather list, add it to the output
- * queue and then kick the Host. Then we sit here waiting for it to finish:
- * inefficient in theory, but in practice implementations will do it
- * immediately (lguest's Launcher does). */
+ * We turn the characters into a scatter-gather list, add it to the
+ * output queue and then kick the Host. Then we sit here waiting for
+ * it to finish: inefficient in theory, but in practice
+ * implementations will do it immediately (lguest's Launcher does).
+ */
static int put_chars(u32 vtermno, const char *buf, int count)
{
struct scatterlist sg[1];
@@ -63,8 +50,10 @@ static int put_chars(u32 vtermno, const char *buf, int count)
/* This is a convenient routine to initialize a single-elem sg list */
sg_init_one(sg, buf, count);
- /* add_buf wants a token to identify this buffer: we hand it any
- * non-NULL pointer, since there's only ever one buffer. */
+ /*
+ * add_buf wants a token to identify this buffer: we hand it
+ * any non-NULL pointer, since there's only ever one buffer.
+ */
if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) >= 0) {
/* Tell Host to go! */
out_vq->vq_ops->kick(out_vq);
@@ -77,8 +66,10 @@ static int put_chars(u32 vtermno, const char *buf, int count)
return count;
}
-/* Create a scatter-gather list representing our input buffer and put it in the
- * queue. */
+/*
+ * Create a scatter-gather list representing our input buffer and put
+ * it in the queue.
+ */
static void add_inbuf(void)
{
struct scatterlist sg[1];
@@ -90,12 +81,14 @@ static void add_inbuf(void)
in_vq->vq_ops->kick(in_vq);
}
-/*D:350 get_chars() is the callback from the hvc_console infrastructure when
- * an interrupt is received.
+/*
+ * 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. */
+ * 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.
+ */
static int get_chars(u32 vtermno, char *buf, int count)
{
/* If we don't have an input queue yet, we can't get input. */
@@ -123,14 +116,16 @@ static int get_chars(u32 vtermno, char *buf, int count)
return count;
}
-/*:*/
-/*D:320 Console drivers are initialized very early so boot messages can go out,
- * so we do things slightly differently from the generic virtio initialization
- * of the net and block drivers.
+/*
+ * Console drivers are initialized very early so boot messages can go
+ * out, so we do things slightly differently from the generic virtio
+ * initialization of the net and block drivers.
*
- * At this stage, the console is output-only. It's too early to set up a
- * virtqueue, so we let the drivers do some boutique early-output thing. */
+ * At this stage, the console is output-only. It's too early to set
+ * up a virtqueue, so we let the drivers do some boutique early-output
+ * thing.
+ */
int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
{
virtio_cons.put_chars = put_chars;
@@ -157,8 +152,8 @@ static void virtcons_apply_config(struct virtio_device *dev)
}
/*
- * we support only one console, the hvc struct is a global var
- * We set the configuration at this point, since we now have a tty
+ * we support only one console, the hvc struct is a global var We set
+ * the configuration at this point, since we now have a tty
*/
static int notifier_add_vio(struct hvc_struct *hp, int data)
{
@@ -179,13 +174,17 @@ static void hvc_handle_input(struct virtqueue *vq)
hvc_kick();
}
-/*D:370 Once we're further in boot, we get probed like any other virtio device.
- * At this stage we set up the output virtqueue.
+/*
+ * Once we're further in boot, we get probed like any other virtio
+ * device. At this stage we set up the output virtqueue.
*
- * To set up and manage our virtual console, we call hvc_alloc(). Since we
- * never remove the console device we never need this pointer again.
+ * To set up and manage our virtual console, we call hvc_alloc().
+ * Since we never remove the console device we never need this pointer
+ * again.
*
- * Finally we put our input buffer in the input queue, ready to receive. */
+ * Finally we put our input buffer in the input queue, ready to
+ * receive.
+ */
static int __devinit virtcons_probe(struct virtio_device *dev)
{
vq_callback_t *callbacks[] = { hvc_handle_input, NULL};
@@ -203,8 +202,6 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
}
/* Find the queues. */
- /* FIXME: This is why we want to wean off hvc: we do nothing
- * when input comes in. */
err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
if (err)
goto free;
@@ -219,15 +216,18 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
virtio_cons.notifier_del = notifier_del_vio;
virtio_cons.notifier_hangup = notifier_del_vio;
- /* The first argument of hvc_alloc() is the virtual console number, so
- * we use zero. The second argument is the parameter for the
- * notification mechanism (like irq number). We currently leave this
- * as zero, virtqueues have implicit notifications.
+ /*
+ * The first argument of hvc_alloc() is the virtual console
+ * number, so we use zero. The second argument is the
+ * parameter for the notification mechanism (like irq
+ * number). We currently leave this as zero, virtqueues have
+ * implicit notifications.
*
- * The third argument is a "struct hv_ops" containing the put_chars()
- * get_chars(), notifier_add() and notifier_del() pointers.
- * The final argument is the output buffer size: we can do any size,
- * so we put PAGE_SIZE here. */
+ * The third argument is a "struct hv_ops" containing the
+ * put_chars(), get_chars(), notifier_add() and notifier_del()
+ * pointers. The final argument is the output buffer size: we
+ * can do any size, so we put PAGE_SIZE here.
+ */
hvc = hvc_alloc(0, 0, &virtio_cons, PAGE_SIZE);
if (IS_ERR(hvc)) {
err = PTR_ERR(hvc);
diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h
index fe88517..9e0da40 100644
--- a/include/linux/virtio_console.h
+++ b/include/linux/virtio_console.h
@@ -3,8 +3,10 @@
#include <linux/types.h>
#include <linux/virtio_ids.h>
#include <linux/virtio_config.h>
-/* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so
- * anyone can use the definitions to implement compatible drivers/servers. */
+/*
+ * This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so
+ * anyone can use the definitions to implement compatible drivers/servers.
+ */
/* Feature bits */
#define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 02/32] virtio: console: statically initialize virtio_cons
2010-01-18 13:44 ` [PATCH 01/32] virtio: console: comment cleanup Amit Shah
@ 2010-01-18 13:44 ` Amit Shah
2010-01-18 13:44 ` [PATCH 03/32] hvc_console: make the ops pointer const Amit Shah
[not found] ` <1263822326-24208-4-git-send-email-amit.shah@redhat.com>
0 siblings, 2 replies; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:44 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
From: Rusty Russell <rusty@rustcorp.com.au>
That way, we can make it const as is good kernel style. We use a separate
indirection for the early console, rather than mugging ops.put_chars.
We rename it hv_ops, too.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 60 +++++++++++++++++++++++-----------------
1 files changed, 34 insertions(+), 26 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 26e238c..1d844a4 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -28,12 +28,12 @@ static struct virtio_device *vdev;
static unsigned int in_len;
static char *in, *inbuf;
-/* The operations for our console. */
-static struct hv_ops virtio_cons;
-
/* The hvc device */
static struct hvc_struct *hvc;
+/* This is the very early arch-specified put chars function. */
+static int (*early_put_chars)(u32, const char *, int);
+
/*
* The put_chars() callback is pretty straightforward.
*
@@ -47,6 +47,9 @@ static int put_chars(u32 vtermno, const char *buf, int count)
struct scatterlist sg[1];
unsigned int len;
+ if (unlikely(early_put_chars))
+ return early_put_chars(vtermno, buf, count);
+
/* This is a convenient routine to initialize a single-elem sg list */
sg_init_one(sg, buf, count);
@@ -118,21 +121,6 @@ static int get_chars(u32 vtermno, char *buf, int count)
}
/*
- * Console drivers are initialized very early so boot messages can go
- * out, so we do things slightly differently from the generic virtio
- * initialization of the net and block drivers.
- *
- * At this stage, the console is output-only. It's too early to set
- * up a virtqueue, so we let the drivers do some boutique early-output
- * thing.
- */
-int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
-{
- virtio_cons.put_chars = put_chars;
- return hvc_instantiate(0, 0, &virtio_cons);
-}
-
-/*
* virtio console configuration. This supports:
* - console resize
*/
@@ -174,6 +162,30 @@ static void hvc_handle_input(struct virtqueue *vq)
hvc_kick();
}
+/* The operations for the console. */
+static struct hv_ops hv_ops = {
+ .get_chars = get_chars,
+ .put_chars = put_chars,
+ .notifier_add = notifier_add_vio,
+ .notifier_del = notifier_del_vio,
+ .notifier_hangup = notifier_del_vio,
+};
+
+/*
+ * Console drivers are initialized very early so boot messages can go
+ * out, so we do things slightly differently from the generic virtio
+ * initialization of the net and block drivers.
+ *
+ * At this stage, the console is output-only. It's too early to set
+ * up a virtqueue, so we let the drivers do some boutique early-output
+ * thing.
+ */
+int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
+{
+ early_put_chars = put_chars;
+ return hvc_instantiate(0, 0, &hv_ops);
+}
+
/*
* Once we're further in boot, we get probed like any other virtio
* device. At this stage we set up the output virtqueue.
@@ -209,13 +221,6 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
in_vq = vqs[0];
out_vq = vqs[1];
- /* Start using the new console output. */
- virtio_cons.get_chars = get_chars;
- virtio_cons.put_chars = put_chars;
- virtio_cons.notifier_add = notifier_add_vio;
- virtio_cons.notifier_del = notifier_del_vio;
- virtio_cons.notifier_hangup = notifier_del_vio;
-
/*
* The first argument of hvc_alloc() is the virtual console
* number, so we use zero. The second argument is the
@@ -228,7 +233,7 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
* pointers. The final argument is the output buffer size: we
* can do any size, so we put PAGE_SIZE here.
*/
- hvc = hvc_alloc(0, 0, &virtio_cons, PAGE_SIZE);
+ hvc = hvc_alloc(0, 0, &hv_ops, PAGE_SIZE);
if (IS_ERR(hvc)) {
err = PTR_ERR(hvc);
goto free_vqs;
@@ -236,6 +241,9 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
/* Register the input buffer the first time. */
add_inbuf();
+
+ /* Start using the new console output. */
+ early_put_chars = NULL;
return 0;
free_vqs:
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 03/32] hvc_console: make the ops pointer const.
2010-01-18 13:44 ` [PATCH 02/32] virtio: console: statically initialize virtio_cons Amit Shah
@ 2010-01-18 13:44 ` Amit Shah
[not found] ` <1263822326-24208-4-git-send-email-amit.shah@redhat.com>
1 sibling, 0 replies; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:44 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, linuxppc-dev, virtualization
From: Rusty Russell <rusty@rustcorp.com.au>
This is nicer for modern R/O protection. And noone needs it non-const, so
constify the callers as well.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
To: Christian Borntraeger <borntraeger@de.ibm.com>
Cc: linuxppc-dev@ozlabs.org
---
drivers/char/hvc_beat.c | 2 +-
drivers/char/hvc_console.c | 7 ++++---
drivers/char/hvc_console.h | 7 ++++---
drivers/char/hvc_iseries.c | 2 +-
drivers/char/hvc_iucv.c | 2 +-
drivers/char/hvc_rtas.c | 2 +-
drivers/char/hvc_udbg.c | 2 +-
drivers/char/hvc_vio.c | 2 +-
drivers/char/hvc_xen.c | 2 +-
drivers/char/virtio_console.c | 2 +-
10 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/drivers/char/hvc_beat.c b/drivers/char/hvc_beat.c
index 0afc8b8..6913fc3 100644
--- a/drivers/char/hvc_beat.c
+++ b/drivers/char/hvc_beat.c
@@ -84,7 +84,7 @@ static int hvc_beat_put_chars(uint32_t vtermno, const char *buf, int cnt)
return cnt;
}
-static struct hv_ops hvc_beat_get_put_ops = {
+static const struct hv_ops hvc_beat_get_put_ops = {
.get_chars = hvc_beat_get_chars,
.put_chars = hvc_beat_put_chars,
};
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index 416d342..d8dac58 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -125,7 +125,7 @@ static struct hvc_struct *hvc_get_by_index(int index)
* console interfaces but can still be used as a tty device. This has to be
* static because kmalloc will not work during early console init.
*/
-static struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES];
+static const struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES];
static uint32_t vtermnos[MAX_NR_HVC_CONSOLES] =
{[0 ... MAX_NR_HVC_CONSOLES - 1] = -1};
@@ -247,7 +247,7 @@ static void destroy_hvc_struct(struct kref *kref)
* vty adapters do NOT get an hvc_instantiate() callback since they
* appear after early console init.
*/
-int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops)
+int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops)
{
struct hvc_struct *hp;
@@ -749,7 +749,8 @@ static const struct tty_operations hvc_ops = {
};
struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data,
- struct hv_ops *ops, int outbuf_size)
+ const struct hv_ops *ops,
+ int outbuf_size)
{
struct hvc_struct *hp;
int i;
diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h
index 10950ca..52ddf4d 100644
--- a/drivers/char/hvc_console.h
+++ b/drivers/char/hvc_console.h
@@ -55,7 +55,7 @@ struct hvc_struct {
int outbuf_size;
int n_outbuf;
uint32_t vtermno;
- struct hv_ops *ops;
+ const struct hv_ops *ops;
int irq_requested;
int data;
struct winsize ws;
@@ -76,11 +76,12 @@ struct hv_ops {
};
/* Register a vterm and a slot index for use as a console (console_init) */
-extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops);
+extern int hvc_instantiate(uint32_t vtermno, int index,
+ const struct hv_ops *ops);
/* register a vterm for hvc tty operation (module_init or hotplug add) */
extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data,
- struct hv_ops *ops, int outbuf_size);
+ const struct hv_ops *ops, int outbuf_size);
/* remove a vterm from hvc tty operation (module_exit or hotplug remove) */
extern int hvc_remove(struct hvc_struct *hp);
diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c
index 936d05b..fd02426 100644
--- a/drivers/char/hvc_iseries.c
+++ b/drivers/char/hvc_iseries.c
@@ -197,7 +197,7 @@ done:
return sent;
}
-static struct hv_ops hvc_get_put_ops = {
+static const struct hv_ops hvc_get_put_ops = {
.get_chars = get_chars,
.put_chars = put_chars,
.notifier_add = notifier_add_irq,
diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c
index fe62bd0..21681a8 100644
--- a/drivers/char/hvc_iucv.c
+++ b/drivers/char/hvc_iucv.c
@@ -922,7 +922,7 @@ static int hvc_iucv_pm_restore_thaw(struct device *dev)
/* HVC operations */
-static struct hv_ops hvc_iucv_ops = {
+static const struct hv_ops hvc_iucv_ops = {
.get_chars = hvc_iucv_get_chars,
.put_chars = hvc_iucv_put_chars,
.notifier_add = hvc_iucv_notifier_add,
diff --git a/drivers/char/hvc_rtas.c b/drivers/char/hvc_rtas.c
index 88590d0..61c4a61 100644
--- a/drivers/char/hvc_rtas.c
+++ b/drivers/char/hvc_rtas.c
@@ -71,7 +71,7 @@ static int hvc_rtas_read_console(uint32_t vtermno, char *buf, int count)
return i;
}
-static struct hv_ops hvc_rtas_get_put_ops = {
+static const struct hv_ops hvc_rtas_get_put_ops = {
.get_chars = hvc_rtas_read_console,
.put_chars = hvc_rtas_write_console,
};
diff --git a/drivers/char/hvc_udbg.c b/drivers/char/hvc_udbg.c
index bd63ba8..b0957e6 100644
--- a/drivers/char/hvc_udbg.c
+++ b/drivers/char/hvc_udbg.c
@@ -58,7 +58,7 @@ static int hvc_udbg_get(uint32_t vtermno, char *buf, int count)
return i;
}
-static struct hv_ops hvc_udbg_ops = {
+static const struct hv_ops hvc_udbg_ops = {
.get_chars = hvc_udbg_get,
.put_chars = hvc_udbg_put,
};
diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c
index 10be343..27370e9 100644
--- a/drivers/char/hvc_vio.c
+++ b/drivers/char/hvc_vio.c
@@ -77,7 +77,7 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count)
return got;
}
-static struct hv_ops hvc_get_put_ops = {
+static const struct hv_ops hvc_get_put_ops = {
.get_chars = filtered_get_chars,
.put_chars = hvc_put_chars,
.notifier_add = notifier_add_irq,
diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c
index b1a7163..60446f8 100644
--- a/drivers/char/hvc_xen.c
+++ b/drivers/char/hvc_xen.c
@@ -122,7 +122,7 @@ static int read_console(uint32_t vtermno, char *buf, int len)
return recv;
}
-static struct hv_ops hvc_ops = {
+static const struct hv_ops hvc_ops = {
.get_chars = read_console,
.put_chars = write_console,
.notifier_add = notifier_add_irq,
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 1d844a4..791be4e 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -163,7 +163,7 @@ static void hvc_handle_input(struct virtqueue *vq)
}
/* The operations for the console. */
-static struct hv_ops hv_ops = {
+static const struct hv_ops hv_ops = {
.get_chars = get_chars,
.put_chars = put_chars,
.notifier_add = notifier_add_vio,
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 04/32] hvc_console: Remove __devinit annotation from hvc_alloc
[not found] ` <1263822326-24208-4-git-send-email-amit.shah@redhat.com>
@ 2010-01-18 13:44 ` Amit Shah
[not found] ` <1263822326-24208-5-git-send-email-amit.shah@redhat.com>
1 sibling, 0 replies; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:44 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, linuxppc-dev, virtualization
Virtio consoles can be hotplugged, so hvc_alloc gets called from
multiple sites: from the initial probe() routine as well as later on
from workqueue handlers which aren't __devinit code.
So, drop the __devinit annotation for hvc_alloc.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
Cc: linuxppc-dev@ozlabs.org
---
drivers/char/hvc_console.c | 6 +++---
drivers/char/hvc_console.h | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index d8dac58..4c3b59b 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -748,9 +748,9 @@ static const struct tty_operations hvc_ops = {
.chars_in_buffer = hvc_chars_in_buffer,
};
-struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data,
- const struct hv_ops *ops,
- int outbuf_size)
+struct hvc_struct *hvc_alloc(uint32_t vtermno, int data,
+ const struct hv_ops *ops,
+ int outbuf_size)
{
struct hvc_struct *hp;
int i;
diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h
index 52ddf4d..54381eb 100644
--- a/drivers/char/hvc_console.h
+++ b/drivers/char/hvc_console.h
@@ -80,8 +80,8 @@ extern int hvc_instantiate(uint32_t vtermno, int index,
const struct hv_ops *ops);
/* register a vterm for hvc tty operation (module_init or hotplug add) */
-extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data,
- const struct hv_ops *ops, int outbuf_size);
+extern struct hvc_struct * hvc_alloc(uint32_t vtermno, int data,
+ const struct hv_ops *ops, int outbuf_size);
/* remove a vterm from hvc tty operation (module_exit or hotplug remove) */
extern int hvc_remove(struct hvc_struct *hp);
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 05/32] virtio: console: We support only one device at a time
[not found] ` <1263822326-24208-5-git-send-email-amit.shah@redhat.com>
@ 2010-01-18 13:44 ` Amit Shah
2010-01-18 13:45 ` [PATCH 06/32] virtio: console: port encapsulation Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:44 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
We support only one virtio_console device at a time. If multiple are
found, error out if one is already initialized.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 791be4e..bfc0abf 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -204,6 +204,11 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
struct virtqueue *vqs[2];
int err;
+ if (vdev) {
+ dev_warn(&vdev->dev,
+ "Multiple virtio-console devices not supported yet\n");
+ return -EEXIST;
+ }
vdev = dev;
/* This is the scratch page we use to receive console input */
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 06/32] virtio: console: port encapsulation
2010-01-18 13:44 ` [PATCH 05/32] virtio: console: We support only one device at a time Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 07/32] virtio: console: encapsulate buffer information in a struct Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
From: Rusty Russell <rusty@rustcorp.com.au>
We are heading towards a multiple-"port" system, so as part of weaning off
globals we encapsulate the information into 'struct port'.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 107 ++++++++++++++++++++++-------------------
1 files changed, 58 insertions(+), 49 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index bfc0abf..9ea9223 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -21,15 +21,19 @@
#include <linux/virtio_console.h>
#include "hvc_console.h"
-static struct virtqueue *in_vq, *out_vq;
-static struct virtio_device *vdev;
-
-/* This is our input buffer, and how much data is left in it. */
-static unsigned int in_len;
-static char *in, *inbuf;
+struct port {
+ struct virtqueue *in_vq, *out_vq;
+ struct virtio_device *vdev;
+ /* This is our input buffer, and how much data is left in it. */
+ char *inbuf;
+ unsigned int used_len, offset;
+
+ /* The hvc device */
+ struct hvc_struct *hvc;
+};
-/* The hvc device */
-static struct hvc_struct *hvc;
+/* We have one port ready to go immediately, for a console. */
+static struct port console;
/* This is the very early arch-specified put chars function. */
static int (*early_put_chars)(u32, const char *, int);
@@ -46,22 +50,21 @@ static int put_chars(u32 vtermno, const char *buf, int count)
{
struct scatterlist sg[1];
unsigned int len;
+ struct port *port;
if (unlikely(early_put_chars))
return early_put_chars(vtermno, buf, count);
+ port = &console;
+
/* This is a convenient routine to initialize a single-elem sg list */
sg_init_one(sg, buf, count);
- /*
- * add_buf wants a token to identify this buffer: we hand it
- * any non-NULL pointer, since there's only ever one buffer.
- */
- if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) >= 0) {
+ /* This shouldn't fail: if it does, we lose chars. */
+ if (port->out_vq->vq_ops->add_buf(port->out_vq, sg, 1, 0, port) >= 0) {
/* Tell Host to go! */
- out_vq->vq_ops->kick(out_vq);
- /* Chill out until it's done with the buffer. */
- while (!out_vq->vq_ops->get_buf(out_vq, &len))
+ port->out_vq->vq_ops->kick(port->out_vq);
+ while (!port->out_vq->vq_ops->get_buf(port->out_vq, &len))
cpu_relax();
}
@@ -73,15 +76,15 @@ static int put_chars(u32 vtermno, const char *buf, int count)
* Create a scatter-gather list representing our input buffer and put
* it in the queue.
*/
-static void add_inbuf(void)
+static void add_inbuf(struct port *port)
{
struct scatterlist sg[1];
- sg_init_one(sg, inbuf, PAGE_SIZE);
+ sg_init_one(sg, port->inbuf, PAGE_SIZE);
- /* We should always be able to add one buffer to an empty queue. */
- if (in_vq->vq_ops->add_buf(in_vq, sg, 0, 1, inbuf) < 0)
+ /* Should always be able to add one buffer to an empty queue. */
+ if (port->in_vq->vq_ops->add_buf(port->in_vq, sg, 0, 1, port) < 0)
BUG();
- in_vq->vq_ops->kick(in_vq);
+ port->in_vq->vq_ops->kick(port->in_vq);
}
/*
@@ -94,28 +97,31 @@ static void add_inbuf(void)
*/
static int get_chars(u32 vtermno, char *buf, int count)
{
+ struct port *port;
+
+ port = &console;
+
/* If we don't have an input queue yet, we can't get input. */
- BUG_ON(!in_vq);
+ BUG_ON(!port->in_vq);
- /* No buffer? Try to get one. */
- if (!in_len) {
- in = in_vq->vq_ops->get_buf(in_vq, &in_len);
- if (!in)
+ /* No more in buffer? See if they've (re)used it. */
+ if (port->offset == port->used_len) {
+ if (!port->in_vq->vq_ops->get_buf(port->in_vq, &port->used_len))
return 0;
+ port->offset = 0;
}
/* You want more than we have to give? Well, try wanting less! */
- if (in_len < count)
- count = in_len;
+ if (port->offset + count > port->used_len)
+ count = port->used_len - port->offset;
/* Copy across to their buffer and increment offset. */
- memcpy(buf, in, count);
- in += count;
- in_len -= count;
+ memcpy(buf, port->inbuf + port->offset, count);
+ port->offset += count;
/* Finished? Re-register buffer so Host will use it again. */
- if (in_len == 0)
- add_inbuf();
+ if (port->offset == port->used_len)
+ add_inbuf(port);
return count;
}
@@ -135,7 +141,7 @@ static void virtcons_apply_config(struct virtio_device *dev)
dev->config->get(dev,
offsetof(struct virtio_console_config, rows),
&ws.ws_row, sizeof(u16));
- hvc_resize(hvc, ws);
+ hvc_resize(console.hvc, ws);
}
}
@@ -146,7 +152,7 @@ static void virtcons_apply_config(struct virtio_device *dev)
static int notifier_add_vio(struct hvc_struct *hp, int data)
{
hp->irq_requested = 1;
- virtcons_apply_config(vdev);
+ virtcons_apply_config(console.vdev);
return 0;
}
@@ -158,7 +164,7 @@ static void notifier_del_vio(struct hvc_struct *hp, int data)
static void hvc_handle_input(struct virtqueue *vq)
{
- if (hvc_poll(hvc))
+ if (hvc_poll(console.hvc))
hvc_kick();
}
@@ -197,23 +203,26 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
* Finally we put our input buffer in the input queue, ready to
* receive.
*/
-static int __devinit virtcons_probe(struct virtio_device *dev)
+static int __devinit virtcons_probe(struct virtio_device *vdev)
{
vq_callback_t *callbacks[] = { hvc_handle_input, NULL};
const char *names[] = { "input", "output" };
struct virtqueue *vqs[2];
+ struct port *port;
int err;
- if (vdev) {
- dev_warn(&vdev->dev,
+ port = &console;
+ if (port->vdev) {
+ dev_warn(&port->vdev->dev,
"Multiple virtio-console devices not supported yet\n");
return -EEXIST;
}
- vdev = dev;
+ port->vdev = vdev;
/* This is the scratch page we use to receive console input */
- inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!inbuf) {
+ port->used_len = 0;
+ port->inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!port->inbuf) {
err = -ENOMEM;
goto fail;
}
@@ -223,8 +232,8 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
if (err)
goto free;
- in_vq = vqs[0];
- out_vq = vqs[1];
+ port->in_vq = vqs[0];
+ port->out_vq = vqs[1];
/*
* The first argument of hvc_alloc() is the virtual console
@@ -238,14 +247,14 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
* pointers. The final argument is the output buffer size: we
* can do any size, so we put PAGE_SIZE here.
*/
- hvc = hvc_alloc(0, 0, &hv_ops, PAGE_SIZE);
- if (IS_ERR(hvc)) {
- err = PTR_ERR(hvc);
+ port->hvc = hvc_alloc(0, 0, &hv_ops, PAGE_SIZE);
+ if (IS_ERR(port->hvc)) {
+ err = PTR_ERR(port->hvc);
goto free_vqs;
}
/* Register the input buffer the first time. */
- add_inbuf();
+ add_inbuf(port);
/* Start using the new console output. */
early_put_chars = NULL;
@@ -254,7 +263,7 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
free_vqs:
vdev->config->del_vqs(vdev);
free:
- kfree(inbuf);
+ kfree(port->inbuf);
fail:
return err;
}
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 07/32] virtio: console: encapsulate buffer information in a struct
2010-01-18 13:45 ` [PATCH 06/32] virtio: console: port encapsulation Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 08/32] virtio: console: ensure add_inbuf can work for multiple ports as well Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
Collect port buffer, used_len, offset fields into a single structure.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 70 ++++++++++++++++++++++++++++++++---------
1 files changed, 55 insertions(+), 15 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 9ea9223..699fc98 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -21,12 +21,24 @@
#include <linux/virtio_console.h>
#include "hvc_console.h"
+struct port_buffer {
+ char *buf;
+
+ /* size of the buffer in *buf above */
+ size_t size;
+
+ /* used length of the buffer */
+ size_t len;
+ /* offset in the buf from which to consume data */
+ size_t offset;
+};
+
struct port {
struct virtqueue *in_vq, *out_vq;
struct virtio_device *vdev;
- /* This is our input buffer, and how much data is left in it. */
- char *inbuf;
- unsigned int used_len, offset;
+
+ /* The current buffer from which data has to be fed to readers */
+ struct port_buffer *inbuf;
/* The hvc device */
struct hvc_struct *hvc;
@@ -38,6 +50,33 @@ static struct port console;
/* This is the very early arch-specified put chars function. */
static int (*early_put_chars)(u32, const char *, int);
+static void free_buf(struct port_buffer *buf)
+{
+ kfree(buf->buf);
+ kfree(buf);
+}
+
+static struct port_buffer *alloc_buf(size_t buf_size)
+{
+ struct port_buffer *buf;
+
+ buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ goto fail;
+ buf->buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!buf->buf)
+ goto free_buf;
+ buf->len = 0;
+ buf->offset = 0;
+ buf->size = buf_size;
+ return buf;
+
+free_buf:
+ kfree(buf);
+fail:
+ return NULL;
+}
+
/*
* The put_chars() callback is pretty straightforward.
*
@@ -79,7 +118,7 @@ static int put_chars(u32 vtermno, const char *buf, int count)
static void add_inbuf(struct port *port)
{
struct scatterlist sg[1];
- sg_init_one(sg, port->inbuf, PAGE_SIZE);
+ sg_init_one(sg, port->inbuf->buf, PAGE_SIZE);
/* Should always be able to add one buffer to an empty queue. */
if (port->in_vq->vq_ops->add_buf(port->in_vq, sg, 0, 1, port) < 0)
@@ -98,6 +137,7 @@ static void add_inbuf(struct port *port)
static int get_chars(u32 vtermno, char *buf, int count)
{
struct port *port;
+ unsigned int len;
port = &console;
@@ -105,22 +145,23 @@ static int get_chars(u32 vtermno, char *buf, int count)
BUG_ON(!port->in_vq);
/* No more in buffer? See if they've (re)used it. */
- if (port->offset == port->used_len) {
- if (!port->in_vq->vq_ops->get_buf(port->in_vq, &port->used_len))
+ if (port->inbuf->offset == port->inbuf->len) {
+ if (!port->in_vq->vq_ops->get_buf(port->in_vq, &len))
return 0;
- port->offset = 0;
+ port->inbuf->offset = 0;
+ port->inbuf->len = len;
}
/* You want more than we have to give? Well, try wanting less! */
- if (port->offset + count > port->used_len)
- count = port->used_len - port->offset;
+ 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 + port->offset, count);
- port->offset += count;
+ 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->offset == port->used_len)
+ if (port->inbuf->offset == port->inbuf->len)
add_inbuf(port);
return count;
@@ -220,8 +261,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
port->vdev = vdev;
/* This is the scratch page we use to receive console input */
- port->used_len = 0;
- port->inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ port->inbuf = alloc_buf(PAGE_SIZE);
if (!port->inbuf) {
err = -ENOMEM;
goto fail;
@@ -263,7 +303,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
free_vqs:
vdev->config->del_vqs(vdev);
free:
- kfree(port->inbuf);
+ free_buf(port->inbuf);
fail:
return err;
}
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 08/32] virtio: console: ensure add_inbuf can work for multiple ports as well
2010-01-18 13:45 ` [PATCH 07/32] virtio: console: encapsulate buffer information in a struct Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 09/32] virtio: console: introduce a get_inbuf helper to fetch bufs from in_vq Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
add_inbuf() assumed one port and one inbuf per port. Remove that
assumption.
Also move the function so that put_chars and get_chars are together.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 35 ++++++++++++++++++-----------------
1 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 699fc98..1dbd46c 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -78,6 +78,22 @@ fail:
}
/*
+ * Create a scatter-gather list representing our input buffer and put
+ * it in the queue.
+ *
+ * Callers should take appropriate locks.
+ */
+static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
+{
+ struct scatterlist sg[1];
+ sg_init_one(sg, buf->buf, buf->size);
+
+ if (vq->vq_ops->add_buf(vq, sg, 0, 1, buf) < 0)
+ BUG();
+ vq->vq_ops->kick(vq);
+}
+
+/*
* The put_chars() callback is pretty straightforward.
*
* We turn the characters into a scatter-gather list, add it to the
@@ -112,21 +128,6 @@ static int put_chars(u32 vtermno, const char *buf, int count)
}
/*
- * Create a scatter-gather list representing our input buffer and put
- * it in the queue.
- */
-static void add_inbuf(struct port *port)
-{
- struct scatterlist sg[1];
- sg_init_one(sg, port->inbuf->buf, PAGE_SIZE);
-
- /* Should always be able to add one buffer to an empty queue. */
- if (port->in_vq->vq_ops->add_buf(port->in_vq, sg, 0, 1, port) < 0)
- BUG();
- port->in_vq->vq_ops->kick(port->in_vq);
-}
-
-/*
* get_chars() is the callback from the hvc_console infrastructure
* when an interrupt is received.
*
@@ -162,7 +163,7 @@ static int get_chars(u32 vtermno, char *buf, int count)
/* Finished? Re-register buffer so Host will use it again. */
if (port->inbuf->offset == port->inbuf->len)
- add_inbuf(port);
+ add_inbuf(port->in_vq, port->inbuf);
return count;
}
@@ -294,7 +295,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
}
/* Register the input buffer the first time. */
- add_inbuf(port);
+ add_inbuf(port->in_vq, port->inbuf);
/* Start using the new console output. */
early_put_chars = NULL;
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 09/32] virtio: console: introduce a get_inbuf helper to fetch bufs from in_vq
2010-01-18 13:45 ` [PATCH 08/32] virtio: console: ensure add_inbuf can work for multiple ports as well Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 10/32] virtio: console: use vdev->priv to avoid accessing global var Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
This makes taking locks around the get_buf vq operation easier, as well
as complements the add_inbuf() operation.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 21 +++++++++++++++++----
1 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 1dbd46c..df45e5e 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -77,6 +77,22 @@ fail:
return NULL;
}
+/* Callers should take appropriate locks */
+static void *get_inbuf(struct port *port)
+{
+ struct port_buffer *buf;
+ struct virtqueue *vq;
+ unsigned int len;
+
+ vq = port->in_vq;
+ buf = vq->vq_ops->get_buf(vq, &len);
+ if (buf) {
+ buf->len = len;
+ buf->offset = 0;
+ }
+ return buf;
+}
+
/*
* Create a scatter-gather list representing our input buffer and put
* it in the queue.
@@ -138,7 +154,6 @@ static int put_chars(u32 vtermno, const char *buf, int count)
static int get_chars(u32 vtermno, char *buf, int count)
{
struct port *port;
- unsigned int len;
port = &console;
@@ -147,10 +162,8 @@ static int get_chars(u32 vtermno, char *buf, int count)
/* No more in buffer? See if they've (re)used it. */
if (port->inbuf->offset == port->inbuf->len) {
- if (!port->in_vq->vq_ops->get_buf(port->in_vq, &len))
+ if (!get_inbuf(port))
return 0;
- port->inbuf->offset = 0;
- port->inbuf->len = len;
}
/* You want more than we have to give? Well, try wanting less! */
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 10/32] virtio: console: use vdev->priv to avoid accessing global var.
2010-01-18 13:45 ` [PATCH 09/32] virtio: console: introduce a get_inbuf helper to fetch bufs from in_vq Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 11/32] virtio: console: don't assume a single console port Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
From: Rusty Russell <rusty@rustcorp.com.au>
Part of removing our "one console" assumptions, use vdev->priv to point
to the port (currently == the global console).
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 10 ++++++++--
1 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index df45e5e..e52ee11 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -187,6 +187,7 @@ static int get_chars(u32 vtermno, char *buf, int count)
*/
static void virtcons_apply_config(struct virtio_device *dev)
{
+ struct port *port = dev->priv;
struct winsize ws;
if (virtio_has_feature(dev, VIRTIO_CONSOLE_F_SIZE)) {
@@ -196,7 +197,7 @@ static void virtcons_apply_config(struct virtio_device *dev)
dev->config->get(dev,
offsetof(struct virtio_console_config, rows),
&ws.ws_row, sizeof(u16));
- hvc_resize(console.hvc, ws);
+ hvc_resize(port->hvc, ws);
}
}
@@ -219,7 +220,9 @@ static void notifier_del_vio(struct hvc_struct *hp, int data)
static void hvc_handle_input(struct virtqueue *vq)
{
- if (hvc_poll(console.hvc))
+ struct port *port = vq->vdev->priv;
+
+ if (hvc_poll(port->hvc))
hvc_kick();
}
@@ -272,7 +275,10 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
"Multiple virtio-console devices not supported yet\n");
return -EEXIST;
}
+
+ /* Attach this port to this virtio_device, and vice-versa. */
port->vdev = vdev;
+ vdev->priv = port;
/* This is the scratch page we use to receive console input */
port->inbuf = alloc_buf(PAGE_SIZE);
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 11/32] virtio: console: don't assume a single console port.
2010-01-18 13:45 ` [PATCH 10/32] virtio: console: use vdev->priv to avoid accessing global var Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 12/32] virtio: console: remove global var Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
Keep a list of all ports being used as a console, and provide a lock
and a lookup function. The hvc callbacks only give us a vterm number,
so we need to map this.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 74 ++++++++++++++++++++++++++++++++++++-----
1 files changed, 65 insertions(+), 9 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index e52ee11..6bbf707 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -17,10 +17,28 @@
*/
#include <linux/err.h>
#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
#include <linux/virtio.h>
#include <linux/virtio_console.h>
#include "hvc_console.h"
+/*
+ * This is a global struct for storing common data for all the devices
+ * this driver handles.
+ *
+ * Mainly, it has a linked list for all the consoles in one place so
+ * that callbacks from hvc for get_chars(), put_chars() work properly
+ * across multiple devices and multiple ports per device.
+ */
+struct ports_driver_data {
+ /* All the console devices handled by this driver */
+ struct list_head consoles;
+};
+static struct ports_driver_data pdrvdata;
+
+DEFINE_SPINLOCK(pdrvdata_lock);
+
struct port_buffer {
char *buf;
@@ -40,8 +58,15 @@ struct port {
/* The current buffer from which data has to be fed to readers */
struct port_buffer *inbuf;
+ /* For console ports, hvc != NULL and these are valid. */
/* The hvc device */
struct hvc_struct *hvc;
+
+ /* We'll place all consoles in a list in the pdrvdata struct */
+ struct list_head list;
+
+ /* Our vterm number. */
+ u32 vtermno;
};
/* We have one port ready to go immediately, for a console. */
@@ -50,6 +75,22 @@ static struct port console;
/* This is the very early arch-specified put chars function. */
static int (*early_put_chars)(u32, const char *, int);
+static struct port *find_port_by_vtermno(u32 vtermno)
+{
+ struct port *port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdrvdata_lock, flags);
+ list_for_each_entry(port, &pdrvdata.consoles, list) {
+ if (port->vtermno == vtermno)
+ goto out;
+ }
+ port = NULL;
+out:
+ spin_unlock_irqrestore(&pdrvdata_lock, flags);
+ return port;
+}
+
static void free_buf(struct port_buffer *buf)
{
kfree(buf->buf);
@@ -120,14 +161,16 @@ static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
static int put_chars(u32 vtermno, const char *buf, int count)
{
struct scatterlist sg[1];
- unsigned int len;
struct port *port;
+ unsigned int len;
+
+ port = find_port_by_vtermno(vtermno);
+ if (!port)
+ return 0;
if (unlikely(early_put_chars))
return early_put_chars(vtermno, buf, count);
- port = &console;
-
/* This is a convenient routine to initialize a single-elem sg list */
sg_init_one(sg, buf, count);
@@ -155,7 +198,10 @@ static int get_chars(u32 vtermno, char *buf, int count)
{
struct port *port;
- port = &console;
+
+ port = find_port_by_vtermno(vtermno);
+ if (!port)
+ return 0;
/* If we don't have an input queue yet, we can't get input. */
BUG_ON(!port->in_vq);
@@ -201,14 +247,17 @@ static void virtcons_apply_config(struct virtio_device *dev)
}
}
-/*
- * we support only one console, the hvc struct is a global var We set
- * the configuration at this point, since we now have a tty
- */
+/* We set the configuration at this point, since we now have a tty */
static int notifier_add_vio(struct hvc_struct *hp, int data)
{
+ struct port *port;
+
+ port = find_port_by_vtermno(hp->vtermno);
+ if (!port)
+ return -EINVAL;
+
hp->irq_requested = 1;
- virtcons_apply_config(console.vdev);
+ virtcons_apply_config(port->vdev);
return 0;
}
@@ -313,6 +362,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
goto free_vqs;
}
+ /* Add to vtermno list. */
+ spin_lock_irq(&pdrvdata_lock);
+ list_add(&port->list, &pdrvdata.consoles);
+ spin_unlock_irq(&pdrvdata_lock);
+
/* Register the input buffer the first time. */
add_inbuf(port->in_vq, port->inbuf);
@@ -349,6 +403,8 @@ static struct virtio_driver virtio_console = {
static int __init init(void)
{
+ INIT_LIST_HEAD(&pdrvdata.consoles);
+
return register_virtio_driver(&virtio_console);
}
module_init(init);
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 12/32] virtio: console: remove global var
2010-01-18 13:45 ` [PATCH 11/32] virtio: console: don't assume a single console port Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 13/32] virtio: console: struct ports for multiple ports per device Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
From: Rusty Russell <rusty@rustcorp.com.au>
Now we can use an allocation function to remove our global console variable.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 67 ++++++++++++++++++++++++++++-------------
1 files changed, 46 insertions(+), 21 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 6bbf707..82f5180 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -32,6 +32,18 @@
* across multiple devices and multiple ports per device.
*/
struct ports_driver_data {
+ /*
+ * This is used to keep track of the number of hvc consoles
+ * spawned by this driver. This number is given as the first
+ * argument to hvc_alloc(). To correctly map an initial
+ * console spawned via hvc_instantiate to the console being
+ * hooked up via hvc_alloc, we need to pass the same vtermno.
+ *
+ * We also just assume the first console being initialised was
+ * the first one that got used as the initial console.
+ */
+ unsigned int next_vtermno;
+
/* All the console devices handled by this driver */
struct list_head consoles;
};
@@ -69,9 +81,6 @@ struct port {
u32 vtermno;
};
-/* We have one port ready to go immediately, for a console. */
-static struct port console;
-
/* This is the very early arch-specified put chars function. */
static int (*early_put_chars)(u32, const char *, int);
@@ -299,6 +308,30 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
return hvc_instantiate(0, 0, &hv_ops);
}
+static struct port *__devinit add_port(u32 vtermno)
+{
+ struct port *port;
+
+ port = kmalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return NULL;
+
+ port->inbuf = alloc_buf(PAGE_SIZE);
+ if (!port->inbuf) {
+ kfree(port);
+ return NULL;
+ }
+ port->hvc = NULL;
+ port->vtermno = vtermno;
+ return port;
+}
+
+static void free_port(struct port *port)
+{
+ free_buf(port->inbuf);
+ kfree(port);
+}
+
/*
* Once we're further in boot, we get probed like any other virtio
* device. At this stage we set up the output virtqueue.
@@ -318,24 +351,16 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
struct port *port;
int err;
- port = &console;
- if (port->vdev) {
- dev_warn(&port->vdev->dev,
- "Multiple virtio-console devices not supported yet\n");
- return -EEXIST;
+ port = add_port(pdrvdata.next_vtermno);
+ if (!port) {
+ err = -ENOMEM;
+ goto fail;
}
/* Attach this port to this virtio_device, and vice-versa. */
port->vdev = vdev;
vdev->priv = port;
- /* This is the scratch page we use to receive console input */
- port->inbuf = alloc_buf(PAGE_SIZE);
- if (!port->inbuf) {
- err = -ENOMEM;
- goto fail;
- }
-
/* Find the queues. */
err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
if (err)
@@ -346,17 +371,16 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
/*
* The first argument of hvc_alloc() is the virtual console
- * number, so we use zero. The second argument is the
- * parameter for the notification mechanism (like irq
- * number). We currently leave this as zero, virtqueues have
- * implicit notifications.
+ * number. The second argument is the parameter for the
+ * notification mechanism (like irq number). We currently
+ * leave this as zero, virtqueues have implicit notifications.
*
* The third argument is a "struct hv_ops" containing the
* put_chars(), get_chars(), notifier_add() and notifier_del()
* pointers. The final argument is the output buffer size: we
* can do any size, so we put PAGE_SIZE here.
*/
- port->hvc = hvc_alloc(0, 0, &hv_ops, PAGE_SIZE);
+ port->hvc = hvc_alloc(port->vtermno, 0, &hv_ops, PAGE_SIZE);
if (IS_ERR(port->hvc)) {
err = PTR_ERR(port->hvc);
goto free_vqs;
@@ -364,6 +388,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
/* Add to vtermno list. */
spin_lock_irq(&pdrvdata_lock);
+ pdrvdata.next_vtermno++;
list_add(&port->list, &pdrvdata.consoles);
spin_unlock_irq(&pdrvdata_lock);
@@ -377,7 +402,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
free_vqs:
vdev->config->del_vqs(vdev);
free:
- free_buf(port->inbuf);
+ free_port(port);
fail:
return err;
}
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 13/32] virtio: console: struct ports for multiple ports per device.
2010-01-18 13:45 ` [PATCH 12/32] virtio: console: remove global var Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 14/32] virtio: console: ensure console size is updated on hvc open Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
Rather than assume a single port, add a 'struct ports_device' which
stores data related to all the ports for that device.
Currently, there's only one port and is hooked up with hvc, but that
will change.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 152 ++++++++++++++++++++++++-----------------
1 files changed, 89 insertions(+), 63 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 82f5180..8631d43 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -51,6 +51,15 @@ static struct ports_driver_data pdrvdata;
DEFINE_SPINLOCK(pdrvdata_lock);
+/*
+ * This is a per-device struct that stores data common to all the
+ * ports for that device (vdev->priv).
+ */
+struct ports_device {
+ struct virtqueue *in_vq, *out_vq;
+ struct virtio_device *vdev;
+};
+
struct port_buffer {
char *buf;
@@ -63,13 +72,17 @@ struct port_buffer {
size_t offset;
};
+/* This struct holds the per-port data */
struct port {
- struct virtqueue *in_vq, *out_vq;
- struct virtio_device *vdev;
+ /* Pointer to the parent virtio_console device */
+ struct ports_device *portdev;
/* The current buffer from which data has to be fed to readers */
struct port_buffer *inbuf;
+ /* The IO vqs for this port */
+ struct virtqueue *in_vq, *out_vq;
+
/* For console ports, hvc != NULL and these are valid. */
/* The hvc device */
struct hvc_struct *hvc;
@@ -152,6 +165,7 @@ static void *get_inbuf(struct port *port)
static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
{
struct scatterlist sg[1];
+
sg_init_one(sg, buf->buf, buf->size);
if (vq->vq_ops->add_buf(vq, sg, 0, 1, buf) < 0)
@@ -171,6 +185,7 @@ 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);
@@ -180,14 +195,15 @@ 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 (port->out_vq->vq_ops->add_buf(port->out_vq, sg, 1, 0, port) >= 0) {
+ if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, port) >= 0) {
/* Tell Host to go! */
- port->out_vq->vq_ops->kick(port->out_vq);
- while (!port->out_vq->vq_ops->get_buf(port->out_vq, &len))
+ out_vq->vq_ops->kick(out_vq);
+ while (!out_vq->vq_ops->get_buf(out_vq, &len))
cpu_relax();
}
@@ -207,7 +223,6 @@ static int get_chars(u32 vtermno, char *buf, int count)
{
struct port *port;
-
port = find_port_by_vtermno(vtermno);
if (!port)
return 0;
@@ -242,7 +257,6 @@ static int get_chars(u32 vtermno, char *buf, int count)
*/
static void virtcons_apply_config(struct virtio_device *dev)
{
- struct port *port = dev->priv;
struct winsize ws;
if (virtio_has_feature(dev, VIRTIO_CONSOLE_F_SIZE)) {
@@ -252,7 +266,9 @@ static void virtcons_apply_config(struct virtio_device *dev)
dev->config->get(dev,
offsetof(struct virtio_console_config, rows),
&ws.ws_row, sizeof(u16));
- hvc_resize(port->hvc, ws);
+ /* This is the pre-multiport style: we use control messages
+ * these days which specify the port. So this means port 0. */
+ hvc_resize(find_port_by_vtermno(0)->hvc, ws);
}
}
@@ -266,7 +282,7 @@ static int notifier_add_vio(struct hvc_struct *hp, int data)
return -EINVAL;
hp->irq_requested = 1;
- virtcons_apply_config(port->vdev);
+ virtcons_apply_config(port->portdev->vdev);
return 0;
}
@@ -278,9 +294,13 @@ static void notifier_del_vio(struct hvc_struct *hp, int data)
static void hvc_handle_input(struct virtqueue *vq)
{
- struct port *port = vq->vdev->priv;
+ struct port *port;
+ bool activity = false;
+
+ list_for_each_entry(port, &pdrvdata.consoles, list)
+ activity |= hvc_poll(port->hvc);
- if (hvc_poll(port->hvc))
+ if (activity)
hvc_kick();
}
@@ -308,66 +328,26 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
return hvc_instantiate(0, 0, &hv_ops);
}
-static struct port *__devinit add_port(u32 vtermno)
-{
- struct port *port;
-
- port = kmalloc(sizeof(*port), GFP_KERNEL);
- if (!port)
- return NULL;
-
- port->inbuf = alloc_buf(PAGE_SIZE);
- if (!port->inbuf) {
- kfree(port);
- return NULL;
- }
- port->hvc = NULL;
- port->vtermno = vtermno;
- return port;
-}
-
-static void free_port(struct port *port)
+static int __devinit add_port(struct ports_device *portdev)
{
- free_buf(port->inbuf);
- kfree(port);
-}
-
-/*
- * Once we're further in boot, we get probed like any other virtio
- * device. At this stage we set up the output virtqueue.
- *
- * To set up and manage our virtual console, we call hvc_alloc().
- * Since we never remove the console device we never need this pointer
- * again.
- *
- * Finally we put our input buffer in the input queue, ready to
- * receive.
- */
-static int __devinit virtcons_probe(struct virtio_device *vdev)
-{
- vq_callback_t *callbacks[] = { hvc_handle_input, NULL};
- const char *names[] = { "input", "output" };
- struct virtqueue *vqs[2];
struct port *port;
int err;
- port = add_port(pdrvdata.next_vtermno);
+ port = kmalloc(sizeof(*port), GFP_KERNEL);
if (!port) {
err = -ENOMEM;
goto fail;
}
- /* Attach this port to this virtio_device, and vice-versa. */
- port->vdev = vdev;
- vdev->priv = port;
+ port->portdev = portdev;
+ port->in_vq = portdev->in_vq;
+ port->out_vq = portdev->out_vq;
- /* Find the queues. */
- err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
- if (err)
- goto free;
-
- port->in_vq = vqs[0];
- port->out_vq = vqs[1];
+ port->inbuf = alloc_buf(PAGE_SIZE);
+ if (!port->inbuf) {
+ err = -ENOMEM;
+ goto free_port;
+ }
/*
* The first argument of hvc_alloc() is the virtual console
@@ -380,10 +360,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
* pointers. The final argument is the output buffer size: we
* can do any size, so we put PAGE_SIZE here.
*/
+ port->vtermno = pdrvdata.next_vtermno;
port->hvc = hvc_alloc(port->vtermno, 0, &hv_ops, PAGE_SIZE);
if (IS_ERR(port->hvc)) {
err = PTR_ERR(port->hvc);
- goto free_vqs;
+ goto free_inbuf;
}
/* Add to vtermno list. */
@@ -395,6 +376,51 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
/* Register the input buffer the first time. */
add_inbuf(port->in_vq, port->inbuf);
+ return 0;
+
+free_inbuf:
+ free_buf(port->inbuf);
+free_port:
+ kfree(port);
+fail:
+ return err;
+}
+
+/*
+ * Once we're further in boot, we get probed like any other virtio
+ * device.
+ */
+static int __devinit virtcons_probe(struct virtio_device *vdev)
+{
+ vq_callback_t *callbacks[] = { hvc_handle_input, NULL};
+ const char *names[] = { "input", "output" };
+ struct virtqueue *vqs[2];
+ struct ports_device *portdev;
+ int err;
+
+ portdev = kmalloc(sizeof(*portdev), GFP_KERNEL);
+ if (!portdev) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ /* Attach this portdev to this virtio_device, and vice-versa. */
+ portdev->vdev = vdev;
+ vdev->priv = portdev;
+
+ /* Find the queues. */
+ err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
+ if (err)
+ goto free;
+
+ portdev->in_vq = vqs[0];
+ portdev->out_vq = vqs[1];
+
+ /* We only have one port. */
+ err = add_port(portdev);
+ if (err)
+ goto free_vqs;
+
/* Start using the new console output. */
early_put_chars = NULL;
return 0;
@@ -402,7 +428,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
free_vqs:
vdev->config->del_vqs(vdev);
free:
- free_port(port);
+ kfree(portdev);
fail:
return err;
}
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 14/32] virtio: console: ensure console size is updated on hvc open
2010-01-18 13:45 ` [PATCH 13/32] virtio: console: struct ports for multiple ports per device Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 15/32] virtio: console: Separate out console-specific data into a separate struct Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
When multiple console support is added, ensure each port's size gets
updated when a new one is opened via hvc.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 33 +++++++++++++++++----------------
1 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 8631d43..debc865 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -251,27 +251,28 @@ static int get_chars(u32 vtermno, char *buf, int count)
return count;
}
-/*
- * virtio console configuration. This supports:
- * - console resize
- */
-static void virtcons_apply_config(struct virtio_device *dev)
+static void resize_console(struct port *port)
{
+ struct virtio_device *vdev;
struct winsize ws;
- if (virtio_has_feature(dev, VIRTIO_CONSOLE_F_SIZE)) {
- dev->config->get(dev,
- offsetof(struct virtio_console_config, cols),
- &ws.ws_col, sizeof(u16));
- dev->config->get(dev,
- offsetof(struct virtio_console_config, rows),
- &ws.ws_row, sizeof(u16));
- /* This is the pre-multiport style: we use control messages
- * these days which specify the port. So this means port 0. */
- hvc_resize(find_port_by_vtermno(0)->hvc, ws);
+ vdev = port->portdev->vdev;
+ if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) {
+ vdev->config->get(vdev,
+ offsetof(struct virtio_console_config, cols),
+ &ws.ws_col, sizeof(u16));
+ vdev->config->get(vdev,
+ offsetof(struct virtio_console_config, rows),
+ &ws.ws_row, sizeof(u16));
+ hvc_resize(port->hvc, ws);
}
}
+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)
{
@@ -282,7 +283,7 @@ static int notifier_add_vio(struct hvc_struct *hp, int data)
return -EINVAL;
hp->irq_requested = 1;
- virtcons_apply_config(port->portdev->vdev);
+ resize_console(port);
return 0;
}
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 15/32] virtio: console: Separate out console-specific data into a separate struct
2010-01-18 13:45 ` [PATCH 14/32] virtio: console: ensure console size is updated on hvc open Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 16/32] virtio: console: Separate out console init into a new function Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
Move out console-specific stuff into a separate struct from 'struct
port' as we need to maintain two lists: one for all the ports (which
includes consoles) and one only for consoles since the hvc callbacks
only give us the vtermno.
This makes console handling cleaner.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 57 ++++++++++++++++++++++++++--------------
1 files changed, 37 insertions(+), 20 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index debc865..c6c6f52 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -51,6 +51,24 @@ static struct ports_driver_data pdrvdata;
DEFINE_SPINLOCK(pdrvdata_lock);
+/* This struct holds information that's relevant only for console ports */
+struct console {
+ /* We'll place all consoles in a list in the pdrvdata struct */
+ struct list_head list;
+
+ /* The hvc device associated with this console port */
+ struct hvc_struct *hvc;
+
+ /*
+ * This number identifies the number that we used to register
+ * with hvc in hvc_instantiate() and hvc_alloc(); this is the
+ * number passed on by the hvc callbacks to us to
+ * differentiate between the other console ports handled by
+ * this driver
+ */
+ u32 vtermno;
+};
+
/*
* This is a per-device struct that stores data common to all the
* ports for that device (vdev->priv).
@@ -83,15 +101,11 @@ struct port {
/* The IO vqs for this port */
struct virtqueue *in_vq, *out_vq;
- /* For console ports, hvc != NULL and these are valid. */
- /* The hvc device */
- struct hvc_struct *hvc;
-
- /* We'll place all consoles in a list in the pdrvdata struct */
- struct list_head list;
-
- /* Our vterm number. */
- u32 vtermno;
+ /*
+ * The entries in this struct will be valid if this port is
+ * hooked up to an hvc console
+ */
+ struct console cons;
};
/* This is the very early arch-specified put chars function. */
@@ -100,12 +114,15 @@ static int (*early_put_chars)(u32, const char *, int);
static struct port *find_port_by_vtermno(u32 vtermno)
{
struct port *port;
+ struct console *cons;
unsigned long flags;
spin_lock_irqsave(&pdrvdata_lock, flags);
- list_for_each_entry(port, &pdrvdata.consoles, list) {
- if (port->vtermno == vtermno)
+ list_for_each_entry(cons, &pdrvdata.consoles, list) {
+ if (cons->vtermno == vtermno) {
+ port = container_of(cons, struct port, cons);
goto out;
+ }
}
port = NULL;
out:
@@ -264,7 +281,7 @@ static void resize_console(struct port *port)
vdev->config->get(vdev,
offsetof(struct virtio_console_config, rows),
&ws.ws_row, sizeof(u16));
- hvc_resize(port->hvc, ws);
+ hvc_resize(port->cons.hvc, ws);
}
}
@@ -295,11 +312,11 @@ static void notifier_del_vio(struct hvc_struct *hp, int data)
static void hvc_handle_input(struct virtqueue *vq)
{
- struct port *port;
+ struct console *cons;
bool activity = false;
- list_for_each_entry(port, &pdrvdata.consoles, list)
- activity |= hvc_poll(port->hvc);
+ list_for_each_entry(cons, &pdrvdata.consoles, list)
+ activity |= hvc_poll(cons->hvc);
if (activity)
hvc_kick();
@@ -361,17 +378,17 @@ static int __devinit add_port(struct ports_device *portdev)
* pointers. The final argument is the output buffer size: we
* can do any size, so we put PAGE_SIZE here.
*/
- port->vtermno = pdrvdata.next_vtermno;
- port->hvc = hvc_alloc(port->vtermno, 0, &hv_ops, PAGE_SIZE);
- if (IS_ERR(port->hvc)) {
- err = PTR_ERR(port->hvc);
+ port->cons.vtermno = pdrvdata.next_vtermno;
+ port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE);
+ if (IS_ERR(port->cons.hvc)) {
+ err = PTR_ERR(port->cons.hvc);
goto free_inbuf;
}
/* Add to vtermno list. */
spin_lock_irq(&pdrvdata_lock);
pdrvdata.next_vtermno++;
- list_add(&port->list, &pdrvdata.consoles);
+ list_add(&port->cons.list, &pdrvdata.consoles);
spin_unlock_irq(&pdrvdata_lock);
/* Register the input buffer the first time. */
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 16/32] virtio: console: Separate out console init into a new function
2010-01-18 13:45 ` [PATCH 15/32] virtio: console: Separate out console-specific data into a separate struct Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 17/32] virtio: console: Separate out find_vqs operation into a different function Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
Console ports could be hot-added. Also, with the new multiport support,
a port is identified as a console port only if the host sends a control
message.
Move the console port init into a separate function so it can be invoked
from other places.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 61 ++++++++++++++++++++++++++---------------
1 files changed, 39 insertions(+), 22 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index c6c6f52..11e5faf 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -346,6 +346,43 @@ 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 ret;
+
+ /*
+ * The Host's telling us this port is a console port. Hook it
+ * up with an hvc console.
+ *
+ * To set up and manage our virtual console, we call
+ * hvc_alloc().
+ *
+ * The first argument of hvc_alloc() is the virtual console
+ * number. The second argument is the parameter for the
+ * notification mechanism (like irq number). We currently
+ * leave this as zero, virtqueues have implicit notifications.
+ *
+ * The third argument is a "struct hv_ops" containing the
+ * put_chars() get_chars(), notifier_add() and notifier_del()
+ * pointers. The final argument is the output buffer size: we
+ * can do any size, so we put PAGE_SIZE here.
+ */
+ port->cons.vtermno = pdrvdata.next_vtermno;
+
+ port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE);
+ if (IS_ERR(port->cons.hvc)) {
+ ret = PTR_ERR(port->cons.hvc);
+ port->cons.hvc = NULL;
+ return ret;
+ }
+ spin_lock_irq(&pdrvdata_lock);
+ pdrvdata.next_vtermno++;
+ list_add_tail(&port->cons.list, &pdrvdata.consoles);
+ spin_unlock_irq(&pdrvdata_lock);
+
+ return 0;
+}
+
static int __devinit add_port(struct ports_device *portdev)
{
struct port *port;
@@ -367,29 +404,9 @@ static int __devinit add_port(struct ports_device *portdev)
goto free_port;
}
- /*
- * The first argument of hvc_alloc() is the virtual console
- * number. The second argument is the parameter for the
- * notification mechanism (like irq number). We currently
- * leave this as zero, virtqueues have implicit notifications.
- *
- * The third argument is a "struct hv_ops" containing the
- * put_chars(), get_chars(), notifier_add() and notifier_del()
- * pointers. The final argument is the output buffer size: we
- * can do any size, so we put PAGE_SIZE here.
- */
- port->cons.vtermno = pdrvdata.next_vtermno;
- port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE);
- if (IS_ERR(port->cons.hvc)) {
- err = PTR_ERR(port->cons.hvc);
+ err = init_port_console(port);
+ if (err)
goto free_inbuf;
- }
-
- /* Add to vtermno list. */
- spin_lock_irq(&pdrvdata_lock);
- pdrvdata.next_vtermno++;
- list_add(&port->cons.list, &pdrvdata.consoles);
- spin_unlock_irq(&pdrvdata_lock);
/* Register the input buffer the first time. */
add_inbuf(port->in_vq, port->inbuf);
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 17/32] virtio: console: Separate out find_vqs operation into a different function
2010-01-18 13:45 ` [PATCH 16/32] virtio: console: Separate out console init into a new function Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 18/32] virtio: console: Introduce function to hand off data from host to readers Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
With support for multiple ports, each port will have its own input and
output vqs. Prepare the probe function for this change.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 98 ++++++++++++++++++++++++++++++++++++-----
1 files changed, 86 insertions(+), 12 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 11e5faf..75c5a35 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -74,7 +74,9 @@ struct console {
* ports for that device (vdev->priv).
*/
struct ports_device {
- struct virtqueue *in_vq, *out_vq;
+ /* Array of per-port IO virtqueues */
+ struct virtqueue **in_vqs, **out_vqs;
+
struct virtio_device *vdev;
};
@@ -395,8 +397,8 @@ static int __devinit add_port(struct ports_device *portdev)
}
port->portdev = portdev;
- port->in_vq = portdev->in_vq;
- port->out_vq = portdev->out_vq;
+ port->in_vq = portdev->in_vqs[0];
+ port->out_vq = portdev->out_vqs[0];
port->inbuf = alloc_buf(PAGE_SIZE);
if (!port->inbuf) {
@@ -421,15 +423,87 @@ fail:
return err;
}
+static int init_vqs(struct ports_device *portdev)
+{
+ vq_callback_t **io_callbacks;
+ char **io_names;
+ struct virtqueue **vqs;
+ u32 nr_ports, nr_queues;
+ int err;
+
+ /* We currently only have one port and two queues for that port */
+ nr_ports = 1;
+ nr_queues = 2;
+
+ vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL);
+ if (!vqs) {
+ err = -ENOMEM;
+ goto fail;
+ }
+ io_callbacks = kmalloc(nr_queues * sizeof(vq_callback_t *), GFP_KERNEL);
+ if (!io_callbacks) {
+ err = -ENOMEM;
+ goto free_vqs;
+ }
+ io_names = kmalloc(nr_queues * sizeof(char *), GFP_KERNEL);
+ if (!io_names) {
+ err = -ENOMEM;
+ goto free_callbacks;
+ }
+ portdev->in_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *),
+ GFP_KERNEL);
+ if (!portdev->in_vqs) {
+ err = -ENOMEM;
+ goto free_names;
+ }
+ portdev->out_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *),
+ GFP_KERNEL);
+ if (!portdev->out_vqs) {
+ err = -ENOMEM;
+ goto free_invqs;
+ }
+
+ io_callbacks[0] = hvc_handle_input;
+ io_callbacks[1] = NULL;
+ io_names[0] = "input";
+ io_names[1] = "output";
+
+ /* Find the queues. */
+ err = portdev->vdev->config->find_vqs(portdev->vdev, nr_queues, vqs,
+ io_callbacks,
+ (const char **)io_names);
+ if (err)
+ goto free_outvqs;
+
+ portdev->in_vqs[0] = vqs[0];
+ portdev->out_vqs[0] = vqs[1];
+
+ kfree(io_callbacks);
+ kfree(io_names);
+ kfree(vqs);
+
+ return 0;
+
+free_names:
+ kfree(io_names);
+free_callbacks:
+ kfree(io_callbacks);
+free_outvqs:
+ kfree(portdev->out_vqs);
+free_invqs:
+ kfree(portdev->in_vqs);
+free_vqs:
+ kfree(vqs);
+fail:
+ return err;
+}
+
/*
* Once we're further in boot, we get probed like any other virtio
* device.
*/
static int __devinit virtcons_probe(struct virtio_device *vdev)
{
- vq_callback_t *callbacks[] = { hvc_handle_input, NULL};
- const char *names[] = { "input", "output" };
- struct virtqueue *vqs[2];
struct ports_device *portdev;
int err;
@@ -443,13 +517,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
portdev->vdev = vdev;
vdev->priv = portdev;
- /* Find the queues. */
- err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
- if (err)
+ err = init_vqs(portdev);
+ if (err < 0) {
+ dev_err(&vdev->dev, "Error %d initializing vqs\n", err);
goto free;
-
- portdev->in_vq = vqs[0];
- portdev->out_vq = vqs[1];
+ }
/* We only have one port. */
err = add_port(portdev);
@@ -462,6 +534,8 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
free_vqs:
vdev->config->del_vqs(vdev);
+ kfree(portdev->in_vqs);
+ kfree(portdev->out_vqs);
free:
kfree(portdev);
fail:
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 18/32] virtio: console: Introduce function to hand off data from host to readers
2010-01-18 13:45 ` [PATCH 17/32] virtio: console: Separate out find_vqs operation into a different function Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 19/32] virtio: console: Introduce a send_buf function for a common path for sending data to host Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In preparation for serving data to userspace (generic ports) as well as
in-kernel users (hvc consoles), separate out the functionality common to
both in a 'fill_readbuf()' function.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 142 ++++++++++++++++++++++++++++++----------
1 files changed, 106 insertions(+), 36 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 75c5a35..5096d92 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,67 @@ 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;
+
+ ret = false;
+ spin_lock_irqsave(&port->inbuf_lock, flags);
+ if (port->inbuf)
+ ret = true;
+ spin_unlock_irqrestore(&port->inbuf_lock, flags);
+
+ return ret;
+}
+
+/*
+ * Give out the data that's requested from the buffer that we have
+ * queued up.
+ */
+static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count)
+{
+ struct port_buffer *buf;
+ unsigned long flags;
+
+ if (!out_count || !port_has_data(port))
+ return 0;
+
+ buf = port->inbuf;
+ if (out_count > buf->len - buf->offset)
+ out_count = buf->len - buf->offset;
+
+ memcpy(out_buf, buf->buf + buf->offset, out_count);
+
+ /* Return the number of bytes actually copied */
+ buf->offset += out_count;
+
+ if (buf->offset == buf->len) {
+ /*
+ * We're done using all the data in this buffer.
+ * Re-queue so that the Host can send us more data.
+ */
+ spin_lock_irqsave(&port->inbuf_lock, flags);
+ port->inbuf = NULL;
+
+ if (add_inbuf(port->in_vq, buf) < 0)
+ dev_warn(&port->portdev->vdev->dev, "failed add_buf\n");
+
+ spin_unlock_irqrestore(&port->inbuf_lock, flags);
+ }
+ return out_count;
}
/*
@@ -234,9 +312,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 +326,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 +373,18 @@ 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;
+
+ port = find_port_by_vq(vq->vdev->priv, vq);
+ if (!port)
+ return;
- list_for_each_entry(cons, &pdrvdata.consoles, list)
- activity |= hvc_poll(cons->hvc);
+ spin_lock_irqsave(&port->inbuf_lock, flags);
+ port->inbuf = get_inbuf(port);
+ spin_unlock_irqrestore(&port->inbuf_lock, flags);
- if (activity)
+ if (hvc_poll(port->cons.hvc))
hvc_kick();
}
@@ -388,6 +452,7 @@ int __devinit init_port_console(struct port *port)
static int __devinit add_port(struct ports_device *portdev)
{
struct port *port;
+ struct port_buffer *inbuf;
int err;
port = kmalloc(sizeof(*port), GFP_KERNEL);
@@ -397,26 +462,31 @@ 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) {
+ spin_lock_init(&port->inbuf_lock);
+
+ inbuf = alloc_buf(PAGE_SIZE);
+ if (!inbuf) {
err = -ENOMEM;
goto free_port;
}
+ /* Register the input buffer the first time. */
+ add_inbuf(port->in_vq, inbuf);
+
err = init_port_console(port);
if (err)
goto free_inbuf;
- /* Register the input buffer the first time. */
- add_inbuf(port->in_vq, port->inbuf);
-
return 0;
free_inbuf:
- free_buf(port->inbuf);
+ free_buf(inbuf);
free_port:
kfree(port);
fail:
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 19/32] virtio: console: Introduce a send_buf function for a common path for sending data to host
2010-01-18 13:45 ` [PATCH 18/32] virtio: console: Introduce function to hand off data from host to readers Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 20/32] virtio: console: Add a new MULTIPORT feature, support for generic ports Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
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 | 78 +++++++++++++++++++++++++++++++---------
1 files changed, 60 insertions(+), 18 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 5096d92..fff47cf 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;
@@ -233,6 +236,54 @@ static bool port_has_data(struct port *port)
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;
+ ssize_t ret;
+ unsigned int tmplen;
+
+ out_vq = port->out_vq;
+ buf = port->outbuf;
+
+ if (buf->len) {
+ /*
+ * Nonzero buf->len means we had queued a buffer
+ * earlier to the Host to be consumed. Get the buffer
+ * back for this write request; wait while the Host
+ * consumes it.
+ */
+ while (!out_vq->vq_ops->get_buf(out_vq, &tmplen))
+ cpu_relax();
+ }
+
+ if (in_count > buf->size)
+ in_count = 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_count);
+
+ buf->len = in_count;
+
+ 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)
+ buf->len = 0;
+
+ /* We're expected to return the amount of data we wrote */
+ return buf->len;
+}
+
/*
* Give out the data that's requested from the buffer that we have
* queued up.
@@ -280,10 +331,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)
@@ -292,20 +340,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);
}
/*
@@ -475,16 +510,23 @@ static int __devinit add_port(struct ports_device *portdev)
err = -ENOMEM;
goto free_port;
}
+ port->outbuf = alloc_buf(PAGE_SIZE);
+ if (!port->outbuf) {
+ err = -ENOMEM;
+ goto free_inbuf;
+ }
/* Register the input buffer the first time. */
add_inbuf(port->in_vq, inbuf);
err = init_port_console(port);
if (err)
- goto free_inbuf;
+ goto free_outbuf;
return 0;
+free_outbuf:
+ free_buf(port->outbuf);
free_inbuf:
free_buf(inbuf);
free_port:
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 20/32] virtio: console: Add a new MULTIPORT feature, support for generic ports
2010-01-18 13:45 ` [PATCH 19/32] virtio: console: Introduce a send_buf function for a common path for sending data to host Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 21/32] virtio: console: Prepare for writing to / reading from userspace buffers Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
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 | 405 ++++++++++++++++++++++++++++++++++------
include/linux/virtio_console.h | 21 ++
2 files changed, 370 insertions(+), 56 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index fff47cf..c0bc146 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,56 @@ 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)
+{
+ /*
+ * This condition can be true when put_chars is called from
+ * early_init
+ */
+ if (!portdev->vdev)
+ return 0;
+ return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT);
+}
+
static void free_buf(struct port_buffer *buf)
{
kfree(buf->buf);
@@ -236,6 +302,36 @@ static bool port_has_data(struct port *port)
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];
@@ -406,24 +502,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);
- 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,
@@ -447,7 +526,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;
@@ -484,7 +563,122 @@ int __devinit init_port_console(struct port *port)
return 0;
}
-static int __devinit add_port(struct ports_device *portdev)
+/* 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);
+ 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;
+ 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 add_port(struct ports_device *portdev, u32 id)
{
struct port *port;
struct port_buffer *inbuf;
@@ -497,11 +691,13 @@ 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);
@@ -519,9 +715,25 @@ static int __devinit add_port(struct ports_device *portdev)
/* Register the input buffer the first time. */
add_inbuf(port->in_vq, inbuf);
- err = init_port_console(port);
- if (err)
- goto free_outbuf;
+ /*
+ * 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_outbuf;
+ }
+
+ spin_lock_irq(&portdev->ports_lock);
+ list_add_tail(&port->list, &port->portdev->ports);
+ spin_unlock_irq(&portdev->ports_lock);
+
+ /*
+ * 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;
@@ -540,12 +752,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) {
@@ -575,11 +786,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,
@@ -587,9 +819,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);
@@ -613,11 +856,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) {
@@ -629,16 +878,59 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
portdev->vdev = vdev;
vdev->priv = portdev;
+ multiport = false;
+ portdev->config.nr_ports = 1;
+ portdev->config.max_nr_ports = 1;
+ 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;
+ }
+ }
+
+ /* 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;
@@ -661,6 +953,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 9e0da40..cada769 100644
--- a/include/linux/virtio_console.h
+++ b/include/linux/virtio_console.h
@@ -6,18 +6,39 @@
/*
* 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
#ifdef __KERNEL__
int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int));
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 21/32] virtio: console: Prepare for writing to / reading from userspace buffers
2010-01-18 13:45 ` [PATCH 20/32] virtio: console: Add a new MULTIPORT feature, support for generic ports Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 22/32] virtio: console: Associate each port with a char device Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
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 | 45 ++++++++++++++++++++++++++--------------
1 files changed, 29 insertions(+), 16 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index c0bc146..35bdd41 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -332,7 +332,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 virtqueue *out_vq;
@@ -357,15 +358,19 @@ static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count)
if (in_count > buf->size)
in_count = 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_count);
-
- buf->len = in_count;
+ if (from_user) {
+ ret = copy_from_user(buf->buf, in_buf, in_count);
+ } else {
+ /*
+ * Since we're not sure when the host will actually
+ * consume the data and tell us about it, we have to
+ * copy the data here in case the caller frees the
+ * in_buf.
+ */
+ memcpy(buf->buf, in_buf, in_count);
+ ret = 0; /* Emulate copy_from_user behaviour */
+ }
+ buf->len = in_count - ret;
sg_init_one(sg, buf->buf, buf->len);
ret = out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, buf);
@@ -384,9 +389,11 @@ 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 buffer 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 ret;
unsigned long flags;
if (!out_count || !port_has_data(port))
@@ -396,10 +403,16 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count)
if (out_count > buf->len - buf->offset)
out_count = buf->len - buf->offset;
- memcpy(out_buf, buf->buf + buf->offset, out_count);
+ if (to_user) {
+ ret = copy_to_user(out_buf, buf->buf + buf->offset, out_count);
+ } else {
+ memcpy(out_buf, buf->buf + buf->offset, out_count);
+ ret = 0; /* Emulate copy_to_user behaviour */
+ }
/* Return the number of bytes actually copied */
- buf->offset += out_count;
+ ret = out_count - ret;
+ buf->offset += ret;
if (buf->offset == buf->len) {
/*
@@ -414,7 +427,7 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count)
spin_unlock_irqrestore(&port->inbuf_lock, flags);
}
- return out_count;
+ return ret;
}
/*
@@ -436,7 +449,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);
}
/*
@@ -457,7 +470,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 [flat|nested] 43+ messages in thread
* [PATCH 22/32] virtio: console: Associate each port with a char device
2010-01-18 13:45 ` [PATCH 21/32] virtio: console: Prepare for writing to / reading from userspace buffers Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 23/32] virtio: console: Add file operations to ports for open/read/write/poll Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
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 | 77 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 82 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 35bdd41..49bf5af 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;
};
@@ -423,7 +441,7 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count,
port->inbuf = NULL;
if (add_inbuf(port->in_vq, buf) < 0)
- dev_warn(&port->portdev->vdev->dev, "failed add_buf\n");
+ dev_warn(port->dev, "failed add_buf\n");
spin_unlock_irqrestore(&port->inbuf_lock, flags);
}
@@ -695,6 +713,7 @@ static int add_port(struct ports_device *portdev, u32 id)
{
struct port *port;
struct port_buffer *inbuf;
+ dev_t devt;
int err;
port = kmalloc(sizeof(*port), GFP_KERNEL);
@@ -712,12 +731,32 @@ 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);
inbuf = alloc_buf(PAGE_SIZE);
if (!inbuf) {
err = -ENOMEM;
- goto free_port;
+ goto free_device;
}
port->outbuf = alloc_buf(PAGE_SIZE);
if (!port->outbuf) {
@@ -754,6 +793,10 @@ free_outbuf:
free_buf(port->outbuf);
free_inbuf:
free_buf(inbuf);
+free_device:
+ device_destroy(pdrvdata.class, port->dev->devt);
+free_cdev:
+ cdev_del(&port->cdev);
free_port:
kfree(port);
fail:
@@ -866,6 +909,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.
@@ -891,6 +938,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;
portdev->config.nr_ports = 1;
portdev->config.max_nr_ports = 1;
@@ -923,7 +984,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);
@@ -953,6 +1014,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:
@@ -981,6 +1044,14 @@ 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 [flat|nested] 43+ messages in thread
* [PATCH 23/32] virtio: console: Add file operations to ports for open/read/write/poll
2010-01-18 13:45 ` [PATCH 22/32] virtio: console: Associate each port with a char device Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 24/32] virtio: console: Ensure only one process can have a port open at a time Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
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 | 147 +++++++++++++++++++++++++++++++++++++++-
include/linux/virtio_console.h | 1 +
2 files changed, 147 insertions(+), 1 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 49bf5af..30cdaf3 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. */
@@ -448,6 +458,129 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count,
return ret;
}
+/* 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.
*
@@ -591,6 +724,9 @@ int init_port_console(struct port *port)
list_add_tail(&port->cons.list, &pdrvdata.consoles);
spin_unlock_irq(&pdrvdata_lock);
+ /* Notify host of port being opened */
+ send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
+
return 0;
}
@@ -630,6 +766,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;
}
}
@@ -676,6 +816,8 @@ static void in_intr(struct virtqueue *vq)
spin_unlock_irqrestore(&port->inbuf_lock, flags);
+ wake_up_interruptible(&port->waitqueue);
+
if (is_console_port(port) && hvc_poll(port->cons.hvc))
hvc_kick();
}
@@ -728,10 +870,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);
@@ -752,6 +896,7 @@ static int add_port(struct ports_device *portdev, u32 id)
}
spin_lock_init(&port->inbuf_lock);
+ init_waitqueue_head(&port->waitqueue);
inbuf = alloc_buf(PAGE_SIZE);
if (!inbuf) {
diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h
index cada769..6a76a44 100644
--- a/include/linux/virtio_console.h
+++ b/include/linux/virtio_console.h
@@ -39,6 +39,7 @@ 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
#ifdef __KERNEL__
int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int));
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 24/32] virtio: console: Ensure only one process can have a port open at a time
2010-01-18 13:45 ` [PATCH 23/32] virtio: console: Add file operations to ports for open/read/write/poll Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 25/32] virtio: console: Register with sysfs and create a 'name' attribute for ports Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
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 | 18 +++++++++++++++++-
1 files changed, 17 insertions(+), 1 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 30cdaf3..49cd185 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. */
@@ -542,6 +545,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;
}
@@ -560,6 +565,16 @@ 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);
@@ -723,6 +738,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;
/* Notify host of port being opened */
send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
@@ -870,7 +886,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 [flat|nested] 43+ messages in thread
* [PATCH 25/32] virtio: console: Register with sysfs and create a 'name' attribute for ports
2010-01-18 13:45 ` [PATCH 24/32] virtio: console: Ensure only one process can have a port open at a time Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 26/32] virtio: console: Add throttling support to prevent flooding ports Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
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 49cd185..e76eacf 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;
@@ -746,12 +749,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);
@@ -786,6 +813,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;
}
}
@@ -883,6 +939,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 6a76a44..13e56c8 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
#ifdef __KERNEL__
int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int));
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 26/32] virtio: console: Add throttling support to prevent flooding ports
2010-01-18 13:45 ` [PATCH 25/32] virtio: console: Register with sysfs and create a 'name' attribute for ports Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 27/32] virtio: console: Remove cached data on port close Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
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 e76eacf..7154224 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. */
@@ -517,6 +520,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);
}
@@ -531,7 +537,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;
@@ -842,6 +848,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;
}
}
@@ -944,6 +962,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 13e56c8..c096eb2 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
#ifdef __KERNEL__
int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int));
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 27/32] virtio: console: Remove cached data on port close
2010-01-18 13:45 ` [PATCH 26/32] virtio: console: Add throttling support to prevent flooding ports Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 28/32] virtio: console: Handle port hot-plug Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
Remove any data that we might have in a port's inbuf when closing a port
or when any data is received when a port is closed.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 files changed, 40 insertions(+), 0 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 7154224..0be5562 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -325,6 +325,31 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
return ret;
}
+/* Discard any unread data this port has. Callers lockers. */
+static void discard_port_data(struct port *port)
+{
+ struct port_buffer *buf;
+ struct virtqueue *vq;
+ unsigned int len;
+
+ vq = port->in_vq;
+ if (port->inbuf)
+ buf = port->inbuf;
+ else
+ buf = vq->vq_ops->get_buf(vq, &len);
+
+ if (!buf)
+ return;
+
+ if (add_inbuf(vq, buf) < 0) {
+ buf->len = buf->offset = 0;
+ dev_warn(port->dev, "Error adding buffer back to vq\n");
+ return;
+ }
+
+ port->inbuf = NULL;
+}
+
static bool port_has_data(struct port *port)
{
unsigned long flags;
@@ -554,8 +579,13 @@ 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;
+ discard_port_data(port);
+
+ spin_unlock_irq(&port->inbuf_lock);
+
return 0;
}
@@ -904,6 +934,16 @@ static void in_intr(struct virtqueue *vq)
spin_lock_irqsave(&port->inbuf_lock, flags);
port->inbuf = get_inbuf(port);
+ /*
+ * Don't queue up data when 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
+ * (shouldn't) send data till the guest is connected.
+ */
+ if (!port->guest_connected)
+ discard_port_data(port);
+
spin_unlock_irqrestore(&port->inbuf_lock, flags);
wake_up_interruptible(&port->waitqueue);
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 28/32] virtio: console: Handle port hot-plug
2010-01-18 13:45 ` [PATCH 27/32] virtio: console: Remove cached data on port close Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 29/32] virtio: Add ability to detach unused buffers from vrings Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
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 0be5562..36cd7d4 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;
@@ -695,11 +696,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)
{
@@ -960,6 +956,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;
@@ -1080,6 +1094,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;
@@ -1270,6 +1335,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) {
@@ -1316,7 +1382,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 [flat|nested] 43+ messages in thread
* [PATCH 29/32] virtio: Add ability to detach unused buffers from vrings
2010-01-18 13:45 ` [PATCH 28/32] virtio: console: Handle port hot-plug Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 30/32] virtio: console: Add ability to hot-unplug ports Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, Shirley Ma, Shirley Ma, virtualization
From: Shirley Ma <mashirle@us.ibm.com>
There's currently no way for a virtio driver to ask for unused
buffers, so it has to keep a list itself to reclaim them at shutdown.
This is redundant, since virtio_ring stores that information. So
add a new hook to do this.
Signed-off-by: Shirley Ma <xma@us.ibm.com>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/virtio/virtio_ring.c | 25 +++++++++++++++++++++++++
include/linux/virtio.h | 4 ++++
2 files changed, 29 insertions(+), 0 deletions(-)
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index fbd2ecd..71929ee 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -334,6 +334,30 @@ static bool vring_enable_cb(struct virtqueue *_vq)
return true;
}
+static void *vring_detach_unused_buf(struct virtqueue *_vq)
+{
+ struct vring_virtqueue *vq = to_vvq(_vq);
+ unsigned int i;
+ void *buf;
+
+ START_USE(vq);
+
+ for (i = 0; i < vq->vring.num; i++) {
+ if (!vq->data[i])
+ continue;
+ /* detach_buf clears data, so grab it now. */
+ buf = vq->data[i];
+ detach_buf(vq, i);
+ END_USE(vq);
+ return buf;
+ }
+ /* That should have freed everything. */
+ BUG_ON(vq->num_free != vq->vring.num);
+
+ END_USE(vq);
+ return NULL;
+}
+
irqreturn_t vring_interrupt(int irq, void *_vq)
{
struct vring_virtqueue *vq = to_vvq(_vq);
@@ -360,6 +384,7 @@ static struct virtqueue_ops vring_vq_ops = {
.kick = vring_kick,
.disable_cb = vring_disable_cb,
.enable_cb = vring_enable_cb,
+ .detach_unused_buf = vring_detach_unused_buf,
};
struct virtqueue *vring_new_virtqueue(unsigned int num,
diff --git a/include/linux/virtio.h b/include/linux/virtio.h
index 057a2e0..f508c65 100644
--- a/include/linux/virtio.h
+++ b/include/linux/virtio.h
@@ -51,6 +51,9 @@ struct virtqueue {
* This re-enables callbacks; it returns "false" if there are pending
* buffers in the queue, to detect a possible race between the driver
* checking for more work, and enabling callbacks.
+ * @detach_unused_buf: detach first unused buffer
+ * vq: the struct virtqueue we're talking about.
+ * Returns NULL or the "data" token handed to add_buf
*
* Locking rules are straightforward: the driver is responsible for
* locking. No two operations may be invoked simultaneously, with the exception
@@ -71,6 +74,7 @@ struct virtqueue_ops {
void (*disable_cb)(struct virtqueue *vq);
bool (*enable_cb)(struct virtqueue *vq);
+ void *(*detach_unused_buf)(struct virtqueue *vq);
};
/**
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 30/32] virtio: console: Add ability to hot-unplug ports
2010-01-18 13:45 ` [PATCH 29/32] virtio: Add ability to detach unused buffers from vrings Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 31/32] virtio: console: Add debugfs files for each port to expose debug info Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
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 | 66 ++++++++++++++++++++++++++++++++++++++-
include/linux/virtio_console.h | 1 +
2 files changed, 65 insertions(+), 2 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 36cd7d4..4a4eaba 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -803,6 +803,37 @@ static struct attribute_group port_attribute_group = {
.attrs = port_sysfs_entries,
};
+/* Remove all port-specific data. */
+static int remove_port(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);
+
+ while (port->in_vq->vq_ops->detach_unused_buf(port->in_vq))
+ ;
+
+ sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
+ device_destroy(pdrvdata.class, port->dev->devt);
+ cdev_del(&port->cdev);
+
+ discard_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)
@@ -886,6 +917,32 @@ static void handle_control_message(struct ports_device *portdev,
*/
port->host_throttled = 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(port);
+ break;
}
}
@@ -1118,12 +1175,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 c096eb2..96c5832 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_PORT_REMOVE 6
#ifdef __KERNEL__
int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int));
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 31/32] virtio: console: Add debugfs files for each port to expose debug info
2010-01-18 13:45 ` [PATCH 30/32] virtio: console: Add ability to hot-unplug ports Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-18 13:45 ` [PATCH 32/32] virtio: console: show error message if hvc_alloc fails for console ports Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
This is helpful in examining ports' state.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 73 +++++++++++++++++++++++++++++++++++++++++
1 files changed, 73 insertions(+), 0 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 4a4eaba..6580d3b 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
@@ -803,6 +810,51 @@ 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,
+ "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 all port-specific data. */
static int remove_port(struct port *port)
{
@@ -830,6 +882,8 @@ static int remove_port(struct port *port)
free_buf(port->outbuf);
kfree(port->name);
+ debugfs_remove(port->debugfs_file);
+
kfree(port);
return 0;
}
@@ -1054,6 +1108,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;
struct port_buffer *inbuf;
dev_t devt;
@@ -1135,6 +1190,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_outbuf:
@@ -1457,6 +1524,12 @@ static int __init init(void)
pr_err("Error %d creating virtio-ports class\n", err);
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 [flat|nested] 43+ messages in thread
* [PATCH 32/32] virtio: console: show error message if hvc_alloc fails for console ports
2010-01-18 13:45 ` [PATCH 31/32] virtio: console: Add debugfs files for each port to expose debug info Amit Shah
@ 2010-01-18 13:45 ` Amit Shah
2010-01-19 1:22 ` Rusty Russell
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-18 13:45 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 6580d3b..166c5b2 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -773,6 +773,8 @@ int init_port_console(struct port *port)
port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE);
if (IS_ERR(port->cons.hvc)) {
ret = PTR_ERR(port->cons.hvc);
+ dev_err(port->dev,
+ "error %d allocating hvc for port\n", ret);
port->cons.hvc = NULL;
return ret;
}
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* Re: [PATCH 32/32] virtio: console: show error message if hvc_alloc fails for console ports
2010-01-18 13:45 ` [PATCH 32/32] virtio: console: show error message if hvc_alloc fails for console ports Amit Shah
@ 2010-01-19 1:22 ` Rusty Russell
2010-01-19 4:57 ` Amit Shah
2010-01-19 17:06 ` Amit Shah
0 siblings, 2 replies; 43+ messages in thread
From: Rusty Russell @ 2010-01-19 1:22 UTC (permalink / raw)
To: Amit Shah; +Cc: virtualization
On Tue, 19 Jan 2010 12:15:26 am Amit Shah wrote:
> Signed-off-by: Amit Shah <amit.shah@redhat.com>
These all look great! I'm not entirely convinced about the host throttling,
but it's harmless to have.
Applied, thanks!
Rusty.
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH 32/32] virtio: console: show error message if hvc_alloc fails for console ports
2010-01-19 1:22 ` Rusty Russell
@ 2010-01-19 4:57 ` Amit Shah
2010-01-20 8:09 ` Rusty Russell
2010-01-19 17:06 ` Amit Shah
1 sibling, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-19 4:57 UTC (permalink / raw)
To: Rusty Russell; +Cc: virtualization
On (Tue) Jan 19 2010 [11:52:50], Rusty Russell wrote:
> On Tue, 19 Jan 2010 12:15:26 am Amit Shah wrote:
> > Signed-off-by: Amit Shah <amit.shah@redhat.com>
>
> These all look great! I'm not entirely convinced about the host throttling,
> but it's harmless to have.
>
> Applied, thanks!
> Rusty.
Thanks, Rusty!
Can you point me to the repo that has the patches?
Amit
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH 32/32] virtio: console: show error message if hvc_alloc fails for console ports
2010-01-19 1:22 ` Rusty Russell
2010-01-19 4:57 ` Amit Shah
@ 2010-01-19 17:06 ` Amit Shah
2010-01-20 8:20 ` Rusty Russell
1 sibling, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-19 17:06 UTC (permalink / raw)
To: Rusty Russell; +Cc: virtualization
On (Tue) Jan 19 2010 [11:52:50], Rusty Russell wrote:
> On Tue, 19 Jan 2010 12:15:26 am Amit Shah wrote:
> > Signed-off-by: Amit Shah <amit.shah@redhat.com>
>
> These all look great! I'm not entirely convinced about the host throttling,
> but it's harmless to have.
I'll rip out the throttling feature -- and ensure write(2) returns only
when the host acks the data.
The changes will touch 3 patches in the series -- do you want me to
resend those 3 patches, or new patches on top of my series, or send a new
series altogether?
Amit
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH 32/32] virtio: console: show error message if hvc_alloc fails for console ports
2010-01-19 4:57 ` Amit Shah
@ 2010-01-20 8:09 ` Rusty Russell
0 siblings, 0 replies; 43+ messages in thread
From: Rusty Russell @ 2010-01-20 8:09 UTC (permalink / raw)
To: Amit Shah; +Cc: virtualization
On Tue, 19 Jan 2010 03:27:30 pm Amit Shah wrote:
> On (Tue) Jan 19 2010 [11:52:50], Rusty Russell wrote:
> > On Tue, 19 Jan 2010 12:15:26 am Amit Shah wrote:
> > > Signed-off-by: Amit Shah <amit.shah@redhat.com>
> >
> > These all look great! I'm not entirely convinced about the host throttling,
> > but it's harmless to have.
> >
> > Applied, thanks!
> > Rusty.
>
> Thanks, Rusty!
>
> Can you point me to the repo that has the patches?
They will be in linux-next tomorrow from my tree:
http://ozlabs.org/~rusty/kernel/rr-latest/
Cheers,
Rusty.
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH 32/32] virtio: console: show error message if hvc_alloc fails for console ports
2010-01-19 17:06 ` Amit Shah
@ 2010-01-20 8:20 ` Rusty Russell
2010-01-20 8:51 ` Remove host throttling Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Rusty Russell @ 2010-01-20 8:20 UTC (permalink / raw)
To: Amit Shah; +Cc: virtualization
On Wed, 20 Jan 2010 03:36:27 am Amit Shah wrote:
> On (Tue) Jan 19 2010 [11:52:50], Rusty Russell wrote:
> > On Tue, 19 Jan 2010 12:15:26 am Amit Shah wrote:
> > > Signed-off-by: Amit Shah <amit.shah@redhat.com>
> >
> > These all look great! I'm not entirely convinced about the host throttling,
> > but it's harmless to have.
>
> I'll rip out the throttling feature -- and ensure write(2) returns only
> when the host acks the data.
>
> The changes will touch 3 patches in the series -- do you want me to
> resend those 3 patches, or new patches on top of my series, or send a new
> series altogether?
Replacements for those will be fine.
Thanks!
Rusty.
^ permalink raw reply [flat|nested] 43+ messages in thread
* Remove host throttling
2010-01-20 8:20 ` Rusty Russell
@ 2010-01-20 8:51 ` Amit Shah
2010-01-20 8:51 ` [PATCH 19/31] virtio: console: Introduce a send_buf function for a common path for sending data to host Amit Shah
2010-01-25 10:32 ` Remove host throttling Rusty Russell
0 siblings, 2 replies; 43+ messages in thread
From: Amit Shah @ 2010-01-20 8:51 UTC (permalink / raw)
To: rusty; +Cc: virtualization
Hey Rusty,
These are the patches to be replaced -- patches 19, 30, 31 from the
previous series. Patch 26 from the earlier series is to be dropped.
The diff to the previous version is:
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index e936026..4218cb9 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -195,9 +195,6 @@ 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. */
@@ -414,17 +411,6 @@ static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count,
out_vq = port->out_vq;
buf = port->outbuf;
- if (buf->len) {
- /*
- * Nonzero buf->len means we had queued a buffer
- * earlier to the Host to be consumed. Get the buffer
- * back for this write request; wait while the Host
- * consumes it.
- */
- while (!out_vq->vq_ops->get_buf(out_vq, &tmplen))
- cpu_relax();
- }
-
if (in_count > buf->size)
in_count = buf->size;
@@ -448,9 +434,21 @@ static ssize_t send_buf(struct port *port, const char *in_buf, size_t in_count,
/* Tell Host to go! */
out_vq->vq_ops->kick(out_vq);
- if (ret < 0)
+ if (ret < 0) {
buf->len = 0;
+ goto fail;
+ }
+ /*
+ * Wait till the host acknowledges it pushed out the data we
+ * sent. Also ensure we return to userspace the number of
+ * bytes that were successfully consumed by the host.
+ */
+ while (!out_vq->vq_ops->get_buf(out_vq, &tmplen))
+ cpu_relax();
+
+ buf->len = tmplen;
+fail:
/* We're expected to return the amount of data we wrote */
return buf->len;
}
@@ -553,9 +551,6 @@ 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);
}
@@ -570,7 +565,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 && !port->host_throttled)
+ if (port->host_connected)
ret |= POLLOUT;
if (!port->host_connected)
ret |= POLLHUP;
@@ -839,8 +834,6 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf,
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,
@@ -961,18 +954,6 @@ 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;
case VIRTIO_CONSOLE_PORT_REMOVE:
/*
* Hot unplug the port. We don't decrement nr_ports
@@ -1130,7 +1111,6 @@ 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 96c5832..dd08675 100644
--- a/include/linux/virtio_console.h
+++ b/include/linux/virtio_console.h
@@ -41,8 +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
-#define VIRTIO_CONSOLE_PORT_REMOVE 6
+#define VIRTIO_CONSOLE_PORT_REMOVE 5
#ifdef __KERNEL__
int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int));
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 19/31] virtio: console: Introduce a send_buf function for a common path for sending data to host
2010-01-20 8:51 ` Remove host throttling Amit Shah
@ 2010-01-20 8:51 ` Amit Shah
2010-01-20 8:51 ` [PATCH 29/31] virtio: console: Add ability to hot-unplug ports Amit Shah
2010-01-25 10:32 ` Remove host throttling Rusty Russell
1 sibling, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-20 8:51 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
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 | 79 +++++++++++++++++++++++++++++++---------
1 files changed, 61 insertions(+), 18 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 5096d92..9400b96 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;
@@ -233,6 +236,55 @@ static bool port_has_data(struct port *port)
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;
+ ssize_t ret;
+ unsigned int tmplen;
+
+ out_vq = port->out_vq;
+ buf = port->outbuf;
+
+ if (in_count > buf->size)
+ in_count = 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_count);
+
+ buf->len = in_count;
+
+ 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) {
+ buf->len = 0;
+ goto fail;
+ }
+
+ /*
+ * Wait till the host acknowledges it pushed out the data we
+ * sent. Also ensure we return to userspace the number of
+ * bytes that were successfully consumed by the host.
+ */
+ while (!out_vq->vq_ops->get_buf(out_vq, &tmplen))
+ cpu_relax();
+
+ buf->len = tmplen;
+fail:
+ /* We're expected to return the amount of data we wrote */
+ return buf->len;
+}
+
/*
* Give out the data that's requested from the buffer that we have
* queued up.
@@ -280,10 +332,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)
@@ -292,20 +341,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);
}
/*
@@ -475,16 +511,23 @@ static int __devinit add_port(struct ports_device *portdev)
err = -ENOMEM;
goto free_port;
}
+ port->outbuf = alloc_buf(PAGE_SIZE);
+ if (!port->outbuf) {
+ err = -ENOMEM;
+ goto free_inbuf;
+ }
/* Register the input buffer the first time. */
add_inbuf(port->in_vq, inbuf);
err = init_port_console(port);
if (err)
- goto free_inbuf;
+ goto free_outbuf;
return 0;
+free_outbuf:
+ free_buf(port->outbuf);
free_inbuf:
free_buf(inbuf);
free_port:
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 29/31] virtio: console: Add ability to hot-unplug ports
2010-01-20 8:51 ` [PATCH 19/31] virtio: console: Introduce a send_buf function for a common path for sending data to host Amit Shah
@ 2010-01-20 8:51 ` Amit Shah
2010-01-20 8:51 ` [PATCH 30/31] virtio: console: Add debugfs files for each port to expose debug info Amit Shah
0 siblings, 1 reply; 43+ messages in thread
From: Amit Shah @ 2010-01-20 8:51 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
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 | 66 ++++++++++++++++++++++++++++++++++++++-
include/linux/virtio_console.h | 1 +
2 files changed, 65 insertions(+), 2 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 5e86b21..1b5fa5c 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -798,6 +798,37 @@ static struct attribute_group port_attribute_group = {
.attrs = port_sysfs_entries,
};
+/* Remove all port-specific data. */
+static int remove_port(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);
+
+ while (port->in_vq->vq_ops->detach_unused_buf(port->in_vq))
+ ;
+
+ sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
+ device_destroy(pdrvdata.class, port->dev->devt);
+ cdev_del(&port->cdev);
+
+ discard_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)
@@ -869,6 +900,32 @@ static void handle_control_message(struct ports_device *portdev,
err);
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(port);
+ break;
}
}
@@ -1100,12 +1157,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 13e56c8..dd08675 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_PORT_REMOVE 5
#ifdef __KERNEL__
int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int));
--
1.6.2.5
^ permalink raw reply related [flat|nested] 43+ messages in thread
* [PATCH 30/31] virtio: console: Add debugfs files for each port to expose debug info
2010-01-20 8:51 ` [PATCH 29/31] virtio: console: Add ability to hot-unplug ports Amit Shah
@ 2010-01-20 8:51 ` Amit Shah
0 siblings, 0 replies; 43+ messages in thread
From: Amit Shah @ 2010-01-20 8:51 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
This is helpful in examining ports' state.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 71 +++++++++++++++++++++++++++++++++++++++++
1 files changed, 71 insertions(+), 0 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 1b5fa5c..f337f9f 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
@@ -798,6 +805,49 @@ 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,
+ "host_connected: %d\n", port->host_connected);
+ 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 all port-specific data. */
static int remove_port(struct port *port)
{
@@ -825,6 +875,8 @@ static int remove_port(struct port *port)
free_buf(port->outbuf);
kfree(port->name);
+ debugfs_remove(port->debugfs_file);
+
kfree(port);
return 0;
}
@@ -1037,6 +1089,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;
struct port_buffer *inbuf;
dev_t devt;
@@ -1117,6 +1170,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_outbuf:
@@ -1439,6 +1504,12 @@ static int __init init(void)
pr_err("Error %d creating virtio-ports class\n", err);
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 [flat|nested] 43+ messages in thread
* Re: Remove host throttling
2010-01-20 8:51 ` Remove host throttling Amit Shah
2010-01-20 8:51 ` [PATCH 19/31] virtio: console: Introduce a send_buf function for a common path for sending data to host Amit Shah
@ 2010-01-25 10:32 ` Rusty Russell
1 sibling, 0 replies; 43+ messages in thread
From: Rusty Russell @ 2010-01-25 10:32 UTC (permalink / raw)
To: Amit Shah; +Cc: virtualization
On Wed, 20 Jan 2010 07:21:02 pm Amit Shah wrote:
>
> Hey Rusty,
>
> These are the patches to be replaced -- patches 19, 30, 31 from the
> previous series. Patch 26 from the earlier series is to be dropped.
Thanks, applied!
Rusty.
^ permalink raw reply [flat|nested] 43+ messages in thread
end of thread, other threads:[~2010-01-25 10:32 UTC | newest]
Thread overview: 43+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-01-18 13:44 [PATCH 00/32] virtio: console: Fixes, multiple devices and generic ports Amit Shah
2010-01-18 13:44 ` [PATCH 01/32] virtio: console: comment cleanup Amit Shah
2010-01-18 13:44 ` [PATCH 02/32] virtio: console: statically initialize virtio_cons Amit Shah
2010-01-18 13:44 ` [PATCH 03/32] hvc_console: make the ops pointer const Amit Shah
[not found] ` <1263822326-24208-4-git-send-email-amit.shah@redhat.com>
2010-01-18 13:44 ` [PATCH 04/32] hvc_console: Remove __devinit annotation from hvc_alloc Amit Shah
[not found] ` <1263822326-24208-5-git-send-email-amit.shah@redhat.com>
2010-01-18 13:44 ` [PATCH 05/32] virtio: console: We support only one device at a time Amit Shah
2010-01-18 13:45 ` [PATCH 06/32] virtio: console: port encapsulation Amit Shah
2010-01-18 13:45 ` [PATCH 07/32] virtio: console: encapsulate buffer information in a struct Amit Shah
2010-01-18 13:45 ` [PATCH 08/32] virtio: console: ensure add_inbuf can work for multiple ports as well Amit Shah
2010-01-18 13:45 ` [PATCH 09/32] virtio: console: introduce a get_inbuf helper to fetch bufs from in_vq Amit Shah
2010-01-18 13:45 ` [PATCH 10/32] virtio: console: use vdev->priv to avoid accessing global var Amit Shah
2010-01-18 13:45 ` [PATCH 11/32] virtio: console: don't assume a single console port Amit Shah
2010-01-18 13:45 ` [PATCH 12/32] virtio: console: remove global var Amit Shah
2010-01-18 13:45 ` [PATCH 13/32] virtio: console: struct ports for multiple ports per device Amit Shah
2010-01-18 13:45 ` [PATCH 14/32] virtio: console: ensure console size is updated on hvc open Amit Shah
2010-01-18 13:45 ` [PATCH 15/32] virtio: console: Separate out console-specific data into a separate struct Amit Shah
2010-01-18 13:45 ` [PATCH 16/32] virtio: console: Separate out console init into a new function Amit Shah
2010-01-18 13:45 ` [PATCH 17/32] virtio: console: Separate out find_vqs operation into a different function Amit Shah
2010-01-18 13:45 ` [PATCH 18/32] virtio: console: Introduce function to hand off data from host to readers Amit Shah
2010-01-18 13:45 ` [PATCH 19/32] virtio: console: Introduce a send_buf function for a common path for sending data to host Amit Shah
2010-01-18 13:45 ` [PATCH 20/32] virtio: console: Add a new MULTIPORT feature, support for generic ports Amit Shah
2010-01-18 13:45 ` [PATCH 21/32] virtio: console: Prepare for writing to / reading from userspace buffers Amit Shah
2010-01-18 13:45 ` [PATCH 22/32] virtio: console: Associate each port with a char device Amit Shah
2010-01-18 13:45 ` [PATCH 23/32] virtio: console: Add file operations to ports for open/read/write/poll Amit Shah
2010-01-18 13:45 ` [PATCH 24/32] virtio: console: Ensure only one process can have a port open at a time Amit Shah
2010-01-18 13:45 ` [PATCH 25/32] virtio: console: Register with sysfs and create a 'name' attribute for ports Amit Shah
2010-01-18 13:45 ` [PATCH 26/32] virtio: console: Add throttling support to prevent flooding ports Amit Shah
2010-01-18 13:45 ` [PATCH 27/32] virtio: console: Remove cached data on port close Amit Shah
2010-01-18 13:45 ` [PATCH 28/32] virtio: console: Handle port hot-plug Amit Shah
2010-01-18 13:45 ` [PATCH 29/32] virtio: Add ability to detach unused buffers from vrings Amit Shah
2010-01-18 13:45 ` [PATCH 30/32] virtio: console: Add ability to hot-unplug ports Amit Shah
2010-01-18 13:45 ` [PATCH 31/32] virtio: console: Add debugfs files for each port to expose debug info Amit Shah
2010-01-18 13:45 ` [PATCH 32/32] virtio: console: show error message if hvc_alloc fails for console ports Amit Shah
2010-01-19 1:22 ` Rusty Russell
2010-01-19 4:57 ` Amit Shah
2010-01-20 8:09 ` Rusty Russell
2010-01-19 17:06 ` Amit Shah
2010-01-20 8:20 ` Rusty Russell
2010-01-20 8:51 ` Remove host throttling Amit Shah
2010-01-20 8:51 ` [PATCH 19/31] virtio: console: Introduce a send_buf function for a common path for sending data to host Amit Shah
2010-01-20 8:51 ` [PATCH 29/31] virtio: console: Add ability to hot-unplug ports Amit Shah
2010-01-20 8:51 ` [PATCH 30/31] virtio: console: Add debugfs files for each port to expose debug info Amit Shah
2010-01-25 10:32 ` Remove host throttling Rusty Russell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).