diff options
Diffstat (limited to 'fs/splice.c')
-rw-r--r-- | fs/splice.c | 120 |
1 files changed, 115 insertions, 5 deletions
diff --git a/fs/splice.c b/fs/splice.c index e405cf552f5c..3bd9cb21b38e 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -507,9 +507,116 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos, | |||
507 | 507 | ||
508 | return ret; | 508 | return ret; |
509 | } | 509 | } |
510 | |||
511 | EXPORT_SYMBOL(generic_file_splice_read); | 510 | EXPORT_SYMBOL(generic_file_splice_read); |
512 | 511 | ||
512 | static const struct pipe_buf_operations default_pipe_buf_ops = { | ||
513 | .can_merge = 0, | ||
514 | .map = generic_pipe_buf_map, | ||
515 | .unmap = generic_pipe_buf_unmap, | ||
516 | .confirm = generic_pipe_buf_confirm, | ||
517 | .release = generic_pipe_buf_release, | ||
518 | .steal = generic_pipe_buf_steal, | ||
519 | .get = generic_pipe_buf_get, | ||
520 | }; | ||
521 | |||
522 | static ssize_t kernel_readv(struct file *file, const struct iovec *vec, | ||
523 | unsigned long vlen, loff_t offset) | ||
524 | { | ||
525 | mm_segment_t old_fs; | ||
526 | loff_t pos = offset; | ||
527 | ssize_t res; | ||
528 | |||
529 | old_fs = get_fs(); | ||
530 | set_fs(get_ds()); | ||
531 | /* The cast to a user pointer is valid due to the set_fs() */ | ||
532 | res = vfs_readv(file, (const struct iovec __user *)vec, vlen, &pos); | ||
533 | set_fs(old_fs); | ||
534 | |||
535 | return res; | ||
536 | } | ||
537 | |||
538 | ssize_t default_file_splice_read(struct file *in, loff_t *ppos, | ||
539 | struct pipe_inode_info *pipe, size_t len, | ||
540 | unsigned int flags) | ||
541 | { | ||
542 | unsigned int nr_pages; | ||
543 | unsigned int nr_freed; | ||
544 | size_t offset; | ||
545 | struct page *pages[PIPE_BUFFERS]; | ||
546 | struct partial_page partial[PIPE_BUFFERS]; | ||
547 | struct iovec vec[PIPE_BUFFERS]; | ||
548 | pgoff_t index; | ||
549 | ssize_t res; | ||
550 | size_t this_len; | ||
551 | int error; | ||
552 | int i; | ||
553 | struct splice_pipe_desc spd = { | ||
554 | .pages = pages, | ||
555 | .partial = partial, | ||
556 | .flags = flags, | ||
557 | .ops = &default_pipe_buf_ops, | ||
558 | .spd_release = spd_release_page, | ||
559 | }; | ||
560 | |||
561 | index = *ppos >> PAGE_CACHE_SHIFT; | ||
562 | offset = *ppos & ~PAGE_CACHE_MASK; | ||
563 | nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
564 | |||
565 | for (i = 0; i < nr_pages && i < PIPE_BUFFERS && len; i++) { | ||
566 | struct page *page; | ||
567 | |||
568 | page = alloc_page(GFP_HIGHUSER); | ||
569 | error = -ENOMEM; | ||
570 | if (!page) | ||
571 | goto err; | ||
572 | |||
573 | this_len = min_t(size_t, len, PAGE_CACHE_SIZE - offset); | ||
574 | vec[i].iov_base = (void __user *) kmap(page); | ||
575 | vec[i].iov_len = this_len; | ||
576 | pages[i] = page; | ||
577 | spd.nr_pages++; | ||
578 | len -= this_len; | ||
579 | offset = 0; | ||
580 | } | ||
581 | |||
582 | res = kernel_readv(in, vec, spd.nr_pages, *ppos); | ||
583 | if (res < 0) | ||
584 | goto err; | ||
585 | |||
586 | error = 0; | ||
587 | if (!res) | ||
588 | goto err; | ||
589 | |||
590 | nr_freed = 0; | ||
591 | for (i = 0; i < spd.nr_pages; i++) { | ||
592 | kunmap(pages[i]); | ||
593 | this_len = min_t(size_t, vec[i].iov_len, res); | ||
594 | partial[i].offset = 0; | ||
595 | partial[i].len = this_len; | ||
596 | if (!this_len) { | ||
597 | __free_page(pages[i]); | ||
598 | pages[i] = NULL; | ||
599 | nr_freed++; | ||
600 | } | ||
601 | res -= this_len; | ||
602 | } | ||
603 | spd.nr_pages -= nr_freed; | ||
604 | |||
605 | res = splice_to_pipe(pipe, &spd); | ||
606 | if (res > 0) | ||
607 | *ppos += res; | ||
608 | |||
609 | return res; | ||
610 | |||
611 | err: | ||
612 | for (i = 0; i < spd.nr_pages; i++) { | ||
613 | kunmap(pages[i]); | ||
614 | __free_page(pages[i]); | ||
615 | } | ||
616 | return error; | ||
617 | } | ||
618 | EXPORT_SYMBOL(default_file_splice_read); | ||
619 | |||
513 | /* | 620 | /* |
514 | * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos' | 621 | * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos' |
515 | * using sendpage(). Return the number of bytes sent. | 622 | * using sendpage(). Return the number of bytes sent. |
@@ -933,11 +1040,10 @@ static long do_splice_to(struct file *in, loff_t *ppos, | |||
933 | struct pipe_inode_info *pipe, size_t len, | 1040 | struct pipe_inode_info *pipe, size_t len, |
934 | unsigned int flags) | 1041 | unsigned int flags) |
935 | { | 1042 | { |
1043 | ssize_t (*splice_read)(struct file *, loff_t *, | ||
1044 | struct pipe_inode_info *, size_t, unsigned int); | ||
936 | int ret; | 1045 | int ret; |
937 | 1046 | ||
938 | if (unlikely(!in->f_op || !in->f_op->splice_read)) | ||
939 | return -EINVAL; | ||
940 | |||
941 | if (unlikely(!(in->f_mode & FMODE_READ))) | 1047 | if (unlikely(!(in->f_mode & FMODE_READ))) |
942 | return -EBADF; | 1048 | return -EBADF; |
943 | 1049 | ||
@@ -945,7 +1051,11 @@ static long do_splice_to(struct file *in, loff_t *ppos, | |||
945 | if (unlikely(ret < 0)) | 1051 | if (unlikely(ret < 0)) |
946 | return ret; | 1052 | return ret; |
947 | 1053 | ||
948 | return in->f_op->splice_read(in, ppos, pipe, len, flags); | 1054 | splice_read = in->f_op->splice_read; |
1055 | if (!splice_read) | ||
1056 | splice_read = default_file_splice_read; | ||
1057 | |||
1058 | return splice_read(in, ppos, pipe, len, flags); | ||
949 | } | 1059 | } |
950 | 1060 | ||
951 | /** | 1061 | /** |