Commit 0b0a47f5c4a30b58432e20ae1706a27baea91a88
Committed by
Jens Axboe
1 parent
6818173bd6
Exists in
master
and in
4 other branches
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>
Showing 1 changed file with 138 additions and 4 deletions Side-by-side Diff
fs/splice.c
... | ... | @@ -535,6 +535,21 @@ |
535 | 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 | 553 | ssize_t default_file_splice_read(struct file *in, loff_t *ppos, |
539 | 554 | struct pipe_inode_info *pipe, size_t len, |
540 | 555 | unsigned int flags) |
... | ... | @@ -988,6 +1003,122 @@ |
988 | 1003 | |
989 | 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 | 1123 | * generic_splice_sendpage - splice data from a pipe to a socket |
993 | 1124 | * @pipe: pipe to splice from |
994 | 1125 | |
... | ... | @@ -1015,11 +1146,10 @@ |
1015 | 1146 | static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, |
1016 | 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 | 1151 | int ret; |
1019 | 1152 | |
1020 | - if (unlikely(!out->f_op || !out->f_op->splice_write)) | |
1021 | - return -EINVAL; | |
1022 | - | |
1023 | 1153 | if (unlikely(!(out->f_mode & FMODE_WRITE))) |
1024 | 1154 | return -EBADF; |
1025 | 1155 | |
... | ... | @@ -1030,7 +1160,11 @@ |
1030 | 1160 | if (unlikely(ret < 0)) |
1031 | 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 | /* |