diff options
author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2012-08-09 08:30:39 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2012-09-28 01:35:12 -0400 |
commit | eb5e89fc70bb3f115b3206ed0c57d3aba1fdd155 (patch) | |
tree | 1efa63701e815ea3ae7996ff505f050bfab56dd4 /drivers/char | |
parent | 925a6f0bf8bd122d5d2429af7f0ca0fecf4ae71f (diff) |
virtio/console: Add splice_write support
Enable to use splice_write from pipe to virtio-console port.
This steals pages from pipe and directly send it to host.
Note that this may accelerate only the guest to host path.
Changes in v2:
- Use GFP_KERNEL instead of GFP_ATOMIC in syscall context function.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Acked-by: Amit Shah <amit.shah@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/virtio_console.c | 136 |
1 files changed, 128 insertions, 8 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index cdf2f5451c76..730816cdeb45 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c | |||
@@ -24,6 +24,8 @@ | |||
24 | #include <linux/err.h> | 24 | #include <linux/err.h> |
25 | #include <linux/freezer.h> | 25 | #include <linux/freezer.h> |
26 | #include <linux/fs.h> | 26 | #include <linux/fs.h> |
27 | #include <linux/splice.h> | ||
28 | #include <linux/pagemap.h> | ||
27 | #include <linux/init.h> | 29 | #include <linux/init.h> |
28 | #include <linux/list.h> | 30 | #include <linux/list.h> |
29 | #include <linux/poll.h> | 31 | #include <linux/poll.h> |
@@ -227,6 +229,7 @@ struct port { | |||
227 | bool guest_connected; | 229 | bool guest_connected; |
228 | }; | 230 | }; |
229 | 231 | ||
232 | #define MAX_SPLICE_PAGES 32 | ||
230 | /* This is the very early arch-specified put chars function. */ | 233 | /* This is the very early arch-specified put chars function. */ |
231 | static int (*early_put_chars)(u32, const char *, int); | 234 | static int (*early_put_chars)(u32, const char *, int); |
232 | 235 | ||
@@ -474,26 +477,52 @@ static ssize_t send_control_msg(struct port *port, unsigned int event, | |||
474 | return 0; | 477 | return 0; |
475 | } | 478 | } |
476 | 479 | ||
480 | struct buffer_token { | ||
481 | union { | ||
482 | void *buf; | ||
483 | struct scatterlist *sg; | ||
484 | } u; | ||
485 | bool sgpages; | ||
486 | }; | ||
487 | |||
488 | static void reclaim_sg_pages(struct scatterlist *sg) | ||
489 | { | ||
490 | int i; | ||
491 | struct page *page; | ||
492 | |||
493 | for (i = 0; i < MAX_SPLICE_PAGES; i++) { | ||
494 | page = sg_page(&sg[i]); | ||
495 | if (!page) | ||
496 | break; | ||
497 | put_page(page); | ||
498 | } | ||
499 | kfree(sg); | ||
500 | } | ||
501 | |||
477 | /* Callers must take the port->outvq_lock */ | 502 | /* Callers must take the port->outvq_lock */ |
478 | static void reclaim_consumed_buffers(struct port *port) | 503 | static void reclaim_consumed_buffers(struct port *port) |
479 | { | 504 | { |
480 | void *buf; | 505 | struct buffer_token *tok; |
481 | unsigned int len; | 506 | unsigned int len; |
482 | 507 | ||
483 | if (!port->portdev) { | 508 | if (!port->portdev) { |
484 | /* Device has been unplugged. vqs are already gone. */ | 509 | /* Device has been unplugged. vqs are already gone. */ |
485 | return; | 510 | return; |
486 | } | 511 | } |
487 | while ((buf = virtqueue_get_buf(port->out_vq, &len))) { | 512 | while ((tok = virtqueue_get_buf(port->out_vq, &len))) { |
488 | kfree(buf); | 513 | if (tok->sgpages) |
514 | reclaim_sg_pages(tok->u.sg); | ||
515 | else | ||
516 | kfree(tok->u.buf); | ||
517 | kfree(tok); | ||
489 | port->outvq_full = false; | 518 | port->outvq_full = false; |
490 | } | 519 | } |
491 | } | 520 | } |
492 | 521 | ||
493 | static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, | 522 | static ssize_t __send_to_port(struct port *port, struct scatterlist *sg, |
494 | bool nonblock) | 523 | int nents, size_t in_count, |
524 | struct buffer_token *tok, bool nonblock) | ||
495 | { | 525 | { |
496 | struct scatterlist sg[1]; | ||
497 | struct virtqueue *out_vq; | 526 | struct virtqueue *out_vq; |
498 | ssize_t ret; | 527 | ssize_t ret; |
499 | unsigned long flags; | 528 | unsigned long flags; |
@@ -505,8 +534,7 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, | |||
505 | 534 | ||
506 | reclaim_consumed_buffers(port); | 535 | reclaim_consumed_buffers(port); |
507 | 536 | ||
508 | sg_init_one(sg, in_buf, in_count); | 537 | ret = virtqueue_add_buf(out_vq, sg, nents, 0, tok, GFP_ATOMIC); |
509 | ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf, GFP_ATOMIC); | ||
510 | 538 | ||
511 | /* Tell Host to go! */ | 539 | /* Tell Host to go! */ |
512 | virtqueue_kick(out_vq); | 540 | virtqueue_kick(out_vq); |
@@ -544,6 +572,37 @@ done: | |||
544 | return in_count; | 572 | return in_count; |
545 | } | 573 | } |
546 | 574 | ||
575 | static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, | ||
576 | bool nonblock) | ||
577 | { | ||
578 | struct scatterlist sg[1]; | ||
579 | struct buffer_token *tok; | ||
580 | |||
581 | tok = kmalloc(sizeof(*tok), GFP_ATOMIC); | ||
582 | if (!tok) | ||
583 | return -ENOMEM; | ||
584 | tok->sgpages = false; | ||
585 | tok->u.buf = in_buf; | ||
586 | |||
587 | sg_init_one(sg, in_buf, in_count); | ||
588 | |||
589 | return __send_to_port(port, sg, 1, in_count, tok, nonblock); | ||
590 | } | ||
591 | |||
592 | static ssize_t send_pages(struct port *port, struct scatterlist *sg, int nents, | ||
593 | size_t in_count, bool nonblock) | ||
594 | { | ||
595 | struct buffer_token *tok; | ||
596 | |||
597 | tok = kmalloc(sizeof(*tok), GFP_ATOMIC); | ||
598 | if (!tok) | ||
599 | return -ENOMEM; | ||
600 | tok->sgpages = true; | ||
601 | tok->u.sg = sg; | ||
602 | |||
603 | return __send_to_port(port, sg, nents, in_count, tok, nonblock); | ||
604 | } | ||
605 | |||
547 | /* | 606 | /* |
548 | * Give out the data that's requested from the buffer that we have | 607 | * Give out the data that's requested from the buffer that we have |
549 | * queued up. | 608 | * queued up. |
@@ -725,6 +784,66 @@ out: | |||
725 | return ret; | 784 | return ret; |
726 | } | 785 | } |
727 | 786 | ||
787 | struct sg_list { | ||
788 | unsigned int n; | ||
789 | size_t len; | ||
790 | struct scatterlist *sg; | ||
791 | }; | ||
792 | |||
793 | static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf, | ||
794 | struct splice_desc *sd) | ||
795 | { | ||
796 | struct sg_list *sgl = sd->u.data; | ||
797 | unsigned int len = 0; | ||
798 | |||
799 | if (sgl->n == MAX_SPLICE_PAGES) | ||
800 | return 0; | ||
801 | |||
802 | /* Try lock this page */ | ||
803 | if (buf->ops->steal(pipe, buf) == 0) { | ||
804 | /* Get reference and unlock page for moving */ | ||
805 | get_page(buf->page); | ||
806 | unlock_page(buf->page); | ||
807 | |||
808 | len = min(buf->len, sd->len); | ||
809 | sg_set_page(&(sgl->sg[sgl->n]), buf->page, len, buf->offset); | ||
810 | sgl->n++; | ||
811 | sgl->len += len; | ||
812 | } | ||
813 | |||
814 | return len; | ||
815 | } | ||
816 | |||
817 | /* Faster zero-copy write by splicing */ | ||
818 | static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, | ||
819 | struct file *filp, loff_t *ppos, | ||
820 | size_t len, unsigned int flags) | ||
821 | { | ||
822 | struct port *port = filp->private_data; | ||
823 | struct sg_list sgl; | ||
824 | ssize_t ret; | ||
825 | struct splice_desc sd = { | ||
826 | .total_len = len, | ||
827 | .flags = flags, | ||
828 | .pos = *ppos, | ||
829 | .u.data = &sgl, | ||
830 | }; | ||
831 | |||
832 | sgl.n = 0; | ||
833 | sgl.len = 0; | ||
834 | sgl.sg = kmalloc(sizeof(struct scatterlist) * MAX_SPLICE_PAGES, | ||
835 | GFP_KERNEL); | ||
836 | if (unlikely(!sgl.sg)) | ||
837 | return -ENOMEM; | ||
838 | |||
839 | sg_init_table(sgl.sg, MAX_SPLICE_PAGES); | ||
840 | ret = __splice_from_pipe(pipe, &sd, pipe_to_sg); | ||
841 | if (likely(ret > 0)) | ||
842 | ret = send_pages(port, sgl.sg, sgl.n, sgl.len, true); | ||
843 | |||
844 | return ret; | ||
845 | } | ||
846 | |||
728 | static unsigned int port_fops_poll(struct file *filp, poll_table *wait) | 847 | static unsigned int port_fops_poll(struct file *filp, poll_table *wait) |
729 | { | 848 | { |
730 | struct port *port; | 849 | struct port *port; |
@@ -856,6 +975,7 @@ static const struct file_operations port_fops = { | |||
856 | .open = port_fops_open, | 975 | .open = port_fops_open, |
857 | .read = port_fops_read, | 976 | .read = port_fops_read, |
858 | .write = port_fops_write, | 977 | .write = port_fops_write, |
978 | .splice_write = port_fops_splice_write, | ||
859 | .poll = port_fops_poll, | 979 | .poll = port_fops_poll, |
860 | .release = port_fops_release, | 980 | .release = port_fops_release, |
861 | .fasync = port_fops_fasync, | 981 | .fasync = port_fops_fasync, |