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 7c53f58c87b..9f20fda9c56 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -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) { diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index 1ebf007812a..ae4f039515b 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h @@ -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));  |