aboutsummaryrefslogtreecommitdiffstats
path: root/fs/quota
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2014-02-20 17:02:27 +0100
committerBen Hutchings <ben@decadent.org.uk>2014-04-02 00:58:55 +0100
commit7149a5e5c1fecc22a838f87ed86bfa3cc0efc984 (patch)
tree68a5c311e7d3eda70d0e07953518cf4eb6e18a79 /fs/quota
parentcf5a6031f2bd8799ad551fb9b65f1b1e3104ea8a (diff)
downloadkernel_samsung_smdk4412-7149a5e5c1fecc22a838f87ed86bfa3cc0efc984.zip
kernel_samsung_smdk4412-7149a5e5c1fecc22a838f87ed86bfa3cc0efc984.tar.gz
kernel_samsung_smdk4412-7149a5e5c1fecc22a838f87ed86bfa3cc0efc984.tar.bz2
quota: Fix race between dqput() and dquot_scan_active()
commit 1362f4ea20fa63688ba6026e586d9746ff13a846 upstream. 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. Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'fs/quota')
-rw-r--r--fs/quota/dquot.c14
1 files changed, 11 insertions, 3 deletions
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 5b572c8..3d5d717 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -580,9 +580,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 */