diff options
Diffstat (limited to 'fs/splice.c')
-rw-r--r-- | fs/splice.c | 64 |
1 files changed, 48 insertions, 16 deletions
diff --git a/fs/splice.c b/fs/splice.c index a555d0a83fe9..07f4d863c2d4 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -22,7 +22,10 @@ | |||
22 | #include <linux/pipe_fs_i.h> | 22 | #include <linux/pipe_fs_i.h> |
23 | #include <linux/mm_inline.h> | 23 | #include <linux/mm_inline.h> |
24 | #include <linux/swap.h> | 24 | #include <linux/swap.h> |
25 | #include <linux/writeback.h> | ||
26 | #include <linux/buffer_head.h> | ||
25 | #include <linux/module.h> | 27 | #include <linux/module.h> |
28 | #include <linux/syscalls.h> | ||
26 | 29 | ||
27 | /* | 30 | /* |
28 | * Passed to the actors | 31 | * Passed to the actors |
@@ -38,11 +41,15 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *info, | |||
38 | struct pipe_buffer *buf) | 41 | struct pipe_buffer *buf) |
39 | { | 42 | { |
40 | struct page *page = buf->page; | 43 | struct page *page = buf->page; |
44 | struct address_space *mapping = page_mapping(page); | ||
41 | 45 | ||
42 | WARN_ON(!PageLocked(page)); | 46 | WARN_ON(!PageLocked(page)); |
43 | WARN_ON(!PageUptodate(page)); | 47 | WARN_ON(!PageUptodate(page)); |
44 | 48 | ||
45 | if (!remove_mapping(page_mapping(page), page)) | 49 | if (PagePrivate(page)) |
50 | try_to_release_page(page, mapping_gfp_mask(mapping)); | ||
51 | |||
52 | if (!remove_mapping(mapping, page)) | ||
46 | return 1; | 53 | return 1; |
47 | 54 | ||
48 | if (PageLRU(page)) { | 55 | if (PageLRU(page)) { |
@@ -55,7 +62,6 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *info, | |||
55 | spin_unlock_irq(&zone->lru_lock); | 62 | spin_unlock_irq(&zone->lru_lock); |
56 | } | 63 | } |
57 | 64 | ||
58 | buf->stolen = 1; | ||
59 | return 0; | 65 | return 0; |
60 | } | 66 | } |
61 | 67 | ||
@@ -64,7 +70,6 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *info, | |||
64 | { | 70 | { |
65 | page_cache_release(buf->page); | 71 | page_cache_release(buf->page); |
66 | buf->page = NULL; | 72 | buf->page = NULL; |
67 | buf->stolen = 0; | ||
68 | } | 73 | } |
69 | 74 | ||
70 | static void *page_cache_pipe_buf_map(struct file *file, | 75 | static void *page_cache_pipe_buf_map(struct file *file, |
@@ -91,8 +96,7 @@ static void *page_cache_pipe_buf_map(struct file *file, | |||
91 | static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info, | 96 | static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info, |
92 | struct pipe_buffer *buf) | 97 | struct pipe_buffer *buf) |
93 | { | 98 | { |
94 | if (!buf->stolen) | 99 | unlock_page(buf->page); |
95 | unlock_page(buf->page); | ||
96 | kunmap(buf->page); | 100 | kunmap(buf->page); |
97 | } | 101 | } |
98 | 102 | ||
@@ -319,7 +323,8 @@ ssize_t generic_file_splice_read(struct file *in, struct inode *pipe, | |||
319 | } | 323 | } |
320 | 324 | ||
321 | /* | 325 | /* |
322 | * Send 'len' bytes to socket from 'file' at position 'pos' using sendpage(). | 326 | * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos' |
327 | * using sendpage(). | ||
323 | */ | 328 | */ |
324 | static int pipe_to_sendpage(struct pipe_inode_info *info, | 329 | static int pipe_to_sendpage(struct pipe_inode_info *info, |
325 | struct pipe_buffer *buf, struct splice_desc *sd) | 330 | struct pipe_buffer *buf, struct splice_desc *sd) |
@@ -379,7 +384,7 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, | |||
379 | struct page *page; | 384 | struct page *page; |
380 | pgoff_t index; | 385 | pgoff_t index; |
381 | char *src; | 386 | char *src; |
382 | int ret; | 387 | int ret, stolen; |
383 | 388 | ||
384 | /* | 389 | /* |
385 | * after this, page will be locked and unmapped | 390 | * after this, page will be locked and unmapped |
@@ -390,6 +395,7 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, | |||
390 | 395 | ||
391 | index = sd->pos >> PAGE_CACHE_SHIFT; | 396 | index = sd->pos >> PAGE_CACHE_SHIFT; |
392 | offset = sd->pos & ~PAGE_CACHE_MASK; | 397 | offset = sd->pos & ~PAGE_CACHE_MASK; |
398 | stolen = 0; | ||
393 | 399 | ||
394 | /* | 400 | /* |
395 | * reuse buf page, if SPLICE_F_MOVE is set | 401 | * reuse buf page, if SPLICE_F_MOVE is set |
@@ -399,6 +405,7 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, | |||
399 | goto find_page; | 405 | goto find_page; |
400 | 406 | ||
401 | page = buf->page; | 407 | page = buf->page; |
408 | stolen = 1; | ||
402 | if (add_to_page_cache_lru(page, mapping, index, | 409 | if (add_to_page_cache_lru(page, mapping, index, |
403 | mapping_gfp_mask(mapping))) | 410 | mapping_gfp_mask(mapping))) |
404 | goto find_page; | 411 | goto find_page; |
@@ -443,10 +450,13 @@ find_page: | |||
443 | } | 450 | } |
444 | 451 | ||
445 | ret = mapping->a_ops->prepare_write(file, page, 0, sd->len); | 452 | ret = mapping->a_ops->prepare_write(file, page, 0, sd->len); |
446 | if (ret) | 453 | if (ret == AOP_TRUNCATED_PAGE) { |
454 | page_cache_release(page); | ||
455 | goto find_page; | ||
456 | } else if (ret) | ||
447 | goto out; | 457 | goto out; |
448 | 458 | ||
449 | if (!buf->stolen) { | 459 | if (!stolen) { |
450 | char *dst = kmap_atomic(page, KM_USER0); | 460 | char *dst = kmap_atomic(page, KM_USER0); |
451 | 461 | ||
452 | memcpy(dst + offset, src + buf->offset, sd->len); | 462 | memcpy(dst + offset, src + buf->offset, sd->len); |
@@ -455,16 +465,18 @@ find_page: | |||
455 | } | 465 | } |
456 | 466 | ||
457 | ret = mapping->a_ops->commit_write(file, page, 0, sd->len); | 467 | ret = mapping->a_ops->commit_write(file, page, 0, sd->len); |
458 | if (ret < 0) | 468 | if (ret == AOP_TRUNCATED_PAGE) { |
469 | page_cache_release(page); | ||
470 | goto find_page; | ||
471 | } else if (ret) | ||
459 | goto out; | 472 | goto out; |
460 | 473 | ||
461 | set_page_dirty(page); | 474 | balance_dirty_pages_ratelimited(mapping); |
462 | ret = write_one_page(page, 0); | ||
463 | out: | 475 | out: |
464 | if (ret < 0) | 476 | if (!stolen) { |
465 | unlock_page(page); | ||
466 | if (!buf->stolen) | ||
467 | page_cache_release(page); | 477 | page_cache_release(page); |
478 | unlock_page(page); | ||
479 | } | ||
468 | buf->ops->unmap(info, buf); | 480 | buf->ops->unmap(info, buf); |
469 | return ret; | 481 | return ret; |
470 | } | 482 | } |
@@ -576,7 +588,27 @@ static ssize_t move_from_pipe(struct inode *inode, struct file *out, | |||
576 | ssize_t generic_file_splice_write(struct inode *inode, struct file *out, | 588 | ssize_t generic_file_splice_write(struct inode *inode, struct file *out, |
577 | size_t len, unsigned int flags) | 589 | size_t len, unsigned int flags) |
578 | { | 590 | { |
579 | return move_from_pipe(inode, out, len, flags, pipe_to_file); | 591 | struct address_space *mapping = out->f_mapping; |
592 | ssize_t ret = move_from_pipe(inode, out, len, flags, pipe_to_file); | ||
593 | |||
594 | /* | ||
595 | * if file or inode is SYNC and we actually wrote some data, sync it | ||
596 | */ | ||
597 | if (unlikely((out->f_flags & O_SYNC) || IS_SYNC(mapping->host)) | ||
598 | && ret > 0) { | ||
599 | struct inode *inode = mapping->host; | ||
600 | int err; | ||
601 | |||
602 | mutex_lock(&inode->i_mutex); | ||
603 | err = generic_osync_inode(mapping->host, mapping, | ||
604 | OSYNC_METADATA|OSYNC_DATA); | ||
605 | mutex_unlock(&inode->i_mutex); | ||
606 | |||
607 | if (err) | ||
608 | ret = err; | ||
609 | } | ||
610 | |||
611 | return ret; | ||
580 | } | 612 | } |
581 | 613 | ||
582 | ssize_t generic_splice_sendpage(struct inode *inode, struct file *out, | 614 | ssize_t generic_splice_sendpage(struct inode *inode, struct file *out, |