Commit bf72de31 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Ben Myers

xfs: nest qm_dqfrlist_lock inside the dquot qlock

Allow xfs_qm_dqput to work without trylock loops by nesting the freelist lock
inside the dquot qlock.  In turn that requires trylocks in the reclaim path
instead, but given it's a classic tradeoff between fast and slow path, and
we follow the model of the inode and dentry caches.

Document our new lock order now that it has settled.
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
Signed-off-by: default avatarBen Myers <bpm@sgi.com>
parent 92678554
...@@ -39,20 +39,19 @@ ...@@ -39,20 +39,19 @@
#include "xfs_qm.h" #include "xfs_qm.h"
#include "xfs_trace.h" #include "xfs_trace.h"
/* /*
LOCK ORDER * Lock order:
*
inode lock (ilock) * ip->i_lock
dquot hash-chain lock (hashlock) * qh->qh_lock
xqm dquot freelist lock (freelistlock * qi->qi_dqlist_lock
mount's dquot list lock (mplistlock) * dquot->q_qlock (xfs_dqlock() and friends)
user dquot lock - lock ordering among dquots is based on the uid or gid * dquot->q_flush (xfs_dqflock() and friends)
group dquot lock - similar to udquots. Between the two dquots, the udquot * xfs_Gqm->qm_dqfrlist_lock
has to be locked first. *
pin lock - the dquot lock must be held to take this lock. * If two dquots need to be locked the order is user before group/project,
flush lock - ditto. * otherwise by the lowest id first, see xfs_dqlock2.
*/ */
#ifdef DEBUG #ifdef DEBUG
xfs_buftarg_t *xfs_dqerror_target; xfs_buftarg_t *xfs_dqerror_target;
...@@ -984,69 +983,49 @@ xfs_qm_dqget( ...@@ -984,69 +983,49 @@ xfs_qm_dqget(
*/ */
void void
xfs_qm_dqput( xfs_qm_dqput(
xfs_dquot_t *dqp) struct xfs_dquot *dqp)
{ {
xfs_dquot_t *gdqp; struct xfs_dquot *gdqp;
ASSERT(dqp->q_nrefs > 0); ASSERT(dqp->q_nrefs > 0);
ASSERT(XFS_DQ_IS_LOCKED(dqp)); ASSERT(XFS_DQ_IS_LOCKED(dqp));
trace_xfs_dqput(dqp); trace_xfs_dqput(dqp);
if (dqp->q_nrefs != 1) { recurse:
dqp->q_nrefs--; if (--dqp->q_nrefs > 0) {
xfs_dqunlock(dqp); xfs_dqunlock(dqp);
return; return;
} }
trace_xfs_dqput_free(dqp);
mutex_lock(&xfs_Gqm->qm_dqfrlist_lock);
if (list_empty(&dqp->q_freelist)) {
list_add_tail(&dqp->q_freelist, &xfs_Gqm->qm_dqfrlist);
xfs_Gqm->qm_dqfrlist_cnt++;
}
mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock);
/* /*
* drop the dqlock and acquire the freelist and dqlock * If we just added a udquot to the freelist, then we want to release
* in the right order; but try to get it out-of-order first * the gdquot reference that it (probably) has. Otherwise it'll keep
* the gdquot from getting reclaimed.
*/ */
if (!mutex_trylock(&xfs_Gqm->qm_dqfrlist_lock)) { gdqp = dqp->q_gdquot;
trace_xfs_dqput_wait(dqp); if (gdqp) {
xfs_dqunlock(dqp); xfs_dqlock(gdqp);
mutex_lock(&xfs_Gqm->qm_dqfrlist_lock); dqp->q_gdquot = NULL;
xfs_dqlock(dqp);
} }
xfs_dqunlock(dqp);
while (1) { /*
gdqp = NULL; * If we had a group quota hint, release it now.
*/
/* We can't depend on nrefs being == 1 here */ if (gdqp) {
if (--dqp->q_nrefs == 0) {
trace_xfs_dqput_free(dqp);
if (list_empty(&dqp->q_freelist)) {
list_add_tail(&dqp->q_freelist, &xfs_Gqm->qm_dqfrlist);
xfs_Gqm->qm_dqfrlist_cnt++;
}
/*
* If we just added a udquot to the freelist, then
* we want to release the gdquot reference that
* it (probably) has. Otherwise it'll keep the
* gdquot from getting reclaimed.
*/
if ((gdqp = dqp->q_gdquot)) {
/*
* Avoid a recursive dqput call
*/
xfs_dqlock(gdqp);
dqp->q_gdquot = NULL;
}
}
xfs_dqunlock(dqp);
/*
* If we had a group quota inside the user quota as a hint,
* release it now.
*/
if (! gdqp)
break;
dqp = gdqp; dqp = gdqp;
goto recurse;
} }
mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock);
} }
/* /*
......
...@@ -1668,7 +1668,9 @@ xfs_qm_dqreclaim_one(void) ...@@ -1668,7 +1668,9 @@ xfs_qm_dqreclaim_one(void)
restart: restart:
list_for_each_entry(dqp, &xfs_Gqm->qm_dqfrlist, q_freelist) { list_for_each_entry(dqp, &xfs_Gqm->qm_dqfrlist, q_freelist) {
struct xfs_mount *mp = dqp->q_mount; struct xfs_mount *mp = dqp->q_mount;
xfs_dqlock(dqp);
if (!xfs_dqlock_nowait(dqp))
continue;
/* /*
* This dquot has already been grabbed by dqlookup. * This dquot has already been grabbed by dqlookup.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment