aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/virtio_console.c
diff options
context:
space:
mode:
authorAmit Shah <amit.shah@redhat.com>2010-09-02 09:08:29 -0400
committerRusty Russell <rusty@rustcorp.com.au>2010-10-21 03:14:03 -0400
commitb353a6b821627053f82b4e7b907e824cb7a6879c (patch)
treea863bec4278a403bd63336b4f01b45794a987805 /drivers/char/virtio_console.c
parentd22a69892bd8f29e3096f6f54c2c00d8aec2e796 (diff)
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 <amit.shah@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/char/virtio_console.c')
-rw-r--r--drivers/char/virtio_console.c80
1 files changed, 64 insertions, 16 deletions
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 {
187 struct cdev *cdev; 187 struct cdev *cdev;
188 struct device *dev; 188 struct device *dev;
189 189
190 /* Reference-counting to handle port hot-unplugs and file operations */
191 struct kref kref;
192
190 /* A waitqueue for poll() or blocking read operations */ 193 /* A waitqueue for poll() or blocking read operations */
191 wait_queue_head_t waitqueue; 194 wait_queue_head_t waitqueue;
192 195
@@ -725,6 +728,8 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait)
725 return ret; 728 return ret;
726} 729}
727 730
731static void remove_port(struct kref *kref);
732
728static int port_fops_release(struct inode *inode, struct file *filp) 733static int port_fops_release(struct inode *inode, struct file *filp)
729{ 734{
730 struct port *port; 735 struct port *port;
@@ -745,6 +750,16 @@ static int port_fops_release(struct inode *inode, struct file *filp)
745 reclaim_consumed_buffers(port); 750 reclaim_consumed_buffers(port);
746 spin_unlock_irq(&port->outvq_lock); 751 spin_unlock_irq(&port->outvq_lock);
747 752
753 /*
754 * Locks aren't necessary here as a port can't be opened after
755 * unplug, and if a port isn't unplugged, a kref would already
756 * exist for the port. Plus, taking ports_lock here would
757 * create a dependency on other locks taken by functions
758 * inside remove_port if we're the last holder of the port,
759 * creating many problems.
760 */
761 kref_put(&port->kref, remove_port);
762
748 return 0; 763 return 0;
749} 764}
750 765
@@ -757,6 +772,11 @@ static int port_fops_open(struct inode *inode, struct file *filp)
757 port = find_port_by_devt(cdev->dev); 772 port = find_port_by_devt(cdev->dev);
758 filp->private_data = port; 773 filp->private_data = port;
759 774
775 /* Prevent against a port getting hot-unplugged at the same time */
776 spin_lock_irq(&port->portdev->ports_lock);
777 kref_get(&port->kref);
778 spin_unlock_irq(&port->portdev->ports_lock);
779
760 /* 780 /*
761 * Don't allow opening of console port devices -- that's done 781 * Don't allow opening of console port devices -- that's done
762 * via /dev/hvc 782 * via /dev/hvc
@@ -791,6 +811,7 @@ static int port_fops_open(struct inode *inode, struct file *filp)
791 811
792 return 0; 812 return 0;
793out: 813out:
814 kref_put(&port->kref, remove_port);
794 return ret; 815 return ret;
795} 816}
796 817
@@ -1079,6 +1100,7 @@ static int add_port(struct ports_device *portdev, u32 id)
1079 err = -ENOMEM; 1100 err = -ENOMEM;
1080 goto fail; 1101 goto fail;
1081 } 1102 }
1103 kref_init(&port->kref);
1082 1104
1083 port->portdev = portdev; 1105 port->portdev = portdev;
1084 port->id = id; 1106 port->id = id;
@@ -1183,22 +1205,43 @@ fail:
1183 return err; 1205 return err;
1184} 1206}
1185 1207
1186/* Remove all port-specific data. */ 1208/* No users remain, remove all port-specific data. */
1187static void remove_port(struct port *port) 1209static void remove_port(struct kref *kref)
1210{
1211 struct port *port;
1212
1213 port = container_of(kref, struct port, kref);
1214
1215 sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
1216 device_destroy(pdrvdata.class, port->dev->devt);
1217 cdev_del(port->cdev);
1218
1219 kfree(port->name);
1220
1221 debugfs_remove(port->debugfs_file);
1222
1223 kfree(port);
1224}
1225
1226/*
1227 * Port got unplugged. Remove port from portdev's list and drop the
1228 * kref reference. If no userspace has this port opened, it will
1229 * result in immediate removal the port.
1230 */
1231static void unplug_port(struct port *port)
1188{ 1232{
1189 struct port_buffer *buf; 1233 struct port_buffer *buf;
1190 1234
1235 spin_lock_irq(&port->portdev->ports_lock);
1236 list_del(&port->list);
1237 spin_unlock_irq(&port->portdev->ports_lock);
1238
1191 if (port->guest_connected) { 1239 if (port->guest_connected) {
1192 port->guest_connected = false; 1240 port->guest_connected = false;
1193 port->host_connected = false; 1241 port->host_connected = false;
1194 wake_up_interruptible(&port->waitqueue); 1242 wake_up_interruptible(&port->waitqueue);
1195 send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
1196 } 1243 }
1197 1244
1198 spin_lock_irq(&port->portdev->ports_lock);
1199 list_del(&port->list);
1200 spin_unlock_irq(&port->portdev->ports_lock);
1201
1202 if (is_console_port(port)) { 1245 if (is_console_port(port)) {
1203 spin_lock_irq(&pdrvdata_lock); 1246 spin_lock_irq(&pdrvdata_lock);
1204 list_del(&port->cons.list); 1247 list_del(&port->cons.list);
@@ -1216,9 +1259,6 @@ static void remove_port(struct port *port)
1216 hvc_remove(port->cons.hvc); 1259 hvc_remove(port->cons.hvc);
1217#endif 1260#endif
1218 } 1261 }
1219 sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
1220 device_destroy(pdrvdata.class, port->dev->devt);
1221 cdev_del(port->cdev);
1222 1262
1223 /* Remove unused data this port might have received. */ 1263 /* Remove unused data this port might have received. */
1224 discard_port_data(port); 1264 discard_port_data(port);
@@ -1229,11 +1269,19 @@ static void remove_port(struct port *port)
1229 while ((buf = virtqueue_detach_unused_buf(port->in_vq))) 1269 while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
1230 free_buf(buf); 1270 free_buf(buf);
1231 1271
1232 kfree(port->name); 1272 /*
1233 1273 * We should just assume the device itself has gone off --
1234 debugfs_remove(port->debugfs_file); 1274 * else a close on an open port later will try to send out a
1275 * control message.
1276 */
1277 port->portdev = NULL;
1235 1278
1236 kfree(port); 1279 /*
1280 * Locks around here are not necessary - a port can't be
1281 * opened after we removed the port struct from ports_list
1282 * above.
1283 */
1284 kref_put(&port->kref, remove_port);
1237} 1285}
1238 1286
1239/* Any private messages that the Host and Guest want to share */ 1287/* Any private messages that the Host and Guest want to share */
@@ -1272,7 +1320,7 @@ static void handle_control_message(struct ports_device *portdev,
1272 add_port(portdev, cpkt->id); 1320 add_port(portdev, cpkt->id);
1273 break; 1321 break;
1274 case VIRTIO_CONSOLE_PORT_REMOVE: 1322 case VIRTIO_CONSOLE_PORT_REMOVE:
1275 remove_port(port); 1323 unplug_port(port);
1276 break; 1324 break;
1277 case VIRTIO_CONSOLE_CONSOLE_PORT: 1325 case VIRTIO_CONSOLE_CONSOLE_PORT:
1278 if (!cpkt->value) 1326 if (!cpkt->value)
@@ -1686,7 +1734,7 @@ static void virtcons_remove(struct virtio_device *vdev)
1686 cancel_work_sync(&portdev->control_work); 1734 cancel_work_sync(&portdev->control_work);
1687 1735
1688 list_for_each_entry_safe(port, port2, &portdev->ports, list) 1736 list_for_each_entry_safe(port, port2, &portdev->ports, list)
1689 remove_port(port); 1737 unplug_port(port);
1690 1738
1691 unregister_chrdev(portdev->chr_major, "virtio-portsdev"); 1739 unregister_chrdev(portdev->chr_major, "virtio-portsdev");
1692 1740