mirror of
https://github.com/torvalds/linux.git
synced 2026-05-23 14:42:08 +02:00
xfs: use a lockref for the xfs_dquot reference count
The xfs_dquot structure currently uses the anti-pattern of using the in-object lock that protects the content to also serialize reference count updates for the structure, leading to a cumbersome free path. This is partially papered over by the fact that we never free the dquot directly but always through the LRU. Switch to use a lockref instead and move the reference counter manipulations out of q_qlock. To make this work, xfs_qm_flush_one and xfs_qm_flush_one are converted to acquire a dquot reference while flushing to integrate with the lockref "get if not dead" scheme. Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Carlos Maiolino <cem@kernel.org>
This commit is contained in:
parent
6129b088e1
commit
0c5e80bd57
|
|
@ -29,11 +29,9 @@ typedef uint8_t xfs_dqtype_t;
|
|||
* flags for q_flags field in the dquot.
|
||||
*/
|
||||
#define XFS_DQFLAG_DIRTY (1u << 0) /* dquot is dirty */
|
||||
#define XFS_DQFLAG_FREEING (1u << 1) /* dquot is being torn down */
|
||||
|
||||
#define XFS_DQFLAG_STRINGS \
|
||||
{ XFS_DQFLAG_DIRTY, "DIRTY" }, \
|
||||
{ XFS_DQFLAG_FREEING, "FREEING" }
|
||||
{ XFS_DQFLAG_DIRTY, "DIRTY" }
|
||||
|
||||
/*
|
||||
* We have the possibility of all three quota types being active at once, and
|
||||
|
|
|
|||
|
|
@ -816,20 +816,17 @@ xfs_qm_dqget_cache_lookup(
|
|||
return NULL;
|
||||
}
|
||||
|
||||
mutex_lock(&dqp->q_qlock);
|
||||
if (dqp->q_flags & XFS_DQFLAG_FREEING) {
|
||||
mutex_unlock(&dqp->q_qlock);
|
||||
if (!lockref_get_not_dead(&dqp->q_lockref)) {
|
||||
mutex_unlock(&qi->qi_tree_lock);
|
||||
trace_xfs_dqget_freeing(dqp);
|
||||
delay(1);
|
||||
goto restart;
|
||||
}
|
||||
|
||||
dqp->q_nrefs++;
|
||||
mutex_unlock(&qi->qi_tree_lock);
|
||||
|
||||
trace_xfs_dqget_hit(dqp);
|
||||
XFS_STATS_INC(mp, xs_qm_dqcachehits);
|
||||
mutex_lock(&dqp->q_qlock);
|
||||
return dqp;
|
||||
}
|
||||
|
||||
|
|
@ -866,7 +863,7 @@ xfs_qm_dqget_cache_insert(
|
|||
|
||||
/* Return a locked dquot to the caller, with a reference taken. */
|
||||
mutex_lock(&dqp->q_qlock);
|
||||
dqp->q_nrefs = 1;
|
||||
lockref_init(&dqp->q_lockref);
|
||||
qi->qi_dquots++;
|
||||
|
||||
out_unlock:
|
||||
|
|
@ -1124,18 +1121,22 @@ void
|
|||
xfs_qm_dqput(
|
||||
struct xfs_dquot *dqp)
|
||||
{
|
||||
ASSERT(dqp->q_nrefs > 0);
|
||||
ASSERT(XFS_DQ_IS_LOCKED(dqp));
|
||||
|
||||
trace_xfs_dqput(dqp);
|
||||
|
||||
if (--dqp->q_nrefs == 0) {
|
||||
if (lockref_put_or_lock(&dqp->q_lockref))
|
||||
goto out_unlock;
|
||||
|
||||
if (!--dqp->q_lockref.count) {
|
||||
struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo;
|
||||
trace_xfs_dqput_free(dqp);
|
||||
|
||||
if (list_lru_add_obj(&qi->qi_lru, &dqp->q_lru))
|
||||
XFS_STATS_INC(dqp->q_mount, xs_qm_dquot_unused);
|
||||
}
|
||||
spin_unlock(&dqp->q_lockref.lock);
|
||||
out_unlock:
|
||||
mutex_unlock(&dqp->q_qlock);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ struct xfs_dquot {
|
|||
xfs_dqtype_t q_type;
|
||||
uint16_t q_flags;
|
||||
xfs_dqid_t q_id;
|
||||
uint q_nrefs;
|
||||
struct lockref q_lockref;
|
||||
int q_bufoffset;
|
||||
xfs_daddr_t q_blkno;
|
||||
xfs_fileoff_t q_fileoffset;
|
||||
|
|
@ -231,9 +231,7 @@ void xfs_dquot_detach_buf(struct xfs_dquot *dqp);
|
|||
|
||||
static inline struct xfs_dquot *xfs_qm_dqhold(struct xfs_dquot *dqp)
|
||||
{
|
||||
mutex_lock(&dqp->q_qlock);
|
||||
dqp->q_nrefs++;
|
||||
mutex_unlock(&dqp->q_qlock);
|
||||
lockref_get(&dqp->q_lockref);
|
||||
return dqp;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -126,14 +126,16 @@ xfs_qm_dqpurge(
|
|||
void *data)
|
||||
{
|
||||
struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo;
|
||||
int error = -EAGAIN;
|
||||
|
||||
spin_lock(&dqp->q_lockref.lock);
|
||||
if (dqp->q_lockref.count > 0 || __lockref_is_dead(&dqp->q_lockref)) {
|
||||
spin_unlock(&dqp->q_lockref.lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
lockref_mark_dead(&dqp->q_lockref);
|
||||
spin_unlock(&dqp->q_lockref.lock);
|
||||
|
||||
mutex_lock(&dqp->q_qlock);
|
||||
if ((dqp->q_flags & XFS_DQFLAG_FREEING) || dqp->q_nrefs != 0)
|
||||
goto out_unlock;
|
||||
|
||||
dqp->q_flags |= XFS_DQFLAG_FREEING;
|
||||
|
||||
xfs_qm_dqunpin_wait(dqp);
|
||||
xfs_dqflock(dqp);
|
||||
|
||||
|
|
@ -144,6 +146,7 @@ xfs_qm_dqpurge(
|
|||
*/
|
||||
if (XFS_DQ_IS_DIRTY(dqp)) {
|
||||
struct xfs_buf *bp = NULL;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* We don't care about getting disk errors here. We need
|
||||
|
|
@ -151,9 +154,9 @@ xfs_qm_dqpurge(
|
|||
*/
|
||||
error = xfs_dquot_use_attached_buf(dqp, &bp);
|
||||
if (error == -EAGAIN) {
|
||||
xfs_dqfunlock(dqp);
|
||||
dqp->q_flags &= ~XFS_DQFLAG_FREEING;
|
||||
goto out_unlock;
|
||||
/* resurrect the refcount from the dead. */
|
||||
dqp->q_lockref.count = 0;
|
||||
goto out_funlock;
|
||||
}
|
||||
if (!bp)
|
||||
goto out_funlock;
|
||||
|
|
@ -192,10 +195,6 @@ xfs_qm_dqpurge(
|
|||
|
||||
xfs_qm_dqdestroy(dqp);
|
||||
return 0;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&dqp->q_qlock);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -468,7 +467,7 @@ xfs_qm_dquot_isolate(
|
|||
struct xfs_qm_isolate *isol = arg;
|
||||
enum lru_status ret = LRU_SKIP;
|
||||
|
||||
if (!mutex_trylock(&dqp->q_qlock))
|
||||
if (!spin_trylock(&dqp->q_lockref.lock))
|
||||
goto out_miss_busy;
|
||||
|
||||
/*
|
||||
|
|
@ -476,7 +475,7 @@ xfs_qm_dquot_isolate(
|
|||
* from the LRU, leave it for the freeing task to complete the freeing
|
||||
* process rather than risk it being free from under us here.
|
||||
*/
|
||||
if (dqp->q_flags & XFS_DQFLAG_FREEING)
|
||||
if (__lockref_is_dead(&dqp->q_lockref))
|
||||
goto out_miss_unlock;
|
||||
|
||||
/*
|
||||
|
|
@ -485,16 +484,15 @@ xfs_qm_dquot_isolate(
|
|||
* again.
|
||||
*/
|
||||
ret = LRU_ROTATE;
|
||||
if (XFS_DQ_IS_DIRTY(dqp) || atomic_read(&dqp->q_pincount) > 0) {
|
||||
if (XFS_DQ_IS_DIRTY(dqp) || atomic_read(&dqp->q_pincount) > 0)
|
||||
goto out_miss_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* This dquot has acquired a reference in the meantime remove it from
|
||||
* the freelist and try again.
|
||||
*/
|
||||
if (dqp->q_nrefs) {
|
||||
mutex_unlock(&dqp->q_qlock);
|
||||
if (dqp->q_lockref.count) {
|
||||
spin_unlock(&dqp->q_lockref.lock);
|
||||
XFS_STATS_INC(dqp->q_mount, xs_qm_dqwants);
|
||||
|
||||
trace_xfs_dqreclaim_want(dqp);
|
||||
|
|
@ -518,10 +516,9 @@ xfs_qm_dquot_isolate(
|
|||
/*
|
||||
* Prevent lookups now that we are past the point of no return.
|
||||
*/
|
||||
dqp->q_flags |= XFS_DQFLAG_FREEING;
|
||||
mutex_unlock(&dqp->q_qlock);
|
||||
lockref_mark_dead(&dqp->q_lockref);
|
||||
spin_unlock(&dqp->q_lockref.lock);
|
||||
|
||||
ASSERT(dqp->q_nrefs == 0);
|
||||
list_lru_isolate_move(lru, &dqp->q_lru, &isol->dispose);
|
||||
XFS_STATS_DEC(dqp->q_mount, xs_qm_dquot_unused);
|
||||
trace_xfs_dqreclaim_done(dqp);
|
||||
|
|
@ -529,7 +526,7 @@ xfs_qm_dquot_isolate(
|
|||
return LRU_REMOVED;
|
||||
|
||||
out_miss_unlock:
|
||||
mutex_unlock(&dqp->q_qlock);
|
||||
spin_unlock(&dqp->q_lockref.lock);
|
||||
out_miss_busy:
|
||||
trace_xfs_dqreclaim_busy(dqp);
|
||||
XFS_STATS_INC(dqp->q_mount, xs_qm_dqreclaim_misses);
|
||||
|
|
@ -1467,9 +1464,10 @@ xfs_qm_flush_one(
|
|||
struct xfs_buf *bp = NULL;
|
||||
int error = 0;
|
||||
|
||||
if (!lockref_get_not_dead(&dqp->q_lockref))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&dqp->q_qlock);
|
||||
if (dqp->q_flags & XFS_DQFLAG_FREEING)
|
||||
goto out_unlock;
|
||||
if (!XFS_DQ_IS_DIRTY(dqp))
|
||||
goto out_unlock;
|
||||
|
||||
|
|
@ -1489,7 +1487,7 @@ xfs_qm_flush_one(
|
|||
xfs_buf_delwri_queue(bp, buffer_list);
|
||||
xfs_buf_relse(bp);
|
||||
out_unlock:
|
||||
mutex_unlock(&dqp->q_qlock);
|
||||
xfs_qm_dqput(dqp);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1350,7 +1350,7 @@ DECLARE_EVENT_CLASS(xfs_dquot_class,
|
|||
__entry->id = dqp->q_id;
|
||||
__entry->type = dqp->q_type;
|
||||
__entry->flags = dqp->q_flags;
|
||||
__entry->nrefs = dqp->q_nrefs;
|
||||
__entry->nrefs = data_race(dqp->q_lockref.count);
|
||||
|
||||
__entry->res_bcount = dqp->q_blk.reserved;
|
||||
__entry->res_rtbcount = dqp->q_rtb.reserved;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user