All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/14] virtio: console: Hot-unplug fixes
@ 2010-09-02 12:41 Amit Shah
  2010-09-02 12:41 ` [PATCH 01/14] virtio: console: Reset vdev before removing device Amit Shah
                   ` (13 more replies)
  0 siblings, 14 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 12:41 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

Hey Rusty,

These are the patches that rework a few bits to make hot-unplug while
ports are open not crash apps (or kernels).

The problem is when hot-unplug is performed when a port is open, the
cdev struct is kept around by the file pointers and when the app later
does a 'close', things go boom-boom.

This patch series makes sure port as well as device hot-unplug is now
safe to perform at any time: when ports are open, not open, hvc
consoles are active, inactive, module removal, applications doing
blocking read/write/poll calls while port / devices are going away...

Ideally this patchset would be applicable for 2.6.36 as well as
2.6.35, but I'll let you decide as it's quite big.

Amit Shah (14):
  virtio: console: Reset vdev before removing device
  virtio: console: Remove control vq data only if using multiport
    support
  virtio: console: Check if portdev is valid in send_control_msg()
  virtio: console: Unblock reads on chardev close
  virtio: console: Unblock poll on port hot-unplug
  virtio: console: Make read() return -ENODEV on hot-unplug
  virtio: console: Make write() return -ENODEV on hot-unplug
  virtio: console: remove_port() should return void
  virtio: console: open: Use a common path for error handling
  virtio: console: Add a list of portdevs that are active
  virtio: console: Add a find_port_by_devt() function
  virtio: console: Use cdev_alloc() instead of cdev_init()
  virtio: console: Add reference counting for port struct
  virtio: console: Reference counting portdev structs is not needed

 drivers/char/virtio_console.c |  206 ++++++++++++++++++++++++++++++++++-------
 1 files changed, 173 insertions(+), 33 deletions(-)

-- 
1.7.2.2

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 01/14] virtio: console: Reset vdev before removing device
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
@ 2010-09-02 12:41 ` Amit Shah
  2010-09-02 12:41 ` [PATCH 02/14] virtio: console: Remove control vq data only if using multiport support Amit Shah
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 12:41 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

The virtqueues should be disabled before attempting to remove the
device.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 942a982..32267d4 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1590,6 +1590,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)
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 02/14] virtio: console: Remove control vq data only if using multiport support
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
  2010-09-02 12:41 ` [PATCH 01/14] virtio: console: Reset vdev before removing device Amit Shah
@ 2010-09-02 12:41 ` Amit Shah
  2010-09-06  8:29   ` Rusty Russell
  2010-09-02 12:41 ` [PATCH 03/14] virtio: console: Check if portdev is valid in send_control_msg() Amit Shah
                   ` (11 subsequent siblings)
  13 siblings, 1 reply; 17+ messages in thread
From: Amit Shah @ 2010-09-02 12:41 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

If a portdev isn't using multiport support, it won't have any control vq
data to remove.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |   15 +++++++++------
 1 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 32267d4..0f115b4 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1585,8 +1585,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;
 
@@ -1600,11 +1598,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);
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 03/14] virtio: console: Check if portdev is valid in send_control_msg()
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
  2010-09-02 12:41 ` [PATCH 01/14] virtio: console: Reset vdev before removing device Amit Shah
  2010-09-02 12:41 ` [PATCH 02/14] virtio: console: Remove control vq data only if using multiport support Amit Shah
@ 2010-09-02 12:41 ` Amit Shah
  2010-09-02 12:41 ` [PATCH 04/14] virtio: console: Un-block reads on chardev close Amit Shah
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 12:41 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

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 <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 0f115b4..5cd5374 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 */
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 04/14] virtio: console: Un-block reads on chardev close
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
                   ` (2 preceding siblings ...)
  2010-09-02 12:41 ` [PATCH 03/14] virtio: console: Check if portdev is valid in send_control_msg() Amit Shah
@ 2010-09-02 12:41 ` Amit Shah
  2010-09-02 12:41 ` [PATCH 05/14] virtio: console: Unblock poll on port hot-unplug Amit Shah
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 12:41 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

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 <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 5cd5374..488d250 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -525,6 +525,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;
 }
 
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 05/14] virtio: console: Unblock poll on port hot-unplug
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
                   ` (3 preceding siblings ...)
  2010-09-02 12:41 ` [PATCH 04/14] virtio: console: Un-block reads on chardev close Amit Shah
@ 2010-09-02 12:41 ` Amit Shah
  2010-09-02 12:41 ` [PATCH 06/14] virtio: console: Make read() return -ENODEV on hot-unplug Amit Shah
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 12:41 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

When a port is hot-unplugged while an app is blocked on poll(), unblock
the poll() and return.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 488d250..c8a5678 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -648,6 +648,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 (port->inbuf)
 		ret |= POLLIN | POLLRDNORM;
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 06/14] virtio: console: Make read() return -ENODEV on hot-unplug
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
                   ` (4 preceding siblings ...)
  2010-09-02 12:41 ` [PATCH 05/14] virtio: console: Unblock poll on port hot-unplug Amit Shah
@ 2010-09-02 12:41 ` Amit Shah
  2010-09-02 12:41 ` [PATCH 07/14] virtio: console: Make write() " Amit Shah
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 12:41 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

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 <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index c8a5678..7fa3c26 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -579,6 +579,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.
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 07/14] virtio: console: Make write() return -ENODEV on hot-unplug
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
                   ` (5 preceding siblings ...)
  2010-09-02 12:41 ` [PATCH 06/14] virtio: console: Make read() return -ENODEV on hot-unplug Amit Shah
@ 2010-09-02 12:41 ` Amit Shah
  2010-09-02 12:41 ` [PATCH 08/14] virtio: console: remove_port() should return void Amit Shah
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 12:41 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

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 <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 7fa3c26..ecadfa2 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -619,6 +619,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);
 
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 08/14] virtio: console: remove_port() should return void
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
                   ` (6 preceding siblings ...)
  2010-09-02 12:41 ` [PATCH 07/14] virtio: console: Make write() " Amit Shah
@ 2010-09-02 12:41 ` Amit Shah
  2010-09-02 12:41 ` [PATCH 09/14] virtio: console: open: Use a common path for error handling Amit Shah
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 12:41 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

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 <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index ecadfa2..0c4ee9b1 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1116,7 +1116,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;
 
@@ -1166,7 +1166,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 */
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 09/14] virtio: console: open: Use a common path for error handling
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
                   ` (7 preceding siblings ...)
  2010-09-02 12:41 ` [PATCH 08/14] virtio: console: remove_port() should return void Amit Shah
@ 2010-09-02 12:41 ` Amit Shah
  2010-09-02 12:41 ` [PATCH 10/14] virtio: console: Add a list of portdevs that are active Amit Shah
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 12:41 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

Just re-arrange code for future patches.

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |   12 +++++++++---
 1 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 0c4ee9b1..d689c76 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -696,6 +696,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;
@@ -704,14 +705,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;
@@ -730,6 +734,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;
 }
 
 /*
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 10/14] virtio: console: Add a list of portdevs that are active
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
                   ` (8 preceding siblings ...)
  2010-09-02 12:41 ` [PATCH 09/14] virtio: console: open: Use a common path for error handling Amit Shah
@ 2010-09-02 12:41 ` Amit Shah
  2010-09-02 12:50 ` [PATCH 11/14] virtio: console: Add a find_port_by_devt() function Amit Shah
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 12:41 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

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 <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |   15 +++++++++++++++
 1 files changed, 15 insertions(+), 0 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index d689c76..a37a868 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
@@ -1584,6 +1590,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;
@@ -1610,6 +1620,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 */
@@ -1676,6 +1690,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);
 }
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 11/14] virtio: console: Add a find_port_by_devt() function
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
                   ` (9 preceding siblings ...)
  2010-09-02 12:41 ` [PATCH 10/14] virtio: console: Add a list of portdevs that are active Amit Shah
@ 2010-09-02 12:50 ` Amit Shah
  2010-09-02 12:50 ` [PATCH 12/14] virtio: console: Use cdev_alloc() instead of cdev_init() Amit Shah
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 12:50 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

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 <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |   37 ++++++++++++++++++++++++++++++++++++-
 1 files changed, 36 insertions(+), 1 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index a37a868..af44332 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;
@@ -704,7 +739,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;
 
 	/*
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 12/14] virtio: console: Use cdev_alloc() instead of cdev_init()
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
                   ` (10 preceding siblings ...)
  2010-09-02 12:50 ` [PATCH 11/14] virtio: console: Add a find_port_by_devt() function Amit Shah
@ 2010-09-02 12:50 ` Amit Shah
  2010-09-02 13:08 ` [PATCH 13/14] virtio: console: Add reference counting for port struct Amit Shah
  2010-09-02 13:08 ` [PATCH 14/14] virtio: console: Reference counting portdev structs is not needed Amit Shah
  13 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 12:50 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

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 <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |   20 +++++++++++++-------
 1 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index af44332..0c4dc26 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:
@@ -1081,14 +1081,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",
@@ -1153,7 +1159,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:
@@ -1197,7 +1203,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);
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 13/14] virtio: console: Add reference counting for port struct
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
                   ` (11 preceding siblings ...)
  2010-09-02 12:50 ` [PATCH 12/14] virtio: console: Use cdev_alloc() instead of cdev_init() Amit Shah
@ 2010-09-02 13:08 ` Amit Shah
  2010-09-02 13:08 ` [PATCH 14/14] virtio: console: Reference counting portdev structs is not needed Amit Shah
  13 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 13:08 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

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 <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |   80 ++++++++++++++++++++++++++++++++--------
 1 files changed, 64 insertions(+), 16 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 0c4dc26..1e00221 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;
 
@@ -710,6 +713,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;
@@ -730,6 +735,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;
 }
 
@@ -742,6 +757,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
@@ -776,6 +796,7 @@ static int port_fops_open(struct inode *inode, struct file *filp)
 
 	return 0;
 out:
+	kref_put(&port->kref, remove_port);
 	return ret;
 }
 
@@ -1064,6 +1085,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;
@@ -1168,22 +1190,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);
@@ -1201,9 +1244,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);
@@ -1214,11 +1254,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 */
@@ -1257,7 +1305,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)
@@ -1671,7 +1719,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");
 
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 14/14] virtio: console: Reference counting portdev structs is not needed
  2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
                   ` (12 preceding siblings ...)
  2010-09-02 13:08 ` [PATCH 13/14] virtio: console: Add reference counting for port struct Amit Shah
@ 2010-09-02 13:08 ` Amit Shah
  13 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-02 13:08 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Amit Shah, Virtualization List

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 <amit.shah@redhat.com>
---
 drivers/char/virtio_console.c |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 1e00221..793817b 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1723,6 +1723,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;
-- 
1.7.2.2

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH 02/14] virtio: console: Remove control vq data only if using multiport support
  2010-09-02 12:41 ` [PATCH 02/14] virtio: console: Remove control vq data only if using multiport support Amit Shah
@ 2010-09-06  8:29   ` Rusty Russell
  2010-09-06 10:22     ` Amit Shah
  0 siblings, 1 reply; 17+ messages in thread
From: Rusty Russell @ 2010-09-06  8:29 UTC (permalink / raw)
  To: Amit Shah; +Cc: Virtualization List

On Thu, 2 Sep 2010 10:11:41 pm Amit Shah wrote:
> If a portdev isn't using multiport support, it won't have any control vq
> data to remove.
> 
> Signed-off-by: Amit Shah <amit.shah@redhat.com>

So previously it would crash if it was ever removed?  If so, it'd be
nice to see that in the patch description :)

Thanks,
Rusty.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 02/14] virtio: console: Remove control vq data only if using multiport support
  2010-09-06  8:29   ` Rusty Russell
@ 2010-09-06 10:22     ` Amit Shah
  0 siblings, 0 replies; 17+ messages in thread
From: Amit Shah @ 2010-09-06 10:22 UTC (permalink / raw)
  To: Rusty Russell; +Cc: Virtualization List

On (Mon) Sep 06 2010 [17:59:39], Rusty Russell wrote:
> On Thu, 2 Sep 2010 10:11:41 pm Amit Shah wrote:
> > If a portdev isn't using multiport support, it won't have any control vq
> > data to remove.
> > 
> > Signed-off-by: Amit Shah <amit.shah@redhat.com>
> 
> So previously it would crash if it was ever removed?  If so, it'd be
> nice to see that in the patch description :)

Well, yeah.  If any module remove or port / device remove was performed
earlier, there would be crashes in multiple places (as noted in the
0/14) message.  I didn't want to scare away people by repeating it for
each patch ;-)

You're right, though, I should've mentioned it.  The 0/N message gets
lost and the commit history won't reflect that info.

Thanks for picking them up!

		Amit

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2010-09-06 10:22 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-02 12:41 [PATCH 00/14] virtio: console: Hot-unplug fixes Amit Shah
2010-09-02 12:41 ` [PATCH 01/14] virtio: console: Reset vdev before removing device Amit Shah
2010-09-02 12:41 ` [PATCH 02/14] virtio: console: Remove control vq data only if using multiport support Amit Shah
2010-09-06  8:29   ` Rusty Russell
2010-09-06 10:22     ` Amit Shah
2010-09-02 12:41 ` [PATCH 03/14] virtio: console: Check if portdev is valid in send_control_msg() Amit Shah
2010-09-02 12:41 ` [PATCH 04/14] virtio: console: Un-block reads on chardev close Amit Shah
2010-09-02 12:41 ` [PATCH 05/14] virtio: console: Unblock poll on port hot-unplug Amit Shah
2010-09-02 12:41 ` [PATCH 06/14] virtio: console: Make read() return -ENODEV on hot-unplug Amit Shah
2010-09-02 12:41 ` [PATCH 07/14] virtio: console: Make write() " Amit Shah
2010-09-02 12:41 ` [PATCH 08/14] virtio: console: remove_port() should return void Amit Shah
2010-09-02 12:41 ` [PATCH 09/14] virtio: console: open: Use a common path for error handling Amit Shah
2010-09-02 12:41 ` [PATCH 10/14] virtio: console: Add a list of portdevs that are active Amit Shah
2010-09-02 12:50 ` [PATCH 11/14] virtio: console: Add a find_port_by_devt() function Amit Shah
2010-09-02 12:50 ` [PATCH 12/14] virtio: console: Use cdev_alloc() instead of cdev_init() Amit Shah
2010-09-02 13:08 ` [PATCH 13/14] virtio: console: Add reference counting for port struct Amit Shah
2010-09-02 13:08 ` [PATCH 14/14] virtio: console: Reference counting portdev structs is not needed Amit Shah

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.