aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/virtio_console.c
diff options
context:
space:
mode:
authorAmit Shah <amit.shah@redhat.com>2010-05-20 00:15:50 -0400
committerRusty Russell <rusty@rustcorp.com.au>2010-05-19 08:45:50 -0400
commitcdfadfc1adb87fc7e8a631b1f299715feacbde90 (patch)
treefc7ace0ea80df94ed85176c493d9ae341f146b9d /drivers/char/virtio_console.c
parent60caacd3eeab68672961e88db01e26735527d521 (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>
Diffstat (limited to 'drivers/char/virtio_console.c')
-rw-r--r--drivers/char/virtio_console.c119
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
408static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count) 413/* Callers must take the port->outvq_lock */
414static 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
425static 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();
431fail: 465done:
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
525static 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
487static ssize_t port_fops_read(struct file *filp, char __user *ubuf, 544static 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
549free_buf: 623free_buf:
550 kfree(buf); 624 kfree(buf);
625out:
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 /*