diff options
author | Dave Chinner <dchinner@redhat.com> | 2012-11-27 21:01:02 -0500 |
---|---|---|
committer | Ben Myers <bpm@sgi.com> | 2012-11-29 15:24:03 -0500 |
commit | b870553cdecb26d5291af09602352b763e323df2 (patch) | |
tree | 4d7cfd6b3485d7e204003abc084245dc9a4e2ab5 /fs/xfs/xfs_qm.c | |
parent | 437a255aa23766666aec78af63be4c253faa8d57 (diff) |
xfs: fix stray dquot unlock when reclaiming dquots
When we fail to get a dquot lock during reclaim, we jump to an error
handler that unlocks the dquot. This is wrong as we didn't lock the
dquot, and unlocking it means who-ever is holding the lock has had
it silently taken away, and hence it results in a lock imbalance.
Found by inspection while modifying the code for the numa-lru
patchset. This fixes a random hang I've been seeing on xfstest 232
for the past several months.
cc: <stable@vger.kernel.org>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Ben Myers <bpm@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_qm.c')
-rw-r--r-- | fs/xfs/xfs_qm.c | 15 |
1 files changed, 7 insertions, 8 deletions
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index e6a0af0ba007..60eff4763156 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c | |||
@@ -1456,7 +1456,7 @@ xfs_qm_dqreclaim_one( | |||
1456 | int error; | 1456 | int error; |
1457 | 1457 | ||
1458 | if (!xfs_dqlock_nowait(dqp)) | 1458 | if (!xfs_dqlock_nowait(dqp)) |
1459 | goto out_busy; | 1459 | goto out_move_tail; |
1460 | 1460 | ||
1461 | /* | 1461 | /* |
1462 | * This dquot has acquired a reference in the meantime remove it from | 1462 | * This dquot has acquired a reference in the meantime remove it from |
@@ -1479,7 +1479,7 @@ xfs_qm_dqreclaim_one( | |||
1479 | * getting flushed to disk, we don't want to reclaim it. | 1479 | * getting flushed to disk, we don't want to reclaim it. |
1480 | */ | 1480 | */ |
1481 | if (!xfs_dqflock_nowait(dqp)) | 1481 | if (!xfs_dqflock_nowait(dqp)) |
1482 | goto out_busy; | 1482 | goto out_unlock_move_tail; |
1483 | 1483 | ||
1484 | if (XFS_DQ_IS_DIRTY(dqp)) { | 1484 | if (XFS_DQ_IS_DIRTY(dqp)) { |
1485 | struct xfs_buf *bp = NULL; | 1485 | struct xfs_buf *bp = NULL; |
@@ -1490,7 +1490,7 @@ xfs_qm_dqreclaim_one( | |||
1490 | if (error) { | 1490 | if (error) { |
1491 | xfs_warn(mp, "%s: dquot %p flush failed", | 1491 | xfs_warn(mp, "%s: dquot %p flush failed", |
1492 | __func__, dqp); | 1492 | __func__, dqp); |
1493 | goto out_busy; | 1493 | goto out_unlock_move_tail; |
1494 | } | 1494 | } |
1495 | 1495 | ||
1496 | xfs_buf_delwri_queue(bp, buffer_list); | 1496 | xfs_buf_delwri_queue(bp, buffer_list); |
@@ -1499,7 +1499,7 @@ xfs_qm_dqreclaim_one( | |||
1499 | * Give the dquot another try on the freelist, as the | 1499 | * Give the dquot another try on the freelist, as the |
1500 | * flushing will take some time. | 1500 | * flushing will take some time. |
1501 | */ | 1501 | */ |
1502 | goto out_busy; | 1502 | goto out_unlock_move_tail; |
1503 | } | 1503 | } |
1504 | xfs_dqfunlock(dqp); | 1504 | xfs_dqfunlock(dqp); |
1505 | 1505 | ||
@@ -1518,14 +1518,13 @@ xfs_qm_dqreclaim_one( | |||
1518 | XFS_STATS_INC(xs_qm_dqreclaims); | 1518 | XFS_STATS_INC(xs_qm_dqreclaims); |
1519 | return; | 1519 | return; |
1520 | 1520 | ||
1521 | out_busy: | ||
1522 | xfs_dqunlock(dqp); | ||
1523 | |||
1524 | /* | 1521 | /* |
1525 | * Move the dquot to the tail of the list so that we don't spin on it. | 1522 | * Move the dquot to the tail of the list so that we don't spin on it. |
1526 | */ | 1523 | */ |
1524 | out_unlock_move_tail: | ||
1525 | xfs_dqunlock(dqp); | ||
1526 | out_move_tail: | ||
1527 | list_move_tail(&dqp->q_lru, &qi->qi_lru_list); | 1527 | list_move_tail(&dqp->q_lru, &qi->qi_lru_list); |
1528 | |||
1529 | trace_xfs_dqreclaim_busy(dqp); | 1528 | trace_xfs_dqreclaim_busy(dqp); |
1530 | XFS_STATS_INC(xs_qm_dqreclaim_misses); | 1529 | XFS_STATS_INC(xs_qm_dqreclaim_misses); |
1531 | } | 1530 | } |