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>
This commit is contained in:
parent
09ebb17ab4
commit
1362f4ea20
@ -581,9 +581,17 @@ int dquot_scan_active(struct super_block *sb,
|
||||
dqstats_inc(DQST_LOOKUPS);
|
||||
dqput(old_dquot);
|
||||
old_dquot = dquot;
|
||||
ret = fn(dquot, priv);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
/*
|
||||
* ->release_dquot() can be racing with us. Our reference
|
||||
* protects us from new calls to it so just wait for any
|
||||
* outstanding call and recheck the DQ_ACTIVE_B after that.
|
||||
*/
|
||||
wait_on_dquot(dquot);
|
||||
if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
|
||||
ret = fn(dquot, priv);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
spin_lock(&dq_list_lock);
|
||||
/* We are safe to continue now because our dquot could not
|
||||
* be moved out of the inuse list while we hold the reference */
|
||||
|
Loading…
Reference in New Issue
Block a user