aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2016-09-17 20:44:45 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2016-10-03 20:40:55 -0400
commit8924feff66f35fe22ce77aafe3f21eb8e5cff881 (patch)
tree77d74a5edba4d1a30f484e0cffdecfcfcc113d79
parentdb85a9eb2e364e24e71f94798e85dbaa8111bb4d (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.c2
-rw-r--r--fs/splice.c129
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
1402out_unlock: 1401out_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
220out:
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}
1340EXPORT_SYMBOL(do_splice_direct); 1302EXPORT_SYMBOL(do_splice_direct);
1341 1303
1304static 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
1342static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe, 1318static 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;