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>
This commit is contained in:
parent
d3834fefcf
commit
e1966b4944
1 changed files with 23 additions and 14 deletions
|
@ -440,7 +440,7 @@ out:
|
|||
* only snap context we are allowed to write back.
|
||||
*/
|
||||
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_snap_context *snapc = NULL;
|
||||
|
@ -480,8 +480,9 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
|
|||
struct ceph_osd_client *osdc;
|
||||
struct ceph_snap_context *snapc, *oldest;
|
||||
loff_t page_off = page_offset(page);
|
||||
loff_t snap_size = -1;
|
||||
long writeback_stat;
|
||||
u64 truncate_size, snap_size = 0;
|
||||
u64 truncate_size;
|
||||
u32 truncate_seq;
|
||||
int err = 0, len = PAGE_CACHE_SIZE;
|
||||
|
||||
|
@ -516,7 +517,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
|
|||
spin_lock(&ci->i_ceph_lock);
|
||||
truncate_seq = ci->i_truncate_seq;
|
||||
truncate_size = ci->i_truncate_size;
|
||||
if (!snap_size)
|
||||
if (snap_size == -1)
|
||||
snap_size = i_size_read(inode);
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
|
||||
|
@ -699,7 +700,8 @@ static int ceph_writepages_start(struct address_space *mapping,
|
|||
unsigned wsize = 1 << inode->i_blkbits;
|
||||
struct ceph_osd_request *req = NULL;
|
||||
int do_sync = 0;
|
||||
u64 truncate_size, snap_size;
|
||||
loff_t snap_size, i_size;
|
||||
u64 truncate_size;
|
||||
u32 truncate_seq;
|
||||
|
||||
/*
|
||||
|
@ -745,7 +747,7 @@ static int ceph_writepages_start(struct address_space *mapping,
|
|||
retry:
|
||||
/* find oldest snap context with dirty data */
|
||||
ceph_put_snap_context(snapc);
|
||||
snap_size = 0;
|
||||
snap_size = -1;
|
||||
snapc = get_oldest_context(inode, &snap_size);
|
||||
if (!snapc) {
|
||||
/* hmm, why does writepages get called when there
|
||||
|
@ -753,16 +755,13 @@ retry:
|
|||
dout(" no snap context with dirty data?\n");
|
||||
goto out;
|
||||
}
|
||||
if (snap_size == 0)
|
||||
snap_size = i_size_read(inode);
|
||||
dout(" oldest snapc is %p seq %lld (%d snaps)\n",
|
||||
snapc, snapc->seq, snapc->num_snaps);
|
||||
|
||||
spin_lock(&ci->i_ceph_lock);
|
||||
truncate_seq = ci->i_truncate_seq;
|
||||
truncate_size = ci->i_truncate_size;
|
||||
if (!snap_size)
|
||||
snap_size = i_size_read(inode);
|
||||
i_size = i_size_read(inode);
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
|
||||
if (last_snapc && snapc != last_snapc) {
|
||||
|
@ -832,8 +831,10 @@ get_more_pages:
|
|||
dout("waiting on writeback %p\n", page);
|
||||
wait_on_page_writeback(page);
|
||||
}
|
||||
if (page_offset(page) >= snap_size) {
|
||||
dout("%p page eof %llu\n", page, snap_size);
|
||||
if (page_offset(page) >=
|
||||
(snap_size == -1 ? i_size : snap_size)) {
|
||||
dout("%p page eof %llu\n", page,
|
||||
(snap_size == -1 ? i_size : snap_size));
|
||||
done = 1;
|
||||
unlock_page(page);
|
||||
break;
|
||||
|
@ -949,10 +950,18 @@ get_more_pages:
|
|||
}
|
||||
|
||||
/* Format the osd request message and submit the write */
|
||||
|
||||
offset = page_offset(pages[0]);
|
||||
len = min(snap_size - offset,
|
||||
(u64)locked_pages << PAGE_CACHE_SHIFT);
|
||||
len = (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",
|
||||
locked_pages, offset, len);
|
||||
|
||||
|
|
Loading…
Reference in a new issue