diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2009-05-07 09:37:36 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2009-05-11 08:13:10 -0400 |
commit | 6818173bd658439b83896a2a7586f64ab51bf29c (patch) | |
tree | 17d25ee77485af18da1a80cb7f1d8ec581c6abfc | |
parent | 7c77f0b3f9208c339a4b40737bb2cb0f0319bb8d (diff) |
splice: implement default splice_read method
If f_op->splice_read() is not implemented, fall back to a plain read.
Use vfs_readv() to read into previously allocated pages.
This will allow splice and functions using splice, such as the loop
device, to work on all filesystems. This includes "direct_io" files
in fuse which bypass the page cache.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
-rw-r--r-- | drivers/block/loop.c | 11 | ||||
-rw-r--r-- | fs/coda/file.c | 9 | ||||
-rw-r--r-- | fs/pipe.c | 14 | ||||
-rw-r--r-- | fs/read_write.c | 7 | ||||
-rw-r--r-- | fs/splice.c | 120 | ||||
-rw-r--r-- | include/linux/fs.h | 2 | ||||
-rw-r--r-- | include/linux/pipe_fs_i.h | 1 |
7 files changed, 140 insertions, 24 deletions
diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 9ca4bb014657..801f4ab83302 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c | |||
@@ -709,10 +709,6 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, | |||
709 | if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode)) | 709 | if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode)) |
710 | goto out_putf; | 710 | goto out_putf; |
711 | 711 | ||
712 | /* new backing store needs to support loop (eg splice_read) */ | ||
713 | if (!inode->i_fop->splice_read) | ||
714 | goto out_putf; | ||
715 | |||
716 | /* size of the new backing store needs to be the same */ | 712 | /* size of the new backing store needs to be the same */ |
717 | if (get_loop_size(lo, file) != get_loop_size(lo, old_file)) | 713 | if (get_loop_size(lo, file) != get_loop_size(lo, old_file)) |
718 | goto out_putf; | 714 | goto out_putf; |
@@ -788,12 +784,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, | |||
788 | error = -EINVAL; | 784 | error = -EINVAL; |
789 | if (S_ISREG(inode->i_mode) || S_ISBLK(inode->i_mode)) { | 785 | if (S_ISREG(inode->i_mode) || S_ISBLK(inode->i_mode)) { |
790 | const struct address_space_operations *aops = mapping->a_ops; | 786 | const struct address_space_operations *aops = mapping->a_ops; |
791 | /* | 787 | |
792 | * If we can't read - sorry. If we only can't write - well, | ||
793 | * it's going to be read-only. | ||
794 | */ | ||
795 | if (!file->f_op->splice_read) | ||
796 | goto out_putf; | ||
797 | if (aops->write_begin) | 788 | if (aops->write_begin) |
798 | lo_flags |= LO_FLAGS_USE_AOPS; | 789 | lo_flags |= LO_FLAGS_USE_AOPS; |
799 | if (!(lo_flags & LO_FLAGS_USE_AOPS) && !file->f_op->write) | 790 | if (!(lo_flags & LO_FLAGS_USE_AOPS) && !file->f_op->write) |
diff --git a/fs/coda/file.c b/fs/coda/file.c index 6a347fbc998a..ffd42815fda1 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c | |||
@@ -47,6 +47,8 @@ coda_file_splice_read(struct file *coda_file, loff_t *ppos, | |||
47 | struct pipe_inode_info *pipe, size_t count, | 47 | struct pipe_inode_info *pipe, size_t count, |
48 | unsigned int flags) | 48 | unsigned int flags) |
49 | { | 49 | { |
50 | ssize_t (*splice_read)(struct file *, loff_t *, | ||
51 | struct pipe_inode_info *, size_t, unsigned int); | ||
50 | struct coda_file_info *cfi; | 52 | struct coda_file_info *cfi; |
51 | struct file *host_file; | 53 | struct file *host_file; |
52 | 54 | ||
@@ -54,10 +56,11 @@ coda_file_splice_read(struct file *coda_file, loff_t *ppos, | |||
54 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); | 56 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); |
55 | host_file = cfi->cfi_container; | 57 | host_file = cfi->cfi_container; |
56 | 58 | ||
57 | if (!host_file->f_op || !host_file->f_op->splice_read) | 59 | splice_read = host_file->f_op->splice_read; |
58 | return -EINVAL; | 60 | if (!splice_read) |
61 | splice_read = default_file_splice_read; | ||
59 | 62 | ||
60 | return host_file->f_op->splice_read(host_file, ppos, pipe, count,flags); | 63 | return splice_read(host_file, ppos, pipe, count, flags); |
61 | } | 64 | } |
62 | 65 | ||
63 | static ssize_t | 66 | static ssize_t |
@@ -302,6 +302,20 @@ int generic_pipe_buf_confirm(struct pipe_inode_info *info, | |||
302 | return 0; | 302 | return 0; |
303 | } | 303 | } |
304 | 304 | ||
305 | /** | ||
306 | * generic_pipe_buf_release - put a reference to a &struct pipe_buffer | ||
307 | * @pipe: the pipe that the buffer belongs to | ||
308 | * @buf: the buffer to put a reference to | ||
309 | * | ||
310 | * Description: | ||
311 | * This function releases a reference to @buf. | ||
312 | */ | ||
313 | void generic_pipe_buf_release(struct pipe_inode_info *pipe, | ||
314 | struct pipe_buffer *buf) | ||
315 | { | ||
316 | page_cache_release(buf->page); | ||
317 | } | ||
318 | |||
305 | static const struct pipe_buf_operations anon_pipe_buf_ops = { | 319 | static const struct pipe_buf_operations anon_pipe_buf_ops = { |
306 | .can_merge = 1, | 320 | .can_merge = 1, |
307 | .map = generic_pipe_buf_map, | 321 | .map = generic_pipe_buf_map, |
diff --git a/fs/read_write.c b/fs/read_write.c index 9d1e76bb9ee1..6c8c55dec2bc 100644 --- a/fs/read_write.c +++ b/fs/read_write.c | |||
@@ -805,12 +805,6 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, | |||
805 | goto out; | 805 | goto out; |
806 | if (!(in_file->f_mode & FMODE_READ)) | 806 | if (!(in_file->f_mode & FMODE_READ)) |
807 | goto fput_in; | 807 | goto fput_in; |
808 | retval = -EINVAL; | ||
809 | in_inode = in_file->f_path.dentry->d_inode; | ||
810 | if (!in_inode) | ||
811 | goto fput_in; | ||
812 | if (!in_file->f_op || !in_file->f_op->splice_read) | ||
813 | goto fput_in; | ||
814 | retval = -ESPIPE; | 808 | retval = -ESPIPE; |
815 | if (!ppos) | 809 | if (!ppos) |
816 | ppos = &in_file->f_pos; | 810 | ppos = &in_file->f_pos; |
@@ -834,6 +828,7 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, | |||
834 | retval = -EINVAL; | 828 | retval = -EINVAL; |
835 | if (!out_file->f_op || !out_file->f_op->sendpage) | 829 | if (!out_file->f_op || !out_file->f_op->sendpage) |
836 | goto fput_out; | 830 | goto fput_out; |
831 | in_inode = in_file->f_path.dentry->d_inode; | ||
837 | out_inode = out_file->f_path.dentry->d_inode; | 832 | out_inode = out_file->f_path.dentry->d_inode; |
838 | retval = rw_verify_area(WRITE, out_file, &out_file->f_pos, count); | 833 | retval = rw_verify_area(WRITE, out_file, &out_file->f_pos, count); |
839 | if (retval < 0) | 834 | if (retval < 0) |
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 | /** |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 5bed436f4353..d926c2bea166 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -2204,6 +2204,8 @@ extern int generic_segment_checks(const struct iovec *iov, | |||
2204 | /* fs/splice.c */ | 2204 | /* fs/splice.c */ |
2205 | extern ssize_t generic_file_splice_read(struct file *, loff_t *, | 2205 | extern ssize_t generic_file_splice_read(struct file *, loff_t *, |
2206 | struct pipe_inode_info *, size_t, unsigned int); | 2206 | struct pipe_inode_info *, size_t, unsigned int); |
2207 | extern ssize_t default_file_splice_read(struct file *, loff_t *, | ||
2208 | struct pipe_inode_info *, size_t, unsigned int); | ||
2207 | extern ssize_t generic_file_splice_write(struct pipe_inode_info *, | 2209 | extern ssize_t generic_file_splice_write(struct pipe_inode_info *, |
2208 | struct file *, loff_t *, size_t, unsigned int); | 2210 | struct file *, loff_t *, size_t, unsigned int); |
2209 | extern ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, | 2211 | extern ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, |
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index c8f038554e80..b43a9e039059 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h | |||
@@ -152,5 +152,6 @@ void generic_pipe_buf_unmap(struct pipe_inode_info *, struct pipe_buffer *, void | |||
152 | void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); | 152 | void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); |
153 | int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); | 153 | int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); |
154 | int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); | 154 | int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); |
155 | void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *); | ||
155 | 156 | ||
156 | #endif | 157 | #endif |