virtio: console: Add ability to hot-unplug ports
authorAmit Shah <amit.shah@redhat.com>
Mon, 21 Dec 2009 16:57:31 +0000 (22:27 +0530)
committerRusty Russell <rusty@rustcorp.com.au>
Wed, 24 Feb 2010 03:53:00 +0000 (14:23 +1030)
Remove port data; deregister from the hvc core if it's a console port.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
drivers/char/virtio_console.c
include/linux/virtio_console.h

index 7c53f58c87ba2b88d2a97715e283ff621faa0865..9f20fda9c56f8733ffbebab638c49ceea11c0e98 100644 (file)
@@ -783,6 +783,36 @@ static struct attribute_group port_attribute_group = {
        .attrs = port_sysfs_entries,
 };
 
+/* Remove all port-specific data. */
+static int remove_port(struct port *port)
+{
+       spin_lock_irq(&port->portdev->ports_lock);
+       list_del(&port->list);
+       spin_unlock_irq(&port->portdev->ports_lock);
+
+       if (is_console_port(port)) {
+               spin_lock_irq(&pdrvdata_lock);
+               list_del(&port->cons.list);
+               spin_unlock_irq(&pdrvdata_lock);
+               hvc_remove(port->cons.hvc);
+       }
+       if (port->guest_connected)
+               send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
+
+       while (port->in_vq->vq_ops->detach_unused_buf(port->in_vq))
+               ;
+
+       sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
+       device_destroy(pdrvdata.class, port->dev->devt);
+       cdev_del(&port->cdev);
+
+       discard_port_data(port);
+       kfree(port->name);
+
+       kfree(port);
+       return 0;
+}
+
 /* Any private messages that the Host and Guest want to share */
 static void handle_control_message(struct ports_device *portdev,
                                   struct port_buffer *buf)
@@ -854,6 +884,32 @@ static void handle_control_message(struct ports_device *portdev,
                                err);
 
                break;
+       case VIRTIO_CONSOLE_PORT_REMOVE:
+               /*
+                * Hot unplug the port.  We don't decrement nr_ports
+                * since we don't want to deal with extra complexities
+                * of using the lowest-available port id: We can just
+                * pick up the nr_ports number as the id and not have
+                * userspace send it to us.  This helps us in two
+                * ways:
+                *
+                * - We don't need to have a 'port_id' field in the
+                *   config space when a port is hot-added.  This is a
+                *   good thing as we might queue up multiple hotplug
+                *   requests issued in our workqueue.
+                *
+                * - Another way to deal with this would have been to
+                *   use a bitmap of the active ports and select the
+                *   lowest non-active port from that map.  That
+                *   bloats the already tight config space and we
+                *   would end up artificially limiting the
+                *   max. number of ports to sizeof(bitmap).  Right
+                *   now we can support 2^32 ports (as the port id is
+                *   stored in a u32 type).
+                *
+                */
+               remove_port(port);
+               break;
        }
 }
 
@@ -1078,12 +1134,17 @@ static void config_work_handler(struct work_struct *work)
                /*
                 * Port 0 got hot-added.  Since we already did all the
                 * other initialisation for it, just tell the Host
-                * that the port is ready.
+                * that the port is ready if we find the port.  In
+                * case the port was hot-removed earlier, we call
+                * add_port to add the port.
                 */
                struct port *port;
 
                port = find_port_by_id(portdev, 0);
-               send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
+               if (!port)
+                       add_port(portdev, 0);
+               else
+                       send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
                return;
        }
        if (virtconconf.nr_ports > portdev->config.max_nr_ports) {
index 1ebf007812a8cd1fd038bd97fc0b8b4e6bb18906..ae4f039515b441f3407d88da1e2172a78bab2e5b 100644 (file)
@@ -41,6 +41,7 @@ struct virtio_console_control {
 #define VIRTIO_CONSOLE_RESIZE          2
 #define VIRTIO_CONSOLE_PORT_OPEN       3
 #define VIRTIO_CONSOLE_PORT_NAME       4
+#define VIRTIO_CONSOLE_PORT_REMOVE     5
 
 #ifdef __KERNEL__
 int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int));