aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/virtio_console.c
diff options
context:
space:
mode:
authorMichael S. Tsirkin <mst@redhat.com>2015-03-04 19:15:49 -0500
committerRusty Russell <rusty@rustcorp.com.au>2015-03-04 22:06:18 -0500
commiteeb8a7e8bb123e84daeef84f5a2eab99ad2839a2 (patch)
tree85ef0ee0a9064144e2562ea28e88c19fe9dec842 /drivers/char/virtio_console.c
parent4f6e24ed9de8634d6471ef86b382cba6d4e57ca8 (diff)
virtio_console: avoid config access from irq
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> Cc: stable@kernel.org
Diffstat (limited to 'drivers/char/virtio_console.c')
-rw-r--r--drivers/char/virtio_console.c16
1 files changed, 16 insertions, 0 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index def736ddfc0e..72d7028f779b 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -142,6 +142,7 @@ struct ports_device {
142 * notification 142 * notification
143 */ 143 */
144 struct work_struct control_work; 144 struct work_struct control_work;
145 struct work_struct config_work;
145 146
146 struct list_head ports; 147 struct list_head ports;
147 148
@@ -1837,10 +1838,21 @@ static void config_intr(struct virtio_device *vdev)
1837 1838
1838 portdev = vdev->priv; 1839 portdev = vdev->priv;
1839 1840
1841 if (!use_multiport(portdev))
1842 schedule_work(&portdev->config_work);
1843}
1844
1845static void config_work_handler(struct work_struct *work)
1846{
1847 struct ports_device *portdev;
1848
1849 portdev = container_of(work, struct ports_device, control_work);
1840 if (!use_multiport(portdev)) { 1850 if (!use_multiport(portdev)) {
1851 struct virtio_device *vdev;
1841 struct port *port; 1852 struct port *port;
1842 u16 rows, cols; 1853 u16 rows, cols;
1843 1854
1855 vdev = portdev->vdev;
1844 virtio_cread(vdev, struct virtio_console_config, cols, &cols); 1856 virtio_cread(vdev, struct virtio_console_config, cols, &cols);
1845 virtio_cread(vdev, struct virtio_console_config, rows, &rows); 1857 virtio_cread(vdev, struct virtio_console_config, rows, &rows);
1846 1858
@@ -2040,6 +2052,7 @@ static int virtcons_probe(struct virtio_device *vdev)
2040 2052
2041 virtio_device_ready(portdev->vdev); 2053 virtio_device_ready(portdev->vdev);
2042 2054
2055 INIT_WORK(&portdev->config_work, &config_work_handler);
2043 INIT_WORK(&portdev->control_work, &control_work_handler); 2056 INIT_WORK(&portdev->control_work, &control_work_handler);
2044 2057
2045 if (multiport) { 2058 if (multiport) {
@@ -2114,6 +2127,8 @@ static void virtcons_remove(struct virtio_device *vdev)
2114 /* Finish up work that's lined up */ 2127 /* Finish up work that's lined up */
2115 if (use_multiport(portdev)) 2128 if (use_multiport(portdev))
2116 cancel_work_sync(&portdev->control_work); 2129 cancel_work_sync(&portdev->control_work);
2130 else
2131 cancel_work_sync(&portdev->config_work);
2117 2132
2118 list_for_each_entry_safe(port, port2, &portdev->ports, list) 2133 list_for_each_entry_safe(port, port2, &portdev->ports, list)
2119 unplug_port(port); 2134 unplug_port(port);
@@ -2165,6 +2180,7 @@ static int virtcons_freeze(struct virtio_device *vdev)
2165 2180
2166 virtqueue_disable_cb(portdev->c_ivq); 2181 virtqueue_disable_cb(portdev->c_ivq);
2167 cancel_work_sync(&portdev->control_work); 2182 cancel_work_sync(&portdev->control_work);
2183 cancel_work_sync(&portdev->config_work);
2168 /* 2184 /*
2169 * Once more: if control_work_handler() was running, it would 2185 * Once more: if control_work_handler() was running, it would
2170 * enable the cb as the last step. 2186 * enable the cb as the last step.