diff options
author | Yan, Zheng <zyan@redhat.com> | 2015-06-17 15:10:58 -0400 |
---|---|---|
committer | Ilya Dryomov <idryomov@gmail.com> | 2015-06-25 11:30:53 -0400 |
commit | e1966b49446a43994c3f25a07d0eb4d05660b429 (patch) | |
tree | 43464087b52c41e58396f7805783bd9e627d2bfb /fs/ceph | |
parent | d3834fefcfe5610702379d78596337875df2db5b (diff) |
ceph: fix ceph_writepages_start()
Before a page get locked, someone else can write data to the page
and increase the i_size. So we should re-check the i_size after
pages are locked.
Signed-off-by: Yan, Zheng <zyan@redhat.com>
Diffstat (limited to 'fs/ceph')
-rw-r--r-- | fs/ceph/addr.c | 37 |
1 files changed, 23 insertions, 14 deletions
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 97102038fe03..890c50971a69 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c | |||
@@ -440,7 +440,7 @@ out: | |||
440 | * only snap context we are allowed to write back. | 440 | * only snap context we are allowed to write back. |
441 | */ | 441 | */ |
442 | static struct ceph_snap_context *get_oldest_context(struct inode *inode, | 442 | static struct ceph_snap_context *get_oldest_context(struct inode *inode, |
443 | u64 *snap_size) | 443 | loff_t *snap_size) |
444 | { | 444 | { |
445 | struct ceph_inode_info *ci = ceph_inode(inode); | 445 | struct ceph_inode_info *ci = ceph_inode(inode); |
446 | struct ceph_snap_context *snapc = NULL; | 446 | struct ceph_snap_context *snapc = NULL; |
@@ -480,8 +480,9 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) | |||
480 | struct ceph_osd_client *osdc; | 480 | struct ceph_osd_client *osdc; |
481 | struct ceph_snap_context *snapc, *oldest; | 481 | struct ceph_snap_context *snapc, *oldest; |
482 | loff_t page_off = page_offset(page); | 482 | loff_t page_off = page_offset(page); |
483 | loff_t snap_size = -1; | ||
483 | long writeback_stat; | 484 | long writeback_stat; |
484 | u64 truncate_size, snap_size = 0; | 485 | u64 truncate_size; |
485 | u32 truncate_seq; | 486 | u32 truncate_seq; |
486 | int err = 0, len = PAGE_CACHE_SIZE; | 487 | int err = 0, len = PAGE_CACHE_SIZE; |
487 | 488 | ||
@@ -516,7 +517,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) | |||
516 | spin_lock(&ci->i_ceph_lock); | 517 | spin_lock(&ci->i_ceph_lock); |
517 | truncate_seq = ci->i_truncate_seq; | 518 | truncate_seq = ci->i_truncate_seq; |
518 | truncate_size = ci->i_truncate_size; | 519 | truncate_size = ci->i_truncate_size; |
519 | if (!snap_size) | 520 | if (snap_size == -1) |
520 | snap_size = i_size_read(inode); | 521 | snap_size = i_size_read(inode); |
521 | spin_unlock(&ci->i_ceph_lock); | 522 | spin_unlock(&ci->i_ceph_lock); |
522 | 523 | ||
@@ -699,7 +700,8 @@ static int ceph_writepages_start(struct address_space *mapping, | |||
699 | unsigned wsize = 1 << inode->i_blkbits; | 700 | unsigned wsize = 1 << inode->i_blkbits; |
700 | struct ceph_osd_request *req = NULL; | 701 | struct ceph_osd_request *req = NULL; |
701 | int do_sync = 0; | 702 | int do_sync = 0; |
702 | u64 truncate_size, snap_size; | 703 | loff_t snap_size, i_size; |
704 | u64 truncate_size; | ||
703 | u32 truncate_seq; | 705 | u32 truncate_seq; |
704 | 706 | ||
705 | /* | 707 | /* |
@@ -745,7 +747,7 @@ static int ceph_writepages_start(struct address_space *mapping, | |||
745 | retry: | 747 | retry: |
746 | /* find oldest snap context with dirty data */ | 748 | /* find oldest snap context with dirty data */ |
747 | ceph_put_snap_context(snapc); | 749 | ceph_put_snap_context(snapc); |
748 | snap_size = 0; | 750 | snap_size = -1; |
749 | snapc = get_oldest_context(inode, &snap_size); | 751 | snapc = get_oldest_context(inode, &snap_size); |
750 | if (!snapc) { | 752 | if (!snapc) { |
751 | /* hmm, why does writepages get called when there | 753 | /* hmm, why does writepages get called when there |
@@ -753,16 +755,13 @@ retry: | |||
753 | dout(" no snap context with dirty data?\n"); | 755 | dout(" no snap context with dirty data?\n"); |
754 | goto out; | 756 | goto out; |
755 | } | 757 | } |
756 | if (snap_size == 0) | ||
757 | snap_size = i_size_read(inode); | ||
758 | dout(" oldest snapc is %p seq %lld (%d snaps)\n", | 758 | dout(" oldest snapc is %p seq %lld (%d snaps)\n", |
759 | snapc, snapc->seq, snapc->num_snaps); | 759 | snapc, snapc->seq, snapc->num_snaps); |
760 | 760 | ||
761 | spin_lock(&ci->i_ceph_lock); | 761 | spin_lock(&ci->i_ceph_lock); |
762 | truncate_seq = ci->i_truncate_seq; | 762 | truncate_seq = ci->i_truncate_seq; |
763 | truncate_size = ci->i_truncate_size; | 763 | truncate_size = ci->i_truncate_size; |
764 | if (!snap_size) | 764 | i_size = i_size_read(inode); |
765 | snap_size = i_size_read(inode); | ||
766 | spin_unlock(&ci->i_ceph_lock); | 765 | spin_unlock(&ci->i_ceph_lock); |
767 | 766 | ||
768 | if (last_snapc && snapc != last_snapc) { | 767 | if (last_snapc && snapc != last_snapc) { |
@@ -832,8 +831,10 @@ get_more_pages: | |||
832 | dout("waiting on writeback %p\n", page); | 831 | dout("waiting on writeback %p\n", page); |
833 | wait_on_page_writeback(page); | 832 | wait_on_page_writeback(page); |
834 | } | 833 | } |
835 | if (page_offset(page) >= snap_size) { | 834 | if (page_offset(page) >= |
836 | dout("%p page eof %llu\n", page, snap_size); | 835 | (snap_size == -1 ? i_size : snap_size)) { |
836 | dout("%p page eof %llu\n", page, | ||
837 | (snap_size == -1 ? i_size : snap_size)); | ||
837 | done = 1; | 838 | done = 1; |
838 | unlock_page(page); | 839 | unlock_page(page); |
839 | break; | 840 | break; |
@@ -949,10 +950,18 @@ get_more_pages: | |||
949 | } | 950 | } |
950 | 951 | ||
951 | /* Format the osd request message and submit the write */ | 952 | /* Format the osd request message and submit the write */ |
952 | |||
953 | offset = page_offset(pages[0]); | 953 | offset = page_offset(pages[0]); |
954 | len = min(snap_size - offset, | 954 | len = (u64)locked_pages << PAGE_CACHE_SHIFT; |
955 | (u64)locked_pages << PAGE_CACHE_SHIFT); | 955 | if (snap_size == -1) { |
956 | len = min(len, (u64)i_size_read(inode) - offset); | ||
957 | /* writepages_finish() clears writeback pages | ||
958 | * according to the data length, so make sure | ||
959 | * data length covers all locked pages */ | ||
960 | len = max(len, 1 + | ||
961 | ((u64)(locked_pages - 1) << PAGE_CACHE_SHIFT)); | ||
962 | } else { | ||
963 | len = min(len, snap_size - offset); | ||
964 | } | ||
956 | dout("writepages got %d pages at %llu~%llu\n", | 965 | dout("writepages got %d pages at %llu~%llu\n", |
957 | locked_pages, offset, len); | 966 | locked_pages, offset, len); |
958 | 967 | ||