Commit e1966b49 authored by Yan, Zheng's avatar Yan, Zheng Committed by Ilya Dryomov

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: default avatarYan, Zheng <zyan@redhat.com>
parent d3834fef
...@@ -440,7 +440,7 @@ static int ceph_readpages(struct file *file, struct address_space *mapping, ...@@ -440,7 +440,7 @@ static int ceph_readpages(struct file *file, struct address_space *mapping,
* only snap context we are allowed to write back. * only snap context we are allowed to write back.
*/ */
static struct ceph_snap_context *get_oldest_context(struct inode *inode, static struct ceph_snap_context *get_oldest_context(struct inode *inode,
u64 *snap_size) loff_t *snap_size)
{ {
struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_inode_info *ci = ceph_inode(inode);
struct ceph_snap_context *snapc = NULL; struct ceph_snap_context *snapc = NULL;
...@@ -480,8 +480,9 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) ...@@ -480,8 +480,9 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
struct ceph_osd_client *osdc; struct ceph_osd_client *osdc;
struct ceph_snap_context *snapc, *oldest; struct ceph_snap_context *snapc, *oldest;
loff_t page_off = page_offset(page); loff_t page_off = page_offset(page);
loff_t snap_size = -1;
long writeback_stat; long writeback_stat;
u64 truncate_size, snap_size = 0; u64 truncate_size;
u32 truncate_seq; u32 truncate_seq;
int err = 0, len = PAGE_CACHE_SIZE; int err = 0, len = PAGE_CACHE_SIZE;
...@@ -516,7 +517,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) ...@@ -516,7 +517,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
spin_lock(&ci->i_ceph_lock); spin_lock(&ci->i_ceph_lock);
truncate_seq = ci->i_truncate_seq; truncate_seq = ci->i_truncate_seq;
truncate_size = ci->i_truncate_size; truncate_size = ci->i_truncate_size;
if (!snap_size) if (snap_size == -1)
snap_size = i_size_read(inode); snap_size = i_size_read(inode);
spin_unlock(&ci->i_ceph_lock); spin_unlock(&ci->i_ceph_lock);
...@@ -699,7 +700,8 @@ static int ceph_writepages_start(struct address_space *mapping, ...@@ -699,7 +700,8 @@ static int ceph_writepages_start(struct address_space *mapping,
unsigned wsize = 1 << inode->i_blkbits; unsigned wsize = 1 << inode->i_blkbits;
struct ceph_osd_request *req = NULL; struct ceph_osd_request *req = NULL;
int do_sync = 0; int do_sync = 0;
u64 truncate_size, snap_size; loff_t snap_size, i_size;
u64 truncate_size;
u32 truncate_seq; u32 truncate_seq;
/* /*
...@@ -745,7 +747,7 @@ static int ceph_writepages_start(struct address_space *mapping, ...@@ -745,7 +747,7 @@ static int ceph_writepages_start(struct address_space *mapping,
retry: retry:
/* find oldest snap context with dirty data */ /* find oldest snap context with dirty data */
ceph_put_snap_context(snapc); ceph_put_snap_context(snapc);
snap_size = 0; snap_size = -1;
snapc = get_oldest_context(inode, &snap_size); snapc = get_oldest_context(inode, &snap_size);
if (!snapc) { if (!snapc) {
/* hmm, why does writepages get called when there /* hmm, why does writepages get called when there
...@@ -753,16 +755,13 @@ static int ceph_writepages_start(struct address_space *mapping, ...@@ -753,16 +755,13 @@ static int ceph_writepages_start(struct address_space *mapping,
dout(" no snap context with dirty data?\n"); dout(" no snap context with dirty data?\n");
goto out; goto out;
} }
if (snap_size == 0)
snap_size = i_size_read(inode);
dout(" oldest snapc is %p seq %lld (%d snaps)\n", dout(" oldest snapc is %p seq %lld (%d snaps)\n",
snapc, snapc->seq, snapc->num_snaps); snapc, snapc->seq, snapc->num_snaps);
spin_lock(&ci->i_ceph_lock); spin_lock(&ci->i_ceph_lock);
truncate_seq = ci->i_truncate_seq; truncate_seq = ci->i_truncate_seq;
truncate_size = ci->i_truncate_size; truncate_size = ci->i_truncate_size;
if (!snap_size) i_size = i_size_read(inode);
snap_size = i_size_read(inode);
spin_unlock(&ci->i_ceph_lock); spin_unlock(&ci->i_ceph_lock);
if (last_snapc && snapc != last_snapc) { if (last_snapc && snapc != last_snapc) {
...@@ -832,8 +831,10 @@ static int ceph_writepages_start(struct address_space *mapping, ...@@ -832,8 +831,10 @@ static int ceph_writepages_start(struct address_space *mapping,
dout("waiting on writeback %p\n", page); dout("waiting on writeback %p\n", page);
wait_on_page_writeback(page); wait_on_page_writeback(page);
} }
if (page_offset(page) >= snap_size) { if (page_offset(page) >=
dout("%p page eof %llu\n", page, snap_size); (snap_size == -1 ? i_size : snap_size)) {
dout("%p page eof %llu\n", page,
(snap_size == -1 ? i_size : snap_size));
done = 1; done = 1;
unlock_page(page); unlock_page(page);
break; break;
...@@ -949,10 +950,18 @@ static int ceph_writepages_start(struct address_space *mapping, ...@@ -949,10 +950,18 @@ static int ceph_writepages_start(struct address_space *mapping,
} }
/* Format the osd request message and submit the write */ /* Format the osd request message and submit the write */
offset = page_offset(pages[0]); offset = page_offset(pages[0]);
len = min(snap_size - offset, len = (u64)locked_pages << PAGE_CACHE_SHIFT;
(u64)locked_pages << PAGE_CACHE_SHIFT); if (snap_size == -1) {
len = min(len, (u64)i_size_read(inode) - offset);
/* writepages_finish() clears writeback pages
* according to the data length, so make sure
* data length covers all locked pages */
len = max(len, 1 +
((u64)(locked_pages - 1) << PAGE_CACHE_SHIFT));
} else {
len = min(len, snap_size - offset);
}
dout("writepages got %d pages at %llu~%llu\n", dout("writepages got %d pages at %llu~%llu\n",
locked_pages, offset, len); locked_pages, offset, len);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment