| From: "Michael S. Tsirkin" <mst@redhat.com> |
| Date: Thu, 5 Mar 2015 10:45:49 +1030 |
| Subject: virtio_console: avoid config access from irq |
| |
| commit eeb8a7e8bb123e84daeef84f5a2eab99ad2839a2 upstream. |
| |
| when multiport is off, virtio console invokes config access from irq |
| context, config access is blocking on s390. |
| Fix this up by scheduling work from config irq - similar to what we do |
| for multiport configs. |
| |
| Signed-off-by: Michael S. Tsirkin <mst@redhat.com> |
| Reviewed-by: Amit Shah <amit.shah@redhat.com> |
| Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> |
| [bwh: Backported to 3.2: |
| - Adjust context |
| - Drop changes to virtcons_freeze()] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/char/virtio_console.c | 16 ++++++++++++++++ |
| 1 file changed, 16 insertions(+) |
| |
| --- a/drivers/char/virtio_console.c |
| +++ b/drivers/char/virtio_console.c |
| @@ -124,6 +124,7 @@ struct ports_device { |
| * notification |
| */ |
| struct work_struct control_work; |
| + struct work_struct config_work; |
| |
| struct list_head ports; |
| |
| @@ -1556,10 +1557,21 @@ static void config_intr(struct virtio_de |
| |
| portdev = vdev->priv; |
| |
| + if (!use_multiport(portdev)) |
| + schedule_work(&portdev->config_work); |
| +} |
| + |
| +static void config_work_handler(struct work_struct *work) |
| +{ |
| + struct ports_device *portdev; |
| + |
| + portdev = container_of(work, struct ports_device, control_work); |
| if (!use_multiport(portdev)) { |
| + struct virtio_device *vdev; |
| struct port *port; |
| u16 rows, cols; |
| |
| + vdev = portdev->vdev; |
| vdev->config->get(vdev, |
| offsetof(struct virtio_console_config, cols), |
| &cols, sizeof(u16)); |
| @@ -1731,6 +1743,8 @@ static int __devinit virtcons_probe(stru |
| spin_lock_init(&portdev->ports_lock); |
| INIT_LIST_HEAD(&portdev->ports); |
| |
| + INIT_WORK(&portdev->config_work, &config_work_handler); |
| + |
| if (multiport) { |
| unsigned int nr_added_bufs; |
| |
| @@ -1806,6 +1820,8 @@ static void virtcons_remove(struct virti |
| /* Finish up work that's lined up */ |
| if (use_multiport(portdev)) |
| cancel_work_sync(&portdev->control_work); |
| + else |
| + cancel_work_sync(&portdev->config_work); |
| |
| list_for_each_entry_safe(port, port2, &portdev->ports, list) |
| unplug_port(port); |