diff options
author | Yan, Zheng <zyan@redhat.com> | 2017-08-28 10:41:28 +0300 |
---|---|---|
committer | Ilya Dryomov <idryomov@gmail.com> | 2017-09-06 20:56:53 +0300 |
commit | c8fd0d37f81dd38e3f319f4938b45a5aaf0dfc58 (patch) | |
tree | e72fcd3a9b1b53e7007cbedfcf39bfe12a06c393 /fs/ceph | |
parent | fa0aa3b839b922c7bb911dbe2435da2a4e59d82d (diff) | |
download | linux-c8fd0d37f81dd38e3f319f4938b45a5aaf0dfc58.tar.xz |
ceph: handle race between vmtruncate and queuing cap snap
It's possible that we create a cap snap while there is pending
vmtruncate (truncate hasn't been processed by worker thread).
We should truncate dirty pages beyond capsnap->size in that case.
Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Diffstat (limited to 'fs/ceph')
-rw-r--r-- | fs/ceph/inode.c | 13 |
1 files changed, 12 insertions, 1 deletions
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index a19fafdf87f8..373dab5173ca 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1833,9 +1833,20 @@ retry: * possibly truncate them.. so write AND block! */ if (ci->i_wrbuffer_ref_head < ci->i_wrbuffer_ref) { + struct ceph_cap_snap *capsnap; + to = ci->i_truncate_size; + list_for_each_entry(capsnap, &ci->i_cap_snaps, ci_item) { + // MDS should have revoked Frw caps + WARN_ON_ONCE(capsnap->writing); + if (capsnap->dirty_pages && capsnap->size > to) + to = capsnap->size; + } + spin_unlock(&ci->i_ceph_lock); dout("__do_pending_vmtruncate %p flushing snaps first\n", inode); - spin_unlock(&ci->i_ceph_lock); + + truncate_pagecache(inode, to); + filemap_write_and_wait_range(&inode->i_data, 0, inode->i_sb->s_maxbytes); goto retry; |