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/namei.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/namei.c')
-rw-r--r-- | fs/namei.c | 46 |
1 files changed, 11 insertions, 35 deletions
diff --git a/fs/namei.c b/fs/namei.c index a83160acd748..b40b8084eefc 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -2729,53 +2729,29 @@ int __page_symlink(struct inode *inode, const char *symname, int len, | |||
2729 | { | 2729 | { |
2730 | struct address_space *mapping = inode->i_mapping; | 2730 | struct address_space *mapping = inode->i_mapping; |
2731 | struct page *page; | 2731 | struct page *page; |
2732 | void *fsdata; | ||
2732 | int err; | 2733 | int err; |
2733 | char *kaddr; | 2734 | char *kaddr; |
2734 | 2735 | ||
2735 | retry: | 2736 | retry: |
2736 | err = -ENOMEM; | 2737 | err = pagecache_write_begin(NULL, mapping, 0, len-1, |
2737 | page = find_or_create_page(mapping, 0, gfp_mask); | 2738 | AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata); |
2738 | if (!page) | ||
2739 | goto fail; | ||
2740 | err = mapping->a_ops->prepare_write(NULL, page, 0, len-1); | ||
2741 | if (err == AOP_TRUNCATED_PAGE) { | ||
2742 | page_cache_release(page); | ||
2743 | goto retry; | ||
2744 | } | ||
2745 | if (err) | 2739 | if (err) |
2746 | goto fail_map; | 2740 | goto fail; |
2741 | |||
2747 | kaddr = kmap_atomic(page, KM_USER0); | 2742 | kaddr = kmap_atomic(page, KM_USER0); |
2748 | memcpy(kaddr, symname, len-1); | 2743 | memcpy(kaddr, symname, len-1); |
2749 | kunmap_atomic(kaddr, KM_USER0); | 2744 | kunmap_atomic(kaddr, KM_USER0); |
2750 | err = mapping->a_ops->commit_write(NULL, page, 0, len-1); | 2745 | |
2751 | if (err == AOP_TRUNCATED_PAGE) { | 2746 | err = pagecache_write_end(NULL, mapping, 0, len-1, len-1, |
2752 | page_cache_release(page); | 2747 | page, fsdata); |
2753 | goto retry; | ||
2754 | } | ||
2755 | if (err) | ||
2756 | goto fail_map; | ||
2757 | /* | ||
2758 | * Notice that we are _not_ going to block here - end of page is | ||
2759 | * unmapped, so this will only try to map the rest of page, see | ||
2760 | * that it is unmapped (typically even will not look into inode - | ||
2761 | * ->i_size will be enough for everything) and zero it out. | ||
2762 | * OTOH it's obviously correct and should make the page up-to-date. | ||
2763 | */ | ||
2764 | if (!PageUptodate(page)) { | ||
2765 | err = mapping->a_ops->readpage(NULL, page); | ||
2766 | if (err != AOP_TRUNCATED_PAGE) | ||
2767 | wait_on_page_locked(page); | ||
2768 | } else { | ||
2769 | unlock_page(page); | ||
2770 | } | ||
2771 | page_cache_release(page); | ||
2772 | if (err < 0) | 2748 | if (err < 0) |
2773 | goto fail; | 2749 | goto fail; |
2750 | if (err < len-1) | ||
2751 | goto retry; | ||
2752 | |||
2774 | mark_inode_dirty(inode); | 2753 | mark_inode_dirty(inode); |
2775 | return 0; | 2754 | return 0; |
2776 | fail_map: | ||
2777 | unlock_page(page); | ||
2778 | page_cache_release(page); | ||
2779 | fail: | 2755 | fail: |
2780 | return err; | 2756 | return err; |
2781 | } | 2757 | } |