diff options
| author | Jens Axboe <axboe@suse.de> | 2006-04-02 17:04:46 -0400 |
|---|---|---|
| committer | Jens Axboe <axboe@suse.de> | 2006-04-02 17:04:46 -0400 |
| commit | 4f6f0bd2ffa4e31c3524f5e65c84a29b6ab73307 (patch) | |
| tree | 682ab1b41c3ee799d37bfa1874c79ae92b3755fd | |
| parent | 53cd9ae886273d6c2b8ba4aa63d6cd6b1217b57f (diff) | |
[PATCH] splice: improve writeback and clean up page stealing
By cleaning up the writeback logic (killing write_one_page() and the manual
set_page_dirty()), we can get rid of ->stolen inside the pipe_buffer and
just keep it local in pipe_to_file().
This also adds dirty page balancing logic and O_SYNC handling.
Signed-off-by: Jens Axboe <axboe@suse.de>
| -rw-r--r-- | fs/pipe.c | 1 | ||||
| -rw-r--r-- | fs/splice.c | 64 | ||||
| -rw-r--r-- | include/linux/pipe_fs_i.h | 1 |
3 files changed, 48 insertions, 18 deletions
| @@ -124,7 +124,6 @@ static void anon_pipe_buf_unmap(struct pipe_inode_info *info, struct pipe_buffer | |||
| 124 | static int anon_pipe_buf_steal(struct pipe_inode_info *info, | 124 | static int anon_pipe_buf_steal(struct pipe_inode_info *info, |
| 125 | struct pipe_buffer *buf) | 125 | struct pipe_buffer *buf) |
| 126 | { | 126 | { |
| 127 | buf->stolen = 1; | ||
| 128 | return 0; | 127 | return 0; |
| 129 | } | 128 | } |
| 130 | 129 | ||
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, |
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index d218fc729319..32865004751c 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h | |||
| @@ -9,7 +9,6 @@ struct pipe_buffer { | |||
| 9 | struct page *page; | 9 | struct page *page; |
| 10 | unsigned int offset, len; | 10 | unsigned int offset, len; |
| 11 | struct pipe_buf_operations *ops; | 11 | struct pipe_buf_operations *ops; |
| 12 | unsigned int stolen; | ||
| 13 | }; | 12 | }; |
| 14 | 13 | ||
| 15 | struct pipe_buf_operations { | 14 | struct pipe_buf_operations { |
