diff options
-rw-r--r-- | drivers/char/virtio_console.c | 65 | ||||
-rw-r--r-- | include/linux/virtio_console.h | 1 |
2 files changed, 64 insertions, 2 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 7c53f58c87ba..9f20fda9c56f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c | |||
@@ -783,6 +783,36 @@ static struct attribute_group port_attribute_group = { | |||
783 | .attrs = port_sysfs_entries, | 783 | .attrs = port_sysfs_entries, |
784 | }; | 784 | }; |
785 | 785 | ||
786 | /* Remove all port-specific data. */ | ||
787 | static int remove_port(struct port *port) | ||
788 | { | ||
789 | spin_lock_irq(&port->portdev->ports_lock); | ||
790 | list_del(&port->list); | ||
791 | spin_unlock_irq(&port->portdev->ports_lock); | ||
792 | |||
793 | if (is_console_port(port)) { | ||
794 | spin_lock_irq(&pdrvdata_lock); | ||
795 | list_del(&port->cons.list); | ||
796 | spin_unlock_irq(&pdrvdata_lock); | ||
797 | hvc_remove(port->cons.hvc); | ||
798 | } | ||
799 | if (port->guest_connected) | ||
800 | send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); | ||
801 | |||
802 | while (port->in_vq->vq_ops->detach_unused_buf(port->in_vq)) | ||
803 | ; | ||
804 | |||
805 | sysfs_remove_group(&port->dev->kobj, &port_attribute_group); | ||
806 | device_destroy(pdrvdata.class, port->dev->devt); | ||
807 | cdev_del(&port->cdev); | ||
808 | |||
809 | discard_port_data(port); | ||
810 | kfree(port->name); | ||
811 | |||
812 | kfree(port); | ||
813 | return 0; | ||
814 | } | ||
815 | |||
786 | /* Any private messages that the Host and Guest want to share */ | 816 | /* Any private messages that the Host and Guest want to share */ |
787 | static void handle_control_message(struct ports_device *portdev, | 817 | static void handle_control_message(struct ports_device *portdev, |
788 | struct port_buffer *buf) | 818 | struct port_buffer *buf) |
@@ -854,6 +884,32 @@ static void handle_control_message(struct ports_device *portdev, | |||
854 | err); | 884 | err); |
855 | 885 | ||
856 | break; | 886 | break; |
887 | case VIRTIO_CONSOLE_PORT_REMOVE: | ||
888 | /* | ||
889 | * Hot unplug the port. We don't decrement nr_ports | ||
890 | * since we don't want to deal with extra complexities | ||
891 | * of using the lowest-available port id: We can just | ||
892 | * pick up the nr_ports number as the id and not have | ||
893 | * userspace send it to us. This helps us in two | ||
894 | * ways: | ||
895 | * | ||
896 | * - We don't need to have a 'port_id' field in the | ||
897 | * config space when a port is hot-added. This is a | ||
898 | * good thing as we might queue up multiple hotplug | ||
899 | * requests issued in our workqueue. | ||
900 | * | ||
901 | * - Another way to deal with this would have been to | ||
902 | * use a bitmap of the active ports and select the | ||
903 | * lowest non-active port from that map. That | ||
904 | * bloats the already tight config space and we | ||
905 | * would end up artificially limiting the | ||
906 | * max. number of ports to sizeof(bitmap). Right | ||
907 | * now we can support 2^32 ports (as the port id is | ||
908 | * stored in a u32 type). | ||
909 | * | ||
910 | */ | ||
911 | remove_port(port); | ||
912 | break; | ||
857 | } | 913 | } |
858 | } | 914 | } |
859 | 915 | ||
@@ -1078,12 +1134,17 @@ static void config_work_handler(struct work_struct *work) | |||
1078 | /* | 1134 | /* |
1079 | * Port 0 got hot-added. Since we already did all the | 1135 | * Port 0 got hot-added. Since we already did all the |
1080 | * other initialisation for it, just tell the Host | 1136 | * other initialisation for it, just tell the Host |
1081 | * that the port is ready. | 1137 | * that the port is ready if we find the port. In |
1138 | * case the port was hot-removed earlier, we call | ||
1139 | * add_port to add the port. | ||
1082 | */ | 1140 | */ |
1083 | struct port *port; | 1141 | struct port *port; |
1084 | 1142 | ||
1085 | port = find_port_by_id(portdev, 0); | 1143 | port = find_port_by_id(portdev, 0); |
1086 | send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); | 1144 | if (!port) |
1145 | add_port(portdev, 0); | ||
1146 | else | ||
1147 | send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); | ||
1087 | return; | 1148 | return; |
1088 | } | 1149 | } |
1089 | if (virtconconf.nr_ports > portdev->config.max_nr_ports) { | 1150 | if (virtconconf.nr_ports > portdev->config.max_nr_ports) { |
diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index 1ebf007812a8..ae4f039515b4 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h | |||
@@ -41,6 +41,7 @@ struct virtio_console_control { | |||
41 | #define VIRTIO_CONSOLE_RESIZE 2 | 41 | #define VIRTIO_CONSOLE_RESIZE 2 |
42 | #define VIRTIO_CONSOLE_PORT_OPEN 3 | 42 | #define VIRTIO_CONSOLE_PORT_OPEN 3 |
43 | #define VIRTIO_CONSOLE_PORT_NAME 4 | 43 | #define VIRTIO_CONSOLE_PORT_NAME 4 |
44 | #define VIRTIO_CONSOLE_PORT_REMOVE 5 | ||
44 | 45 | ||
45 | #ifdef __KERNEL__ | 46 | #ifdef __KERNEL__ |
46 | int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)); | 47 | int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)); |