Always try to reclaim clean dquots before doing I/O in the dquot shrinker.
This is still a bit suboptimal compared to how e.g. the inode shrinker
works, but it is required for the removal of the global delwri lists.
Signed-off-by: Christoph Hellwig <hch@xxxxxx>
---
fs/xfs/xfs_qm.c | 35 +++++++++++++++++++++--------------
1 file changed, 21 insertions(+), 14 deletions(-)
Index: xfs/fs/xfs/xfs_qm.c
===================================================================
--- xfs.orig/fs/xfs/xfs_qm.c 2011-10-27 22:40:08.594173542 +0200
+++ xfs/fs/xfs/xfs_qm.c 2011-10-27 22:40:09.097172140 +0200
@@ -1599,7 +1599,8 @@ xfs_qm_init_quotainos(
STATIC bool
xfs_qm_dqreclaim_one(
- struct xfs_dquot *dqp)
+ struct xfs_dquot *dqp,
+ bool write_dirty)
{
struct xfs_mount *mp = dqp->q_mount;
int error;
@@ -1642,23 +1643,18 @@ xfs_qm_dqreclaim_one(
* dirty dquots.
*/
if (XFS_DQ_IS_DIRTY(dqp)) {
+ if (!write_dirty)
+ goto out_busy;
+
trace_xfs_dqreclaim_dirty(dqp);
- /*
- * We flush it delayed write, so don't bother releasing the
- * freelist lock.
- */
- error = xfs_qm_dqflush(dqp, 0);
+ mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock);
+ error = xfs_qm_dqflush(dqp, SYNC_WAIT);
if (error) {
xfs_warn(mp, "%s: dquot %p flush failed",
__func__, dqp);
}
-
- /*
- * Give the dquot another try on the freelist, as the
- * flushing will take some time.
- */
- goto out_busy;
+ mutex_lock(&xfs_Gqm->qm_dqfrlist_lock);
}
xfs_dqfunlock(dqp);
@@ -1698,7 +1694,7 @@ xfs_qm_shake(
struct shrink_control *sc)
{
int nr_to_scan = sc->nr_to_scan;
- struct xfs_dquot *dqp;
+ struct xfs_dquot *dqp, *n;
if ((sc->gfp_mask & (__GFP_FS|__GFP_WAIT)) != (__GFP_FS|__GFP_WAIT))
return 0;
@@ -1706,16 +1702,27 @@ xfs_qm_shake(
if (!nr_to_scan)
goto out;
+ /*
+ * We first try to reclaim only clean dquots, and only if we have to
+ * start writing dirty ones.
+ */
mutex_lock(&xfs_Gqm->qm_dqfrlist_lock);
+ list_for_each_entry_safe(dqp, n, &xfs_Gqm->qm_dqfrlist, q_freelist) {
+ if (nr_to_scan-- <= 0)
+ goto out_unlock;
+ xfs_qm_dqreclaim_one(dqp, false);
+ }
+
while (!list_empty(&xfs_Gqm->qm_dqfrlist)) {
if (nr_to_scan-- <= 0)
break;
dqp = list_first_entry(&xfs_Gqm->qm_dqfrlist, struct xfs_dquot,
q_freelist);
- if (!xfs_qm_dqreclaim_one(dqp))
+ if (!xfs_qm_dqreclaim_one(dqp, true))
list_move_tail(&dqp->q_freelist, &xfs_Gqm->qm_dqfrlist);
}
+out_unlock:
mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock);
out:
return (xfs_Gqm->qm_dqfrlist_cnt / 100) * sysctl_vfs_cache_pressure;
|