virtio: console: don't assume a single console port.
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>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index e52ee11..6bbf707 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -17,10 +17,28 @@
*/
#include <linux/err.h>
#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
#include <linux/virtio.h>
#include <linux/virtio_console.h>
#include "hvc_console.h"
+/*
+ * This is a global struct for storing common data for all the devices
+ * this driver handles.
+ *
+ * Mainly, it has a linked list for all the consoles in one place so
+ * that callbacks from hvc for get_chars(), put_chars() work properly
+ * across multiple devices and multiple ports per device.
+ */
+struct ports_driver_data {
+ /* All the console devices handled by this driver */
+ struct list_head consoles;
+};
+static struct ports_driver_data pdrvdata;
+
+DEFINE_SPINLOCK(pdrvdata_lock);
+
struct port_buffer {
char *buf;
@@ -40,8 +58,15 @@
/* 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 @@
/* 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 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 @@
{
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 @@
}
}
-/*
- * 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 @@
goto free_vqs;
}
+ /* Add to vtermno list. */
+ spin_lock_irq(&pdrvdata_lock);
+ list_add(&port->list, &pdrvdata.consoles);
+ spin_unlock_irq(&pdrvdata_lock);
+
/* Register the input buffer the first time. */
add_inbuf(port->in_vq, port->inbuf);
@@ -349,6 +403,8 @@
static int __init init(void)
{
+ INIT_LIST_HEAD(&pdrvdata.consoles);
+
return register_virtio_driver(&virtio_console);
}
module_init(init);