summaryrefslogtreecommitdiffstats
path: root/fs/ceph/addr.c
diff options
context:
space:
mode:
authorYan, Zheng <zheng.z.yan@intel.com>2013-04-12 15:45:42 +0200
committerSage Weil <sage@inktank.com>2013-05-02 06:18:55 +0200
commit1ac0fc8adfc725660ee53a953b06855f64f8e792 (patch)
treeeeaaaf8462b98de6775c5df323cc714ada1b95d2 /fs/ceph/addr.c
parentceph: apply write checks in ceph_aio_write (diff)
downloadlinux-1ac0fc8adfc725660ee53a953b06855f64f8e792.tar.xz
linux-1ac0fc8adfc725660ee53a953b06855f64f8e792.zip
ceph: fix race between writepages and truncate
ceph_writepages_start() reads inode->i_size in two places. It can get different values between successive read, because truncate can change inode->i_size at any time. The race can lead to mismatch between data length of osd request and pages marked as writeback. When osd request finishes, it clear writeback page according to its data length. So some pages can be left in writeback state forever. The fix is only read inode->i_size once, save its value to a local variable and use the local variable when i_size is needed. Signed-off-by: Yan, Zheng <zheng.z.yan@intel.com> Reviewed-by: Alex Elder <elder@inktank.com>
Diffstat (limited to 'fs/ceph/addr.c')
-rw-r--r--fs/ceph/addr.c14
1 files changed, 7 insertions, 7 deletions
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 27d62070a8e9..2d6466b5fe82 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -671,7 +671,7 @@ static int ceph_writepages_start(struct address_space *mapping,
unsigned wsize = 1 << inode->i_blkbits;
struct ceph_osd_request *req = NULL;
int do_sync;
- u64 snap_size = 0;
+ u64 snap_size;
/*
* Include a 'sync' in the OSD request if this is a data
@@ -717,6 +717,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;
snapc = get_oldest_context(inode, &snap_size);
if (!snapc) {
/* hmm, why does writepages get called when there
@@ -724,6 +725,8 @@ 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);
if (last_snapc && snapc != last_snapc) {
@@ -795,11 +798,8 @@ get_more_pages:
dout("waiting on writeback %p\n", page);
wait_on_page_writeback(page);
}
- if ((snap_size && page_offset(page) > snap_size) ||
- (!snap_size &&
- page_offset(page) > i_size_read(inode))) {
- dout("%p page eof %llu\n", page, snap_size ?
- snap_size : i_size_read(inode));
+ if (page_offset(page) >= snap_size) {
+ dout("%p page eof %llu\n", page, snap_size);
done = 1;
unlock_page(page);
break;
@@ -911,7 +911,7 @@ get_more_pages:
/* Format the osd request message and submit the write */
offset = page_offset(pages[0]);
- len = min((snap_size ? snap_size : i_size_read(inode)) - offset,
+ len = min(snap_size - offset,
(u64)locked_pages << PAGE_CACHE_SHIFT);
dout("writepages got %d pages at %llu~%llu\n",
locked_pages, offset, len);