* [PATCH 18/31] virtio: console: Buffer data that comes from the host
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-18-git-send-email-amit.shah@redhat.com>
The console could be flooded with data from the host; handle this
situation by buffering the data.
We use the virtio queue ring to maintain our buffer. This buffer length
is set by the host while initializing the queue.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
drivers/char/virtio_console.c | 172 +++++++++++++++++++++++++++++++----------
1 files changed, 131 insertions(+), 41 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index e4b6a37..2ab15a5 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -100,6 +100,13 @@ struct port {
/* The current buffer from which data has to be fed to readers */
struct port_buffer *inbuf;
+ /*
+ * To protect the operations on the in_vq associated with this
+ * port. Has to be a spinlock because it can be called from
+ * interrupt context (get_char()).
+ */
+ spinlock_t inbuf_lock;
+
/* The IO vqs for this port */
struct virtqueue *in_vq, *out_vq;
@@ -132,6 +139,25 @@ out:
return port;
}
+static struct port *find_port_by_vq(struct ports_device *portdev,
+ struct virtqueue *vq)
+{
+ struct port *port;
+ struct console *cons;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdrvdata_lock, flags);
+ list_for_each_entry(cons, &pdrvdata.consoles, list) {
+ port = container_of(cons, struct port, cons);
+ if (port->in_vq == vq || port->out_vq == vq)
+ goto out;
+ }
+ port = NULL;
+out:
+ spin_unlock_irqrestore(&pdrvdata_lock, flags);
+ return port;
+}
+
static void free_buf(struct port_buffer *buf)
{
kfree(buf->buf);
@@ -181,15 +207,75 @@ static void *get_inbuf(struct port *port)
*
* Callers should take appropriate locks.
*/
-static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
+static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
{
struct scatterlist sg[1];
+ int ret;
sg_init_one(sg, buf->buf, buf->size);
- if (vq->vq_ops->add_buf(vq, sg, 0, 1, buf) < 0)
- BUG();
+ ret = vq->vq_ops->add_buf(vq, sg, 0, 1, buf);
vq->vq_ops->kick(vq);
+ return ret;
+}
+
+static bool port_has_data(struct port *port)
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&port->inbuf_lock, flags);
+ if (port->inbuf) {
+ ret = true;
+ goto out;
+ }
+ port->inbuf = get_inbuf(port);
+ if (port->inbuf) {
+ ret = true;
+ goto out;
+ }
+ ret = false;
+out:
+ spin_unlock_irqrestore(&port->inbuf_lock, flags);
+ return ret;
+}
+
+/*
+ * Give out the data that's requested from the buffers that we have
+ * queued up.
+ */
+static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count)
+{
+ struct port_buffer *buf;
+ ssize_t out_offset, ret;
+ unsigned long flags;
+
+ out_offset = 0;
+ while (out_count && port_has_data(port)) {
+ size_t copy_size;
+
+ buf = port->inbuf;
+ copy_size = out_count;
+ if (copy_size > buf->len - buf->offset)
+ copy_size = buf->len - buf->offset;
+
+ memcpy(out_buf + out_offset, buf->buf + buf->offset, copy_size);
+
+ ret = copy_size;
+ buf->offset += ret;
+ out_offset += ret;
+ out_count -= ret;
+
+ if (buf->offset == buf->len) {
+ spin_lock_irqsave(&port->inbuf_lock, flags);
+ port->inbuf = NULL;
+
+ if (add_inbuf(port->in_vq, buf) < 0)
+ free_buf(buf);
+ spin_unlock_irqrestore(&port->inbuf_lock, flags);
+ }
+ }
+ return out_offset;
}
/*
@@ -234,9 +320,8 @@ static int put_chars(u32 vtermno, const char *buf, int count)
* get_chars() is the callback from the hvc_console infrastructure
* when an interrupt is received.
*
- * Most of the code deals with the fact that the hvc_console()
- * infrastructure only asks us for 16 bytes at a time. We keep
- * in_offset and in_used fields for partially-filled buffers.
+ * We call out to fill_readbuf that gets us the required data from the
+ * buffers that are queued up.
*/
static int get_chars(u32 vtermno, char *buf, int count)
{
@@ -249,25 +334,7 @@ static int get_chars(u32 vtermno, char *buf, int count)
/* If we don't have an input queue yet, we can't get input. */
BUG_ON(!port->in_vq);
- /* No more in buffer? See if they've (re)used it. */
- if (port->inbuf->offset == port->inbuf->len) {
- if (!get_inbuf(port))
- return 0;
- }
-
- /* You want more than we have to give? Well, try wanting less! */
- if (port->inbuf->offset + count > port->inbuf->len)
- count = port->inbuf->len - port->inbuf->offset;
-
- /* Copy across to their buffer and increment offset. */
- memcpy(buf, port->inbuf->buf + port->inbuf->offset, count);
- port->inbuf->offset += count;
-
- /* Finished? Re-register buffer so Host will use it again. */
- if (port->inbuf->offset == port->inbuf->len)
- add_inbuf(port->in_vq, port->inbuf);
-
- return count;
+ return fill_readbuf(port, buf, count);
}
static void resize_console(struct port *port)
@@ -314,13 +381,19 @@ static void notifier_del_vio(struct hvc_struct *hp, int data)
static void hvc_handle_input(struct virtqueue *vq)
{
- struct console *cons;
- bool activity = false;
+ struct port *port;
+ unsigned long flags;
- list_for_each_entry(cons, &pdrvdata.consoles, list)
- activity |= hvc_poll(cons->hvc);
+ port = find_port_by_vq(vq->vdev->priv, vq);
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&port->inbuf_lock, flags);
+ if (!port->inbuf)
+ port->inbuf = get_inbuf(port);
+ spin_unlock_irqrestore(&port->inbuf_lock, flags);
- if (activity)
+ if (hvc_poll(port->cons.hvc))
hvc_kick();
}
@@ -385,6 +458,27 @@ int __devinit init_port_console(struct port *port)
return 0;
}
+static void fill_queue(struct virtqueue *vq, spinlock_t *lock)
+{
+ struct port_buffer *buf;
+ int ret;
+
+ do {
+ buf = alloc_buf(PAGE_SIZE);
+ if (!buf)
+ break;
+
+ spin_lock_irq(lock);
+ ret = add_inbuf(vq, buf);
+ if (ret < 0) {
+ spin_unlock_irq(lock);
+ free_buf(buf);
+ break;
+ }
+ spin_unlock_irq(lock);
+ } while (ret > 0);
+}
+
static int __devinit add_port(struct ports_device *portdev)
{
struct port *port;
@@ -397,26 +491,22 @@ static int __devinit add_port(struct ports_device *portdev)
}
port->portdev = portdev;
+
+ port->inbuf = NULL;
+
port->in_vq = portdev->in_vqs[0];
port->out_vq = portdev->out_vqs[0];
- port->inbuf = alloc_buf(PAGE_SIZE);
- if (!port->inbuf) {
- err = -ENOMEM;
- goto free_port;
- }
+ spin_lock_init(&port->inbuf_lock);
+
+ fill_queue(port->in_vq, &port->inbuf_lock);
err = init_port_console(port);
if (err)
- goto free_inbuf;
-
- /* Register the input buffer the first time. */
- add_inbuf(port->in_vq, port->inbuf);
+ goto free_port;
return 0;
-free_inbuf:
- free_buf(port->inbuf);
free_port:
kfree(port);
fail:
--
1.6.2.5
^ permalink raw reply related
* [PATCH 17/31] virtio: console: Separate out find_vqs operation into a different function
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-17-git-send-email-amit.shah@redhat.com>
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 d90092f..e4b6a37 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
* [PATCH 16/31] virtio: console: Separate out console init into a new function
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-16-git-send-email-amit.shah@redhat.com>
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 d918c67..d90092f 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
* [PATCH 15/31] virtio: console: Separate out console-specific data into a separate struct
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-15-git-send-email-amit.shah@redhat.com>
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 97ba3ed..d918c67 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
* [PATCH 14/31] virtio: console: ensure console size is updated on hvc open
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-14-git-send-email-amit.shah@redhat.com>
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 bef9554..97ba3ed 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
* [PATCH 13/31] virtio: console: struct ports for multiple ports per device.
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-13-git-send-email-amit.shah@redhat.com>
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 5c0ce16..bef9554 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
* [PATCH 12/31] virtio: console: remove global var
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-12-git-send-email-amit.shah@redhat.com>
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 fded596..5c0ce16 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
* [PATCH 11/31] virtio: console: don't assume a single console port.
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-11-git-send-email-amit.shah@redhat.com>
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 | 73 ++++++++++++++++++++++++++++++++++++-----
1 files changed, 64 insertions(+), 9 deletions(-)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index e52ee11..fded596 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,7 @@ 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
* [PATCH 10/31] virtio: console: use vdev->priv to avoid accessing global var.
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-10-git-send-email-amit.shah@redhat.com>
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
* [PATCH 09/31] virtio: console: introduce a get_inbuf helper to fetch bufs from in_vq
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-9-git-send-email-amit.shah@redhat.com>
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
* [PATCH 08/31] virtio: console: ensure add_inbuf can work for multiple ports as well
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-8-git-send-email-amit.shah@redhat.com>
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
* [PATCH 07/31] virtio: console: encapsulate buffer information in a struct
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-7-git-send-email-amit.shah@redhat.com>
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
* [PATCH 06/31] virtio: console: port encapsulation
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-6-git-send-email-amit.shah@redhat.com>
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
* [PATCH 05/31] virtio: console: We support only one device at a time
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-5-git-send-email-amit.shah@redhat.com>
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
* [PATCH 04/31] hvc_console: Remove __devinit annotation from hvc_alloc
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, linuxppc-dev, virtualization
In-Reply-To: <1261492481-19817-4-git-send-email-amit.shah@redhat.com>
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
* [PATCH 03/31] hvc_console: make the ops pointer const.
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, linuxppc-dev, virtualization
In-Reply-To: <1261492481-19817-3-git-send-email-amit.shah@redhat.com>
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
* [PATCH 02/31] virtio: console: statically initialize virtio_cons
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-2-git-send-email-amit.shah@redhat.com>
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
* [PATCH 01/31] virtio: console: comment cleanup
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
In-Reply-To: <1261492481-19817-1-git-send-email-amit.shah@redhat.com>
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
* [PATCH 00/31] virtio: console: Fixes, multiple devices and generic ports
From: Amit Shah @ 2009-12-22 14:34 UTC (permalink / raw)
To: rusty; +Cc: Amit Shah, virtualization
Hey Rusty,
This series adds support for generic ports with each port getting two
vqs: one for input and one for output. The host notifies us via the
config space of the max. number of ports that can be added for a
particular device.
As a result of that change, the buffer management for find_readbufs
and send_bufs is moved to the vqs. Only one outbuf per port is used
so we bide away some time in the send_buf routine if output data spans
multiple pages.
There are a few things which aren't done, they can be built upon this
series once it's accepted. One of them is maintaining a list of the
pages that are fed into the virtqueues. This list will be needed when
those buffers have to be freed.
I've passed each patch through an automated test suite that tests for
all the functionality here (caching, throttling, hotplug, hot-unplug,
console IO) twice: once with a qemu that supports this functionality
and once without, to test for backward compat. It all works fine.
The testsuite tests for:
* the 'name' attribute and the symlinks that get created via udev
scripts in /dev/virtio-ports
* caching
* throttling
* console - with running 'find /' in the guest
* open/close of chardevs
* read/write/poll
* sending a large file (> 100 MB) in either direction and matching
sha1sums.
Please review.
Thanks,
Amit
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: Buffer data that comes from the host
virtio: console: Introduce a send_buf function for a common path for
sending data to host
virtio: console: Introduce a 'header' for each buffer towards
supporting multiport
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: Add option to remove cached buffers 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
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
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 | 1575 ++++++++++++++++++++++++++++++++++++----
include/linux/virtio_console.h | 44 ++-
12 files changed, 1498 insertions(+), 161 deletions(-)
^ permalink raw reply
* [PATCH] vhost: fix high 32 bit in FEATURES ioctls
From: Michael S. Tsirkin @ 2009-12-22 11:39 UTC (permalink / raw)
To: David Stevens; +Cc: kvm, virtualization
From: David Stevens <dlstevens@us.ibm.com>
Subject: vhost: fix high 32 bit in FEATURES ioctls
VHOST_GET_FEATURES fails to initialize high-order 32 bits
in the returned value, and VHOST_SET_FEATURES fails to check them.
This patch fixes it to use 64 bits throughout.
Signed-off-by: David L Stevens <dlstevens@us.ibm.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
Rusty, can you queue this for 2.6.33 as well please?
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 22d5fef..d6db10c 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -563,7 +563,7 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
{
struct vhost_net *n = f->private_data;
void __user *argp = (void __user *)arg;
- u32 __user *featurep = argp;
+ u64 __user *featurep = argp;
struct vhost_vring_file backend;
u64 features;
int r;
@@ -575,10 +575,9 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
return vhost_net_set_backend(n, backend.index, backend.fd);
case VHOST_GET_FEATURES:
features = VHOST_FEATURES;
- return put_user(features, featurep);
+ return copy_to_user(featurep, &features, sizeof features);
case VHOST_SET_FEATURES:
- r = get_user(features, featurep);
- /* No features for now */
+ r = copy_from_user(&features, featurep, sizeof features);
if (r < 0)
return r;
if (features & ~VHOST_FEATURES)
--
MST
^ permalink raw reply related
* Re: [PATCH 0/3] vhost: fix use_mm usage
From: Rusty Russell @ 2009-12-21 11:29 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: Linus Torvalds, linux-kernel, Al Viro, virtualization
In-Reply-To: <20091220171608.GA31713@redhat.com>
On Mon, 21 Dec 2009 03:46:08 am Michael S. Tsirkin wrote:
> This patchset fixes an issue, pointed out by Al Viro, in how vhost net
> calls use_mm, Works fine for me, and it's pretty straightforward. If no
> one sees any issues with it, this will be a good candidate for 2.6.33
> (assuming vhost net itself gets pulled :( ).
No, Linus didn't respond to either pull request. Either he considers it a
new driver and hence not in a hurry to enter merge window, or there's some
other issue.
I've taken these fixes too, meanwhile.
Thanks,
Rusty.
^ permalink raw reply
* [PATCH 3/3] vhost: make default mapping empty by default
From: Michael S. Tsirkin @ 2009-12-20 17:16 UTC (permalink / raw)
To: Rusty Russell, virtualization, linux-kernel; +Cc: Al Viro
In-Reply-To: <cover.1261329297.git.mst@redhat.com>
vhost now validates each region with access_ok in calling process
context before access. Since this fails on a full 64 bit 1:1 mapping
that vhost had by default, we can't support such a mapping: users will
have to set up a table with real addresses that actually matches their
address space.
Make the default mapping empty.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
drivers/vhost/vhost.c | 13 +++----------
1 files changed, 3 insertions(+), 10 deletions(-)
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 33e06bf..2b65d9b 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -170,21 +170,14 @@ long vhost_dev_reset_owner(struct vhost_dev *dev)
{
struct vhost_memory *memory;
- /* Restore memory to default 1:1 mapping. */
- memory = kmalloc(offsetof(struct vhost_memory, regions) +
- 2 * sizeof *memory->regions, GFP_KERNEL);
+ /* Restore memory to default empty mapping. */
+ memory = kmalloc(offsetof(struct vhost_memory, regions), GFP_KERNEL);
if (!memory)
return -ENOMEM;
vhost_dev_cleanup(dev);
- memory->nregions = 2;
- memory->regions[0].guest_phys_addr = 1;
- memory->regions[0].userspace_addr = 1;
- memory->regions[0].memory_size = ~0ULL;
- memory->regions[1].guest_phys_addr = 0;
- memory->regions[1].userspace_addr = 0;
- memory->regions[1].memory_size = 1;
+ memory->nregions = 0;
dev->memory = memory;
return 0;
}
--
1.6.6.rc1.43.gf55cc
^ permalink raw reply related
* [PATCH 2/3] vhost: add access_ok checks
From: Michael S. Tsirkin @ 2009-12-20 17:16 UTC (permalink / raw)
To: Rusty Russell, virtualization, linux-kernel; +Cc: Al Viro
In-Reply-To: <cover.1261329297.git.mst@redhat.com>
On biarch kernels, it is not safe to do copy from/to user, even with use_mm,
unless we checked the address range with access_ok previously. Implement these
checks to enforce safe memory accesses.
Reported-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
drivers/vhost/net.c | 17 ++++++-
drivers/vhost/vhost.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++
drivers/vhost/vhost.h | 2 +
3 files changed, 127 insertions(+), 3 deletions(-)
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 1b509a0..1aacd8c 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -493,6 +493,12 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
}
vq = n->vqs + index;
mutex_lock(&vq->mutex);
+
+ /* Verify that ring has been setup correctly. */
+ if (!vhost_vq_access_ok(vq)) {
+ r = -EFAULT;
+ goto err;
+ }
sock = get_socket(fd);
if (IS_ERR(sock)) {
r = PTR_ERR(sock);
@@ -539,12 +545,17 @@ done:
return err;
}
-static void vhost_net_set_features(struct vhost_net *n, u64 features)
+static int vhost_net_set_features(struct vhost_net *n, u64 features)
{
size_t hdr_size = features & (1 << VHOST_NET_F_VIRTIO_NET_HDR) ?
sizeof(struct virtio_net_hdr) : 0;
int i;
mutex_lock(&n->dev.mutex);
+ if ((features & (1 << VHOST_F_LOG_ALL)) &&
+ !vhost_log_access_ok(&n->dev)) {
+ mutex_unlock(&n->dev.mutex);
+ return -EFAULT;
+ }
n->dev.acked_features = features;
smp_wmb();
for (i = 0; i < VHOST_NET_VQ_MAX; ++i) {
@@ -554,6 +565,7 @@ static void vhost_net_set_features(struct vhost_net *n, u64 features)
}
vhost_net_flush(n);
mutex_unlock(&n->dev.mutex);
+ return 0;
}
static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
@@ -580,8 +592,7 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
return r;
if (features & ~VHOST_FEATURES)
return -EOPNOTSUPP;
- vhost_net_set_features(n, features);
- return 0;
+ return vhost_net_set_features(n, features);
case VHOST_RESET_OWNER:
return vhost_net_reset_owner(n);
default:
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 29f1675..33e06bf 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -224,6 +224,91 @@ void vhost_dev_cleanup(struct vhost_dev *dev)
dev->mm = NULL;
}
+static int log_access_ok(void __user *log_base, u64 addr, unsigned long sz)
+{
+ u64 a = addr / VHOST_PAGE_SIZE / 8;
+ /* Make sure 64 bit math will not overflow. */
+ if (a > ULONG_MAX - (unsigned long)log_base ||
+ a + (unsigned long)log_base > ULONG_MAX)
+ return -EFAULT;
+
+ return access_ok(VERIFY_WRITE, log_base + a,
+ (sz + VHOST_PAGE_SIZE * 8 - 1) / VHOST_PAGE_SIZE / 8);
+}
+
+/* Caller should have vq mutex and device mutex. */
+static int vq_memory_access_ok(struct vhost_virtqueue *vq, struct vhost_memory *mem,
+ int log_all)
+{
+ int i;
+ for (i = 0; i < mem->nregions; ++i) {
+ struct vhost_memory_region *m = mem->regions + i;
+ unsigned long a = m->userspace_addr;
+ if (m->memory_size > ULONG_MAX)
+ return 0;
+ else if (!access_ok(VERIFY_WRITE, (void __user *)a,
+ m->memory_size))
+ return 0;
+ else if (log_all && !log_access_ok(vq->log_base,
+ m->guest_phys_addr,
+ m->memory_size))
+ return 0;
+ }
+ return 1;
+}
+
+/* Can we switch to this memory table? */
+/* Caller should have device mutex but not vq mutex */
+static int memory_access_ok(struct vhost_dev *d, struct vhost_memory *mem,
+ int log_all)
+{
+ int i;
+ for (i = 0; i < d->nvqs; ++i) {
+ int ok;
+ mutex_lock(&d->vqs[i].mutex);
+ /* If ring is not running, will check when it's enabled. */
+ if (&d->vqs[i].private_data)
+ ok = vq_memory_access_ok(&d->vqs[i], mem, log_all);
+ else
+ ok = 1;
+ mutex_unlock(&d->vqs[i].mutex);
+ if (!ok)
+ return 0;
+ }
+ return 1;
+}
+
+static int vq_access_ok(unsigned int num,
+ struct vring_desc __user *desc,
+ struct vring_avail __user *avail,
+ struct vring_used __user *used)
+{
+ return access_ok(VERIFY_READ, desc, num * sizeof *desc) &&
+ access_ok(VERIFY_READ, avail,
+ sizeof *avail + num * sizeof *avail->ring) &&
+ access_ok(VERIFY_WRITE, used,
+ sizeof *used + num * sizeof *used->ring);
+}
+
+/* Can we log writes? */
+/* Caller should have device mutex but not vq mutex */
+int vhost_log_access_ok(struct vhost_dev *dev)
+{
+ return memory_access_ok(dev, dev->memory, 1);
+}
+
+/* Can we start vq? */
+/* Caller should have vq mutex and device mutex */
+int vhost_vq_access_ok(struct vhost_virtqueue *vq)
+{
+ return vq_access_ok(vq->num, vq->desc, vq->avail, vq->used) &&
+ vq_memory_access_ok(vq, vq->dev->memory,
+ vhost_has_feature(vq->dev, VHOST_F_LOG_ALL)) &&
+ (!vq->log_used || log_access_ok(vq->log_base, vq->log_addr,
+ sizeof *vq->used +
+ vq->num * sizeof *vq->used->ring));
+}
+
static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
{
struct vhost_memory mem, *newmem, *oldmem;
@@ -247,6 +332,9 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
kfree(newmem);
return r;
}
+
+ if (!memory_access_ok(d, newmem, vhost_has_feature(d, VHOST_F_LOG_ALL)))
+ return -EFAULT;
oldmem = d->memory;
rcu_assign_pointer(d->memory, newmem);
synchronize_rcu();
@@ -348,6 +436,29 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
r = -EINVAL;
break;
}
+
+ /* We only verify access here if backend is configured.
+ * If it is not, we don't as size might not have been setup.
+ * We will verify when backend is configured. */
+ if (vq->private_data) {
+ if (!vq_access_ok(vq->num,
+ (void __user *)(unsigned long)a.desc_user_addr,
+ (void __user *)(unsigned long)a.avail_user_addr,
+ (void __user *)(unsigned long)a.used_user_addr)) {
+ r = -EINVAL;
+ break;
+ }
+
+ /* Also validate log access for used ring if enabled. */
+ if ((a.flags & (0x1 << VHOST_VRING_F_LOG)) &&
+ !log_access_ok(vq->log_base, a.log_guest_addr,
+ sizeof *vq->used +
+ vq->num * sizeof *vq->used->ring)) {
+ r = -EINVAL;
+ break;
+ }
+ }
+
r = init_used(vq, (struct vring_used __user *)(unsigned long)
a.used_user_addr);
if (r)
diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
index d1f0453..44591ba 100644
--- a/drivers/vhost/vhost.h
+++ b/drivers/vhost/vhost.h
@@ -117,6 +117,8 @@ long vhost_dev_check_owner(struct vhost_dev *);
long vhost_dev_reset_owner(struct vhost_dev *);
void vhost_dev_cleanup(struct vhost_dev *);
long vhost_dev_ioctl(struct vhost_dev *, unsigned int ioctl, unsigned long arg);
+int vhost_vq_access_ok(struct vhost_virtqueue *vq);
+int vhost_log_access_ok(struct vhost_dev *);
unsigned vhost_get_vq_desc(struct vhost_dev *, struct vhost_virtqueue *,
struct iovec iov[], unsigned int iov_count,
--
1.6.6.rc1.43.gf55cc
^ permalink raw reply related
* [PATCH 1/3] vhost: prevent modification of an active ring
From: Michael S. Tsirkin @ 2009-12-20 17:16 UTC (permalink / raw)
To: Rusty Russell, virtualization, linux-kernel; +Cc: Al Viro
In-Reply-To: <cover.1261329297.git.mst@redhat.com>
We don't support changing ring size or log base while ring is running.
If user violates these rules, he might get his memory silently
corrupted. It's better to be explicit, and fail such modification
attempts with an error.
To make these "vq running" checks in ioctl context robust, this patch
also moves all vq flushes to under device mutex.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
drivers/vhost/net.c | 6 +++---
drivers/vhost/vhost.c | 29 ++++++++++++++++++++++++-----
2 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index d6db10c..1b509a0 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -509,12 +509,10 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
vhost_net_enable_vq(n, vq);
mutex_unlock(&vq->mutex);
done:
- mutex_unlock(&n->dev.mutex);
if (oldsock) {
vhost_net_flush_vq(n, index);
fput(oldsock->file);
}
- return r;
err:
mutex_unlock(&n->dev.mutex);
return r;
@@ -554,8 +552,8 @@ static void vhost_net_set_features(struct vhost_net *n, u64 features)
n->vqs[i].hdr_size = hdr_size;
mutex_unlock(&n->vqs[i].mutex);
}
- mutex_unlock(&n->dev.mutex);
vhost_net_flush(n);
+ mutex_unlock(&n->dev.mutex);
}
static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
@@ -587,8 +585,10 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
case VHOST_RESET_OWNER:
return vhost_net_reset_owner(n);
default:
+ mutex_lock(&n->dev.mutex);
r = vhost_dev_ioctl(&n->dev, ioctl, arg);
vhost_net_flush(n);
+ mutex_unlock(&n->dev.mutex);
return r;
}
}
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index e7b4dea..29f1675 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -288,6 +288,12 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
switch (ioctl) {
case VHOST_SET_VRING_NUM:
+ /* Resizing ring with an active backend?
+ * You don't want to do that. */
+ if (vq->private_data) {
+ r = -EBUSY;
+ break;
+ }
r = copy_from_user(&s, argp, sizeof s);
if (r < 0)
break;
@@ -298,6 +304,12 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
vq->num = s.num;
break;
case VHOST_SET_VRING_BASE:
+ /* Moving base with an active backend?
+ * You don't want to do that. */
+ if (vq->private_data) {
+ r = -EBUSY;
+ break;
+ }
r = copy_from_user(&s, argp, sizeof s);
if (r < 0)
break;
@@ -413,6 +425,7 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
return r;
}
+/* Caller must have device mutex */
long vhost_dev_ioctl(struct vhost_dev *d, unsigned int ioctl, unsigned long arg)
{
void __user *argp = (void __user *)arg;
@@ -422,7 +435,6 @@ long vhost_dev_ioctl(struct vhost_dev *d, unsigned int ioctl, unsigned long arg)
long r;
int i, fd;
- mutex_lock(&d->mutex);
/* If you are not the owner, you can become one */
if (ioctl == VHOST_SET_OWNER) {
r = vhost_dev_set_owner(d);
@@ -447,9 +459,17 @@ long vhost_dev_ioctl(struct vhost_dev *d, unsigned int ioctl, unsigned long arg)
break;
}
for (i = 0; i < d->nvqs; ++i) {
- mutex_lock(&d->vqs[i].mutex);
- d->vqs[i].log_base = (void __user *)(unsigned long)p;
- mutex_unlock(&d->vqs[i].mutex);
+ struct vhost_virtqueue *vq;
+ vq = d->vqs + i;
+ mutex_lock(&vq->mutex);
+ /* Moving log base with an active backend?
+ * You don't want to do that. */
+ if (vq->private_data && (vq->log_used ||
+ vhost_has_feature(d, VHOST_F_LOG_ALL)))
+ r = -EBUSY;
+ else
+ vq->log_base = (void __user *)(unsigned long)p;
+ mutex_unlock(&vq->mutex);
}
break;
case VHOST_SET_LOG_FD:
@@ -483,7 +503,6 @@ long vhost_dev_ioctl(struct vhost_dev *d, unsigned int ioctl, unsigned long arg)
break;
}
done:
- mutex_unlock(&d->mutex);
return r;
}
--
1.6.6.rc1.43.gf55cc
^ permalink raw reply related
* [PATCH 0/3] vhost: fix use_mm usage
From: Michael S. Tsirkin @ 2009-12-20 17:16 UTC (permalink / raw)
To: Rusty Russell, virtualization, linux-kernel; +Cc: Al Viro
This patchset fixes an issue, pointed out by Al Viro, in how vhost net
calls use_mm, Works fine for me, and it's pretty straightforward. If no
one sees any issues with it, this will be a good candidate for 2.6.33
(assuming vhost net itself gets pulled :( ).
Michael S. Tsirkin (3):
vhost: prevent modification of an active ring
vhost: add access_ok checks
vhost: make default mapping empty by default
drivers/vhost/net.c | 23 ++++++--
drivers/vhost/vhost.c | 153 ++++++++++++++++++++++++++++++++++++++++++++-----
drivers/vhost/vhost.h | 2 +
3 files changed, 157 insertions(+), 21 deletions(-)
--
MST
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox