diff options
-rw-r--r-- | drivers/char/virtio_console.c | 119 |
1 files changed, 111 insertions, 8 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index a39bf191da02..4175a2ae972f 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c | |||
@@ -159,6 +159,9 @@ struct port { | |||
159 | */ | 159 | */ |
160 | spinlock_t inbuf_lock; | 160 | spinlock_t inbuf_lock; |
161 | 161 | ||
162 | /* Protect the operations on the out_vq. */ | ||
163 | spinlock_t outvq_lock; | ||
164 | |||
162 | /* The IO vqs for this port */ | 165 | /* The IO vqs for this port */ |
163 | struct virtqueue *in_vq, *out_vq; | 166 | struct virtqueue *in_vq, *out_vq; |
164 | 167 | ||
@@ -184,6 +187,8 @@ struct port { | |||
184 | /* The 'id' to identify the port with the Host */ | 187 | /* The 'id' to identify the port with the Host */ |
185 | u32 id; | 188 | u32 id; |
186 | 189 | ||
190 | bool outvq_full; | ||
191 | |||
187 | /* Is the host device open */ | 192 | /* Is the host device open */ |
188 | bool host_connected; | 193 | bool host_connected; |
189 | 194 | ||
@@ -405,15 +410,33 @@ static ssize_t send_control_msg(struct port *port, unsigned int event, | |||
405 | return __send_control_msg(port->portdev, port->id, event, value); | 410 | return __send_control_msg(port->portdev, port->id, event, value); |
406 | } | 411 | } |
407 | 412 | ||
408 | static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count) | 413 | /* Callers must take the port->outvq_lock */ |
414 | static void reclaim_consumed_buffers(struct port *port) | ||
415 | { | ||
416 | void *buf; | ||
417 | unsigned int len; | ||
418 | |||
419 | while ((buf = virtqueue_get_buf(port->out_vq, &len))) { | ||
420 | kfree(buf); | ||
421 | port->outvq_full = false; | ||
422 | } | ||
423 | } | ||
424 | |||
425 | static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, | ||
426 | bool nonblock) | ||
409 | { | 427 | { |
410 | struct scatterlist sg[1]; | 428 | struct scatterlist sg[1]; |
411 | struct virtqueue *out_vq; | 429 | struct virtqueue *out_vq; |
412 | ssize_t ret; | 430 | ssize_t ret; |
431 | unsigned long flags; | ||
413 | unsigned int len; | 432 | unsigned int len; |
414 | 433 | ||
415 | out_vq = port->out_vq; | 434 | out_vq = port->out_vq; |
416 | 435 | ||
436 | spin_lock_irqsave(&port->outvq_lock, flags); | ||
437 | |||
438 | reclaim_consumed_buffers(port); | ||
439 | |||
417 | sg_init_one(sg, in_buf, in_count); | 440 | sg_init_one(sg, in_buf, in_count); |
418 | ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf); | 441 | ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf); |
419 | 442 | ||
@@ -422,14 +445,29 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count) | |||
422 | 445 | ||
423 | if (ret < 0) { | 446 | if (ret < 0) { |
424 | in_count = 0; | 447 | in_count = 0; |
425 | goto fail; | 448 | goto done; |
426 | } | 449 | } |
427 | 450 | ||
428 | /* Wait till the host acknowledges it pushed out the data we sent. */ | 451 | if (ret == 0) |
452 | port->outvq_full = true; | ||
453 | |||
454 | if (nonblock) | ||
455 | goto done; | ||
456 | |||
457 | /* | ||
458 | * Wait till the host acknowledges it pushed out the data we | ||
459 | * sent. This is done for ports in blocking mode or for data | ||
460 | * from the hvc_console; the tty operations are performed with | ||
461 | * spinlocks held so we can't sleep here. | ||
462 | */ | ||
429 | while (!virtqueue_get_buf(out_vq, &len)) | 463 | while (!virtqueue_get_buf(out_vq, &len)) |
430 | cpu_relax(); | 464 | cpu_relax(); |
431 | fail: | 465 | done: |
432 | /* We're expected to return the amount of data we wrote */ | 466 | spin_unlock_irqrestore(&port->outvq_lock, flags); |
467 | /* | ||
468 | * We're expected to return the amount of data we wrote -- all | ||
469 | * of it | ||
470 | */ | ||
433 | return in_count; | 471 | return in_count; |
434 | } | 472 | } |
435 | 473 | ||
@@ -484,6 +522,25 @@ static bool will_read_block(struct port *port) | |||
484 | return !port_has_data(port) && port->host_connected; | 522 | return !port_has_data(port) && port->host_connected; |
485 | } | 523 | } |
486 | 524 | ||
525 | static bool will_write_block(struct port *port) | ||
526 | { | ||
527 | bool ret; | ||
528 | |||
529 | if (!port->host_connected) | ||
530 | return true; | ||
531 | |||
532 | spin_lock_irq(&port->outvq_lock); | ||
533 | /* | ||
534 | * Check if the Host has consumed any buffers since we last | ||
535 | * sent data (this is only applicable for nonblocking ports). | ||
536 | */ | ||
537 | reclaim_consumed_buffers(port); | ||
538 | ret = port->outvq_full; | ||
539 | spin_unlock_irq(&port->outvq_lock); | ||
540 | |||
541 | return ret; | ||
542 | } | ||
543 | |||
487 | static ssize_t port_fops_read(struct file *filp, char __user *ubuf, | 544 | static ssize_t port_fops_read(struct file *filp, char __user *ubuf, |
488 | size_t count, loff_t *offp) | 545 | size_t count, loff_t *offp) |
489 | { | 546 | { |
@@ -530,9 +587,22 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, | |||
530 | struct port *port; | 587 | struct port *port; |
531 | char *buf; | 588 | char *buf; |
532 | ssize_t ret; | 589 | ssize_t ret; |
590 | bool nonblock; | ||
533 | 591 | ||
534 | port = filp->private_data; | 592 | port = filp->private_data; |
535 | 593 | ||
594 | nonblock = filp->f_flags & O_NONBLOCK; | ||
595 | |||
596 | if (will_write_block(port)) { | ||
597 | if (nonblock) | ||
598 | return -EAGAIN; | ||
599 | |||
600 | ret = wait_event_interruptible(port->waitqueue, | ||
601 | !will_write_block(port)); | ||
602 | if (ret < 0) | ||
603 | return ret; | ||
604 | } | ||
605 | |||
536 | count = min((size_t)(32 * 1024), count); | 606 | count = min((size_t)(32 * 1024), count); |
537 | 607 | ||
538 | buf = kmalloc(count, GFP_KERNEL); | 608 | buf = kmalloc(count, GFP_KERNEL); |
@@ -545,9 +615,14 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, | |||
545 | goto free_buf; | 615 | goto free_buf; |
546 | } | 616 | } |
547 | 617 | ||
548 | ret = send_buf(port, buf, count); | 618 | ret = send_buf(port, buf, count, nonblock); |
619 | |||
620 | if (nonblock && ret > 0) | ||
621 | goto out; | ||
622 | |||
549 | free_buf: | 623 | free_buf: |
550 | kfree(buf); | 624 | kfree(buf); |
625 | out: | ||
551 | return ret; | 626 | return ret; |
552 | } | 627 | } |
553 | 628 | ||
@@ -562,7 +637,7 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait) | |||
562 | ret = 0; | 637 | ret = 0; |
563 | if (port->inbuf) | 638 | if (port->inbuf) |
564 | ret |= POLLIN | POLLRDNORM; | 639 | ret |= POLLIN | POLLRDNORM; |
565 | if (port->host_connected) | 640 | if (!will_write_block(port)) |
566 | ret |= POLLOUT; | 641 | ret |= POLLOUT; |
567 | if (!port->host_connected) | 642 | if (!port->host_connected) |
568 | ret |= POLLHUP; | 643 | ret |= POLLHUP; |
@@ -586,6 +661,10 @@ static int port_fops_release(struct inode *inode, struct file *filp) | |||
586 | 661 | ||
587 | spin_unlock_irq(&port->inbuf_lock); | 662 | spin_unlock_irq(&port->inbuf_lock); |
588 | 663 | ||
664 | spin_lock_irq(&port->outvq_lock); | ||
665 | reclaim_consumed_buffers(port); | ||
666 | spin_unlock_irq(&port->outvq_lock); | ||
667 | |||
589 | return 0; | 668 | return 0; |
590 | } | 669 | } |
591 | 670 | ||
@@ -614,6 +693,15 @@ static int port_fops_open(struct inode *inode, struct file *filp) | |||
614 | port->guest_connected = true; | 693 | port->guest_connected = true; |
615 | spin_unlock_irq(&port->inbuf_lock); | 694 | spin_unlock_irq(&port->inbuf_lock); |
616 | 695 | ||
696 | spin_lock_irq(&port->outvq_lock); | ||
697 | /* | ||
698 | * There might be a chance that we missed reclaiming a few | ||
699 | * buffers in the window of the port getting previously closed | ||
700 | * and opening now. | ||
701 | */ | ||
702 | reclaim_consumed_buffers(port); | ||
703 | spin_unlock_irq(&port->outvq_lock); | ||
704 | |||
617 | /* Notify host of port being opened */ | 705 | /* Notify host of port being opened */ |
618 | send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); | 706 | send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); |
619 | 707 | ||
@@ -654,7 +742,7 @@ static int put_chars(u32 vtermno, const char *buf, int count) | |||
654 | if (!port) | 742 | if (!port) |
655 | return -EPIPE; | 743 | return -EPIPE; |
656 | 744 | ||
657 | return send_buf(port, (void *)buf, count); | 745 | return send_buf(port, (void *)buf, count, false); |
658 | } | 746 | } |
659 | 747 | ||
660 | /* | 748 | /* |
@@ -846,6 +934,8 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, | |||
846 | out_offset += snprintf(buf + out_offset, out_count - out_offset, | 934 | out_offset += snprintf(buf + out_offset, out_count - out_offset, |
847 | "host_connected: %d\n", port->host_connected); | 935 | "host_connected: %d\n", port->host_connected); |
848 | out_offset += snprintf(buf + out_offset, out_count - out_offset, | 936 | out_offset += snprintf(buf + out_offset, out_count - out_offset, |
937 | "outvq_full: %d\n", port->outvq_full); | ||
938 | out_offset += snprintf(buf + out_offset, out_count - out_offset, | ||
849 | "is_console: %s\n", | 939 | "is_console: %s\n", |
850 | is_console_port(port) ? "yes" : "no"); | 940 | is_console_port(port) ? "yes" : "no"); |
851 | out_offset += snprintf(buf + out_offset, out_count - out_offset, | 941 | out_offset += snprintf(buf + out_offset, out_count - out_offset, |
@@ -912,6 +1002,8 @@ static int add_port(struct ports_device *portdev, u32 id) | |||
912 | 1002 | ||
913 | port->host_connected = port->guest_connected = false; | 1003 | port->host_connected = port->guest_connected = false; |
914 | 1004 | ||
1005 | port->outvq_full = false; | ||
1006 | |||
915 | port->in_vq = portdev->in_vqs[port->id]; | 1007 | port->in_vq = portdev->in_vqs[port->id]; |
916 | port->out_vq = portdev->out_vqs[port->id]; | 1008 | port->out_vq = portdev->out_vqs[port->id]; |
917 | 1009 | ||
@@ -936,6 +1028,7 @@ static int add_port(struct ports_device *portdev, u32 id) | |||
936 | } | 1028 | } |
937 | 1029 | ||
938 | spin_lock_init(&port->inbuf_lock); | 1030 | spin_lock_init(&port->inbuf_lock); |
1031 | spin_lock_init(&port->outvq_lock); | ||
939 | init_waitqueue_head(&port->waitqueue); | 1032 | init_waitqueue_head(&port->waitqueue); |
940 | 1033 | ||
941 | /* Fill the in_vq with buffers so the host can send us data. */ | 1034 | /* Fill the in_vq with buffers so the host can send us data. */ |
@@ -1031,6 +1124,8 @@ static int remove_port(struct port *port) | |||
1031 | /* Remove unused data this port might have received. */ | 1124 | /* Remove unused data this port might have received. */ |
1032 | discard_port_data(port); | 1125 | discard_port_data(port); |
1033 | 1126 | ||
1127 | reclaim_consumed_buffers(port); | ||
1128 | |||
1034 | /* Remove buffers we queued up for the Host to send us data in. */ | 1129 | /* Remove buffers we queued up for the Host to send us data in. */ |
1035 | while ((buf = virtqueue_detach_unused_buf(port->in_vq))) | 1130 | while ((buf = virtqueue_detach_unused_buf(port->in_vq))) |
1036 | free_buf(buf); | 1131 | free_buf(buf); |
@@ -1102,6 +1197,14 @@ static void handle_control_message(struct ports_device *portdev, | |||
1102 | case VIRTIO_CONSOLE_PORT_OPEN: | 1197 | case VIRTIO_CONSOLE_PORT_OPEN: |
1103 | port->host_connected = cpkt->value; | 1198 | port->host_connected = cpkt->value; |
1104 | wake_up_interruptible(&port->waitqueue); | 1199 | wake_up_interruptible(&port->waitqueue); |
1200 | /* | ||
1201 | * If the host port got closed and the host had any | ||
1202 | * unconsumed buffers, we'll be able to reclaim them | ||
1203 | * now. | ||
1204 | */ | ||
1205 | spin_lock_irq(&port->outvq_lock); | ||
1206 | reclaim_consumed_buffers(port); | ||
1207 | spin_unlock_irq(&port->outvq_lock); | ||
1105 | break; | 1208 | break; |
1106 | case VIRTIO_CONSOLE_PORT_NAME: | 1209 | case VIRTIO_CONSOLE_PORT_NAME: |
1107 | /* | 1210 | /* |