Skip to content

Commit 0c5e80b

Browse files
Christoph Hellwigcmaiolino
authored andcommitted
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>
1 parent 6129b08 commit 0c5e80b

5 files changed

Lines changed: 37 additions & 42 deletions

File tree

fs/xfs/libxfs/xfs_quota_defs.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,9 @@ typedef uint8_t xfs_dqtype_t;
2929
* flags for q_flags field in the dquot.
3030
*/
3131
#define XFS_DQFLAG_DIRTY (1u << 0) /* dquot is dirty */
32-
#define XFS_DQFLAG_FREEING (1u << 1) /* dquot is being torn down */
3332

3433
#define XFS_DQFLAG_STRINGS \
35-
{ XFS_DQFLAG_DIRTY, "DIRTY" }, \
36-
{ XFS_DQFLAG_FREEING, "FREEING" }
34+
{ XFS_DQFLAG_DIRTY, "DIRTY" }
3735

3836
/*
3937
* We have the possibility of all three quota types being active at once, and

fs/xfs/xfs_dquot.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -816,20 +816,17 @@ xfs_qm_dqget_cache_lookup(
816816
return NULL;
817817
}
818818

819-
mutex_lock(&dqp->q_qlock);
820-
if (dqp->q_flags & XFS_DQFLAG_FREEING) {
821-
mutex_unlock(&dqp->q_qlock);
819+
if (!lockref_get_not_dead(&dqp->q_lockref)) {
822820
mutex_unlock(&qi->qi_tree_lock);
823821
trace_xfs_dqget_freeing(dqp);
824822
delay(1);
825823
goto restart;
826824
}
827-
828-
dqp->q_nrefs++;
829825
mutex_unlock(&qi->qi_tree_lock);
830826

831827
trace_xfs_dqget_hit(dqp);
832828
XFS_STATS_INC(mp, xs_qm_dqcachehits);
829+
mutex_lock(&dqp->q_qlock);
833830
return dqp;
834831
}
835832

@@ -866,7 +863,7 @@ xfs_qm_dqget_cache_insert(
866863

867864
/* Return a locked dquot to the caller, with a reference taken. */
868865
mutex_lock(&dqp->q_qlock);
869-
dqp->q_nrefs = 1;
866+
lockref_init(&dqp->q_lockref);
870867
qi->qi_dquots++;
871868

872869
out_unlock:
@@ -1124,18 +1121,22 @@ void
11241121
xfs_qm_dqput(
11251122
struct xfs_dquot *dqp)
11261123
{
1127-
ASSERT(dqp->q_nrefs > 0);
11281124
ASSERT(XFS_DQ_IS_LOCKED(dqp));
11291125

11301126
trace_xfs_dqput(dqp);
11311127

1132-
if (--dqp->q_nrefs == 0) {
1128+
if (lockref_put_or_lock(&dqp->q_lockref))
1129+
goto out_unlock;
1130+
1131+
if (!--dqp->q_lockref.count) {
11331132
struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo;
11341133
trace_xfs_dqput_free(dqp);
11351134

11361135
if (list_lru_add_obj(&qi->qi_lru, &dqp->q_lru))
11371136
XFS_STATS_INC(dqp->q_mount, xs_qm_dquot_unused);
11381137
}
1138+
spin_unlock(&dqp->q_lockref.lock);
1139+
out_unlock:
11391140
mutex_unlock(&dqp->q_qlock);
11401141
}
11411142

fs/xfs/xfs_dquot.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ struct xfs_dquot {
7171
xfs_dqtype_t q_type;
7272
uint16_t q_flags;
7373
xfs_dqid_t q_id;
74-
uint q_nrefs;
74+
struct lockref q_lockref;
7575
int q_bufoffset;
7676
xfs_daddr_t q_blkno;
7777
xfs_fileoff_t q_fileoffset;
@@ -231,9 +231,7 @@ void xfs_dquot_detach_buf(struct xfs_dquot *dqp);
231231

232232
static inline struct xfs_dquot *xfs_qm_dqhold(struct xfs_dquot *dqp)
233233
{
234-
mutex_lock(&dqp->q_qlock);
235-
dqp->q_nrefs++;
236-
mutex_unlock(&dqp->q_qlock);
234+
lockref_get(&dqp->q_lockref);
237235
return dqp;
238236
}
239237

fs/xfs/xfs_qm.c

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -126,14 +126,16 @@ xfs_qm_dqpurge(
126126
void *data)
127127
{
128128
struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo;
129-
int error = -EAGAIN;
130129

131-
mutex_lock(&dqp->q_qlock);
132-
if ((dqp->q_flags & XFS_DQFLAG_FREEING) || dqp->q_nrefs != 0)
133-
goto out_unlock;
134-
135-
dqp->q_flags |= XFS_DQFLAG_FREEING;
130+
spin_lock(&dqp->q_lockref.lock);
131+
if (dqp->q_lockref.count > 0 || __lockref_is_dead(&dqp->q_lockref)) {
132+
spin_unlock(&dqp->q_lockref.lock);
133+
return -EAGAIN;
134+
}
135+
lockref_mark_dead(&dqp->q_lockref);
136+
spin_unlock(&dqp->q_lockref.lock);
136137

138+
mutex_lock(&dqp->q_qlock);
137139
xfs_qm_dqunpin_wait(dqp);
138140
xfs_dqflock(dqp);
139141

@@ -144,16 +146,17 @@ xfs_qm_dqpurge(
144146
*/
145147
if (XFS_DQ_IS_DIRTY(dqp)) {
146148
struct xfs_buf *bp = NULL;
149+
int error;
147150

148151
/*
149152
* We don't care about getting disk errors here. We need
150153
* to purge this dquot anyway, so we go ahead regardless.
151154
*/
152155
error = xfs_dquot_use_attached_buf(dqp, &bp);
153156
if (error == -EAGAIN) {
154-
xfs_dqfunlock(dqp);
155-
dqp->q_flags &= ~XFS_DQFLAG_FREEING;
156-
goto out_unlock;
157+
/* resurrect the refcount from the dead. */
158+
dqp->q_lockref.count = 0;
159+
goto out_funlock;
157160
}
158161
if (!bp)
159162
goto out_funlock;
@@ -192,10 +195,6 @@ xfs_qm_dqpurge(
192195

193196
xfs_qm_dqdestroy(dqp);
194197
return 0;
195-
196-
out_unlock:
197-
mutex_unlock(&dqp->q_qlock);
198-
return error;
199198
}
200199

201200
/*
@@ -468,15 +467,15 @@ xfs_qm_dquot_isolate(
468467
struct xfs_qm_isolate *isol = arg;
469468
enum lru_status ret = LRU_SKIP;
470469

471-
if (!mutex_trylock(&dqp->q_qlock))
470+
if (!spin_trylock(&dqp->q_lockref.lock))
472471
goto out_miss_busy;
473472

474473
/*
475474
* If something else is freeing this dquot and hasn't yet removed it
476475
* from the LRU, leave it for the freeing task to complete the freeing
477476
* process rather than risk it being free from under us here.
478477
*/
479-
if (dqp->q_flags & XFS_DQFLAG_FREEING)
478+
if (__lockref_is_dead(&dqp->q_lockref))
480479
goto out_miss_unlock;
481480

482481
/*
@@ -485,16 +484,15 @@ xfs_qm_dquot_isolate(
485484
* again.
486485
*/
487486
ret = LRU_ROTATE;
488-
if (XFS_DQ_IS_DIRTY(dqp) || atomic_read(&dqp->q_pincount) > 0) {
487+
if (XFS_DQ_IS_DIRTY(dqp) || atomic_read(&dqp->q_pincount) > 0)
489488
goto out_miss_unlock;
490-
}
491489

492490
/*
493491
* This dquot has acquired a reference in the meantime remove it from
494492
* the freelist and try again.
495493
*/
496-
if (dqp->q_nrefs) {
497-
mutex_unlock(&dqp->q_qlock);
494+
if (dqp->q_lockref.count) {
495+
spin_unlock(&dqp->q_lockref.lock);
498496
XFS_STATS_INC(dqp->q_mount, xs_qm_dqwants);
499497

500498
trace_xfs_dqreclaim_want(dqp);
@@ -518,18 +516,17 @@ xfs_qm_dquot_isolate(
518516
/*
519517
* Prevent lookups now that we are past the point of no return.
520518
*/
521-
dqp->q_flags |= XFS_DQFLAG_FREEING;
522-
mutex_unlock(&dqp->q_qlock);
519+
lockref_mark_dead(&dqp->q_lockref);
520+
spin_unlock(&dqp->q_lockref.lock);
523521

524-
ASSERT(dqp->q_nrefs == 0);
525522
list_lru_isolate_move(lru, &dqp->q_lru, &isol->dispose);
526523
XFS_STATS_DEC(dqp->q_mount, xs_qm_dquot_unused);
527524
trace_xfs_dqreclaim_done(dqp);
528525
XFS_STATS_INC(dqp->q_mount, xs_qm_dqreclaims);
529526
return LRU_REMOVED;
530527

531528
out_miss_unlock:
532-
mutex_unlock(&dqp->q_qlock);
529+
spin_unlock(&dqp->q_lockref.lock);
533530
out_miss_busy:
534531
trace_xfs_dqreclaim_busy(dqp);
535532
XFS_STATS_INC(dqp->q_mount, xs_qm_dqreclaim_misses);
@@ -1467,9 +1464,10 @@ xfs_qm_flush_one(
14671464
struct xfs_buf *bp = NULL;
14681465
int error = 0;
14691466

1467+
if (!lockref_get_not_dead(&dqp->q_lockref))
1468+
return 0;
1469+
14701470
mutex_lock(&dqp->q_qlock);
1471-
if (dqp->q_flags & XFS_DQFLAG_FREEING)
1472-
goto out_unlock;
14731471
if (!XFS_DQ_IS_DIRTY(dqp))
14741472
goto out_unlock;
14751473

@@ -1489,7 +1487,7 @@ xfs_qm_flush_one(
14891487
xfs_buf_delwri_queue(bp, buffer_list);
14901488
xfs_buf_relse(bp);
14911489
out_unlock:
1492-
mutex_unlock(&dqp->q_qlock);
1490+
xfs_qm_dqput(dqp);
14931491
return error;
14941492
}
14951493

fs/xfs/xfs_trace.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1350,7 +1350,7 @@ DECLARE_EVENT_CLASS(xfs_dquot_class,
13501350
__entry->id = dqp->q_id;
13511351
__entry->type = dqp->q_type;
13521352
__entry->flags = dqp->q_flags;
1353-
__entry->nrefs = dqp->q_nrefs;
1353+
__entry->nrefs = data_race(dqp->q_lockref.count);
13541354

13551355
__entry->res_bcount = dqp->q_blk.reserved;
13561356
__entry->res_rtbcount = dqp->q_rtb.reserved;

0 commit comments

Comments
 (0)