aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/virtio_console.c
diff options
context:
space:
mode:
authorAmit Shah <amit.shah@redhat.com>2009-12-21 10:33:25 -0500
committerRusty Russell <rusty@rustcorp.com.au>2010-02-23 22:52:52 -0500
commit17634ba25544d60af1968982929150efad755032 (patch)
tree9ac1acbc260e3421b282b9ebe79f8ae76bb5e14a /drivers/char/virtio_console.c
parentf997f00bf8c3ddf748d757105afa1a7dd5297208 (diff)
virtio: console: Add a new MULTIPORT feature, support for generic ports
This commit adds a new feature, MULTIPORT. If the host supports this feature as well, the config space has the number of ports defined for that device. New ports are spawned according to this information. The config space also has the maximum number of ports that can be spawned for a particular device. This is useful in initializing the appropriate number of virtqueues in advance, as ports might be hot-plugged in later. Using this feature, generic ports can be created which are not tied to hvc consoles. We also open up a private channel between the host and the guest via which some "control" messages are exchanged for the ports, like whether the port being spawned is a console port, resizing the console window, etc. Next commits will add support for hotplugging and presenting char devices in /dev/ for bi-directional guest-host communication. Signed-off-by: Amit Shah <amit.shah@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/char/virtio_console.c')
-rw-r--r--drivers/char/virtio_console.c392
1 files changed, 336 insertions, 56 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index d01051060be3..a70f2b3a9e64 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1,5 +1,6 @@
1/* 1/*
2 * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation 2 * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation
3 * Copyright (C) 2009, 2010 Red Hat, Inc.
3 * 4 *
4 * This program is free software; you can redistribute it and/or modify 5 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by 6 * it under the terms of the GNU General Public License as published by
@@ -21,6 +22,7 @@
21#include <linux/spinlock.h> 22#include <linux/spinlock.h>
22#include <linux/virtio.h> 23#include <linux/virtio.h>
23#include <linux/virtio_console.h> 24#include <linux/virtio_console.h>
25#include <linux/workqueue.h>
24#include "hvc_console.h" 26#include "hvc_console.h"
25 27
26/* 28/*
@@ -69,17 +71,6 @@ struct console {
69 u32 vtermno; 71 u32 vtermno;
70}; 72};
71 73
72/*
73 * This is a per-device struct that stores data common to all the
74 * ports for that device (vdev->priv).
75 */
76struct ports_device {
77 /* Array of per-port IO virtqueues */
78 struct virtqueue **in_vqs, **out_vqs;
79
80 struct virtio_device *vdev;
81};
82
83struct port_buffer { 74struct port_buffer {
84 char *buf; 75 char *buf;
85 76
@@ -92,8 +83,46 @@ struct port_buffer {
92 size_t offset; 83 size_t offset;
93}; 84};
94 85
86/*
87 * This is a per-device struct that stores data common to all the
88 * ports for that device (vdev->priv).
89 */
90struct ports_device {
91 /*
92 * Workqueue handlers where we process deferred work after
93 * notification
94 */
95 struct work_struct control_work;
96
97 struct list_head ports;
98
99 /* To protect the list of ports */
100 spinlock_t ports_lock;
101
102 /* To protect the vq operations for the control channel */
103 spinlock_t cvq_lock;
104
105 /* The current config space is stored here */
106 struct virtio_console_config config;
107
108 /* The virtio device we're associated with */
109 struct virtio_device *vdev;
110
111 /*
112 * A couple of virtqueues for the control channel: one for
113 * guest->host transfers, one for host->guest transfers
114 */
115 struct virtqueue *c_ivq, *c_ovq;
116
117 /* Array of per-port IO virtqueues */
118 struct virtqueue **in_vqs, **out_vqs;
119};
120
95/* This struct holds the per-port data */ 121/* This struct holds the per-port data */
96struct port { 122struct port {
123 /* Next port in the list, head is in the ports_device */
124 struct list_head list;
125
97 /* Pointer to the parent virtio_console device */ 126 /* Pointer to the parent virtio_console device */
98 struct ports_device *portdev; 127 struct ports_device *portdev;
99 128
@@ -115,6 +144,9 @@ struct port {
115 * hooked up to an hvc console 144 * hooked up to an hvc console
116 */ 145 */
117 struct console cons; 146 struct console cons;
147
148 /* The 'id' to identify the port with the Host */
149 u32 id;
118}; 150};
119 151
120/* This is the very early arch-specified put chars function. */ 152/* This is the very early arch-specified put chars function. */
@@ -139,25 +171,56 @@ out:
139 return port; 171 return port;
140} 172}
141 173
174static struct port *find_port_by_id(struct ports_device *portdev, u32 id)
175{
176 struct port *port;
177 unsigned long flags;
178
179 spin_lock_irqsave(&portdev->ports_lock, flags);
180 list_for_each_entry(port, &portdev->ports, list)
181 if (port->id == id)
182 goto out;
183 port = NULL;
184out:
185 spin_unlock_irqrestore(&portdev->ports_lock, flags);
186
187 return port;
188}
189
142static struct port *find_port_by_vq(struct ports_device *portdev, 190static struct port *find_port_by_vq(struct ports_device *portdev,
143 struct virtqueue *vq) 191 struct virtqueue *vq)
144{ 192{
145 struct port *port; 193 struct port *port;
146 struct console *cons;
147 unsigned long flags; 194 unsigned long flags;
148 195
149 spin_lock_irqsave(&pdrvdata_lock, flags); 196 spin_lock_irqsave(&portdev->ports_lock, flags);
150 list_for_each_entry(cons, &pdrvdata.consoles, list) { 197 list_for_each_entry(port, &portdev->ports, list)
151 port = container_of(cons, struct port, cons);
152 if (port->in_vq == vq || port->out_vq == vq) 198 if (port->in_vq == vq || port->out_vq == vq)
153 goto out; 199 goto out;
154 }
155 port = NULL; 200 port = NULL;
156out: 201out:
157 spin_unlock_irqrestore(&pdrvdata_lock, flags); 202 spin_unlock_irqrestore(&portdev->ports_lock, flags);
158 return port; 203 return port;
159} 204}
160 205
206static bool is_console_port(struct port *port)
207{
208 if (port->cons.hvc)
209 return true;
210 return false;
211}
212
213static inline bool use_multiport(struct ports_device *portdev)
214{
215 /*
216 * This condition can be true when put_chars is called from
217 * early_init
218 */
219 if (!portdev->vdev)
220 return 0;
221 return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT);
222}
223
161static void free_buf(struct port_buffer *buf) 224static void free_buf(struct port_buffer *buf)
162{ 225{
163 kfree(buf->buf); 226 kfree(buf->buf);
@@ -233,6 +296,32 @@ static bool port_has_data(struct port *port)
233 return ret; 296 return ret;
234} 297}
235 298
299static ssize_t send_control_msg(struct port *port, unsigned int event,
300 unsigned int value)
301{
302 struct scatterlist sg[1];
303 struct virtio_console_control cpkt;
304 struct virtqueue *vq;
305 int len;
306
307 if (!use_multiport(port->portdev))
308 return 0;
309
310 cpkt.id = port->id;
311 cpkt.event = event;
312 cpkt.value = value;
313
314 vq = port->portdev->c_ovq;
315
316 sg_init_one(sg, &cpkt, sizeof(cpkt));
317 if (vq->vq_ops->add_buf(vq, sg, 1, 0, &cpkt) >= 0) {
318 vq->vq_ops->kick(vq);
319 while (!vq->vq_ops->get_buf(vq, &len))
320 cpu_relax();
321 }
322 return 0;
323}
324
236static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count) 325static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count)
237{ 326{
238 struct scatterlist sg[1]; 327 struct scatterlist sg[1];
@@ -387,24 +476,7 @@ static void notifier_del_vio(struct hvc_struct *hp, int data)
387 hp->irq_requested = 0; 476 hp->irq_requested = 0;
388} 477}
389 478
390static void hvc_handle_input(struct virtqueue *vq) 479/* The operations for console ports. */
391{
392 struct port *port;
393 unsigned long flags;
394
395 port = find_port_by_vq(vq->vdev->priv, vq);
396 if (!port)
397 return;
398
399 spin_lock_irqsave(&port->inbuf_lock, flags);
400 port->inbuf = get_inbuf(port);
401 spin_unlock_irqrestore(&port->inbuf_lock, flags);
402
403 if (hvc_poll(port->cons.hvc))
404 hvc_kick();
405}
406
407/* The operations for the console. */
408static const struct hv_ops hv_ops = { 480static const struct hv_ops hv_ops = {
409 .get_chars = get_chars, 481 .get_chars = get_chars,
410 .put_chars = put_chars, 482 .put_chars = put_chars,
@@ -428,7 +500,7 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
428 return hvc_instantiate(0, 0, &hv_ops); 500 return hvc_instantiate(0, 0, &hv_ops);
429} 501}
430 502
431int __devinit init_port_console(struct port *port) 503int init_port_console(struct port *port)
432{ 504{
433 int ret; 505 int ret;
434 506
@@ -465,7 +537,122 @@ int __devinit init_port_console(struct port *port)
465 return 0; 537 return 0;
466} 538}
467 539
468static int __devinit add_port(struct ports_device *portdev) 540/* Any private messages that the Host and Guest want to share */
541static void handle_control_message(struct ports_device *portdev,
542 struct port_buffer *buf)
543{
544 struct virtio_console_control *cpkt;
545 struct port *port;
546
547 cpkt = (struct virtio_console_control *)(buf->buf + buf->offset);
548
549 port = find_port_by_id(portdev, cpkt->id);
550 if (!port) {
551 /* No valid header at start of buffer. Drop it. */
552 dev_dbg(&portdev->vdev->dev,
553 "Invalid index %u in control packet\n", cpkt->id);
554 return;
555 }
556
557 switch (cpkt->event) {
558 case VIRTIO_CONSOLE_CONSOLE_PORT:
559 if (!cpkt->value)
560 break;
561 if (is_console_port(port))
562 break;
563
564 init_port_console(port);
565 /*
566 * Could remove the port here in case init fails - but
567 * have to notify the host first.
568 */
569 break;
570 case VIRTIO_CONSOLE_RESIZE:
571 if (!is_console_port(port))
572 break;
573 port->cons.hvc->irq_requested = 1;
574 resize_console(port);
575 break;
576 }
577}
578
579static void control_work_handler(struct work_struct *work)
580{
581 struct ports_device *portdev;
582 struct virtqueue *vq;
583 struct port_buffer *buf;
584 unsigned int len;
585
586 portdev = container_of(work, struct ports_device, control_work);
587 vq = portdev->c_ivq;
588
589 spin_lock(&portdev->cvq_lock);
590 while ((buf = vq->vq_ops->get_buf(vq, &len))) {
591 spin_unlock(&portdev->cvq_lock);
592
593 buf->len = len;
594 buf->offset = 0;
595
596 handle_control_message(portdev, buf);
597
598 spin_lock(&portdev->cvq_lock);
599 if (add_inbuf(portdev->c_ivq, buf) < 0) {
600 dev_warn(&portdev->vdev->dev,
601 "Error adding buffer to queue\n");
602 free_buf(buf);
603 }
604 }
605 spin_unlock(&portdev->cvq_lock);
606}
607
608static void in_intr(struct virtqueue *vq)
609{
610 struct port *port;
611 unsigned long flags;
612
613 port = find_port_by_vq(vq->vdev->priv, vq);
614 if (!port)
615 return;
616
617 spin_lock_irqsave(&port->inbuf_lock, flags);
618 port->inbuf = get_inbuf(port);
619
620 spin_unlock_irqrestore(&port->inbuf_lock, flags);
621
622 if (is_console_port(port) && hvc_poll(port->cons.hvc))
623 hvc_kick();
624}
625
626static void control_intr(struct virtqueue *vq)
627{
628 struct ports_device *portdev;
629
630 portdev = vq->vdev->priv;
631 schedule_work(&portdev->control_work);
632}
633
634static void fill_queue(struct virtqueue *vq, spinlock_t *lock)
635{
636 struct port_buffer *buf;
637 int ret;
638
639 do {
640 buf = alloc_buf(PAGE_SIZE);
641 if (!buf)
642 break;
643
644 spin_lock_irq(lock);
645 ret = add_inbuf(vq, buf);
646 if (ret < 0) {
647 spin_unlock_irq(lock);
648 free_buf(buf);
649 break;
650 }
651 spin_unlock_irq(lock);
652 } while (ret > 0);
653}
654
655static int add_port(struct ports_device *portdev, u32 id)
469{ 656{
470 struct port *port; 657 struct port *port;
471 struct port_buffer *inbuf; 658 struct port_buffer *inbuf;
@@ -478,11 +665,13 @@ static int __devinit add_port(struct ports_device *portdev)
478 } 665 }
479 666
480 port->portdev = portdev; 667 port->portdev = portdev;
668 port->id = id;
481 669
482 port->inbuf = NULL; 670 port->inbuf = NULL;
671 port->cons.hvc = NULL;
483 672
484 port->in_vq = portdev->in_vqs[0]; 673 port->in_vq = portdev->in_vqs[port->id];
485 port->out_vq = portdev->out_vqs[0]; 674 port->out_vq = portdev->out_vqs[port->id];
486 675
487 spin_lock_init(&port->inbuf_lock); 676 spin_lock_init(&port->inbuf_lock);
488 677
@@ -495,9 +684,25 @@ static int __devinit add_port(struct ports_device *portdev)
495 /* Register the input buffer the first time. */ 684 /* Register the input buffer the first time. */
496 add_inbuf(port->in_vq, inbuf); 685 add_inbuf(port->in_vq, inbuf);
497 686
498 err = init_port_console(port); 687 /*
499 if (err) 688 * If we're not using multiport support, this has to be a console port
500 goto free_inbuf; 689 */
690 if (!use_multiport(port->portdev)) {
691 err = init_port_console(port);
692 if (err)
693 goto free_inbuf;
694 }
695
696 spin_lock_irq(&portdev->ports_lock);
697 list_add_tail(&port->list, &port->portdev->ports);
698 spin_unlock_irq(&portdev->ports_lock);
699
700 /*
701 * Tell the Host we're set so that it can send us various
702 * configuration parameters for this port (eg, port name,
703 * caching, whether this is a console port, etc.)
704 */
705 send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
501 706
502 return 0; 707 return 0;
503 708
@@ -514,12 +719,11 @@ static int init_vqs(struct ports_device *portdev)
514 vq_callback_t **io_callbacks; 719 vq_callback_t **io_callbacks;
515 char **io_names; 720 char **io_names;
516 struct virtqueue **vqs; 721 struct virtqueue **vqs;
517 u32 nr_ports, nr_queues; 722 u32 i, j, nr_ports, nr_queues;
518 int err; 723 int err;
519 724
520 /* We currently only have one port and two queues for that port */ 725 nr_ports = portdev->config.max_nr_ports;
521 nr_ports = 1; 726 nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2;
522 nr_queues = 2;
523 727
524 vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL); 728 vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL);
525 if (!vqs) { 729 if (!vqs) {
@@ -549,11 +753,32 @@ static int init_vqs(struct ports_device *portdev)
549 goto free_invqs; 753 goto free_invqs;
550 } 754 }
551 755
552 io_callbacks[0] = hvc_handle_input; 756 /*
553 io_callbacks[1] = NULL; 757 * For backward compat (newer host but older guest), the host
554 io_names[0] = "input"; 758 * spawns a console port first and also inits the vqs for port
555 io_names[1] = "output"; 759 * 0 before others.
556 760 */
761 j = 0;
762 io_callbacks[j] = in_intr;
763 io_callbacks[j + 1] = NULL;
764 io_names[j] = "input";
765 io_names[j + 1] = "output";
766 j += 2;
767
768 if (use_multiport(portdev)) {
769 io_callbacks[j] = control_intr;
770 io_callbacks[j + 1] = NULL;
771 io_names[j] = "control-i";
772 io_names[j + 1] = "control-o";
773
774 for (i = 1; i < nr_ports; i++) {
775 j += 2;
776 io_callbacks[j] = in_intr;
777 io_callbacks[j + 1] = NULL;
778 io_names[j] = "input";
779 io_names[j + 1] = "output";
780 }
781 }
557 /* Find the queues. */ 782 /* Find the queues. */
558 err = portdev->vdev->config->find_vqs(portdev->vdev, nr_queues, vqs, 783 err = portdev->vdev->config->find_vqs(portdev->vdev, nr_queues, vqs,
559 io_callbacks, 784 io_callbacks,
@@ -561,9 +786,20 @@ static int init_vqs(struct ports_device *portdev)
561 if (err) 786 if (err)
562 goto free_outvqs; 787 goto free_outvqs;
563 788
789 j = 0;
564 portdev->in_vqs[0] = vqs[0]; 790 portdev->in_vqs[0] = vqs[0];
565 portdev->out_vqs[0] = vqs[1]; 791 portdev->out_vqs[0] = vqs[1];
566 792 j += 2;
793 if (use_multiport(portdev)) {
794 portdev->c_ivq = vqs[j];
795 portdev->c_ovq = vqs[j + 1];
796
797 for (i = 1; i < nr_ports; i++) {
798 j += 2;
799 portdev->in_vqs[i] = vqs[j];
800 portdev->out_vqs[i] = vqs[j + 1];
801 }
802 }
567 kfree(io_callbacks); 803 kfree(io_callbacks);
568 kfree(io_names); 804 kfree(io_names);
569 kfree(vqs); 805 kfree(vqs);
@@ -587,11 +823,17 @@ fail:
587/* 823/*
588 * Once we're further in boot, we get probed like any other virtio 824 * Once we're further in boot, we get probed like any other virtio
589 * device. 825 * device.
826 *
827 * If the host also supports multiple console ports, we check the
828 * config space to see how many ports the host has spawned. We
829 * initialize each port found.
590 */ 830 */
591static int __devinit virtcons_probe(struct virtio_device *vdev) 831static int __devinit virtcons_probe(struct virtio_device *vdev)
592{ 832{
593 struct ports_device *portdev; 833 struct ports_device *portdev;
834 u32 i;
594 int err; 835 int err;
836 bool multiport;
595 837
596 portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); 838 portdev = kmalloc(sizeof(*portdev), GFP_KERNEL);
597 if (!portdev) { 839 if (!portdev) {
@@ -603,16 +845,53 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
603 portdev->vdev = vdev; 845 portdev->vdev = vdev;
604 vdev->priv = portdev; 846 vdev->priv = portdev;
605 847
848 multiport = false;
849 portdev->config.nr_ports = 1;
850 portdev->config.max_nr_ports = 1;
851 if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) {
852 multiport = true;
853 vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT;
854
855 vdev->config->get(vdev, offsetof(struct virtio_console_config,
856 nr_ports),
857 &portdev->config.nr_ports,
858 sizeof(portdev->config.nr_ports));
859 vdev->config->get(vdev, offsetof(struct virtio_console_config,
860 max_nr_ports),
861 &portdev->config.max_nr_ports,
862 sizeof(portdev->config.max_nr_ports));
863 if (portdev->config.nr_ports > portdev->config.max_nr_ports) {
864 dev_warn(&vdev->dev,
865 "More ports (%u) specified than allowed (%u). Will init %u ports.",
866 portdev->config.nr_ports,
867 portdev->config.max_nr_ports,
868 portdev->config.max_nr_ports);
869
870 portdev->config.nr_ports = portdev->config.max_nr_ports;
871 }
872 }
873
874 /* Let the Host know we support multiple ports.*/
875 vdev->config->finalize_features(vdev);
876
606 err = init_vqs(portdev); 877 err = init_vqs(portdev);
607 if (err < 0) { 878 if (err < 0) {
608 dev_err(&vdev->dev, "Error %d initializing vqs\n", err); 879 dev_err(&vdev->dev, "Error %d initializing vqs\n", err);
609 goto free; 880 goto free;
610 } 881 }
611 882
612 /* We only have one port. */ 883 spin_lock_init(&portdev->ports_lock);
613 err = add_port(portdev); 884 INIT_LIST_HEAD(&portdev->ports);
614 if (err) 885
615 goto free_vqs; 886 if (multiport) {
887 spin_lock_init(&portdev->cvq_lock);
888 INIT_WORK(&portdev->control_work, &control_work_handler);
889
890 fill_queue(portdev->c_ivq, &portdev->cvq_lock);
891 }
892
893 for (i = 0; i < portdev->config.nr_ports; i++)
894 add_port(portdev, i);
616 895
617 /* Start using the new console output. */ 896 /* Start using the new console output. */
618 early_put_chars = NULL; 897 early_put_chars = NULL;
@@ -635,6 +914,7 @@ static struct virtio_device_id id_table[] = {
635 914
636static unsigned int features[] = { 915static unsigned int features[] = {
637 VIRTIO_CONSOLE_F_SIZE, 916 VIRTIO_CONSOLE_F_SIZE,
917 VIRTIO_CONSOLE_F_MULTIPORT,
638}; 918};
639 919
640static struct virtio_driver virtio_console = { 920static struct virtio_driver virtio_console = {