diff options
| -rw-r--r-- | fs/gfs2/super.c | 35 |
1 files changed, 30 insertions, 5 deletions
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index fd8eb9e15719..9149fa375ef3 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -1324,6 +1324,35 @@ out: return ret; } +static int gfs2_truncate_inode_pages(struct inode *inode) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); + struct address_space *mapping = &inode->i_data; + bool need_trans = gfs2_is_jdata(ip) && mapping->nrpages; + int ret; + + /* + * Truncating a jdata inode address space may create revokes in + * truncate_inode_pages() -> gfs2_invalidate_folio() -> ... -> + * gfs2_remove_from_journal(), so we need a transaction here. + * + * FIXME: During a withdraw, no new transactions can be created. + * In that case, we skip the truncate, but that doesn't help because + * truncate_inode_pages_final() will then call gfs2_invalidate_folio() + * again, and outside of a transaction. + */ + if (need_trans) { + ret = gfs2_trans_begin(sdp, 0, sdp->sd_jdesc->jd_blocks); + if (ret) + return ret; + } + truncate_inode_pages(mapping, 0); + if (need_trans) + gfs2_trans_end(sdp); + return 0; +} + /* * evict_linked_inode - evict an inode whose dinode has not been unlinked * @inode: The inode to evict @@ -1346,14 +1375,10 @@ static int evict_linked_inode(struct inode *inode) write_inode_now(inode, 1); gfs2_ail_flush(ip->i_gl, 0); - ret = gfs2_trans_begin(sdp, 0, sdp->sd_jdesc->jd_blocks); + ret = gfs2_truncate_inode_pages(inode); if (ret) return ret; - - /* Needs to be done before glock release & also in a transaction */ - truncate_inode_pages(&inode->i_data, 0); truncate_inode_pages(metamapping, 0); - gfs2_trans_end(sdp); return 0; } |
