diff options
Diffstat (limited to 'fs/xfs/xfs_qm.c')
| -rw-r--r-- | fs/xfs/xfs_qm.c | 289 | 
1 files changed, 110 insertions, 179 deletions
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 671f37eae1c..c436def733b 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -50,7 +50,6 @@   */  struct mutex	xfs_Gqm_lock;  struct xfs_qm	*xfs_Gqm; -uint		ndquot;  kmem_zone_t	*qm_dqzone;  kmem_zone_t	*qm_dqtrxzone; @@ -93,7 +92,6 @@ xfs_Gqm_init(void)  		goto out_free_udqhash;  	hsize /= sizeof(xfs_dqhash_t); -	ndquot = hsize << 8;  	xqm = kmem_zalloc(sizeof(xfs_qm_t), KM_SLEEP);  	xqm->qm_dqhashmask = hsize - 1; @@ -137,7 +135,6 @@ xfs_Gqm_init(void)  		xqm->qm_dqtrxzone = qm_dqtrxzone;  	atomic_set(&xqm->qm_totaldquots, 0); -	xqm->qm_dqfree_ratio = XFS_QM_DQFREE_RATIO;  	xqm->qm_nrefs = 0;  	return xqm; @@ -1600,216 +1597,150 @@ xfs_qm_init_quotainos(  	return 0;  } +STATIC void +xfs_qm_dqfree_one( +	struct xfs_dquot	*dqp) +{ +	struct xfs_mount	*mp = dqp->q_mount; +	struct xfs_quotainfo	*qi = mp->m_quotainfo; +	mutex_lock(&dqp->q_hash->qh_lock); +	list_del_init(&dqp->q_hashlist); +	dqp->q_hash->qh_version++; +	mutex_unlock(&dqp->q_hash->qh_lock); -/* - * Pop the least recently used dquot off the freelist and recycle it. - */ -STATIC struct xfs_dquot * -xfs_qm_dqreclaim_one(void) +	mutex_lock(&qi->qi_dqlist_lock); +	list_del_init(&dqp->q_mplist); +	qi->qi_dquots--; +	qi->qi_dqreclaims++; +	mutex_unlock(&qi->qi_dqlist_lock); + +	xfs_qm_dqdestroy(dqp); +} + +STATIC void +xfs_qm_dqreclaim_one( +	struct xfs_dquot	*dqp, +	struct list_head	*dispose_list)  { -	struct xfs_dquot	*dqp; -	int			restarts = 0; +	struct xfs_mount	*mp = dqp->q_mount; +	int			error; -	mutex_lock(&xfs_Gqm->qm_dqfrlist_lock); -restart: -	list_for_each_entry(dqp, &xfs_Gqm->qm_dqfrlist, q_freelist) { -		struct xfs_mount *mp = dqp->q_mount; +	if (!xfs_dqlock_nowait(dqp)) +		goto out_busy; -		if (!xfs_dqlock_nowait(dqp)) -			continue; +	/* +	 * This dquot has acquired a reference in the meantime remove it from +	 * the freelist and try again. +	 */ +	if (dqp->q_nrefs) { +		xfs_dqunlock(dqp); -		/* -		 * This dquot has already been grabbed by dqlookup. -		 * Remove it from the freelist and try again. -		 */ -		if (dqp->q_nrefs) { -			trace_xfs_dqreclaim_want(dqp); -			XQM_STATS_INC(xqmstats.xs_qm_dqwants); +		trace_xfs_dqreclaim_want(dqp); +		XQM_STATS_INC(xqmstats.xs_qm_dqwants); -			list_del_init(&dqp->q_freelist); -			xfs_Gqm->qm_dqfrlist_cnt--; -			restarts++; -			goto dqunlock; -		} +		list_del_init(&dqp->q_freelist); +		xfs_Gqm->qm_dqfrlist_cnt--; +		return; +	} -		ASSERT(dqp->q_hash); -		ASSERT(!list_empty(&dqp->q_mplist)); +	ASSERT(dqp->q_hash); +	ASSERT(!list_empty(&dqp->q_mplist)); -		/* -		 * Try to grab the flush lock. If this dquot is in the process -		 * of getting flushed to disk, we don't want to reclaim it. -		 */ -		if (!xfs_dqflock_nowait(dqp)) -			goto dqunlock; +	/* +	 * Try to grab the flush lock. If this dquot is in the process of +	 * getting flushed to disk, we don't want to reclaim it. +	 */ +	if (!xfs_dqflock_nowait(dqp)) +		goto out_busy; + +	/* +	 * We have the flush lock so we know that this is not in the +	 * process of being flushed. So, if this is dirty, flush it +	 * DELWRI so that we don't get a freelist infested with +	 * dirty dquots. +	 */ +	if (XFS_DQ_IS_DIRTY(dqp)) { +		trace_xfs_dqreclaim_dirty(dqp);  		/* -		 * We have the flush lock so we know that this is not in the -		 * process of being flushed. So, if this is dirty, flush it -		 * DELWRI so that we don't get a freelist infested with -		 * dirty dquots. +		 * We flush it delayed write, so don't bother releasing the +		 * freelist lock.  		 */ -		if (XFS_DQ_IS_DIRTY(dqp)) { -			int	error; - -			trace_xfs_dqreclaim_dirty(dqp); - -			/* -			 * We flush it delayed write, so don't bother -			 * releasing the freelist lock. -			 */ -			error = xfs_qm_dqflush(dqp, SYNC_TRYLOCK); -			if (error) { -				xfs_warn(mp, "%s: dquot %p flush failed", -					__func__, dqp); -			} -			goto dqunlock; +		error = xfs_qm_dqflush(dqp, 0); +		if (error) { +			xfs_warn(mp, "%s: dquot %p flush failed", +				 __func__, dqp);  		} -		xfs_dqfunlock(dqp);  		/* -		 * Prevent lookup now that we are going to reclaim the dquot. -		 * Once XFS_DQ_FREEING is set lookup won't touch the dquot, -		 * thus we can drop the lock now. +		 * Give the dquot another try on the freelist, as the +		 * flushing will take some time.  		 */ -		dqp->dq_flags |= XFS_DQ_FREEING; -		xfs_dqunlock(dqp); - -		mutex_lock(&dqp->q_hash->qh_lock); -		list_del_init(&dqp->q_hashlist); -		dqp->q_hash->qh_version++; -		mutex_unlock(&dqp->q_hash->qh_lock); +		goto out_busy; +	} +	xfs_dqfunlock(dqp); -		mutex_lock(&mp->m_quotainfo->qi_dqlist_lock); -		list_del_init(&dqp->q_mplist); -		mp->m_quotainfo->qi_dquots--; -		mp->m_quotainfo->qi_dqreclaims++; -		mutex_unlock(&mp->m_quotainfo->qi_dqlist_lock); +	/* +	 * Prevent lookups now that we are past the point of no return. +	 */ +	dqp->dq_flags |= XFS_DQ_FREEING; +	xfs_dqunlock(dqp); -		ASSERT(dqp->q_nrefs == 0); -		list_del_init(&dqp->q_freelist); -		xfs_Gqm->qm_dqfrlist_cnt--; +	ASSERT(dqp->q_nrefs == 0); +	list_move_tail(&dqp->q_freelist, dispose_list); +	xfs_Gqm->qm_dqfrlist_cnt--; -		mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); -		return dqp; -dqunlock: -		xfs_dqunlock(dqp); -		if (restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) -			break; -		goto restart; -	} +	trace_xfs_dqreclaim_done(dqp); +	XQM_STATS_INC(xqmstats.xs_qm_dqreclaims); +	return; -	mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); -	return NULL; -} - -/* - * Traverse the freelist of dquots and attempt to reclaim a maximum of - * 'howmany' dquots. This operation races with dqlookup(), and attempts to - * favor the lookup function ... - */ -STATIC int -xfs_qm_shake_freelist( -	int	howmany) -{ -	int		nreclaimed = 0; -	xfs_dquot_t	*dqp; +out_busy: +	xfs_dqunlock(dqp); -	if (howmany <= 0) -		return 0; +	/* +	 * Move the dquot to the tail of the list so that we don't spin on it. +	 */ +	list_move_tail(&dqp->q_freelist, &xfs_Gqm->qm_dqfrlist); -	while (nreclaimed < howmany) { -		dqp = xfs_qm_dqreclaim_one(); -		if (!dqp) -			return nreclaimed; -		xfs_qm_dqdestroy(dqp); -		nreclaimed++; -	} -	return nreclaimed; +	trace_xfs_dqreclaim_busy(dqp); +	XQM_STATS_INC(xqmstats.xs_qm_dqreclaim_misses);  } -/* - * The kmem_shake interface is invoked when memory is running low. - */ -/* ARGSUSED */  STATIC int  xfs_qm_shake( -	struct shrinker	*shrink, -	struct shrink_control *sc) +	struct shrinker		*shrink, +	struct shrink_control	*sc)  { -	int	ndqused, nfree, n; -	gfp_t gfp_mask = sc->gfp_mask; - -	if (!kmem_shake_allow(gfp_mask)) -		return 0; -	if (!xfs_Gqm) -		return 0; - -	nfree = xfs_Gqm->qm_dqfrlist_cnt; /* free dquots */ -	/* incore dquots in all f/s's */ -	ndqused = atomic_read(&xfs_Gqm->qm_totaldquots) - nfree; - -	ASSERT(ndqused >= 0); +	int			nr_to_scan = sc->nr_to_scan; +	LIST_HEAD		(dispose_list); +	struct xfs_dquot	*dqp; -	if (nfree <= ndqused && nfree < ndquot) +	if ((sc->gfp_mask & (__GFP_FS|__GFP_WAIT)) != (__GFP_FS|__GFP_WAIT))  		return 0; +	if (!nr_to_scan) +		goto out; -	ndqused *= xfs_Gqm->qm_dqfree_ratio;	/* target # of free dquots */ -	n = nfree - ndqused - ndquot;		/* # over target */ - -	return xfs_qm_shake_freelist(MAX(nfree, n)); -} - - -/*------------------------------------------------------------------*/ - -/* - * Return a new incore dquot. Depending on the number of - * dquots in the system, we either allocate a new one on the kernel heap, - * or reclaim a free one. - * Return value is B_TRUE if we allocated a new dquot, B_FALSE if we managed - * to reclaim an existing one from the freelist. - */ -boolean_t -xfs_qm_dqalloc_incore( -	xfs_dquot_t **O_dqpp) -{ -	xfs_dquot_t	*dqp; - -	/* -	 * Check against high water mark to see if we want to pop -	 * a nincompoop dquot off the freelist. -	 */ -	if (atomic_read(&xfs_Gqm->qm_totaldquots) >= ndquot) { -		/* -		 * Try to recycle a dquot from the freelist. -		 */ -		if ((dqp = xfs_qm_dqreclaim_one())) { -			XQM_STATS_INC(xqmstats.xs_qm_dqreclaims); -			/* -			 * Just zero the core here. The rest will get -			 * reinitialized by caller. XXX we shouldn't even -			 * do this zero ... -			 */ -			memset(&dqp->q_core, 0, sizeof(dqp->q_core)); -			*O_dqpp = dqp; -			return B_FALSE; -		} -		XQM_STATS_INC(xqmstats.xs_qm_dqreclaim_misses); +	mutex_lock(&xfs_Gqm->qm_dqfrlist_lock); +	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); +		xfs_qm_dqreclaim_one(dqp, &dispose_list);  	} +	mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); -	/* -	 * Allocate a brand new dquot on the kernel heap and return it -	 * to the caller to initialize. -	 */ -	ASSERT(xfs_Gqm->qm_dqzone != NULL); -	*O_dqpp = kmem_zone_zalloc(xfs_Gqm->qm_dqzone, KM_SLEEP); -	atomic_inc(&xfs_Gqm->qm_totaldquots); - -	return B_TRUE; +	while (!list_empty(&dispose_list)) { +		dqp = list_first_entry(&dispose_list, struct xfs_dquot, +				       q_freelist); +		list_del_init(&dqp->q_freelist); +		xfs_qm_dqfree_one(dqp); +	} +out: +	return (xfs_Gqm->qm_dqfrlist_cnt / 100) * sysctl_vfs_cache_pressure;  } -  /*   * Start a transaction and write the incore superblock changes to   * disk. flags parameter indicates which fields have changed.  |