diff options
author | Jan Kara <jack@suse.cz> | 2014-02-20 11:02:27 -0500 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2014-02-20 15:57:04 -0500 |
commit | 1362f4ea20fa63688ba6026e586d9746ff13a846 (patch) | |
tree | 6c80329bee77974426663a1af8e5d70ef62759f9 /fs/quota | |
parent | 09ebb17ab476b6ac1cc07b53d07e88f4d31ee4d3 (diff) |
quota: Fix race between dqput() and dquot_scan_active()
Currently last dqput() can race with dquot_scan_active() causing it to
call callback for an already deactivated dquot. The race is as follows:
CPU1 CPU2
dqput()
spin_lock(&dq_list_lock);
if (atomic_read(&dquot->dq_count) > 1) {
- not taken
if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
spin_unlock(&dq_list_lock);
->release_dquot(dquot);
if (atomic_read(&dquot->dq_count) > 1)
- not taken
dquot_scan_active()
spin_lock(&dq_list_lock);
if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
- not taken
atomic_inc(&dquot->dq_count);
spin_unlock(&dq_list_lock);
- proceeds to release dquot
ret = fn(dquot, priv);
- called for inactive dquot
Fix the problem by making sure possible ->release_dquot() is finished by
the time we call the callback and new calls to it will notice reference
dquot_scan_active() has taken and bail out.
CC: stable@vger.kernel.org # >= 2.6.29
Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/quota')
-rw-r--r-- | fs/quota/dquot.c | 14 |
1 files changed, 11 insertions, 3 deletions
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 831d49a4111f..cfc8dcc16043 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c | |||
@@ -581,9 +581,17 @@ int dquot_scan_active(struct super_block *sb, | |||
581 | dqstats_inc(DQST_LOOKUPS); | 581 | dqstats_inc(DQST_LOOKUPS); |
582 | dqput(old_dquot); | 582 | dqput(old_dquot); |
583 | old_dquot = dquot; | 583 | old_dquot = dquot; |
584 | ret = fn(dquot, priv); | 584 | /* |
585 | if (ret < 0) | 585 | * ->release_dquot() can be racing with us. Our reference |
586 | goto out; | 586 | * protects us from new calls to it so just wait for any |
587 | * outstanding call and recheck the DQ_ACTIVE_B after that. | ||
588 | */ | ||
589 | wait_on_dquot(dquot); | ||
590 | if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) { | ||
591 | ret = fn(dquot, priv); | ||
592 | if (ret < 0) | ||
593 | goto out; | ||
594 | } | ||
587 | spin_lock(&dq_list_lock); | 595 | spin_lock(&dq_list_lock); |
588 | /* We are safe to continue now because our dquot could not | 596 | /* We are safe to continue now because our dquot could not |
589 | * be moved out of the inuse list while we hold the reference */ | 597 | * be moved out of the inuse list while we hold the reference */ |