aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ceph
diff options
context:
space:
mode:
authorYan, Zheng <zyan@redhat.com>2015-06-17 15:10:58 -0400
committerIlya Dryomov <idryomov@gmail.com>2015-06-25 11:30:53 -0400
commite1966b49446a43994c3f25a07d0eb4d05660b429 (patch)
tree43464087b52c41e58396f7805783bd9e627d2bfb /fs/ceph
parentd3834fefcfe5610702379d78596337875df2db5b (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.c37
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 */
442static struct ceph_snap_context *get_oldest_context(struct inode *inode, 442static 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,
745retry: 747retry:
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