diff options
| author | Amit Shah <amit.shah@redhat.com> | 2010-05-20 00:15:50 -0400 |
|---|---|---|
| committer | Rusty Russell <rusty@rustcorp.com.au> | 2010-05-19 08:45:50 -0400 |
| commit | cdfadfc1adb87fc7e8a631b1f299715feacbde90 (patch) | |
| tree | fc7ace0ea80df94ed85176c493d9ae341f146b9d | |
| parent | 60caacd3eeab68672961e88db01e26735527d521 (diff) | |
virtio: console: Add support for nonblocking write()s
If the host port is not open, a write() should either just return if the
file is opened in non-blocking mode, or block till the host port is
opened.
Also, don't spin till host consumes data for nonblocking ports. For
non-blocking ports, we can do away with the spinning and reclaim the
buffers consumed by the host on the next write call or on the condition
that'll make poll return.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
| -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 | |||
