quota: Protect dquot writeout with dq_lock
authorJan Kara <jack@suse.cz>
Thu, 8 Jun 2017 13:16:41 +0000 (15:16 +0200)
committerJan Kara <jack@suse.cz>
Thu, 17 Aug 2017 17:00:50 +0000 (19:00 +0200)
Currently dquot writeout is only protected by dqio_sem held for writing.
As we transition to a finer grained locking we will use dquot->dq_lock
instead. So acquire it in dquot_commit() and move dqio_sem just around
->commit_dqblk() call as it is still needed to serialize quota file
changes.

Reviewed-by: Andreas Dilger <adilger@dilger.ca>
Signed-off-by: Jan Kara <jack@suse.cz>
fs/quota/dquot.c

index 3852a3c79ac90291b8ae2a185636e858989d33ac..8b52e852eba253da4b20cb4de295072831d028a1 100644 (file)
  * sure they cannot race with quotaon which first sets S_NOQUOTA flag and
  * then drops all pointers to dquots from an inode.
  *
  * sure they cannot race with quotaon which first sets S_NOQUOTA flag and
  * then drops all pointers to dquots from an inode.
  *
- * Each dquot has its dq_lock mutex. Locked dquots might not be referenced
- * from inodes (dquot_alloc_space() and such don't check the dq_lock).
- * Currently dquot is locked only when it is being read to memory (or space for
- * it is being allocated) on the first dqget() and when it is being released on
- * the last dqput(). The allocation and release oparations are serialized by
- * the dq_lock and by checking the use count in dquot_release().  Write
- * operations on dquots don't hold dq_lock as they copy data under dq_data_lock
- * spinlock to internal buffers before writing.
+ * Each dquot has its dq_lock mutex.  Dquot is locked when it is being read to
+ * memory (or space for it is being allocated) on the first dqget(), when it is
+ * being written out, and when it is being released on the last dqput(). The
+ * allocation and release operations are serialized by the dq_lock and by
+ * checking the use count in dquot_release().
  *
  * Lock ordering (including related VFS locks) is the following:
  *   s_umount > i_mutex > journal_lock > dquot->dq_lock > dqio_sem
  *
  * Lock ordering (including related VFS locks) is the following:
  *   s_umount > i_mutex > journal_lock > dquot->dq_lock > dqio_sem
@@ -453,21 +450,24 @@ int dquot_commit(struct dquot *dquot)
        int ret = 0;
        struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
 
        int ret = 0;
        struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
 
-       down_write(&dqopt->dqio_sem);
+       mutex_lock(&dquot->dq_lock);
        spin_lock(&dq_list_lock);
        if (!clear_dquot_dirty(dquot)) {
                spin_unlock(&dq_list_lock);
        spin_lock(&dq_list_lock);
        if (!clear_dquot_dirty(dquot)) {
                spin_unlock(&dq_list_lock);
-               goto out_sem;
+               goto out_lock;
        }
        spin_unlock(&dq_list_lock);
        /* Inactive dquot can be only if there was error during read/init
         * => we have better not writing it */
        }
        spin_unlock(&dq_list_lock);
        /* Inactive dquot can be only if there was error during read/init
         * => we have better not writing it */
-       if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
+       if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
+               down_write(&dqopt->dqio_sem);
                ret = dqopt->ops[dquot->dq_id.type]->commit_dqblk(dquot);
                ret = dqopt->ops[dquot->dq_id.type]->commit_dqblk(dquot);
-       else
+               up_write(&dqopt->dqio_sem);
+       } else {
                ret = -EIO;
                ret = -EIO;
-out_sem:
-       up_write(&dqopt->dqio_sem);
+       }
+out_lock:
+       mutex_unlock(&dquot->dq_lock);
        return ret;
 }
 EXPORT_SYMBOL(dquot_commit);
        return ret;
 }
 EXPORT_SYMBOL(dquot_commit);