diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/hw_random/virtio-rng.c | 2 | ||||
-rw-r--r-- | drivers/char/virtio_console.c | 140 |
2 files changed, 110 insertions, 32 deletions
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index fd699ccecf5b..723725bbb96b 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c | |||
@@ -47,7 +47,7 @@ static void register_buffer(u8 *buf, size_t size) | |||
47 | sg_init_one(&sg, buf, size); | 47 | sg_init_one(&sg, buf, size); |
48 | 48 | ||
49 | /* There should always be room for one buffer. */ | 49 | /* There should always be room for one buffer. */ |
50 | if (virtqueue_add_buf(vq, &sg, 0, 1, buf) < 0) | 50 | if (virtqueue_add_buf(vq, &sg, 0, 1, buf, GFP_KERNEL) < 0) |
51 | BUG(); | 51 | BUG(); |
52 | 52 | ||
53 | virtqueue_kick(vq); | 53 | virtqueue_kick(vq); |
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 8e3c46d67cb3..b58b56187065 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c | |||
@@ -392,7 +392,7 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf) | |||
392 | 392 | ||
393 | sg_init_one(sg, buf->buf, buf->size); | 393 | sg_init_one(sg, buf->buf, buf->size); |
394 | 394 | ||
395 | ret = virtqueue_add_buf(vq, sg, 0, 1, buf); | 395 | ret = virtqueue_add_buf(vq, sg, 0, 1, buf, GFP_ATOMIC); |
396 | virtqueue_kick(vq); | 396 | virtqueue_kick(vq); |
397 | return ret; | 397 | return ret; |
398 | } | 398 | } |
@@ -457,7 +457,7 @@ static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id, | |||
457 | vq = portdev->c_ovq; | 457 | vq = portdev->c_ovq; |
458 | 458 | ||
459 | sg_init_one(sg, &cpkt, sizeof(cpkt)); | 459 | sg_init_one(sg, &cpkt, sizeof(cpkt)); |
460 | if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt) >= 0) { | 460 | if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt, GFP_ATOMIC) >= 0) { |
461 | virtqueue_kick(vq); | 461 | virtqueue_kick(vq); |
462 | while (!virtqueue_get_buf(vq, &len)) | 462 | while (!virtqueue_get_buf(vq, &len)) |
463 | cpu_relax(); | 463 | cpu_relax(); |
@@ -506,7 +506,7 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, | |||
506 | reclaim_consumed_buffers(port); | 506 | reclaim_consumed_buffers(port); |
507 | 507 | ||
508 | sg_init_one(sg, in_buf, in_count); | 508 | sg_init_one(sg, in_buf, in_count); |
509 | ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf); | 509 | ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf, GFP_ATOMIC); |
510 | 510 | ||
511 | /* Tell Host to go! */ | 511 | /* Tell Host to go! */ |
512 | virtqueue_kick(out_vq); | 512 | virtqueue_kick(out_vq); |
@@ -1271,6 +1271,20 @@ static void remove_port(struct kref *kref) | |||
1271 | kfree(port); | 1271 | kfree(port); |
1272 | } | 1272 | } |
1273 | 1273 | ||
1274 | static void remove_port_data(struct port *port) | ||
1275 | { | ||
1276 | struct port_buffer *buf; | ||
1277 | |||
1278 | /* Remove unused data this port might have received. */ | ||
1279 | discard_port_data(port); | ||
1280 | |||
1281 | reclaim_consumed_buffers(port); | ||
1282 | |||
1283 | /* Remove buffers we queued up for the Host to send us data in. */ | ||
1284 | while ((buf = virtqueue_detach_unused_buf(port->in_vq))) | ||
1285 | free_buf(buf); | ||
1286 | } | ||
1287 | |||
1274 | /* | 1288 | /* |
1275 | * Port got unplugged. Remove port from portdev's list and drop the | 1289 | * Port got unplugged. Remove port from portdev's list and drop the |
1276 | * kref reference. If no userspace has this port opened, it will | 1290 | * kref reference. If no userspace has this port opened, it will |
@@ -1278,8 +1292,6 @@ static void remove_port(struct kref *kref) | |||
1278 | */ | 1292 | */ |
1279 | static void unplug_port(struct port *port) | 1293 | static void unplug_port(struct port *port) |
1280 | { | 1294 | { |
1281 | struct port_buffer *buf; | ||
1282 | |||
1283 | spin_lock_irq(&port->portdev->ports_lock); | 1295 | spin_lock_irq(&port->portdev->ports_lock); |
1284 | list_del(&port->list); | 1296 | list_del(&port->list); |
1285 | spin_unlock_irq(&port->portdev->ports_lock); | 1297 | spin_unlock_irq(&port->portdev->ports_lock); |
@@ -1300,14 +1312,7 @@ static void unplug_port(struct port *port) | |||
1300 | hvc_remove(port->cons.hvc); | 1312 | hvc_remove(port->cons.hvc); |
1301 | } | 1313 | } |
1302 | 1314 | ||
1303 | /* Remove unused data this port might have received. */ | 1315 | remove_port_data(port); |
1304 | discard_port_data(port); | ||
1305 | |||
1306 | reclaim_consumed_buffers(port); | ||
1307 | |||
1308 | /* Remove buffers we queued up for the Host to send us data in. */ | ||
1309 | while ((buf = virtqueue_detach_unused_buf(port->in_vq))) | ||
1310 | free_buf(buf); | ||
1311 | 1316 | ||
1312 | /* | 1317 | /* |
1313 | * We should just assume the device itself has gone off -- | 1318 | * We should just assume the device itself has gone off -- |
@@ -1659,6 +1664,28 @@ static const struct file_operations portdev_fops = { | |||
1659 | .owner = THIS_MODULE, | 1664 | .owner = THIS_MODULE, |
1660 | }; | 1665 | }; |
1661 | 1666 | ||
1667 | static void remove_vqs(struct ports_device *portdev) | ||
1668 | { | ||
1669 | portdev->vdev->config->del_vqs(portdev->vdev); | ||
1670 | kfree(portdev->in_vqs); | ||
1671 | kfree(portdev->out_vqs); | ||
1672 | } | ||
1673 | |||
1674 | static void remove_controlq_data(struct ports_device *portdev) | ||
1675 | { | ||
1676 | struct port_buffer *buf; | ||
1677 | unsigned int len; | ||
1678 | |||
1679 | if (!use_multiport(portdev)) | ||
1680 | return; | ||
1681 | |||
1682 | while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) | ||
1683 | free_buf(buf); | ||
1684 | |||
1685 | while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) | ||
1686 | free_buf(buf); | ||
1687 | } | ||
1688 | |||
1662 | /* | 1689 | /* |
1663 | * Once we're further in boot, we get probed like any other virtio | 1690 | * Once we're further in boot, we get probed like any other virtio |
1664 | * device. | 1691 | * device. |
@@ -1764,9 +1791,7 @@ free_vqs: | |||
1764 | /* The host might want to notify mgmt sw about device add failure */ | 1791 | /* The host might want to notify mgmt sw about device add failure */ |
1765 | __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, | 1792 | __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, |
1766 | VIRTIO_CONSOLE_DEVICE_READY, 0); | 1793 | VIRTIO_CONSOLE_DEVICE_READY, 0); |
1767 | vdev->config->del_vqs(vdev); | 1794 | remove_vqs(portdev); |
1768 | kfree(portdev->in_vqs); | ||
1769 | kfree(portdev->out_vqs); | ||
1770 | free_chrdev: | 1795 | free_chrdev: |
1771 | unregister_chrdev(portdev->chr_major, "virtio-portsdev"); | 1796 | unregister_chrdev(portdev->chr_major, "virtio-portsdev"); |
1772 | free: | 1797 | free: |
@@ -1804,21 +1829,8 @@ static void virtcons_remove(struct virtio_device *vdev) | |||
1804 | * have to just stop using the port, as the vqs are going | 1829 | * have to just stop using the port, as the vqs are going |
1805 | * away. | 1830 | * away. |
1806 | */ | 1831 | */ |
1807 | if (use_multiport(portdev)) { | 1832 | remove_controlq_data(portdev); |
1808 | struct port_buffer *buf; | 1833 | remove_vqs(portdev); |
1809 | unsigned int len; | ||
1810 | |||
1811 | while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) | ||
1812 | free_buf(buf); | ||
1813 | |||
1814 | while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) | ||
1815 | free_buf(buf); | ||
1816 | } | ||
1817 | |||
1818 | vdev->config->del_vqs(vdev); | ||
1819 | kfree(portdev->in_vqs); | ||
1820 | kfree(portdev->out_vqs); | ||
1821 | |||
1822 | kfree(portdev); | 1834 | kfree(portdev); |
1823 | } | 1835 | } |
1824 | 1836 | ||
@@ -1832,6 +1844,68 @@ static unsigned int features[] = { | |||
1832 | VIRTIO_CONSOLE_F_MULTIPORT, | 1844 | VIRTIO_CONSOLE_F_MULTIPORT, |
1833 | }; | 1845 | }; |
1834 | 1846 | ||
1847 | #ifdef CONFIG_PM | ||
1848 | static int virtcons_freeze(struct virtio_device *vdev) | ||
1849 | { | ||
1850 | struct ports_device *portdev; | ||
1851 | struct port *port; | ||
1852 | |||
1853 | portdev = vdev->priv; | ||
1854 | |||
1855 | vdev->config->reset(vdev); | ||
1856 | |||
1857 | virtqueue_disable_cb(portdev->c_ivq); | ||
1858 | cancel_work_sync(&portdev->control_work); | ||
1859 | /* | ||
1860 | * Once more: if control_work_handler() was running, it would | ||
1861 | * enable the cb as the last step. | ||
1862 | */ | ||
1863 | virtqueue_disable_cb(portdev->c_ivq); | ||
1864 | remove_controlq_data(portdev); | ||
1865 | |||
1866 | list_for_each_entry(port, &portdev->ports, list) { | ||
1867 | virtqueue_disable_cb(port->in_vq); | ||
1868 | virtqueue_disable_cb(port->out_vq); | ||
1869 | /* | ||
1870 | * We'll ask the host later if the new invocation has | ||
1871 | * the port opened or closed. | ||
1872 | */ | ||
1873 | port->host_connected = false; | ||
1874 | remove_port_data(port); | ||
1875 | } | ||
1876 | remove_vqs(portdev); | ||
1877 | |||
1878 | return 0; | ||
1879 | } | ||
1880 | |||
1881 | static int virtcons_restore(struct virtio_device *vdev) | ||
1882 | { | ||
1883 | struct ports_device *portdev; | ||
1884 | struct port *port; | ||
1885 | int ret; | ||
1886 | |||
1887 | portdev = vdev->priv; | ||
1888 | |||
1889 | ret = init_vqs(portdev); | ||
1890 | if (ret) | ||
1891 | return ret; | ||
1892 | |||
1893 | if (use_multiport(portdev)) | ||
1894 | fill_queue(portdev->c_ivq, &portdev->cvq_lock); | ||
1895 | |||
1896 | list_for_each_entry(port, &portdev->ports, list) { | ||
1897 | port->in_vq = portdev->in_vqs[port->id]; | ||
1898 | port->out_vq = portdev->out_vqs[port->id]; | ||
1899 | |||
1900 | fill_queue(port->in_vq, &port->inbuf_lock); | ||
1901 | |||
1902 | /* Get port open/close status on the host */ | ||
1903 | send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); | ||
1904 | } | ||
1905 | return 0; | ||
1906 | } | ||
1907 | #endif | ||
1908 | |||
1835 | static struct virtio_driver virtio_console = { | 1909 | static struct virtio_driver virtio_console = { |
1836 | .feature_table = features, | 1910 | .feature_table = features, |
1837 | .feature_table_size = ARRAY_SIZE(features), | 1911 | .feature_table_size = ARRAY_SIZE(features), |
@@ -1841,6 +1915,10 @@ static struct virtio_driver virtio_console = { | |||
1841 | .probe = virtcons_probe, | 1915 | .probe = virtcons_probe, |
1842 | .remove = virtcons_remove, | 1916 | .remove = virtcons_remove, |
1843 | .config_changed = config_intr, | 1917 | .config_changed = config_intr, |
1918 | #ifdef CONFIG_PM | ||
1919 | .freeze = virtcons_freeze, | ||
1920 | .restore = virtcons_restore, | ||
1921 | #endif | ||
1844 | }; | 1922 | }; |
1845 | 1923 | ||
1846 | static int __init init(void) | 1924 | static int __init init(void) |