diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-02 18:00:56 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-02 18:00:56 -0400 |
commit | 80c2861672bbf000f6af838656959ee937e4ee4d (patch) | |
tree | 78b361fd7278d461b1e664272f3b27660b64c642 /drivers/char | |
parent | d211858837ff8d8e31942ca7d27e6e08b3b46f5e (diff) | |
parent | 5087a50e66bd51b6e72c60bce4757a42b93f6b2c (diff) |
Merge git://github.com/rustyrussell/linux
* git://github.com/rustyrussell/linux:
virtio-blk: use ida to allocate disk index
virtio: Add platform bus driver for memory mapped virtio device
virtio: Dont add "config" to list for !per_vq_vector
virtio: console: wait for first console port for early console output
virtio: console: add port stats for bytes received, sent and discarded
virtio: console: make discard_port_data() use get_inbuf()
virtio: console: rename variable
virtio: console: make get_inbuf() return port->inbuf if present
virtio: console: Fix return type for get_inbuf()
virtio: console: Use wait_event_freezable instead of _interruptible
virtio: console: Ignore port name update request if name already set
virtio: console: Fix indentation
virtio: modify vring_init and vring_size to take account of the layout containing *_event_idx
virtio.h: correct comment for struct virtio_driver
virtio-net: Use virtio_config_val() for retrieving config
virtio_config: Add virtio_config_val_len()
virtio-console: Use virtio_config_val() for retrieving config
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/virtio_console.c | 120 |
1 files changed, 79 insertions, 41 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index fb68b1295373..4ca181f1378b 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c | |||
@@ -19,8 +19,10 @@ | |||
19 | */ | 19 | */ |
20 | #include <linux/cdev.h> | 20 | #include <linux/cdev.h> |
21 | #include <linux/debugfs.h> | 21 | #include <linux/debugfs.h> |
22 | #include <linux/completion.h> | ||
22 | #include <linux/device.h> | 23 | #include <linux/device.h> |
23 | #include <linux/err.h> | 24 | #include <linux/err.h> |
25 | #include <linux/freezer.h> | ||
24 | #include <linux/fs.h> | 26 | #include <linux/fs.h> |
25 | #include <linux/init.h> | 27 | #include <linux/init.h> |
26 | #include <linux/list.h> | 28 | #include <linux/list.h> |
@@ -73,6 +75,7 @@ struct ports_driver_data { | |||
73 | static struct ports_driver_data pdrvdata; | 75 | static struct ports_driver_data pdrvdata; |
74 | 76 | ||
75 | DEFINE_SPINLOCK(pdrvdata_lock); | 77 | DEFINE_SPINLOCK(pdrvdata_lock); |
78 | DECLARE_COMPLETION(early_console_added); | ||
76 | 79 | ||
77 | /* This struct holds information that's relevant only for console ports */ | 80 | /* This struct holds information that's relevant only for console ports */ |
78 | struct console { | 81 | struct console { |
@@ -151,6 +154,10 @@ struct ports_device { | |||
151 | int chr_major; | 154 | int chr_major; |
152 | }; | 155 | }; |
153 | 156 | ||
157 | struct port_stats { | ||
158 | unsigned long bytes_sent, bytes_received, bytes_discarded; | ||
159 | }; | ||
160 | |||
154 | /* This struct holds the per-port data */ | 161 | /* This struct holds the per-port data */ |
155 | struct port { | 162 | struct port { |
156 | /* Next port in the list, head is in the ports_device */ | 163 | /* Next port in the list, head is in the ports_device */ |
@@ -179,6 +186,13 @@ struct port { | |||
179 | struct dentry *debugfs_file; | 186 | struct dentry *debugfs_file; |
180 | 187 | ||
181 | /* | 188 | /* |
189 | * Keep count of the bytes sent, received and discarded for | ||
190 | * this port for accounting and debugging purposes. These | ||
191 | * counts are not reset across port open / close events. | ||
192 | */ | ||
193 | struct port_stats stats; | ||
194 | |||
195 | /* | ||
182 | * The entries in this struct will be valid if this port is | 196 | * The entries in this struct will be valid if this port is |
183 | * hooked up to an hvc console | 197 | * hooked up to an hvc console |
184 | */ | 198 | */ |
@@ -347,17 +361,19 @@ fail: | |||
347 | } | 361 | } |
348 | 362 | ||
349 | /* Callers should take appropriate locks */ | 363 | /* Callers should take appropriate locks */ |
350 | static void *get_inbuf(struct port *port) | 364 | static struct port_buffer *get_inbuf(struct port *port) |
351 | { | 365 | { |
352 | struct port_buffer *buf; | 366 | struct port_buffer *buf; |
353 | struct virtqueue *vq; | ||
354 | unsigned int len; | 367 | unsigned int len; |
355 | 368 | ||
356 | vq = port->in_vq; | 369 | if (port->inbuf) |
357 | buf = virtqueue_get_buf(vq, &len); | 370 | return port->inbuf; |
371 | |||
372 | buf = virtqueue_get_buf(port->in_vq, &len); | ||
358 | if (buf) { | 373 | if (buf) { |
359 | buf->len = len; | 374 | buf->len = len; |
360 | buf->offset = 0; | 375 | buf->offset = 0; |
376 | port->stats.bytes_received += len; | ||
361 | } | 377 | } |
362 | return buf; | 378 | return buf; |
363 | } | 379 | } |
@@ -384,32 +400,27 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf) | |||
384 | static void discard_port_data(struct port *port) | 400 | static void discard_port_data(struct port *port) |
385 | { | 401 | { |
386 | struct port_buffer *buf; | 402 | struct port_buffer *buf; |
387 | struct virtqueue *vq; | 403 | unsigned int err; |
388 | unsigned int len; | ||
389 | int ret; | ||
390 | 404 | ||
391 | if (!port->portdev) { | 405 | if (!port->portdev) { |
392 | /* Device has been unplugged. vqs are already gone. */ | 406 | /* Device has been unplugged. vqs are already gone. */ |
393 | return; | 407 | return; |
394 | } | 408 | } |
395 | vq = port->in_vq; | 409 | buf = get_inbuf(port); |
396 | if (port->inbuf) | ||
397 | buf = port->inbuf; | ||
398 | else | ||
399 | buf = virtqueue_get_buf(vq, &len); | ||
400 | 410 | ||
401 | ret = 0; | 411 | err = 0; |
402 | while (buf) { | 412 | while (buf) { |
403 | if (add_inbuf(vq, buf) < 0) { | 413 | port->stats.bytes_discarded += buf->len - buf->offset; |
404 | ret++; | 414 | if (add_inbuf(port->in_vq, buf) < 0) { |
415 | err++; | ||
405 | free_buf(buf); | 416 | free_buf(buf); |
406 | } | 417 | } |
407 | buf = virtqueue_get_buf(vq, &len); | 418 | port->inbuf = NULL; |
419 | buf = get_inbuf(port); | ||
408 | } | 420 | } |
409 | port->inbuf = NULL; | 421 | if (err) |
410 | if (ret) | ||
411 | dev_warn(port->dev, "Errors adding %d buffers back to vq\n", | 422 | dev_warn(port->dev, "Errors adding %d buffers back to vq\n", |
412 | ret); | 423 | err); |
413 | } | 424 | } |
414 | 425 | ||
415 | static bool port_has_data(struct port *port) | 426 | static bool port_has_data(struct port *port) |
@@ -417,18 +428,12 @@ static bool port_has_data(struct port *port) | |||
417 | unsigned long flags; | 428 | unsigned long flags; |
418 | bool ret; | 429 | bool ret; |
419 | 430 | ||
431 | ret = false; | ||
420 | spin_lock_irqsave(&port->inbuf_lock, flags); | 432 | spin_lock_irqsave(&port->inbuf_lock, flags); |
421 | if (port->inbuf) { | ||
422 | ret = true; | ||
423 | goto out; | ||
424 | } | ||
425 | port->inbuf = get_inbuf(port); | 433 | port->inbuf = get_inbuf(port); |
426 | if (port->inbuf) { | 434 | if (port->inbuf) |
427 | ret = true; | 435 | ret = true; |
428 | goto out; | 436 | |
429 | } | ||
430 | ret = false; | ||
431 | out: | ||
432 | spin_unlock_irqrestore(&port->inbuf_lock, flags); | 437 | spin_unlock_irqrestore(&port->inbuf_lock, flags); |
433 | return ret; | 438 | return ret; |
434 | } | 439 | } |
@@ -529,6 +534,8 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, | |||
529 | cpu_relax(); | 534 | cpu_relax(); |
530 | done: | 535 | done: |
531 | spin_unlock_irqrestore(&port->outvq_lock, flags); | 536 | spin_unlock_irqrestore(&port->outvq_lock, flags); |
537 | |||
538 | port->stats.bytes_sent += in_count; | ||
532 | /* | 539 | /* |
533 | * We're expected to return the amount of data we wrote -- all | 540 | * We're expected to return the amount of data we wrote -- all |
534 | * of it | 541 | * of it |
@@ -633,8 +640,8 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf, | |||
633 | if (filp->f_flags & O_NONBLOCK) | 640 | if (filp->f_flags & O_NONBLOCK) |
634 | return -EAGAIN; | 641 | return -EAGAIN; |
635 | 642 | ||
636 | ret = wait_event_interruptible(port->waitqueue, | 643 | ret = wait_event_freezable(port->waitqueue, |
637 | !will_read_block(port)); | 644 | !will_read_block(port)); |
638 | if (ret < 0) | 645 | if (ret < 0) |
639 | return ret; | 646 | return ret; |
640 | } | 647 | } |
@@ -677,8 +684,8 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, | |||
677 | if (nonblock) | 684 | if (nonblock) |
678 | return -EAGAIN; | 685 | return -EAGAIN; |
679 | 686 | ||
680 | ret = wait_event_interruptible(port->waitqueue, | 687 | ret = wait_event_freezable(port->waitqueue, |
681 | !will_write_block(port)); | 688 | !will_write_block(port)); |
682 | if (ret < 0) | 689 | if (ret < 0) |
683 | return ret; | 690 | return ret; |
684 | } | 691 | } |
@@ -1059,6 +1066,14 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, | |||
1059 | out_offset += snprintf(buf + out_offset, out_count - out_offset, | 1066 | out_offset += snprintf(buf + out_offset, out_count - out_offset, |
1060 | "outvq_full: %d\n", port->outvq_full); | 1067 | "outvq_full: %d\n", port->outvq_full); |
1061 | out_offset += snprintf(buf + out_offset, out_count - out_offset, | 1068 | out_offset += snprintf(buf + out_offset, out_count - out_offset, |
1069 | "bytes_sent: %lu\n", port->stats.bytes_sent); | ||
1070 | out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||
1071 | "bytes_received: %lu\n", | ||
1072 | port->stats.bytes_received); | ||
1073 | out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||
1074 | "bytes_discarded: %lu\n", | ||
1075 | port->stats.bytes_discarded); | ||
1076 | out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||
1062 | "is_console: %s\n", | 1077 | "is_console: %s\n", |
1063 | is_console_port(port) ? "yes" : "no"); | 1078 | is_console_port(port) ? "yes" : "no"); |
1064 | out_offset += snprintf(buf + out_offset, out_count - out_offset, | 1079 | out_offset += snprintf(buf + out_offset, out_count - out_offset, |
@@ -1143,6 +1158,7 @@ static int add_port(struct ports_device *portdev, u32 id) | |||
1143 | port->cons.ws.ws_row = port->cons.ws.ws_col = 0; | 1158 | port->cons.ws.ws_row = port->cons.ws.ws_col = 0; |
1144 | 1159 | ||
1145 | port->host_connected = port->guest_connected = false; | 1160 | port->host_connected = port->guest_connected = false; |
1161 | port->stats = (struct port_stats) { 0 }; | ||
1146 | 1162 | ||
1147 | port->outvq_full = false; | 1163 | port->outvq_full = false; |
1148 | 1164 | ||
@@ -1352,6 +1368,7 @@ static void handle_control_message(struct ports_device *portdev, | |||
1352 | break; | 1368 | break; |
1353 | 1369 | ||
1354 | init_port_console(port); | 1370 | init_port_console(port); |
1371 | complete(&early_console_added); | ||
1355 | /* | 1372 | /* |
1356 | * Could remove the port here in case init fails - but | 1373 | * Could remove the port here in case init fails - but |
1357 | * have to notify the host first. | 1374 | * have to notify the host first. |
@@ -1394,6 +1411,13 @@ static void handle_control_message(struct ports_device *portdev, | |||
1394 | break; | 1411 | break; |
1395 | case VIRTIO_CONSOLE_PORT_NAME: | 1412 | case VIRTIO_CONSOLE_PORT_NAME: |
1396 | /* | 1413 | /* |
1414 | * If we woke up after hibernation, we can get this | ||
1415 | * again. Skip it in that case. | ||
1416 | */ | ||
1417 | if (port->name) | ||
1418 | break; | ||
1419 | |||
1420 | /* | ||
1397 | * Skip the size of the header and the cpkt to get the size | 1421 | * Skip the size of the header and the cpkt to get the size |
1398 | * of the name that was sent | 1422 | * of the name that was sent |
1399 | */ | 1423 | */ |
@@ -1481,8 +1505,7 @@ static void in_intr(struct virtqueue *vq) | |||
1481 | return; | 1505 | return; |
1482 | 1506 | ||
1483 | spin_lock_irqsave(&port->inbuf_lock, flags); | 1507 | spin_lock_irqsave(&port->inbuf_lock, flags); |
1484 | if (!port->inbuf) | 1508 | port->inbuf = get_inbuf(port); |
1485 | port->inbuf = get_inbuf(port); | ||
1486 | 1509 | ||
1487 | /* | 1510 | /* |
1488 | * Don't queue up data when port is closed. This condition | 1511 | * Don't queue up data when port is closed. This condition |
@@ -1563,7 +1586,7 @@ static int init_vqs(struct ports_device *portdev) | |||
1563 | portdev->out_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), | 1586 | portdev->out_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), |
1564 | GFP_KERNEL); | 1587 | GFP_KERNEL); |
1565 | if (!vqs || !io_callbacks || !io_names || !portdev->in_vqs || | 1588 | if (!vqs || !io_callbacks || !io_names || !portdev->in_vqs || |
1566 | !portdev->out_vqs) { | 1589 | !portdev->out_vqs) { |
1567 | err = -ENOMEM; | 1590 | err = -ENOMEM; |
1568 | goto free; | 1591 | goto free; |
1569 | } | 1592 | } |
@@ -1648,6 +1671,10 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) | |||
1648 | struct ports_device *portdev; | 1671 | struct ports_device *portdev; |
1649 | int err; | 1672 | int err; |
1650 | bool multiport; | 1673 | bool multiport; |
1674 | bool early = early_put_chars != NULL; | ||
1675 | |||
1676 | /* Ensure to read early_put_chars now */ | ||
1677 | barrier(); | ||
1651 | 1678 | ||
1652 | portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); | 1679 | portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); |
1653 | if (!portdev) { | 1680 | if (!portdev) { |
@@ -1675,13 +1702,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) | |||
1675 | 1702 | ||
1676 | multiport = false; | 1703 | multiport = false; |
1677 | portdev->config.max_nr_ports = 1; | 1704 | portdev->config.max_nr_ports = 1; |
1678 | if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { | 1705 | if (virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT, |
1706 | offsetof(struct virtio_console_config, | ||
1707 | max_nr_ports), | ||
1708 | &portdev->config.max_nr_ports) == 0) | ||
1679 | multiport = true; | 1709 | multiport = true; |
1680 | vdev->config->get(vdev, offsetof(struct virtio_console_config, | ||
1681 | max_nr_ports), | ||
1682 | &portdev->config.max_nr_ports, | ||
1683 | sizeof(portdev->config.max_nr_ports)); | ||
1684 | } | ||
1685 | 1710 | ||
1686 | err = init_vqs(portdev); | 1711 | err = init_vqs(portdev); |
1687 | if (err < 0) { | 1712 | if (err < 0) { |
@@ -1719,6 +1744,19 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) | |||
1719 | 1744 | ||
1720 | __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, | 1745 | __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, |
1721 | VIRTIO_CONSOLE_DEVICE_READY, 1); | 1746 | VIRTIO_CONSOLE_DEVICE_READY, 1); |
1747 | |||
1748 | /* | ||
1749 | * If there was an early virtio console, assume that there are no | ||
1750 | * other consoles. We need to wait until the hvc_alloc matches the | ||
1751 | * hvc_instantiate, otherwise tty_open will complain, resulting in | ||
1752 | * a "Warning: unable to open an initial console" boot failure. | ||
1753 | * Without multiport this is done in add_port above. With multiport | ||
1754 | * this might take some host<->guest communication - thus we have to | ||
1755 | * wait. | ||
1756 | */ | ||
1757 | if (multiport && early) | ||
1758 | wait_for_completion(&early_console_added); | ||
1759 | |||
1722 | return 0; | 1760 | return 0; |
1723 | 1761 | ||
1724 | free_vqs: | 1762 | free_vqs: |