Commit 0e20a057 authored by Michael S. Tsirkin's avatar Michael S. Tsirkin Committed by Ben Hutchings

virtio_console: avoid config access from irq

commit eeb8a7e8 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: default avatarMichael S. Tsirkin <mst@redhat.com>
Reviewed-by: default avatarAmit Shah <amit.shah@redhat.com>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
[bwh: Backported to 3.2:
 - Adjust context
 - Drop changes to virtcons_freeze()]
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 711077a6
...@@ -124,6 +124,7 @@ struct ports_device { ...@@ -124,6 +124,7 @@ struct ports_device {
* notification * notification
*/ */
struct work_struct control_work; struct work_struct control_work;
struct work_struct config_work;
struct list_head ports; struct list_head ports;
...@@ -1556,10 +1557,21 @@ static void config_intr(struct virtio_device *vdev) ...@@ -1556,10 +1557,21 @@ static void config_intr(struct virtio_device *vdev)
portdev = vdev->priv; 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)) { if (!use_multiport(portdev)) {
struct virtio_device *vdev;
struct port *port; struct port *port;
u16 rows, cols; u16 rows, cols;
vdev = portdev->vdev;
vdev->config->get(vdev, vdev->config->get(vdev,
offsetof(struct virtio_console_config, cols), offsetof(struct virtio_console_config, cols),
&cols, sizeof(u16)); &cols, sizeof(u16));
...@@ -1731,6 +1743,8 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) ...@@ -1731,6 +1743,8 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
spin_lock_init(&portdev->ports_lock); spin_lock_init(&portdev->ports_lock);
INIT_LIST_HEAD(&portdev->ports); INIT_LIST_HEAD(&portdev->ports);
INIT_WORK(&portdev->config_work, &config_work_handler);
if (multiport) { if (multiport) {
unsigned int nr_added_bufs; unsigned int nr_added_bufs;
...@@ -1806,6 +1820,8 @@ static void virtcons_remove(struct virtio_device *vdev) ...@@ -1806,6 +1820,8 @@ static void virtcons_remove(struct virtio_device *vdev)
/* Finish up work that's lined up */ /* Finish up work that's lined up */
if (use_multiport(portdev)) if (use_multiport(portdev))
cancel_work_sync(&portdev->control_work); cancel_work_sync(&portdev->control_work);
else
cancel_work_sync(&portdev->config_work);
list_for_each_entry_safe(port, port2, &portdev->ports, list) list_for_each_entry_safe(port, port2, &portdev->ports, list)
unplug_port(port); unplug_port(port);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment