From: Darrick J. Wong <djwong@xxxxxxxxxx> Since we now defer inode inactivation to a background workqueue, there can be a considerable delay between the point in time when an inode moves into NEED_INACTIVE state (and hence quotaoff cannot reach it) and when xfs_inactive() actually finishes the inode. To avoid delaying quotaoff any more than necessary, drop dead dquots as soon as we know that we're going to inactivate the inode. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- fs/xfs/xfs_icache.c | 6 ++++++ fs/xfs/xfs_qm.c | 28 ++++++++++++++++++++++++++++ fs/xfs/xfs_quota.h | 2 ++ 3 files changed, 36 insertions(+) diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index e094c16aa8c5..17c4cd91ea15 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -376,6 +376,12 @@ xfs_inode_mark_reclaimable( xfs_check_delalloc(ip, XFS_COW_FORK); ASSERT(0); } + } else { + /* + * Drop dquots for disabled quota types to avoid delaying + * quotaoff while we wait for inactivation to occur. + */ + xfs_qm_prepare_inactive(ip); } XFS_STATS_INC(mp, vn_reclaim); diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index fe341f3fd419..b193a84e47c2 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -408,6 +408,34 @@ xfs_qm_dqdetach( } } +/* + * If a quota type is turned off but we still have a dquot attached to the + * inode, detach it before tagging this inode for inactivation (or reclaim) to + * avoid delaying quotaoff for longer than is necessary. Because the inode has + * no VFS state and has not yet been tagged for reclaim or inactivation, it is + * safe to drop the dquots locklessly because iget, quotaoff, blockgc, and + * reclaim will not touch the inode. + */ +void +xfs_qm_prepare_inactive( + struct xfs_inode *ip) +{ + struct xfs_mount *mp = ip->i_mount; + + if (!XFS_IS_UQUOTA_ON(mp)) { + xfs_qm_dqrele(ip->i_udquot); + ip->i_udquot = NULL; + } + if (!XFS_IS_GQUOTA_ON(mp)) { + xfs_qm_dqrele(ip->i_gdquot); + ip->i_gdquot = NULL; + } + if (!XFS_IS_PQUOTA_ON(mp)) { + xfs_qm_dqrele(ip->i_pdquot); + ip->i_pdquot = NULL; + } +} + struct xfs_qm_isolate { struct list_head buffers; struct list_head dispose; diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h index d00d01302545..75d8b7bc0e25 100644 --- a/fs/xfs/xfs_quota.h +++ b/fs/xfs/xfs_quota.h @@ -101,6 +101,7 @@ extern struct xfs_dquot *xfs_qm_vop_chown(struct xfs_trans *, extern int xfs_qm_dqattach(struct xfs_inode *); extern int xfs_qm_dqattach_locked(struct xfs_inode *ip, bool doalloc); extern void xfs_qm_dqdetach(struct xfs_inode *); +void xfs_qm_prepare_inactive(struct xfs_inode *ip); extern void xfs_qm_dqrele(struct xfs_dquot *); extern void xfs_qm_statvfs(struct xfs_inode *, struct kstatfs *); extern int xfs_qm_newmount(struct xfs_mount *, uint *, uint *); @@ -162,6 +163,7 @@ xfs_trans_reserve_quota_icreate(struct xfs_trans *tp, struct xfs_dquot *udqp, #define xfs_qm_dqattach(ip) (0) #define xfs_qm_dqattach_locked(ip, fl) (0) #define xfs_qm_dqdetach(ip) +#define xfs_qm_prepare_inactive(ip) ((void)0) #define xfs_qm_dqrele(d) do { (d) = (d); } while(0) #define xfs_qm_statvfs(ip, s) do { } while(0) #define xfs_qm_newmount(mp, a, b) (0)