quota: Push dqio_sem down to ->get_next_id()
[sfrench/cifs-2.6.git] / fs / quota / quota_v2.c
index ca71bf881ad1e33d11c1fdd10b71070c5079358d..f82638e43c07d20074c13ab2a70b0da59ba6b410 100644 (file)
@@ -285,17 +285,51 @@ static int v2r1_is_id(void *dp, struct dquot *dquot)
 
 static int v2_read_dquot(struct dquot *dquot)
 {
-       return qtree_read_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot);
+       struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
+       int ret;
+
+       down_read(&dqopt->dqio_sem);
+       ret = qtree_read_dquot(
+                       sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,
+                       dquot);
+       up_read(&dqopt->dqio_sem);
+       return ret;
 }
 
 static int v2_write_dquot(struct dquot *dquot)
 {
-       return qtree_write_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot);
+       struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
+       int ret;
+       bool alloc = false;
+
+       /*
+        * If space for dquot is already allocated, we don't need any
+        * protection as we'll only overwrite the place of dquot. We are
+        * still protected by concurrent writes of the same dquot by
+        * dquot->dq_lock.
+        */
+       if (!dquot->dq_off) {
+               alloc = true;
+               down_write(&dqopt->dqio_sem);
+       }
+       ret = qtree_write_dquot(
+                       sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,
+                       dquot);
+       if (alloc)
+               up_write(&dqopt->dqio_sem);
+       return ret;
 }
 
 static int v2_release_dquot(struct dquot *dquot)
 {
-       return qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot);
+       struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
+       int ret;
+
+       down_write(&dqopt->dqio_sem);
+       ret = qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot);
+       up_write(&dqopt->dqio_sem);
+
+       return ret;
 }
 
 static int v2_free_file_info(struct super_block *sb, int type)
@@ -306,7 +340,13 @@ static int v2_free_file_info(struct super_block *sb, int type)
 
 static int v2_get_next_id(struct super_block *sb, struct kqid *qid)
 {
-       return qtree_get_next_id(sb_dqinfo(sb, qid->type)->dqi_priv, qid);
+       struct quota_info *dqopt = sb_dqopt(sb);
+       int ret;
+
+       down_read(&dqopt->dqio_sem);
+       ret = qtree_get_next_id(sb_dqinfo(sb, qid->type)->dqi_priv, qid);
+       up_read(&dqopt->dqio_sem);
+       return ret;
 }
 
 static const struct quota_format_ops v2_format_ops = {