aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 /*