diff options
author | Yan, Zheng <zyan@redhat.com> | 2016-04-15 13:56:12 +0800 |
---|---|---|
committer | Ilya Dryomov <idryomov@gmail.com> | 2016-05-26 01:15:32 +0200 |
commit | 6c93df5db628e710697c43bc1bd78a786549a548 (patch) | |
tree | 8f74edaae6bfb61178af444a9c68fc5cf791f8da /fs/ceph/addr.c | |
parent | 77310320c299b0dc050037ff8fc29fd1861fb005 (diff) | |
download | linux-stable-6c93df5db628e710697c43bc1bd78a786549a548.tar.gz linux-stable-6c93df5db628e710697c43bc1bd78a786549a548.tar.bz2 linux-stable-6c93df5db628e710697c43bc1bd78a786549a548.zip |
ceph: don't call truncate_pagecache in ceph_writepages_start
truncate_pagecache() may decrease inode's reference. This can cause
deadlock if inode's last reference is dropped and iput_final() wants
to evict the inode. (evict() calls inode_wait_for_writeback(), which
waits for ceph_writepages_start() to return).
The fix is use work thead to truncate dirty pages. Also add 'forced
umount' check to ceph_update_writeable_page(), which prevents new
pages getting dirty.
Signed-off-by: Yan, Zheng <zyan@redhat.com>
Diffstat (limited to 'fs/ceph/addr.c')
-rw-r--r-- | fs/ceph/addr.c | 14 |
1 files changed, 12 insertions, 2 deletions
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index f47418477629..d52e3bcfda7c 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -715,8 +715,11 @@ static int ceph_writepages_start(struct address_space *mapping, (wbc->sync_mode == WB_SYNC_ALL ? "ALL" : "HOLD")); if (ACCESS_ONCE(fsc->mount_state) == CEPH_MOUNT_SHUTDOWN) { - pr_warn("writepage_start %p on forced umount\n", inode); - truncate_pagecache(inode, 0); + if (ci->i_wrbuffer_ref > 0) { + pr_warn_ratelimited( + "writepage_start %p %lld forced umount\n", + inode, ceph_ino(inode)); + } mapping_set_error(mapping, -EIO); return -EIO; /* we're in a forced umount, don't write! */ } @@ -1127,6 +1130,7 @@ static int ceph_update_writeable_page(struct file *file, struct page *page) { struct inode *inode = file_inode(file); + struct ceph_fs_client *fsc = ceph_inode_to_client(inode); struct ceph_inode_info *ci = ceph_inode(inode); loff_t page_off = pos & PAGE_MASK; int pos_in_page = pos & ~PAGE_MASK; @@ -1135,6 +1139,12 @@ static int ceph_update_writeable_page(struct file *file, int r; struct ceph_snap_context *snapc, *oldest; + if (ACCESS_ONCE(fsc->mount_state) == CEPH_MOUNT_SHUTDOWN) { + dout(" page %p forced umount\n", page); + unlock_page(page); + return -EIO; + } + retry_locked: /* writepages currently holds page lock, but if we change that later, */ wait_on_page_writeback(page); |