diff options
author | Nick Piggin <npiggin@suse.de> | 2007-10-16 04:25:01 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-16 12:42:55 -0400 |
commit | afddba49d18f346e5cc2938b6ed7c512db18ca68 (patch) | |
tree | 4726e3d3b0e9e8e5b5d3b2b0cccb36446bbdf3ca /fs/splice.c | |
parent | 637aff46f94a754207c80c8c64bf1b74f24b967d (diff) |
fs: introduce write_begin, write_end, and perform_write aops
These are intended to replace prepare_write and commit_write with more
flexible alternatives that are also able to avoid the buffered write
deadlock problems efficiently (which prepare_write is unable to do).
[mark.fasheh@oracle.com: API design contributions, code review and fixes]
[akpm@linux-foundation.org: various fixes]
[dmonakhov@sw.ru: new aop block_write_begin fix]
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Mark Fasheh <mark.fasheh@oracle.com>
Signed-off-by: Dmitriy Monakhov <dmonakhov@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/splice.c')
-rw-r--r-- | fs/splice.c | 69 |
1 files changed, 7 insertions, 62 deletions
diff --git a/fs/splice.c b/fs/splice.c index 2df6be43c667..a7568bcc0f99 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -563,7 +563,7 @@ static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf, | |||
563 | struct address_space *mapping = file->f_mapping; | 563 | struct address_space *mapping = file->f_mapping; |
564 | unsigned int offset, this_len; | 564 | unsigned int offset, this_len; |
565 | struct page *page; | 565 | struct page *page; |
566 | pgoff_t index; | 566 | void *fsdata; |
567 | int ret; | 567 | int ret; |
568 | 568 | ||
569 | /* | 569 | /* |
@@ -573,49 +573,16 @@ static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf, | |||
573 | if (unlikely(ret)) | 573 | if (unlikely(ret)) |
574 | return ret; | 574 | return ret; |
575 | 575 | ||
576 | index = sd->pos >> PAGE_CACHE_SHIFT; | ||
577 | offset = sd->pos & ~PAGE_CACHE_MASK; | 576 | offset = sd->pos & ~PAGE_CACHE_MASK; |
578 | 577 | ||
579 | this_len = sd->len; | 578 | this_len = sd->len; |
580 | if (this_len + offset > PAGE_CACHE_SIZE) | 579 | if (this_len + offset > PAGE_CACHE_SIZE) |
581 | this_len = PAGE_CACHE_SIZE - offset; | 580 | this_len = PAGE_CACHE_SIZE - offset; |
582 | 581 | ||
583 | find_page: | 582 | ret = pagecache_write_begin(file, mapping, sd->pos, this_len, |
584 | page = find_lock_page(mapping, index); | 583 | AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata); |
585 | if (!page) { | 584 | if (unlikely(ret)) |
586 | ret = -ENOMEM; | 585 | goto out; |
587 | page = page_cache_alloc_cold(mapping); | ||
588 | if (unlikely(!page)) | ||
589 | goto out_ret; | ||
590 | |||
591 | /* | ||
592 | * This will also lock the page | ||
593 | */ | ||
594 | ret = add_to_page_cache_lru(page, mapping, index, | ||
595 | GFP_KERNEL); | ||
596 | if (unlikely(ret)) | ||
597 | goto out_release; | ||
598 | } | ||
599 | |||
600 | ret = mapping->a_ops->prepare_write(file, page, offset, offset+this_len); | ||
601 | if (unlikely(ret)) { | ||
602 | loff_t isize = i_size_read(mapping->host); | ||
603 | |||
604 | if (ret != AOP_TRUNCATED_PAGE) | ||
605 | unlock_page(page); | ||
606 | page_cache_release(page); | ||
607 | if (ret == AOP_TRUNCATED_PAGE) | ||
608 | goto find_page; | ||
609 | |||
610 | /* | ||
611 | * prepare_write() may have instantiated a few blocks | ||
612 | * outside i_size. Trim these off again. | ||
613 | */ | ||
614 | if (sd->pos + this_len > isize) | ||
615 | vmtruncate(mapping->host, isize); | ||
616 | |||
617 | goto out_ret; | ||
618 | } | ||
619 | 586 | ||
620 | if (buf->page != page) { | 587 | if (buf->page != page) { |
621 | /* | 588 | /* |
@@ -629,31 +596,9 @@ find_page: | |||
629 | kunmap_atomic(dst, KM_USER1); | 596 | kunmap_atomic(dst, KM_USER1); |
630 | buf->ops->unmap(pipe, buf, src); | 597 | buf->ops->unmap(pipe, buf, src); |
631 | } | 598 | } |
632 | 599 | ret = pagecache_write_end(file, mapping, sd->pos, this_len, this_len, | |
633 | ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len); | 600 | page, fsdata); |
634 | if (ret) { | ||
635 | if (ret == AOP_TRUNCATED_PAGE) { | ||
636 | page_cache_release(page); | ||
637 | goto find_page; | ||
638 | } | ||
639 | if (ret < 0) | ||
640 | goto out; | ||
641 | /* | ||
642 | * Partial write has happened, so 'ret' already initialized by | ||
643 | * number of bytes written, Where is nothing we have to do here. | ||
644 | */ | ||
645 | } else | ||
646 | ret = this_len; | ||
647 | /* | ||
648 | * Return the number of bytes written and mark page as | ||
649 | * accessed, we are now done! | ||
650 | */ | ||
651 | mark_page_accessed(page); | ||
652 | out: | 601 | out: |
653 | unlock_page(page); | ||
654 | out_release: | ||
655 | page_cache_release(page); | ||
656 | out_ret: | ||
657 | return ret; | 602 | return ret; |
658 | } | 603 | } |
659 | 604 | ||