diff options
author | Christoph Hellwig <hch@infradead.org> | 2011-12-06 16:58:19 -0500 |
---|---|---|
committer | Ben Myers <bpm@sgi.com> | 2011-12-14 22:15:42 -0500 |
commit | bf72de3194e73fa210a904b0bd951135286bb385 (patch) | |
tree | 408ead179da91befd82e0cc23ed66830ead2fa59 /fs/xfs | |
parent | 92678554abfc2a2f2727ad168da87d8d434ac904 (diff) |
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: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/xfs_dquot.c | 97 | ||||
-rw-r--r-- | fs/xfs/xfs_qm.c | 4 |
2 files changed, 41 insertions, 60 deletions
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index d06d2a61e31b..f1d3ccb2980e 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c | |||
@@ -39,20 +39,19 @@ | |||
39 | #include "xfs_qm.h" | 39 | #include "xfs_qm.h" |
40 | #include "xfs_trace.h" | 40 | #include "xfs_trace.h" |
41 | 41 | ||
42 | |||
43 | /* | 42 | /* |
44 | LOCK ORDER | 43 | * Lock order: |
45 | 44 | * | |
46 | inode lock (ilock) | 45 | * ip->i_lock |
47 | dquot hash-chain lock (hashlock) | 46 | * qh->qh_lock |
48 | xqm dquot freelist lock (freelistlock | 47 | * qi->qi_dqlist_lock |
49 | mount's dquot list lock (mplistlock) | 48 | * dquot->q_qlock (xfs_dqlock() and friends) |
50 | user dquot lock - lock ordering among dquots is based on the uid or gid | 49 | * dquot->q_flush (xfs_dqflock() and friends) |
51 | group dquot lock - similar to udquots. Between the two dquots, the udquot | 50 | * xfs_Gqm->qm_dqfrlist_lock |
52 | has to be locked first. | 51 | * |
53 | pin lock - the dquot lock must be held to take this lock. | 52 | * If two dquots need to be locked the order is user before group/project, |
54 | flush lock - ditto. | 53 | * otherwise by the lowest id first, see xfs_dqlock2. |
55 | */ | 54 | */ |
56 | 55 | ||
57 | #ifdef DEBUG | 56 | #ifdef DEBUG |
58 | xfs_buftarg_t *xfs_dqerror_target; | 57 | xfs_buftarg_t *xfs_dqerror_target; |
@@ -984,69 +983,49 @@ restart: | |||
984 | */ | 983 | */ |
985 | void | 984 | void |
986 | xfs_qm_dqput( | 985 | xfs_qm_dqput( |
987 | xfs_dquot_t *dqp) | 986 | struct xfs_dquot *dqp) |
988 | { | 987 | { |
989 | xfs_dquot_t *gdqp; | 988 | struct xfs_dquot *gdqp; |
990 | 989 | ||
991 | ASSERT(dqp->q_nrefs > 0); | 990 | ASSERT(dqp->q_nrefs > 0); |
992 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | 991 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); |
993 | 992 | ||
994 | trace_xfs_dqput(dqp); | 993 | trace_xfs_dqput(dqp); |
995 | 994 | ||
996 | if (dqp->q_nrefs != 1) { | 995 | recurse: |
997 | dqp->q_nrefs--; | 996 | if (--dqp->q_nrefs > 0) { |
998 | xfs_dqunlock(dqp); | 997 | xfs_dqunlock(dqp); |
999 | return; | 998 | return; |
1000 | } | 999 | } |
1001 | 1000 | ||
1001 | trace_xfs_dqput_free(dqp); | ||
1002 | |||
1003 | mutex_lock(&xfs_Gqm->qm_dqfrlist_lock); | ||
1004 | if (list_empty(&dqp->q_freelist)) { | ||
1005 | list_add_tail(&dqp->q_freelist, &xfs_Gqm->qm_dqfrlist); | ||
1006 | xfs_Gqm->qm_dqfrlist_cnt++; | ||
1007 | } | ||
1008 | mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); | ||
1009 | |||
1002 | /* | 1010 | /* |
1003 | * drop the dqlock and acquire the freelist and dqlock | 1011 | * If we just added a udquot to the freelist, then we want to release |
1004 | * in the right order; but try to get it out-of-order first | 1012 | * the gdquot reference that it (probably) has. Otherwise it'll keep |
1013 | * the gdquot from getting reclaimed. | ||
1005 | */ | 1014 | */ |
1006 | if (!mutex_trylock(&xfs_Gqm->qm_dqfrlist_lock)) { | 1015 | gdqp = dqp->q_gdquot; |
1007 | trace_xfs_dqput_wait(dqp); | 1016 | if (gdqp) { |
1008 | xfs_dqunlock(dqp); | 1017 | xfs_dqlock(gdqp); |
1009 | mutex_lock(&xfs_Gqm->qm_dqfrlist_lock); | 1018 | dqp->q_gdquot = NULL; |
1010 | xfs_dqlock(dqp); | ||
1011 | } | 1019 | } |
1020 | xfs_dqunlock(dqp); | ||
1012 | 1021 | ||
1013 | while (1) { | 1022 | /* |
1014 | gdqp = NULL; | 1023 | * If we had a group quota hint, release it now. |
1015 | 1024 | */ | |
1016 | /* We can't depend on nrefs being == 1 here */ | 1025 | if (gdqp) { |
1017 | if (--dqp->q_nrefs == 0) { | ||
1018 | trace_xfs_dqput_free(dqp); | ||
1019 | |||
1020 | if (list_empty(&dqp->q_freelist)) { | ||
1021 | list_add_tail(&dqp->q_freelist, &xfs_Gqm->qm_dqfrlist); | ||
1022 | xfs_Gqm->qm_dqfrlist_cnt++; | ||
1023 | } | ||
1024 | |||
1025 | /* | ||
1026 | * If we just added a udquot to the freelist, then | ||
1027 | * we want to release the gdquot reference that | ||
1028 | * it (probably) has. Otherwise it'll keep the | ||
1029 | * gdquot from getting reclaimed. | ||
1030 | */ | ||
1031 | if ((gdqp = dqp->q_gdquot)) { | ||
1032 | /* | ||
1033 | * Avoid a recursive dqput call | ||
1034 | */ | ||
1035 | xfs_dqlock(gdqp); | ||
1036 | dqp->q_gdquot = NULL; | ||
1037 | } | ||
1038 | } | ||
1039 | xfs_dqunlock(dqp); | ||
1040 | |||
1041 | /* | ||
1042 | * If we had a group quota inside the user quota as a hint, | ||
1043 | * release it now. | ||
1044 | */ | ||
1045 | if (! gdqp) | ||
1046 | break; | ||
1047 | dqp = gdqp; | 1026 | dqp = gdqp; |
1027 | goto recurse; | ||
1048 | } | 1028 | } |
1049 | mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); | ||
1050 | } | 1029 | } |
1051 | 1030 | ||
1052 | /* | 1031 | /* |
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index f418731e90f4..22360bb26af9 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c | |||
@@ -1668,7 +1668,9 @@ xfs_qm_dqreclaim_one(void) | |||
1668 | restart: | 1668 | restart: |
1669 | list_for_each_entry(dqp, &xfs_Gqm->qm_dqfrlist, q_freelist) { | 1669 | list_for_each_entry(dqp, &xfs_Gqm->qm_dqfrlist, q_freelist) { |
1670 | struct xfs_mount *mp = dqp->q_mount; | 1670 | struct xfs_mount *mp = dqp->q_mount; |
1671 | xfs_dqlock(dqp); | 1671 | |
1672 | if (!xfs_dqlock_nowait(dqp)) | ||
1673 | continue; | ||
1672 | 1674 | ||
1673 | /* | 1675 | /* |
1674 | * This dquot has already been grabbed by dqlookup. | 1676 | * This dquot has already been grabbed by dqlookup. |