diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2016-09-17 20:44:45 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2016-10-03 20:40:55 -0400 |
commit | 8924feff66f35fe22ce77aafe3f21eb8e5cff881 (patch) | |
tree | 77d74a5edba4d1a30f484e0cffdecfcfcc113d79 | |
parent | db85a9eb2e364e24e71f94798e85dbaa8111bb4d (diff) |
splice: lift pipe_lock out of splice_to_pipe()
* splice_to_pipe() stops at pipe overflow and does *not* take pipe_lock
* ->splice_read() instances do the same
* vmsplice_to_pipe() and do_splice() (ultimate callers of splice_to_pipe())
arrange for waiting, looping, etc. themselves.
That should make pipe_lock the outermost one.
Unfortunately, existing rules for the amount passed by vmsplice_to_pipe()
and do_splice() are quite ugly _and_ userland code can be easily broken
by changing those. It's not even "no more than the maximal capacity of
this pipe" - it's "once we'd fed pipe->nr_buffers pages into the pipe,
leave instead of waiting".
Considering how poorly these rules are documented, let's try "wait for some
space to appear, unless given SPLICE_F_NONBLOCK, then push into pipe
and if we run into overflow, we are done".
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/fuse/dev.c | 2 | ||||
-rw-r--r-- | fs/splice.c | 129 |
2 files changed, 58 insertions, 73 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index a94d2ed81ab4..eaf56c6e9123 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -1364,7 +1364,6 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, | |||
1364 | goto out; | 1364 | goto out; |
1365 | 1365 | ||
1366 | ret = 0; | 1366 | ret = 0; |
1367 | pipe_lock(pipe); | ||
1368 | 1367 | ||
1369 | if (!pipe->readers) { | 1368 | if (!pipe->readers) { |
1370 | send_sig(SIGPIPE, current, 0); | 1369 | send_sig(SIGPIPE, current, 0); |
@@ -1400,7 +1399,6 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, | |||
1400 | } | 1399 | } |
1401 | 1400 | ||
1402 | out_unlock: | 1401 | out_unlock: |
1403 | pipe_unlock(pipe); | ||
1404 | 1402 | ||
1405 | if (do_wakeup) { | 1403 | if (do_wakeup) { |
1406 | smp_mb(); | 1404 | smp_mb(); |
diff --git a/fs/splice.c b/fs/splice.c index 31c52e0269c2..2ca7e6931ad5 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -183,79 +183,41 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe, | |||
183 | struct splice_pipe_desc *spd) | 183 | struct splice_pipe_desc *spd) |
184 | { | 184 | { |
185 | unsigned int spd_pages = spd->nr_pages; | 185 | unsigned int spd_pages = spd->nr_pages; |
186 | int ret, do_wakeup, page_nr; | 186 | int ret = 0, page_nr = 0; |
187 | 187 | ||
188 | if (!spd_pages) | 188 | if (!spd_pages) |
189 | return 0; | 189 | return 0; |
190 | 190 | ||
191 | ret = 0; | 191 | if (unlikely(!pipe->readers)) { |
192 | do_wakeup = 0; | 192 | send_sig(SIGPIPE, current, 0); |
193 | page_nr = 0; | 193 | ret = -EPIPE; |
194 | 194 | goto out; | |
195 | pipe_lock(pipe); | 195 | } |
196 | |||
197 | for (;;) { | ||
198 | if (!pipe->readers) { | ||
199 | send_sig(SIGPIPE, current, 0); | ||
200 | if (!ret) | ||
201 | ret = -EPIPE; | ||
202 | break; | ||
203 | } | ||
204 | |||
205 | if (pipe->nrbufs < pipe->buffers) { | ||
206 | int newbuf = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1); | ||
207 | struct pipe_buffer *buf = pipe->bufs + newbuf; | ||
208 | |||
209 | buf->page = spd->pages[page_nr]; | ||
210 | buf->offset = spd->partial[page_nr].offset; | ||
211 | buf->len = spd->partial[page_nr].len; | ||
212 | buf->private = spd->partial[page_nr].private; | ||
213 | buf->ops = spd->ops; | ||
214 | if (spd->flags & SPLICE_F_GIFT) | ||
215 | buf->flags |= PIPE_BUF_FLAG_GIFT; | ||
216 | |||
217 | pipe->nrbufs++; | ||
218 | page_nr++; | ||
219 | ret += buf->len; | ||
220 | |||
221 | if (pipe->files) | ||
222 | do_wakeup = 1; | ||
223 | 196 | ||
224 | if (!--spd->nr_pages) | 197 | while (pipe->nrbufs < pipe->buffers) { |
225 | break; | 198 | int newbuf = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1); |
226 | if (pipe->nrbufs < pipe->buffers) | 199 | struct pipe_buffer *buf = pipe->bufs + newbuf; |
227 | continue; | ||
228 | 200 | ||
229 | break; | 201 | buf->page = spd->pages[page_nr]; |
230 | } | 202 | buf->offset = spd->partial[page_nr].offset; |
203 | buf->len = spd->partial[page_nr].len; | ||
204 | buf->private = spd->partial[page_nr].private; | ||
205 | buf->ops = spd->ops; | ||
206 | if (spd->flags & SPLICE_F_GIFT) | ||
207 | buf->flags |= PIPE_BUF_FLAG_GIFT; | ||
231 | 208 | ||
232 | if (spd->flags & SPLICE_F_NONBLOCK) { | 209 | pipe->nrbufs++; |
233 | if (!ret) | 210 | page_nr++; |
234 | ret = -EAGAIN; | 211 | ret += buf->len; |
235 | break; | ||
236 | } | ||
237 | 212 | ||
238 | if (signal_pending(current)) { | 213 | if (!--spd->nr_pages) |
239 | if (!ret) | ||
240 | ret = -ERESTARTSYS; | ||
241 | break; | 214 | break; |
242 | } | ||
243 | |||
244 | if (do_wakeup) { | ||
245 | wakeup_pipe_readers(pipe); | ||
246 | do_wakeup = 0; | ||
247 | } | ||
248 | |||
249 | pipe->waiting_writers++; | ||
250 | pipe_wait(pipe); | ||
251 | pipe->waiting_writers--; | ||
252 | } | 215 | } |
253 | 216 | ||
254 | pipe_unlock(pipe); | 217 | if (!ret) |
255 | 218 | ret = -EAGAIN; | |
256 | if (do_wakeup) | ||
257 | wakeup_pipe_readers(pipe); | ||
258 | 219 | ||
220 | out: | ||
259 | while (page_nr < spd_pages) | 221 | while (page_nr < spd_pages) |
260 | spd->spd_release(spd, page_nr++); | 222 | spd->spd_release(spd, page_nr++); |
261 | 223 | ||
@@ -1339,6 +1301,20 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, | |||
1339 | } | 1301 | } |
1340 | EXPORT_SYMBOL(do_splice_direct); | 1302 | EXPORT_SYMBOL(do_splice_direct); |
1341 | 1303 | ||
1304 | static int wait_for_space(struct pipe_inode_info *pipe, unsigned flags) | ||
1305 | { | ||
1306 | while (pipe->nrbufs == pipe->buffers) { | ||
1307 | if (flags & SPLICE_F_NONBLOCK) | ||
1308 | return -EAGAIN; | ||
1309 | if (signal_pending(current)) | ||
1310 | return -ERESTARTSYS; | ||
1311 | pipe->waiting_writers++; | ||
1312 | pipe_wait(pipe); | ||
1313 | pipe->waiting_writers--; | ||
1314 | } | ||
1315 | return 0; | ||
1316 | } | ||
1317 | |||
1342 | static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe, | 1318 | static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe, |
1343 | struct pipe_inode_info *opipe, | 1319 | struct pipe_inode_info *opipe, |
1344 | size_t len, unsigned int flags); | 1320 | size_t len, unsigned int flags); |
@@ -1421,8 +1397,13 @@ static long do_splice(struct file *in, loff_t __user *off_in, | |||
1421 | offset = in->f_pos; | 1397 | offset = in->f_pos; |
1422 | } | 1398 | } |
1423 | 1399 | ||
1424 | ret = do_splice_to(in, &offset, opipe, len, flags); | 1400 | pipe_lock(opipe); |
1425 | 1401 | ret = wait_for_space(opipe, flags); | |
1402 | if (!ret) | ||
1403 | ret = do_splice_to(in, &offset, opipe, len, flags); | ||
1404 | pipe_unlock(opipe); | ||
1405 | if (ret > 0) | ||
1406 | wakeup_pipe_readers(opipe); | ||
1426 | if (!off_in) | 1407 | if (!off_in) |
1427 | in->f_pos = offset; | 1408 | in->f_pos = offset; |
1428 | else if (copy_to_user(off_in, &offset, sizeof(loff_t))) | 1409 | else if (copy_to_user(off_in, &offset, sizeof(loff_t))) |
@@ -1546,14 +1527,20 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *uiov, | |||
1546 | return -ENOMEM; | 1527 | return -ENOMEM; |
1547 | } | 1528 | } |
1548 | 1529 | ||
1549 | spd.nr_pages = get_iovec_page_array(&from, spd.pages, | 1530 | pipe_lock(pipe); |
1550 | spd.partial, | 1531 | ret = wait_for_space(pipe, flags); |
1551 | spd.nr_pages_max); | 1532 | if (!ret) { |
1552 | if (spd.nr_pages <= 0) | 1533 | spd.nr_pages = get_iovec_page_array(&from, spd.pages, |
1553 | ret = spd.nr_pages; | 1534 | spd.partial, |
1554 | else | 1535 | spd.nr_pages_max); |
1555 | ret = splice_to_pipe(pipe, &spd); | 1536 | if (spd.nr_pages <= 0) |
1556 | 1537 | ret = spd.nr_pages; | |
1538 | else | ||
1539 | ret = splice_to_pipe(pipe, &spd); | ||
1540 | } | ||
1541 | pipe_unlock(pipe); | ||
1542 | if (ret > 0) | ||
1543 | wakeup_pipe_readers(pipe); | ||
1557 | splice_shrink_spd(&spd); | 1544 | splice_shrink_spd(&spd); |
1558 | kfree(iov); | 1545 | kfree(iov); |
1559 | return ret; | 1546 | return ret; |