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 | /* |
