diff options
Diffstat (limited to 'drivers/char/virtio_console.c')
-rw-r--r-- | drivers/char/virtio_console.c | 321 |
1 files changed, 240 insertions, 81 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 0f69c5ec0ecd..fb68b1295373 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c | |||
@@ -1,6 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation | 2 | * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation |
3 | * Copyright (C) 2009, 2010 Red Hat, Inc. | 3 | * Copyright (C) 2009, 2010, 2011 Red Hat, Inc. |
4 | * Copyright (C) 2009, 2010, 2011 Amit Shah <amit.shah@redhat.com> | ||
4 | * | 5 | * |
5 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by |
@@ -31,7 +32,7 @@ | |||
31 | #include <linux/virtio_console.h> | 32 | #include <linux/virtio_console.h> |
32 | #include <linux/wait.h> | 33 | #include <linux/wait.h> |
33 | #include <linux/workqueue.h> | 34 | #include <linux/workqueue.h> |
34 | #include "hvc_console.h" | 35 | #include "../tty/hvc/hvc_console.h" |
35 | 36 | ||
36 | /* | 37 | /* |
37 | * This is a global struct for storing common data for all the devices | 38 | * This is a global struct for storing common data for all the devices |
@@ -48,6 +49,9 @@ struct ports_driver_data { | |||
48 | /* Used for exporting per-port information to debugfs */ | 49 | /* Used for exporting per-port information to debugfs */ |
49 | struct dentry *debugfs_dir; | 50 | struct dentry *debugfs_dir; |
50 | 51 | ||
52 | /* List of all the devices we're handling */ | ||
53 | struct list_head portdevs; | ||
54 | |||
51 | /* Number of devices this driver is handling */ | 55 | /* Number of devices this driver is handling */ |
52 | unsigned int index; | 56 | unsigned int index; |
53 | 57 | ||
@@ -108,6 +112,9 @@ struct port_buffer { | |||
108 | * ports for that device (vdev->priv). | 112 | * ports for that device (vdev->priv). |
109 | */ | 113 | */ |
110 | struct ports_device { | 114 | struct ports_device { |
115 | /* Next portdev in the list, head is in the pdrvdata struct */ | ||
116 | struct list_head list; | ||
117 | |||
111 | /* | 118 | /* |
112 | * Workqueue handlers where we process deferred work after | 119 | * Workqueue handlers where we process deferred work after |
113 | * notification | 120 | * notification |
@@ -178,15 +185,21 @@ struct port { | |||
178 | struct console cons; | 185 | struct console cons; |
179 | 186 | ||
180 | /* Each port associates with a separate char device */ | 187 | /* Each port associates with a separate char device */ |
181 | struct cdev cdev; | 188 | struct cdev *cdev; |
182 | struct device *dev; | 189 | struct device *dev; |
183 | 190 | ||
191 | /* Reference-counting to handle port hot-unplugs and file operations */ | ||
192 | struct kref kref; | ||
193 | |||
184 | /* A waitqueue for poll() or blocking read operations */ | 194 | /* A waitqueue for poll() or blocking read operations */ |
185 | wait_queue_head_t waitqueue; | 195 | wait_queue_head_t waitqueue; |
186 | 196 | ||
187 | /* The 'name' of the port that we expose via sysfs properties */ | 197 | /* The 'name' of the port that we expose via sysfs properties */ |
188 | char *name; | 198 | char *name; |
189 | 199 | ||
200 | /* We can notify apps of host connect / disconnect events via SIGIO */ | ||
201 | struct fasync_struct *async_queue; | ||
202 | |||
190 | /* The 'id' to identify the port with the Host */ | 203 | /* The 'id' to identify the port with the Host */ |
191 | u32 id; | 204 | u32 id; |
192 | 205 | ||
@@ -221,6 +234,41 @@ out: | |||
221 | return port; | 234 | return port; |
222 | } | 235 | } |
223 | 236 | ||
237 | static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev, | ||
238 | dev_t dev) | ||
239 | { | ||
240 | struct port *port; | ||
241 | unsigned long flags; | ||
242 | |||
243 | spin_lock_irqsave(&portdev->ports_lock, flags); | ||
244 | list_for_each_entry(port, &portdev->ports, list) | ||
245 | if (port->cdev->dev == dev) | ||
246 | goto out; | ||
247 | port = NULL; | ||
248 | out: | ||
249 | spin_unlock_irqrestore(&portdev->ports_lock, flags); | ||
250 | |||
251 | return port; | ||
252 | } | ||
253 | |||
254 | static struct port *find_port_by_devt(dev_t dev) | ||
255 | { | ||
256 | struct ports_device *portdev; | ||
257 | struct port *port; | ||
258 | unsigned long flags; | ||
259 | |||
260 | spin_lock_irqsave(&pdrvdata_lock, flags); | ||
261 | list_for_each_entry(portdev, &pdrvdata.portdevs, list) { | ||
262 | port = find_port_by_devt_in_portdev(portdev, dev); | ||
263 | if (port) | ||
264 | goto out; | ||
265 | } | ||
266 | port = NULL; | ||
267 | out: | ||
268 | spin_unlock_irqrestore(&pdrvdata_lock, flags); | ||
269 | return port; | ||
270 | } | ||
271 | |||
224 | static struct port *find_port_by_id(struct ports_device *portdev, u32 id) | 272 | static struct port *find_port_by_id(struct ports_device *portdev, u32 id) |
225 | { | 273 | { |
226 | struct port *port; | 274 | struct port *port; |
@@ -340,6 +388,10 @@ static void discard_port_data(struct port *port) | |||
340 | unsigned int len; | 388 | unsigned int len; |
341 | int ret; | 389 | int ret; |
342 | 390 | ||
391 | if (!port->portdev) { | ||
392 | /* Device has been unplugged. vqs are already gone. */ | ||
393 | return; | ||
394 | } | ||
343 | vq = port->in_vq; | 395 | vq = port->in_vq; |
344 | if (port->inbuf) | 396 | if (port->inbuf) |
345 | buf = port->inbuf; | 397 | buf = port->inbuf; |
@@ -410,7 +462,10 @@ static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id, | |||
410 | static ssize_t send_control_msg(struct port *port, unsigned int event, | 462 | static ssize_t send_control_msg(struct port *port, unsigned int event, |
411 | unsigned int value) | 463 | unsigned int value) |
412 | { | 464 | { |
413 | return __send_control_msg(port->portdev, port->id, event, value); | 465 | /* Did the port get unplugged before userspace closed it? */ |
466 | if (port->portdev) | ||
467 | return __send_control_msg(port->portdev, port->id, event, value); | ||
468 | return 0; | ||
414 | } | 469 | } |
415 | 470 | ||
416 | /* Callers must take the port->outvq_lock */ | 471 | /* Callers must take the port->outvq_lock */ |
@@ -419,6 +474,10 @@ static void reclaim_consumed_buffers(struct port *port) | |||
419 | void *buf; | 474 | void *buf; |
420 | unsigned int len; | 475 | unsigned int len; |
421 | 476 | ||
477 | if (!port->portdev) { | ||
478 | /* Device has been unplugged. vqs are already gone. */ | ||
479 | return; | ||
480 | } | ||
422 | while ((buf = virtqueue_get_buf(port->out_vq, &len))) { | 481 | while ((buf = virtqueue_get_buf(port->out_vq, &len))) { |
423 | kfree(buf); | 482 | kfree(buf); |
424 | port->outvq_full = false; | 483 | port->outvq_full = false; |
@@ -525,6 +584,10 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, | |||
525 | /* The condition that must be true for polling to end */ | 584 | /* The condition that must be true for polling to end */ |
526 | static bool will_read_block(struct port *port) | 585 | static bool will_read_block(struct port *port) |
527 | { | 586 | { |
587 | if (!port->guest_connected) { | ||
588 | /* Port got hot-unplugged. Let's exit. */ | ||
589 | return false; | ||
590 | } | ||
528 | return !port_has_data(port) && port->host_connected; | 591 | return !port_has_data(port) && port->host_connected; |
529 | } | 592 | } |
530 | 593 | ||
@@ -575,6 +638,9 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf, | |||
575 | if (ret < 0) | 638 | if (ret < 0) |
576 | return ret; | 639 | return ret; |
577 | } | 640 | } |
641 | /* Port got hot-unplugged. */ | ||
642 | if (!port->guest_connected) | ||
643 | return -ENODEV; | ||
578 | /* | 644 | /* |
579 | * We could've received a disconnection message while we were | 645 | * We could've received a disconnection message while we were |
580 | * waiting for more data. | 646 | * waiting for more data. |
@@ -616,6 +682,9 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, | |||
616 | if (ret < 0) | 682 | if (ret < 0) |
617 | return ret; | 683 | return ret; |
618 | } | 684 | } |
685 | /* Port got hot-unplugged. */ | ||
686 | if (!port->guest_connected) | ||
687 | return -ENODEV; | ||
619 | 688 | ||
620 | count = min((size_t)(32 * 1024), count); | 689 | count = min((size_t)(32 * 1024), count); |
621 | 690 | ||
@@ -656,6 +725,10 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait) | |||
656 | port = filp->private_data; | 725 | port = filp->private_data; |
657 | poll_wait(filp, &port->waitqueue, wait); | 726 | poll_wait(filp, &port->waitqueue, wait); |
658 | 727 | ||
728 | if (!port->guest_connected) { | ||
729 | /* Port got unplugged */ | ||
730 | return POLLHUP; | ||
731 | } | ||
659 | ret = 0; | 732 | ret = 0; |
660 | if (!will_read_block(port)) | 733 | if (!will_read_block(port)) |
661 | ret |= POLLIN | POLLRDNORM; | 734 | ret |= POLLIN | POLLRDNORM; |
@@ -667,6 +740,8 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait) | |||
667 | return ret; | 740 | return ret; |
668 | } | 741 | } |
669 | 742 | ||
743 | static void remove_port(struct kref *kref); | ||
744 | |||
670 | static int port_fops_release(struct inode *inode, struct file *filp) | 745 | static int port_fops_release(struct inode *inode, struct file *filp) |
671 | { | 746 | { |
672 | struct port *port; | 747 | struct port *port; |
@@ -687,6 +762,16 @@ static int port_fops_release(struct inode *inode, struct file *filp) | |||
687 | reclaim_consumed_buffers(port); | 762 | reclaim_consumed_buffers(port); |
688 | spin_unlock_irq(&port->outvq_lock); | 763 | spin_unlock_irq(&port->outvq_lock); |
689 | 764 | ||
765 | /* | ||
766 | * Locks aren't necessary here as a port can't be opened after | ||
767 | * unplug, and if a port isn't unplugged, a kref would already | ||
768 | * exist for the port. Plus, taking ports_lock here would | ||
769 | * create a dependency on other locks taken by functions | ||
770 | * inside remove_port if we're the last holder of the port, | ||
771 | * creating many problems. | ||
772 | */ | ||
773 | kref_put(&port->kref, remove_port); | ||
774 | |||
690 | return 0; | 775 | return 0; |
691 | } | 776 | } |
692 | 777 | ||
@@ -694,22 +779,31 @@ static int port_fops_open(struct inode *inode, struct file *filp) | |||
694 | { | 779 | { |
695 | struct cdev *cdev = inode->i_cdev; | 780 | struct cdev *cdev = inode->i_cdev; |
696 | struct port *port; | 781 | struct port *port; |
782 | int ret; | ||
697 | 783 | ||
698 | port = container_of(cdev, struct port, cdev); | 784 | port = find_port_by_devt(cdev->dev); |
699 | filp->private_data = port; | 785 | filp->private_data = port; |
700 | 786 | ||
787 | /* Prevent against a port getting hot-unplugged at the same time */ | ||
788 | spin_lock_irq(&port->portdev->ports_lock); | ||
789 | kref_get(&port->kref); | ||
790 | spin_unlock_irq(&port->portdev->ports_lock); | ||
791 | |||
701 | /* | 792 | /* |
702 | * Don't allow opening of console port devices -- that's done | 793 | * Don't allow opening of console port devices -- that's done |
703 | * via /dev/hvc | 794 | * via /dev/hvc |
704 | */ | 795 | */ |
705 | if (is_console_port(port)) | 796 | if (is_console_port(port)) { |
706 | return -ENXIO; | 797 | ret = -ENXIO; |
798 | goto out; | ||
799 | } | ||
707 | 800 | ||
708 | /* Allow only one process to open a particular port at a time */ | 801 | /* Allow only one process to open a particular port at a time */ |
709 | spin_lock_irq(&port->inbuf_lock); | 802 | spin_lock_irq(&port->inbuf_lock); |
710 | if (port->guest_connected) { | 803 | if (port->guest_connected) { |
711 | spin_unlock_irq(&port->inbuf_lock); | 804 | spin_unlock_irq(&port->inbuf_lock); |
712 | return -EMFILE; | 805 | ret = -EMFILE; |
806 | goto out; | ||
713 | } | 807 | } |
714 | 808 | ||
715 | port->guest_connected = true; | 809 | port->guest_connected = true; |
@@ -724,10 +818,23 @@ static int port_fops_open(struct inode *inode, struct file *filp) | |||
724 | reclaim_consumed_buffers(port); | 818 | reclaim_consumed_buffers(port); |
725 | spin_unlock_irq(&port->outvq_lock); | 819 | spin_unlock_irq(&port->outvq_lock); |
726 | 820 | ||
821 | nonseekable_open(inode, filp); | ||
822 | |||
727 | /* Notify host of port being opened */ | 823 | /* Notify host of port being opened */ |
728 | send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); | 824 | send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); |
729 | 825 | ||
730 | return 0; | 826 | return 0; |
827 | out: | ||
828 | kref_put(&port->kref, remove_port); | ||
829 | return ret; | ||
830 | } | ||
831 | |||
832 | static int port_fops_fasync(int fd, struct file *filp, int mode) | ||
833 | { | ||
834 | struct port *port; | ||
835 | |||
836 | port = filp->private_data; | ||
837 | return fasync_helper(fd, filp, mode, &port->async_queue); | ||
731 | } | 838 | } |
732 | 839 | ||
733 | /* | 840 | /* |
@@ -743,6 +850,8 @@ static const struct file_operations port_fops = { | |||
743 | .write = port_fops_write, | 850 | .write = port_fops_write, |
744 | .poll = port_fops_poll, | 851 | .poll = port_fops_poll, |
745 | .release = port_fops_release, | 852 | .release = port_fops_release, |
853 | .fasync = port_fops_fasync, | ||
854 | .llseek = no_llseek, | ||
746 | }; | 855 | }; |
747 | 856 | ||
748 | /* | 857 | /* |
@@ -1001,6 +1110,12 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) | |||
1001 | return nr_added_bufs; | 1110 | return nr_added_bufs; |
1002 | } | 1111 | } |
1003 | 1112 | ||
1113 | static void send_sigio_to_port(struct port *port) | ||
1114 | { | ||
1115 | if (port->async_queue && port->guest_connected) | ||
1116 | kill_fasync(&port->async_queue, SIGIO, POLL_OUT); | ||
1117 | } | ||
1118 | |||
1004 | static int add_port(struct ports_device *portdev, u32 id) | 1119 | static int add_port(struct ports_device *portdev, u32 id) |
1005 | { | 1120 | { |
1006 | char debugfs_name[16]; | 1121 | char debugfs_name[16]; |
@@ -1015,6 +1130,7 @@ static int add_port(struct ports_device *portdev, u32 id) | |||
1015 | err = -ENOMEM; | 1130 | err = -ENOMEM; |
1016 | goto fail; | 1131 | goto fail; |
1017 | } | 1132 | } |
1133 | kref_init(&port->kref); | ||
1018 | 1134 | ||
1019 | port->portdev = portdev; | 1135 | port->portdev = portdev; |
1020 | port->id = id; | 1136 | port->id = id; |
@@ -1022,6 +1138,7 @@ static int add_port(struct ports_device *portdev, u32 id) | |||
1022 | port->name = NULL; | 1138 | port->name = NULL; |
1023 | port->inbuf = NULL; | 1139 | port->inbuf = NULL; |
1024 | port->cons.hvc = NULL; | 1140 | port->cons.hvc = NULL; |
1141 | port->async_queue = NULL; | ||
1025 | 1142 | ||
1026 | port->cons.ws.ws_row = port->cons.ws.ws_col = 0; | 1143 | port->cons.ws.ws_row = port->cons.ws.ws_col = 0; |
1027 | 1144 | ||
@@ -1032,14 +1149,20 @@ static int add_port(struct ports_device *portdev, u32 id) | |||
1032 | port->in_vq = portdev->in_vqs[port->id]; | 1149 | port->in_vq = portdev->in_vqs[port->id]; |
1033 | port->out_vq = portdev->out_vqs[port->id]; | 1150 | port->out_vq = portdev->out_vqs[port->id]; |
1034 | 1151 | ||
1035 | cdev_init(&port->cdev, &port_fops); | 1152 | port->cdev = cdev_alloc(); |
1153 | if (!port->cdev) { | ||
1154 | dev_err(&port->portdev->vdev->dev, "Error allocating cdev\n"); | ||
1155 | err = -ENOMEM; | ||
1156 | goto free_port; | ||
1157 | } | ||
1158 | port->cdev->ops = &port_fops; | ||
1036 | 1159 | ||
1037 | devt = MKDEV(portdev->chr_major, id); | 1160 | devt = MKDEV(portdev->chr_major, id); |
1038 | err = cdev_add(&port->cdev, devt, 1); | 1161 | err = cdev_add(port->cdev, devt, 1); |
1039 | if (err < 0) { | 1162 | if (err < 0) { |
1040 | dev_err(&port->portdev->vdev->dev, | 1163 | dev_err(&port->portdev->vdev->dev, |
1041 | "Error %d adding cdev for port %u\n", err, id); | 1164 | "Error %d adding cdev for port %u\n", err, id); |
1042 | goto free_port; | 1165 | goto free_cdev; |
1043 | } | 1166 | } |
1044 | port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, | 1167 | port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, |
1045 | devt, port, "vport%up%u", | 1168 | devt, port, "vport%up%u", |
@@ -1104,7 +1227,7 @@ free_inbufs: | |||
1104 | free_device: | 1227 | free_device: |
1105 | device_destroy(pdrvdata.class, port->dev->devt); | 1228 | device_destroy(pdrvdata.class, port->dev->devt); |
1106 | free_cdev: | 1229 | free_cdev: |
1107 | cdev_del(&port->cdev); | 1230 | cdev_del(port->cdev); |
1108 | free_port: | 1231 | free_port: |
1109 | kfree(port); | 1232 | kfree(port); |
1110 | fail: | 1233 | fail: |
@@ -1113,42 +1236,52 @@ fail: | |||
1113 | return err; | 1236 | return err; |
1114 | } | 1237 | } |
1115 | 1238 | ||
1116 | /* Remove all port-specific data. */ | 1239 | /* No users remain, remove all port-specific data. */ |
1117 | static int remove_port(struct port *port) | 1240 | static void remove_port(struct kref *kref) |
1241 | { | ||
1242 | struct port *port; | ||
1243 | |||
1244 | port = container_of(kref, struct port, kref); | ||
1245 | |||
1246 | sysfs_remove_group(&port->dev->kobj, &port_attribute_group); | ||
1247 | device_destroy(pdrvdata.class, port->dev->devt); | ||
1248 | cdev_del(port->cdev); | ||
1249 | |||
1250 | kfree(port->name); | ||
1251 | |||
1252 | debugfs_remove(port->debugfs_file); | ||
1253 | |||
1254 | kfree(port); | ||
1255 | } | ||
1256 | |||
1257 | /* | ||
1258 | * Port got unplugged. Remove port from portdev's list and drop the | ||
1259 | * kref reference. If no userspace has this port opened, it will | ||
1260 | * result in immediate removal the port. | ||
1261 | */ | ||
1262 | static void unplug_port(struct port *port) | ||
1118 | { | 1263 | { |
1119 | struct port_buffer *buf; | 1264 | struct port_buffer *buf; |
1120 | 1265 | ||
1266 | spin_lock_irq(&port->portdev->ports_lock); | ||
1267 | list_del(&port->list); | ||
1268 | spin_unlock_irq(&port->portdev->ports_lock); | ||
1269 | |||
1121 | if (port->guest_connected) { | 1270 | if (port->guest_connected) { |
1122 | port->guest_connected = false; | 1271 | port->guest_connected = false; |
1123 | port->host_connected = false; | 1272 | port->host_connected = false; |
1124 | wake_up_interruptible(&port->waitqueue); | 1273 | wake_up_interruptible(&port->waitqueue); |
1125 | send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); | ||
1126 | } | ||
1127 | 1274 | ||
1128 | spin_lock_irq(&port->portdev->ports_lock); | 1275 | /* Let the app know the port is going down. */ |
1129 | list_del(&port->list); | 1276 | send_sigio_to_port(port); |
1130 | spin_unlock_irq(&port->portdev->ports_lock); | 1277 | } |
1131 | 1278 | ||
1132 | if (is_console_port(port)) { | 1279 | if (is_console_port(port)) { |
1133 | spin_lock_irq(&pdrvdata_lock); | 1280 | spin_lock_irq(&pdrvdata_lock); |
1134 | list_del(&port->cons.list); | 1281 | list_del(&port->cons.list); |
1135 | spin_unlock_irq(&pdrvdata_lock); | 1282 | spin_unlock_irq(&pdrvdata_lock); |
1136 | #if 0 | ||
1137 | /* | ||
1138 | * hvc_remove() not called as removing one hvc port | ||
1139 | * results in other hvc ports getting frozen. | ||
1140 | * | ||
1141 | * Once this is resolved in hvc, this functionality | ||
1142 | * will be enabled. Till that is done, the -EPIPE | ||
1143 | * return from get_chars() above will help | ||
1144 | * hvc_console.c to clean up on ports we remove here. | ||
1145 | */ | ||
1146 | hvc_remove(port->cons.hvc); | 1283 | hvc_remove(port->cons.hvc); |
1147 | #endif | ||
1148 | } | 1284 | } |
1149 | sysfs_remove_group(&port->dev->kobj, &port_attribute_group); | ||
1150 | device_destroy(pdrvdata.class, port->dev->devt); | ||
1151 | cdev_del(&port->cdev); | ||
1152 | 1285 | ||
1153 | /* Remove unused data this port might have received. */ | 1286 | /* Remove unused data this port might have received. */ |
1154 | discard_port_data(port); | 1287 | discard_port_data(port); |
@@ -1159,12 +1292,19 @@ static int remove_port(struct port *port) | |||
1159 | while ((buf = virtqueue_detach_unused_buf(port->in_vq))) | 1292 | while ((buf = virtqueue_detach_unused_buf(port->in_vq))) |
1160 | free_buf(buf); | 1293 | free_buf(buf); |
1161 | 1294 | ||
1162 | kfree(port->name); | 1295 | /* |
1163 | 1296 | * We should just assume the device itself has gone off -- | |
1164 | debugfs_remove(port->debugfs_file); | 1297 | * else a close on an open port later will try to send out a |
1298 | * control message. | ||
1299 | */ | ||
1300 | port->portdev = NULL; | ||
1165 | 1301 | ||
1166 | kfree(port); | 1302 | /* |
1167 | return 0; | 1303 | * Locks around here are not necessary - a port can't be |
1304 | * opened after we removed the port struct from ports_list | ||
1305 | * above. | ||
1306 | */ | ||
1307 | kref_put(&port->kref, remove_port); | ||
1168 | } | 1308 | } |
1169 | 1309 | ||
1170 | /* Any private messages that the Host and Guest want to share */ | 1310 | /* Any private messages that the Host and Guest want to share */ |
@@ -1203,7 +1343,7 @@ static void handle_control_message(struct ports_device *portdev, | |||
1203 | add_port(portdev, cpkt->id); | 1343 | add_port(portdev, cpkt->id); |
1204 | break; | 1344 | break; |
1205 | case VIRTIO_CONSOLE_PORT_REMOVE: | 1345 | case VIRTIO_CONSOLE_PORT_REMOVE: |
1206 | remove_port(port); | 1346 | unplug_port(port); |
1207 | break; | 1347 | break; |
1208 | case VIRTIO_CONSOLE_CONSOLE_PORT: | 1348 | case VIRTIO_CONSOLE_CONSOLE_PORT: |
1209 | if (!cpkt->value) | 1349 | if (!cpkt->value) |
@@ -1245,6 +1385,12 @@ static void handle_control_message(struct ports_device *portdev, | |||
1245 | spin_lock_irq(&port->outvq_lock); | 1385 | spin_lock_irq(&port->outvq_lock); |
1246 | reclaim_consumed_buffers(port); | 1386 | reclaim_consumed_buffers(port); |
1247 | spin_unlock_irq(&port->outvq_lock); | 1387 | spin_unlock_irq(&port->outvq_lock); |
1388 | |||
1389 | /* | ||
1390 | * If the guest is connected, it'll be interested in | ||
1391 | * knowing the host connection state changed. | ||
1392 | */ | ||
1393 | send_sigio_to_port(port); | ||
1248 | break; | 1394 | break; |
1249 | case VIRTIO_CONSOLE_PORT_NAME: | 1395 | case VIRTIO_CONSOLE_PORT_NAME: |
1250 | /* | 1396 | /* |
@@ -1314,6 +1460,17 @@ static void control_work_handler(struct work_struct *work) | |||
1314 | spin_unlock(&portdev->cvq_lock); | 1460 | spin_unlock(&portdev->cvq_lock); |
1315 | } | 1461 | } |
1316 | 1462 | ||
1463 | static void out_intr(struct virtqueue *vq) | ||
1464 | { | ||
1465 | struct port *port; | ||
1466 | |||
1467 | port = find_port_by_vq(vq->vdev->priv, vq); | ||
1468 | if (!port) | ||
1469 | return; | ||
1470 | |||
1471 | wake_up_interruptible(&port->waitqueue); | ||
1472 | } | ||
1473 | |||
1317 | static void in_intr(struct virtqueue *vq) | 1474 | static void in_intr(struct virtqueue *vq) |
1318 | { | 1475 | { |
1319 | struct port *port; | 1476 | struct port *port; |
@@ -1341,6 +1498,9 @@ static void in_intr(struct virtqueue *vq) | |||
1341 | 1498 | ||
1342 | wake_up_interruptible(&port->waitqueue); | 1499 | wake_up_interruptible(&port->waitqueue); |
1343 | 1500 | ||
1501 | /* Send a SIGIO indicating new data in case the process asked for it */ | ||
1502 | send_sigio_to_port(port); | ||
1503 | |||
1344 | if (is_console_port(port) && hvc_poll(port->cons.hvc)) | 1504 | if (is_console_port(port) && hvc_poll(port->cons.hvc)) |
1345 | hvc_kick(); | 1505 | hvc_kick(); |
1346 | } | 1506 | } |
@@ -1396,31 +1556,16 @@ static int init_vqs(struct ports_device *portdev) | |||
1396 | nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2; | 1556 | nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2; |
1397 | 1557 | ||
1398 | vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL); | 1558 | vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL); |
1399 | if (!vqs) { | ||
1400 | err = -ENOMEM; | ||
1401 | goto fail; | ||
1402 | } | ||
1403 | io_callbacks = kmalloc(nr_queues * sizeof(vq_callback_t *), GFP_KERNEL); | 1559 | io_callbacks = kmalloc(nr_queues * sizeof(vq_callback_t *), GFP_KERNEL); |
1404 | if (!io_callbacks) { | ||
1405 | err = -ENOMEM; | ||
1406 | goto free_vqs; | ||
1407 | } | ||
1408 | io_names = kmalloc(nr_queues * sizeof(char *), GFP_KERNEL); | 1560 | io_names = kmalloc(nr_queues * sizeof(char *), GFP_KERNEL); |
1409 | if (!io_names) { | ||
1410 | err = -ENOMEM; | ||
1411 | goto free_callbacks; | ||
1412 | } | ||
1413 | portdev->in_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), | 1561 | portdev->in_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), |
1414 | GFP_KERNEL); | 1562 | GFP_KERNEL); |
1415 | if (!portdev->in_vqs) { | ||
1416 | err = -ENOMEM; | ||
1417 | goto free_names; | ||
1418 | } | ||
1419 | portdev->out_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), | 1563 | portdev->out_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), |
1420 | GFP_KERNEL); | 1564 | GFP_KERNEL); |
1421 | if (!portdev->out_vqs) { | 1565 | if (!vqs || !io_callbacks || !io_names || !portdev->in_vqs || |
1566 | !portdev->out_vqs) { | ||
1422 | err = -ENOMEM; | 1567 | err = -ENOMEM; |
1423 | goto free_invqs; | 1568 | goto free; |
1424 | } | 1569 | } |
1425 | 1570 | ||
1426 | /* | 1571 | /* |
@@ -1430,7 +1575,7 @@ static int init_vqs(struct ports_device *portdev) | |||
1430 | */ | 1575 | */ |
1431 | j = 0; | 1576 | j = 0; |
1432 | io_callbacks[j] = in_intr; | 1577 | io_callbacks[j] = in_intr; |
1433 | io_callbacks[j + 1] = NULL; | 1578 | io_callbacks[j + 1] = out_intr; |
1434 | io_names[j] = "input"; | 1579 | io_names[j] = "input"; |
1435 | io_names[j + 1] = "output"; | 1580 | io_names[j + 1] = "output"; |
1436 | j += 2; | 1581 | j += 2; |
@@ -1444,7 +1589,7 @@ static int init_vqs(struct ports_device *portdev) | |||
1444 | for (i = 1; i < nr_ports; i++) { | 1589 | for (i = 1; i < nr_ports; i++) { |
1445 | j += 2; | 1590 | j += 2; |
1446 | io_callbacks[j] = in_intr; | 1591 | io_callbacks[j] = in_intr; |
1447 | io_callbacks[j + 1] = NULL; | 1592 | io_callbacks[j + 1] = out_intr; |
1448 | io_names[j] = "input"; | 1593 | io_names[j] = "input"; |
1449 | io_names[j + 1] = "output"; | 1594 | io_names[j + 1] = "output"; |
1450 | } | 1595 | } |
@@ -1454,7 +1599,7 @@ static int init_vqs(struct ports_device *portdev) | |||
1454 | io_callbacks, | 1599 | io_callbacks, |
1455 | (const char **)io_names); | 1600 | (const char **)io_names); |
1456 | if (err) | 1601 | if (err) |
1457 | goto free_outvqs; | 1602 | goto free; |
1458 | 1603 | ||
1459 | j = 0; | 1604 | j = 0; |
1460 | portdev->in_vqs[0] = vqs[0]; | 1605 | portdev->in_vqs[0] = vqs[0]; |
@@ -1470,23 +1615,19 @@ static int init_vqs(struct ports_device *portdev) | |||
1470 | portdev->out_vqs[i] = vqs[j + 1]; | 1615 | portdev->out_vqs[i] = vqs[j + 1]; |
1471 | } | 1616 | } |
1472 | } | 1617 | } |
1473 | kfree(io_callbacks); | ||
1474 | kfree(io_names); | 1618 | kfree(io_names); |
1619 | kfree(io_callbacks); | ||
1475 | kfree(vqs); | 1620 | kfree(vqs); |
1476 | 1621 | ||
1477 | return 0; | 1622 | return 0; |
1478 | 1623 | ||
1479 | free_names: | 1624 | free: |
1480 | kfree(io_names); | ||
1481 | free_callbacks: | ||
1482 | kfree(io_callbacks); | ||
1483 | free_outvqs: | ||
1484 | kfree(portdev->out_vqs); | 1625 | kfree(portdev->out_vqs); |
1485 | free_invqs: | ||
1486 | kfree(portdev->in_vqs); | 1626 | kfree(portdev->in_vqs); |
1487 | free_vqs: | 1627 | kfree(io_names); |
1628 | kfree(io_callbacks); | ||
1488 | kfree(vqs); | 1629 | kfree(vqs); |
1489 | fail: | 1630 | |
1490 | return err; | 1631 | return err; |
1491 | } | 1632 | } |
1492 | 1633 | ||
@@ -1536,17 +1677,12 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) | |||
1536 | portdev->config.max_nr_ports = 1; | 1677 | portdev->config.max_nr_ports = 1; |
1537 | if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { | 1678 | if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { |
1538 | multiport = true; | 1679 | multiport = true; |
1539 | vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT; | ||
1540 | |||
1541 | vdev->config->get(vdev, offsetof(struct virtio_console_config, | 1680 | vdev->config->get(vdev, offsetof(struct virtio_console_config, |
1542 | max_nr_ports), | 1681 | max_nr_ports), |
1543 | &portdev->config.max_nr_ports, | 1682 | &portdev->config.max_nr_ports, |
1544 | sizeof(portdev->config.max_nr_ports)); | 1683 | sizeof(portdev->config.max_nr_ports)); |
1545 | } | 1684 | } |
1546 | 1685 | ||
1547 | /* Let the Host know we support multiple ports.*/ | ||
1548 | vdev->config->finalize_features(vdev); | ||
1549 | |||
1550 | err = init_vqs(portdev); | 1686 | err = init_vqs(portdev); |
1551 | if (err < 0) { | 1687 | if (err < 0) { |
1552 | dev_err(&vdev->dev, "Error %d initializing vqs\n", err); | 1688 | dev_err(&vdev->dev, "Error %d initializing vqs\n", err); |
@@ -1577,6 +1713,10 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) | |||
1577 | add_port(portdev, 0); | 1713 | add_port(portdev, 0); |
1578 | } | 1714 | } |
1579 | 1715 | ||
1716 | spin_lock_irq(&pdrvdata_lock); | ||
1717 | list_add_tail(&portdev->list, &pdrvdata.portdevs); | ||
1718 | spin_unlock_irq(&pdrvdata_lock); | ||
1719 | |||
1580 | __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, | 1720 | __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, |
1581 | VIRTIO_CONSOLE_DEVICE_READY, 1); | 1721 | VIRTIO_CONSOLE_DEVICE_READY, 1); |
1582 | return 0; | 1722 | return 0; |
@@ -1600,23 +1740,41 @@ static void virtcons_remove(struct virtio_device *vdev) | |||
1600 | { | 1740 | { |
1601 | struct ports_device *portdev; | 1741 | struct ports_device *portdev; |
1602 | struct port *port, *port2; | 1742 | struct port *port, *port2; |
1603 | struct port_buffer *buf; | ||
1604 | unsigned int len; | ||
1605 | 1743 | ||
1606 | portdev = vdev->priv; | 1744 | portdev = vdev->priv; |
1607 | 1745 | ||
1746 | spin_lock_irq(&pdrvdata_lock); | ||
1747 | list_del(&portdev->list); | ||
1748 | spin_unlock_irq(&pdrvdata_lock); | ||
1749 | |||
1750 | /* Disable interrupts for vqs */ | ||
1751 | vdev->config->reset(vdev); | ||
1752 | /* Finish up work that's lined up */ | ||
1608 | cancel_work_sync(&portdev->control_work); | 1753 | cancel_work_sync(&portdev->control_work); |
1609 | 1754 | ||
1610 | list_for_each_entry_safe(port, port2, &portdev->ports, list) | 1755 | list_for_each_entry_safe(port, port2, &portdev->ports, list) |
1611 | remove_port(port); | 1756 | unplug_port(port); |
1612 | 1757 | ||
1613 | unregister_chrdev(portdev->chr_major, "virtio-portsdev"); | 1758 | unregister_chrdev(portdev->chr_major, "virtio-portsdev"); |
1614 | 1759 | ||
1615 | while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) | 1760 | /* |
1616 | free_buf(buf); | 1761 | * When yanking out a device, we immediately lose the |
1762 | * (device-side) queues. So there's no point in keeping the | ||
1763 | * guest side around till we drop our final reference. This | ||
1764 | * also means that any ports which are in an open state will | ||
1765 | * have to just stop using the port, as the vqs are going | ||
1766 | * away. | ||
1767 | */ | ||
1768 | if (use_multiport(portdev)) { | ||
1769 | struct port_buffer *buf; | ||
1770 | unsigned int len; | ||
1617 | 1771 | ||
1618 | while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) | 1772 | while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) |
1619 | free_buf(buf); | 1773 | free_buf(buf); |
1774 | |||
1775 | while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) | ||
1776 | free_buf(buf); | ||
1777 | } | ||
1620 | 1778 | ||
1621 | vdev->config->del_vqs(vdev); | 1779 | vdev->config->del_vqs(vdev); |
1622 | kfree(portdev->in_vqs); | 1780 | kfree(portdev->in_vqs); |
@@ -1663,6 +1821,7 @@ static int __init init(void) | |||
1663 | PTR_ERR(pdrvdata.debugfs_dir)); | 1821 | PTR_ERR(pdrvdata.debugfs_dir)); |
1664 | } | 1822 | } |
1665 | INIT_LIST_HEAD(&pdrvdata.consoles); | 1823 | INIT_LIST_HEAD(&pdrvdata.consoles); |
1824 | INIT_LIST_HEAD(&pdrvdata.portdevs); | ||
1666 | 1825 | ||
1667 | return register_virtio_driver(&virtio_console); | 1826 | return register_virtio_driver(&virtio_console); |
1668 | } | 1827 | } |