From 02238959944ce031f066f21e541a14933aca6575 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:11:40 +0530 Subject: virtio: console: Reset vdev before removing device The virtqueues should be disabled before attempting to remove the device. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 0f69c5ec0ecd..076d0358cf0e 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1605,6 +1605,9 @@ static void virtcons_remove(struct virtio_device *vdev) portdev = vdev->priv; + /* Disable interrupts for vqs */ + vdev->config->reset(vdev); + /* Finish up work that's lined up */ cancel_work_sync(&portdev->control_work); list_for_each_entry_safe(port, port2, &portdev->ports, list) -- cgit v1.2.3 From 96eb872b2a041b1536ccc6ae2fa87eb28f6e2bb2 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:11:41 +0530 Subject: virtio: console: Remove control vq data only if using multiport support If a portdev isn't using multiport support, it won't have any control vq data to remove. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 076d0358cf0e..e15dbe72edbf 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1600,8 +1600,6 @@ static void virtcons_remove(struct virtio_device *vdev) { struct ports_device *portdev; struct port *port, *port2; - struct port_buffer *buf; - unsigned int len; portdev = vdev->priv; @@ -1615,11 +1613,16 @@ static void virtcons_remove(struct virtio_device *vdev) unregister_chrdev(portdev->chr_major, "virtio-portsdev"); - while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) - free_buf(buf); + if (use_multiport(portdev)) { + struct port_buffer *buf; + unsigned int len; - while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) - free_buf(buf); + while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) + free_buf(buf); + + while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) + free_buf(buf); + } vdev->config->del_vqs(vdev); kfree(portdev->in_vqs); -- cgit v1.2.3 From 84ec06c59a14d0941dd58ca6793b24a7e86b3b85 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:11:42 +0530 Subject: virtio: console: Check if portdev is valid in send_control_msg() A portdev may have been hot-unplugged while a port was open()ed. Skip sending control messages when the portdev isn't valid. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index e15dbe72edbf..e1d382b15532 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -410,7 +410,10 @@ static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id, static ssize_t send_control_msg(struct port *port, unsigned int event, unsigned int value) { - return __send_control_msg(port->portdev, port->id, event, value); + /* Did the port get unplugged before userspace closed it? */ + if (port->portdev) + return __send_control_msg(port->portdev, port->id, event, value); + return 0; } /* Callers must take the port->outvq_lock */ -- cgit v1.2.3 From 3709ea7ae7d698b428576c2db0bbb6e08a18cf12 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:11:43 +0530 Subject: virtio: console: Un-block reads on chardev close If a chardev is closed, any blocked read / poll calls should just return and not attempt to use other state. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index e1d382b15532..92f1f65d57a9 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -528,6 +528,10 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, /* The condition that must be true for polling to end */ static bool will_read_block(struct port *port) { + if (!port->guest_connected) { + /* Port got hot-unplugged. Let's exit. */ + return false; + } return !port_has_data(port) && port->host_connected; } -- cgit v1.2.3 From 8529a504273d4efa6bb004dcd6ef28fe67b64ae9 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:11:44 +0530 Subject: virtio: console: Unblock poll on port hot-unplug When a port is hot-unplugged while an app is blocked on poll(), unblock the poll() and return. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 92f1f65d57a9..47b710ca4ab3 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -663,6 +663,10 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait) port = filp->private_data; poll_wait(filp, &port->waitqueue, wait); + if (!port->guest_connected) { + /* Port got unplugged */ + return POLLHUP; + } ret = 0; if (!will_read_block(port)) ret |= POLLIN | POLLRDNORM; -- cgit v1.2.3 From b3dddb9e6ddab74327f5557c1a6640ea0f56ad1c Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:11:45 +0530 Subject: virtio: console: Make read() return -ENODEV on hot-unplug When a port is hot-unplugged while an app was blocked on a read() call, the call was unblocked but would not get an error returned. Return -ENODEV to ensure the app knows the port has gone away. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 47b710ca4ab3..e17ecf5d42b5 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -582,6 +582,9 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf, if (ret < 0) return ret; } + /* Port got hot-unplugged. */ + if (!port->guest_connected) + return -ENODEV; /* * We could've received a disconnection message while we were * waiting for more data. -- cgit v1.2.3 From f4028119714e452f9b49377ec55e0ed1e5d1dfa4 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:11:46 +0530 Subject: virtio: console: Make write() return -ENODEV on hot-unplug When a port is hot-unplugged while an app was blocked on a write() call, the call was unblocked but would not get an error returned. Return -ENODEV to ensure the app knows the port has gone away. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index e17ecf5d42b5..70f1c38fa14c 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -626,6 +626,9 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, if (ret < 0) return ret; } + /* Port got hot-unplugged. */ + if (!port->guest_connected) + return -ENODEV; count = min((size_t)(32 * 1024), count); -- cgit v1.2.3 From 7a2853178dfba9553d58f356113f47fd582e9cc6 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:11:47 +0530 Subject: virtio: console: remove_port() should return void When a port is removed, we have to assume the port is gone. So a success/failure return value doesn't make sense. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 70f1c38fa14c..21b621343033 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1131,7 +1131,7 @@ fail: } /* Remove all port-specific data. */ -static int remove_port(struct port *port) +static void remove_port(struct port *port) { struct port_buffer *buf; @@ -1181,7 +1181,6 @@ static int remove_port(struct port *port) debugfs_remove(port->debugfs_file); kfree(port); - return 0; } /* Any private messages that the Host and Guest want to share */ -- cgit v1.2.3 From 8ad37e83c8dc413f92b10c3d9bdeabe9237f521d Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:11:48 +0530 Subject: virtio: console: open: Use a common path for error handling Just re-arrange code for future patches. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 21b621343033..2f4f0b23ea00 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -711,6 +711,7 @@ static int port_fops_open(struct inode *inode, struct file *filp) { struct cdev *cdev = inode->i_cdev; struct port *port; + int ret; port = container_of(cdev, struct port, cdev); filp->private_data = port; @@ -719,14 +720,17 @@ static int port_fops_open(struct inode *inode, struct file *filp) * Don't allow opening of console port devices -- that's done * via /dev/hvc */ - if (is_console_port(port)) - return -ENXIO; + if (is_console_port(port)) { + ret = -ENXIO; + goto out; + } /* Allow only one process to open a particular port at a time */ spin_lock_irq(&port->inbuf_lock); if (port->guest_connected) { spin_unlock_irq(&port->inbuf_lock); - return -EMFILE; + ret = -EMFILE; + goto out; } port->guest_connected = true; @@ -745,6 +749,8 @@ static int port_fops_open(struct inode *inode, struct file *filp) send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); return 0; +out: + return ret; } /* -- cgit v1.2.3 From 6bdf2afd02ae12bf8ac93e6d14c4b4dfef7c4c59 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:11:49 +0530 Subject: virtio: console: Add a list of portdevs that are active The virtio_console.c driver is capable of handling multiple devices at a time. Maintain a list of devices for future traversal. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 2f4f0b23ea00..b70fe96c1ccd 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -48,6 +48,9 @@ struct ports_driver_data { /* Used for exporting per-port information to debugfs */ struct dentry *debugfs_dir; + /* List of all the devices we're handling */ + struct list_head portdevs; + /* Number of devices this driver is handling */ unsigned int index; @@ -108,6 +111,9 @@ struct port_buffer { * ports for that device (vdev->priv). */ struct ports_device { + /* Next portdev in the list, head is in the pdrvdata struct */ + struct list_head list; + /* * Workqueue handlers where we process deferred work after * notification @@ -1599,6 +1605,10 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) add_port(portdev, 0); } + spin_lock_irq(&pdrvdata_lock); + list_add_tail(&portdev->list, &pdrvdata.portdevs); + spin_unlock_irq(&pdrvdata_lock); + __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, VIRTIO_CONSOLE_DEVICE_READY, 1); return 0; @@ -1625,6 +1635,10 @@ static void virtcons_remove(struct virtio_device *vdev) portdev = vdev->priv; + spin_lock_irq(&pdrvdata_lock); + list_del(&portdev->list); + spin_unlock_irq(&pdrvdata_lock); + /* Disable interrupts for vqs */ vdev->config->reset(vdev); /* Finish up work that's lined up */ @@ -1691,6 +1705,7 @@ static int __init init(void) PTR_ERR(pdrvdata.debugfs_dir)); } INIT_LIST_HEAD(&pdrvdata.consoles); + INIT_LIST_HEAD(&pdrvdata.portdevs); return register_virtio_driver(&virtio_console); } -- cgit v1.2.3 From 04950cdf071b6e5aa4794c93ad3e3ce8a1c4aa8c Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:20:58 +0530 Subject: virtio: console: Add a find_port_by_devt() function To convert to using cdev as a pointer to avoid kref troubles, we have to use a different method to get to a port from an inode than the current container_of method. Add find_port_by_devt() that looks up all portdevs and ports with those portdevs to find the right port. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index b70fe96c1ccd..0c8f2aa3074a 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -227,6 +227,41 @@ out: return port; } +static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev, + dev_t dev) +{ + struct port *port; + unsigned long flags; + + spin_lock_irqsave(&portdev->ports_lock, flags); + list_for_each_entry(port, &portdev->ports, list) + if (port->cdev.dev == dev) + goto out; + port = NULL; +out: + spin_unlock_irqrestore(&portdev->ports_lock, flags); + + return port; +} + +static struct port *find_port_by_devt(dev_t dev) +{ + struct ports_device *portdev; + struct port *port; + unsigned long flags; + + spin_lock_irqsave(&pdrvdata_lock, flags); + list_for_each_entry(portdev, &pdrvdata.portdevs, list) { + port = find_port_by_devt_in_portdev(portdev, dev); + if (port) + goto out; + } + port = NULL; +out: + spin_unlock_irqrestore(&pdrvdata_lock, flags); + return port; +} + static struct port *find_port_by_id(struct ports_device *portdev, u32 id) { struct port *port; @@ -719,7 +754,7 @@ static int port_fops_open(struct inode *inode, struct file *filp) struct port *port; int ret; - port = container_of(cdev, struct port, cdev); + port = find_port_by_devt(cdev->dev); filp->private_data = port; /* -- cgit v1.2.3 From d22a69892bd8f29e3096f6f54c2c00d8aec2e796 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:20:59 +0530 Subject: virtio: console: Use cdev_alloc() instead of cdev_init() This moves to using cdev on the heap instead of it being embedded in the ports struct. This helps individual refcounting and will allow us to properly remove cdev structs after hot-unplugs and close operations. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 0c8f2aa3074a..8a9c140d19be 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -184,7 +184,7 @@ struct port { struct console cons; /* Each port associates with a separate char device */ - struct cdev cdev; + struct cdev *cdev; struct device *dev; /* A waitqueue for poll() or blocking read operations */ @@ -235,7 +235,7 @@ static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev, spin_lock_irqsave(&portdev->ports_lock, flags); list_for_each_entry(port, &portdev->ports, list) - if (port->cdev.dev == dev) + if (port->cdev->dev == dev) goto out; port = NULL; out: @@ -1096,14 +1096,20 @@ static int add_port(struct ports_device *portdev, u32 id) port->in_vq = portdev->in_vqs[port->id]; port->out_vq = portdev->out_vqs[port->id]; - cdev_init(&port->cdev, &port_fops); + port->cdev = cdev_alloc(); + if (!port->cdev) { + dev_err(&port->portdev->vdev->dev, "Error allocating cdev\n"); + err = -ENOMEM; + goto free_port; + } + port->cdev->ops = &port_fops; devt = MKDEV(portdev->chr_major, id); - err = cdev_add(&port->cdev, devt, 1); + err = cdev_add(port->cdev, devt, 1); if (err < 0) { dev_err(&port->portdev->vdev->dev, "Error %d adding cdev for port %u\n", err, id); - goto free_port; + goto free_cdev; } port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, devt, port, "vport%up%u", @@ -1168,7 +1174,7 @@ free_inbufs: free_device: device_destroy(pdrvdata.class, port->dev->devt); free_cdev: - cdev_del(&port->cdev); + cdev_del(port->cdev); free_port: kfree(port); fail: @@ -1212,7 +1218,7 @@ static void remove_port(struct port *port) } sysfs_remove_group(&port->dev->kobj, &port_attribute_group); device_destroy(pdrvdata.class, port->dev->devt); - cdev_del(&port->cdev); + cdev_del(port->cdev); /* Remove unused data this port might have received. */ discard_port_data(port); -- cgit v1.2.3 From b353a6b821627053f82b4e7b907e824cb7a6879c Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:38:29 +0530 Subject: virtio: console: Add reference counting for port struct When a port got hot-unplugged, when a port was open, any file operation after the unplugging resulted in a crash. This is fixed by ref-counting the port structure, and releasing it only when the file is closed. This splits the unplug operation in two parts: first marks the port as unavailable, removes all the buffers in the vqs and removes the port from the per-device list of ports. The second stage, invoked when all references drop to zero, releases the chardev and frees all other memory. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 80 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 8a9c140d19be..288701ccbf7a 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -187,6 +187,9 @@ struct port { struct cdev *cdev; struct device *dev; + /* Reference-counting to handle port hot-unplugs and file operations */ + struct kref kref; + /* A waitqueue for poll() or blocking read operations */ wait_queue_head_t waitqueue; @@ -725,6 +728,8 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait) return ret; } +static void remove_port(struct kref *kref); + static int port_fops_release(struct inode *inode, struct file *filp) { struct port *port; @@ -745,6 +750,16 @@ static int port_fops_release(struct inode *inode, struct file *filp) reclaim_consumed_buffers(port); spin_unlock_irq(&port->outvq_lock); + /* + * Locks aren't necessary here as a port can't be opened after + * unplug, and if a port isn't unplugged, a kref would already + * exist for the port. Plus, taking ports_lock here would + * create a dependency on other locks taken by functions + * inside remove_port if we're the last holder of the port, + * creating many problems. + */ + kref_put(&port->kref, remove_port); + return 0; } @@ -757,6 +772,11 @@ static int port_fops_open(struct inode *inode, struct file *filp) port = find_port_by_devt(cdev->dev); filp->private_data = port; + /* Prevent against a port getting hot-unplugged at the same time */ + spin_lock_irq(&port->portdev->ports_lock); + kref_get(&port->kref); + spin_unlock_irq(&port->portdev->ports_lock); + /* * Don't allow opening of console port devices -- that's done * via /dev/hvc @@ -791,6 +811,7 @@ static int port_fops_open(struct inode *inode, struct file *filp) return 0; out: + kref_put(&port->kref, remove_port); return ret; } @@ -1079,6 +1100,7 @@ static int add_port(struct ports_device *portdev, u32 id) err = -ENOMEM; goto fail; } + kref_init(&port->kref); port->portdev = portdev; port->id = id; @@ -1183,22 +1205,43 @@ fail: return err; } -/* Remove all port-specific data. */ -static void remove_port(struct port *port) +/* No users remain, remove all port-specific data. */ +static void remove_port(struct kref *kref) +{ + struct port *port; + + port = container_of(kref, struct port, kref); + + sysfs_remove_group(&port->dev->kobj, &port_attribute_group); + device_destroy(pdrvdata.class, port->dev->devt); + cdev_del(port->cdev); + + kfree(port->name); + + debugfs_remove(port->debugfs_file); + + kfree(port); +} + +/* + * Port got unplugged. Remove port from portdev's list and drop the + * kref reference. If no userspace has this port opened, it will + * result in immediate removal the port. + */ +static void unplug_port(struct port *port) { struct port_buffer *buf; + spin_lock_irq(&port->portdev->ports_lock); + list_del(&port->list); + spin_unlock_irq(&port->portdev->ports_lock); + if (port->guest_connected) { port->guest_connected = false; port->host_connected = false; wake_up_interruptible(&port->waitqueue); - send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); } - 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); @@ -1216,9 +1259,6 @@ static void remove_port(struct port *port) hvc_remove(port->cons.hvc); #endif } - sysfs_remove_group(&port->dev->kobj, &port_attribute_group); - device_destroy(pdrvdata.class, port->dev->devt); - cdev_del(port->cdev); /* Remove unused data this port might have received. */ discard_port_data(port); @@ -1229,11 +1269,19 @@ static void remove_port(struct port *port) while ((buf = virtqueue_detach_unused_buf(port->in_vq))) free_buf(buf); - kfree(port->name); - - debugfs_remove(port->debugfs_file); + /* + * We should just assume the device itself has gone off -- + * else a close on an open port later will try to send out a + * control message. + */ + port->portdev = NULL; - kfree(port); + /* + * Locks around here are not necessary - a port can't be + * opened after we removed the port struct from ports_list + * above. + */ + kref_put(&port->kref, remove_port); } /* Any private messages that the Host and Guest want to share */ @@ -1272,7 +1320,7 @@ static void handle_control_message(struct ports_device *portdev, add_port(portdev, cpkt->id); break; case VIRTIO_CONSOLE_PORT_REMOVE: - remove_port(port); + unplug_port(port); break; case VIRTIO_CONSOLE_CONSOLE_PORT: if (!cpkt->value) @@ -1686,7 +1734,7 @@ static void virtcons_remove(struct virtio_device *vdev) cancel_work_sync(&portdev->control_work); list_for_each_entry_safe(port, port2, &portdev->ports, list) - remove_port(port); + unplug_port(port); unregister_chrdev(portdev->chr_major, "virtio-portsdev"); -- cgit v1.2.3 From e062013c7d22e40ee634b818d28fd615db36998e Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:38:30 +0530 Subject: virtio: console: Reference counting portdev structs is not needed Explain in a comment why there's no need to reference-count the portdev struct: when a device is yanked out, we can't do anything more with it anyway so just give up doing anything more with the data or the vqs and exit cleanly. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 288701ccbf7a..c84486b3e43d 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1738,6 +1738,14 @@ static void virtcons_remove(struct virtio_device *vdev) unregister_chrdev(portdev->chr_major, "virtio-portsdev"); + /* + * When yanking out a device, we immediately lose the + * (device-side) queues. So there's no point in keeping the + * guest side around till we drop our final reference. This + * also means that any ports which are in an open state will + * have to just stop using the port, as the vqs are going + * away. + */ if (use_multiport(portdev)) { struct port_buffer *buf; unsigned int len; -- cgit v1.2.3 From 3eae0adea949d8fdd8fa3e5301192901219d2c64 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:47:52 +0530 Subject: virtio: console: Send SIGIO to processes that request it for host events A process can request for SIGIO on host connect / disconnect events using the O_ASYNC file flag using fcntl(). If that's requested, and if the guest-side connection for the port is open, any host-side open/close events for that port will raise a SIGIO. The process can then use poll() within the signal handler to find out which port triggered the signal. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index c84486b3e43d..c4ca43694233 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -196,6 +196,9 @@ struct port { /* The 'name' of the port that we expose via sysfs properties */ char *name; + /* We can notify apps of host connect / disconnect events via SIGIO */ + struct fasync_struct *async_queue; + /* The 'id' to identify the port with the Host */ u32 id; @@ -815,6 +818,14 @@ out: return ret; } +static int port_fops_fasync(int fd, struct file *filp, int mode) +{ + struct port *port; + + port = filp->private_data; + return fasync_helper(fd, filp, mode, &port->async_queue); +} + /* * The file operations that we support: programs in the guest can open * a console device, read from it, write to it, poll for data and @@ -828,6 +839,7 @@ static const struct file_operations port_fops = { .write = port_fops_write, .poll = port_fops_poll, .release = port_fops_release, + .fasync = port_fops_fasync, }; /* @@ -1086,6 +1098,12 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) return nr_added_bufs; } +static void send_sigio_to_port(struct port *port) +{ + if (port->async_queue && port->guest_connected) + kill_fasync(&port->async_queue, SIGIO, POLL_OUT); +} + static int add_port(struct ports_device *portdev, u32 id) { char debugfs_name[16]; @@ -1108,6 +1126,7 @@ static int add_port(struct ports_device *portdev, u32 id) port->name = NULL; port->inbuf = NULL; port->cons.hvc = NULL; + port->async_queue = NULL; port->cons.ws.ws_row = port->cons.ws.ws_col = 0; @@ -1362,6 +1381,12 @@ static void handle_control_message(struct ports_device *portdev, spin_lock_irq(&port->outvq_lock); reclaim_consumed_buffers(port); spin_unlock_irq(&port->outvq_lock); + + /* + * If the guest is connected, it'll be interested in + * knowing the host connection state changed. + */ + send_sigio_to_port(port); break; case VIRTIO_CONSOLE_PORT_NAME: /* -- cgit v1.2.3 From 55f6bcce3691f68476a530daa6666b66c43420a8 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:47:53 +0530 Subject: virtio: console: Send SIGIO on new data arrival on ports Send a SIGIO signal when new data arrives on a port. This is sent only when the process has requested for the signal to be sent using fcntl(). Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index c4ca43694233..6d5c579b1693 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1483,6 +1483,9 @@ static void in_intr(struct virtqueue *vq) wake_up_interruptible(&port->waitqueue); + /* Send a SIGIO indicating new data in case the process asked for it */ + send_sigio_to_port(port); + if (is_console_port(port) && hvc_poll(port->cons.hvc)) hvc_kick(); } -- cgit v1.2.3 From a461e11e7b8ca2705889bcf9582f6a8f84884bd2 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 2 Sep 2010 18:47:54 +0530 Subject: virtio: console: Send SIGIO in case of port unplug If a port has registered for SIGIO signals, let the application know that the port is getting unplugged. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 6d5c579b1693..dc42d5689483 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1259,6 +1259,9 @@ static void unplug_port(struct port *port) port->guest_connected = false; port->host_connected = false; wake_up_interruptible(&port->waitqueue); + + /* Let the app know the port is going down. */ + send_sigio_to_port(port); } if (is_console_port(port)) { -- cgit v1.2.3 From 299fb61c08c2fcd1bb6d3a4e87e53dc368475416 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Thu, 16 Sep 2010 14:43:09 +0530 Subject: virtio: console: Disable lseek(2) for port file operations The ports are char devices; do not have seeking capabilities. Calling nonseekable_open() from the fops_open() call and setting the llseek fops pointer to no_llseek ensures an lseek() call from userspace returns -ESPIPE. Signed-off-by: Amit Shah CC: Arnd Bergmann Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index dc42d5689483..6c1b676643a9 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -809,6 +809,8 @@ static int port_fops_open(struct inode *inode, struct file *filp) reclaim_consumed_buffers(port); spin_unlock_irq(&port->outvq_lock); + nonseekable_open(inode, filp); + /* Notify host of port being opened */ send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); @@ -840,6 +842,7 @@ static const struct file_operations port_fops = { .poll = port_fops_poll, .release = port_fops_release, .fasync = port_fops_fasync, + .llseek = no_llseek, }; /* -- cgit v1.2.3 From fe5a50a10c7b3bf38e47045eadc6b87a5c157f44 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 15 Sep 2010 01:27:23 +0200 Subject: virtio_blk: remove BKL leftovers Remove the BKL usage added in "block: push down BKL into .locked_ioctl". Virtio-blk doesn't use the BKL for anything, and doesn't implement any ioctl command by itself, but only uses the generic scsi_cmd_ioctl which is fine without the BKL. Signed-off-by: Christoph Hellwig Signed-off-by: Rusty Russell --- drivers/block/virtio_blk.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 1101e251a629..8320490226b7 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -222,8 +221,8 @@ static int virtblk_get_id(struct gendisk *disk, char *id_str) return err; } -static int virtblk_locked_ioctl(struct block_device *bdev, fmode_t mode, - unsigned cmd, unsigned long data) +static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long data) { struct gendisk *disk = bdev->bd_disk; struct virtio_blk *vblk = disk->private_data; @@ -238,18 +237,6 @@ static int virtblk_locked_ioctl(struct block_device *bdev, fmode_t mode, (void __user *)data); } -static int virtblk_ioctl(struct block_device *bdev, fmode_t mode, - unsigned int cmd, unsigned long param) -{ - int ret; - - lock_kernel(); - ret = virtblk_locked_ioctl(bdev, mode, cmd, param); - unlock_kernel(); - - return ret; -} - /* We provide getgeo only to please some old bootloader/partitioning tools */ static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo) { -- cgit v1.2.3