diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2009-05-07 09:37:37 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2009-05-11 08:13:10 -0400 |
commit | 0b0a47f5c4a30b58432e20ae1706a27baea91a88 (patch) | |
tree | 294de65aae0eaf2b7865ff1db971226c8a77635b /fs/splice.c | |
parent | 6818173bd658439b83896a2a7586f64ab51bf29c (diff) |
splice: implement default splice_write method
If f_op->splice_write() is not implemented, fall back to a plain write.
Use vfs_writev() to write from the pipe buffers.
This will allow splice on all filesystems and file types. 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>
Diffstat (limited to 'fs/splice.c')
-rw-r--r-- | fs/splice.c | 142 |
1 files changed, 138 insertions, 4 deletions
diff --git a/fs/splice.c b/fs/splice.c index 3bd9cb21b38e..eefd96b1d7fb 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -535,6 +535,21 @@ static ssize_t kernel_readv(struct file *file, const struct iovec *vec, | |||
535 | return res; | 535 | return res; |
536 | } | 536 | } |
537 | 537 | ||
538 | static ssize_t kernel_writev(struct file *file, const struct iovec *vec, | ||
539 | unsigned long vlen, loff_t *ppos) | ||
540 | { | ||
541 | mm_segment_t old_fs; | ||
542 | ssize_t res; | ||
543 | |||
544 | old_fs = get_fs(); | ||
545 | set_fs(get_ds()); | ||
546 | /* The cast to a user pointer is valid due to the set_fs() */ | ||
547 | res = vfs_writev(file, (const struct iovec __user *)vec, vlen, ppos); | ||
548 | set_fs(old_fs); | ||
549 | |||
550 | return res; | ||
551 | } | ||
552 | |||
538 | ssize_t default_file_splice_read(struct file *in, loff_t *ppos, | 553 | ssize_t default_file_splice_read(struct file *in, loff_t *ppos, |
539 | struct pipe_inode_info *pipe, size_t len, | 554 | struct pipe_inode_info *pipe, size_t len, |
540 | unsigned int flags) | 555 | unsigned int flags) |
@@ -988,6 +1003,122 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out, | |||
988 | 1003 | ||
989 | EXPORT_SYMBOL(generic_file_splice_write); | 1004 | EXPORT_SYMBOL(generic_file_splice_write); |
990 | 1005 | ||
1006 | static struct pipe_buffer *nth_pipe_buf(struct pipe_inode_info *pipe, int n) | ||
1007 | { | ||
1008 | return &pipe->bufs[(pipe->curbuf + n) % PIPE_BUFFERS]; | ||
1009 | } | ||
1010 | |||
1011 | static ssize_t default_file_splice_write(struct pipe_inode_info *pipe, | ||
1012 | struct file *out, loff_t *ppos, | ||
1013 | size_t len, unsigned int flags) | ||
1014 | { | ||
1015 | ssize_t ret = 0; | ||
1016 | ssize_t total_len = 0; | ||
1017 | int do_wakeup = 0; | ||
1018 | |||
1019 | pipe_lock(pipe); | ||
1020 | while (len) { | ||
1021 | struct pipe_buffer *buf; | ||
1022 | void *data[PIPE_BUFFERS]; | ||
1023 | struct iovec vec[PIPE_BUFFERS]; | ||
1024 | unsigned int nr_pages = 0; | ||
1025 | unsigned int write_len = 0; | ||
1026 | unsigned int now_len = len; | ||
1027 | unsigned int this_len; | ||
1028 | int i; | ||
1029 | |||
1030 | BUG_ON(pipe->nrbufs > PIPE_BUFFERS); | ||
1031 | for (i = 0; i < pipe->nrbufs && now_len; i++) { | ||
1032 | buf = nth_pipe_buf(pipe, i); | ||
1033 | |||
1034 | ret = buf->ops->confirm(pipe, buf); | ||
1035 | if (ret) | ||
1036 | break; | ||
1037 | |||
1038 | data[i] = buf->ops->map(pipe, buf, 0); | ||
1039 | this_len = min(buf->len, now_len); | ||
1040 | vec[i].iov_base = (void __user *) data[i] + buf->offset; | ||
1041 | vec[i].iov_len = this_len; | ||
1042 | now_len -= this_len; | ||
1043 | write_len += this_len; | ||
1044 | nr_pages++; | ||
1045 | } | ||
1046 | |||
1047 | if (nr_pages) { | ||
1048 | ret = kernel_writev(out, vec, nr_pages, ppos); | ||
1049 | if (ret == 0) | ||
1050 | ret = -EIO; | ||
1051 | if (ret > 0) { | ||
1052 | len -= ret; | ||
1053 | total_len += ret; | ||
1054 | } | ||
1055 | } | ||
1056 | |||
1057 | for (i = 0; i < nr_pages; i++) { | ||
1058 | buf = nth_pipe_buf(pipe, i); | ||
1059 | buf->ops->unmap(pipe, buf, data[i]); | ||
1060 | |||
1061 | if (ret > 0) { | ||
1062 | this_len = min_t(unsigned, vec[i].iov_len, ret); | ||
1063 | buf->offset += this_len; | ||
1064 | buf->len -= this_len; | ||
1065 | ret -= this_len; | ||
1066 | } | ||
1067 | } | ||
1068 | |||
1069 | if (ret < 0) | ||
1070 | break; | ||
1071 | |||
1072 | while (pipe->nrbufs) { | ||
1073 | const struct pipe_buf_operations *ops; | ||
1074 | |||
1075 | buf = nth_pipe_buf(pipe, 0); | ||
1076 | if (buf->len) | ||
1077 | break; | ||
1078 | |||
1079 | ops = buf->ops; | ||
1080 | buf->ops = NULL; | ||
1081 | ops->release(pipe, buf); | ||
1082 | pipe->curbuf = (pipe->curbuf + 1) % PIPE_BUFFERS; | ||
1083 | pipe->nrbufs--; | ||
1084 | if (pipe->inode) | ||
1085 | do_wakeup = 1; | ||
1086 | } | ||
1087 | |||
1088 | if (pipe->nrbufs) | ||
1089 | continue; | ||
1090 | if (!pipe->writers) | ||
1091 | break; | ||
1092 | if (!pipe->waiting_writers) { | ||
1093 | if (total_len) | ||
1094 | break; | ||
1095 | } | ||
1096 | |||
1097 | if (flags & SPLICE_F_NONBLOCK) { | ||
1098 | ret = -EAGAIN; | ||
1099 | break; | ||
1100 | } | ||
1101 | |||
1102 | if (signal_pending(current)) { | ||
1103 | ret = -ERESTARTSYS; | ||
1104 | break; | ||
1105 | } | ||
1106 | |||
1107 | if (do_wakeup) { | ||
1108 | wakeup_pipe_writers(pipe); | ||
1109 | do_wakeup = 0; | ||
1110 | } | ||
1111 | |||
1112 | pipe_wait(pipe); | ||
1113 | } | ||
1114 | pipe_unlock(pipe); | ||
1115 | |||
1116 | if (do_wakeup) | ||
1117 | wakeup_pipe_writers(pipe); | ||
1118 | |||
1119 | return total_len ? total_len : ret; | ||
1120 | } | ||
1121 | |||
991 | /** | 1122 | /** |
992 | * generic_splice_sendpage - splice data from a pipe to a socket | 1123 | * generic_splice_sendpage - splice data from a pipe to a socket |
993 | * @pipe: pipe to splice from | 1124 | * @pipe: pipe to splice from |
@@ -1015,11 +1146,10 @@ EXPORT_SYMBOL(generic_splice_sendpage); | |||
1015 | static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, | 1146 | static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, |
1016 | loff_t *ppos, size_t len, unsigned int flags) | 1147 | loff_t *ppos, size_t len, unsigned int flags) |
1017 | { | 1148 | { |
1149 | ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, | ||
1150 | loff_t *, size_t, unsigned int); | ||
1018 | int ret; | 1151 | int ret; |
1019 | 1152 | ||
1020 | if (unlikely(!out->f_op || !out->f_op->splice_write)) | ||
1021 | return -EINVAL; | ||
1022 | |||
1023 | if (unlikely(!(out->f_mode & FMODE_WRITE))) | 1153 | if (unlikely(!(out->f_mode & FMODE_WRITE))) |
1024 | return -EBADF; | 1154 | return -EBADF; |
1025 | 1155 | ||
@@ -1030,7 +1160,11 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, | |||
1030 | if (unlikely(ret < 0)) | 1160 | if (unlikely(ret < 0)) |
1031 | return ret; | 1161 | return ret; |
1032 | 1162 | ||
1033 | return out->f_op->splice_write(pipe, out, ppos, len, flags); | 1163 | splice_write = out->f_op->splice_write; |
1164 | if (!splice_write) | ||
1165 | splice_write = default_file_splice_write; | ||
1166 | |||
1167 | return splice_write(pipe, out, ppos, len, flags); | ||
1034 | } | 1168 | } |
1035 | 1169 | ||
1036 | /* | 1170 | /* |