diff mbox

[05/27] quota: Protect dquot writeout with dq_lock

Message ID 20170816154127.7048-6-jack@suse.cz (mailing list archive)
State New, archived
Headers show

Commit Message

Jan Kara Aug. 16, 2017, 3:41 p.m. UTC
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.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/quota/dquot.c | 27 +++++++++++++--------------
 1 file changed, 13 insertions(+), 14 deletions(-)

Comments

Andreas Dilger Aug. 16, 2017, 4:44 p.m. UTC | #1
On Aug 16, 2017, at 9:41 AM, Jan Kara <jack@suse.cz> wrote:
> 
> 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.
> 
> Signed-off-by: Jan Kara <jack@suse.cz>
> ---
> fs/quota/dquot.c | 27 +++++++++++++--------------
> 1 file changed, 13 insertions(+), 14 deletions(-)
> 
> diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
> index 3852a3c79ac9..99693c6d5dae 100644
> --- a/fs/quota/dquot.c
> +++ b/fs/quota/dquot.c
> @@ -110,14 +110,11 @@
>  * 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
> @@ -453,21 +450,23 @@ int dquot_commit(struct dquot *dquot)
> 	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);
> -		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 */
> -	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);
> -	else
> +		up_write(&dqopt->dqio_sem);
> +	} else
> 		ret = -EIO;

(style) typically braces are put around both branches of an if/else if either
one needs it.

Otherwise, looks fine to me.

Reviewed-by: Andreas Dilger <adilger@dilger.ca>

> -out_sem:
> -	up_write(&dqopt->dqio_sem);
> +out_lock:
> +	mutex_unlock(&dquot->dq_lock);
> 	return ret;
> }
> EXPORT_SYMBOL(dquot_commit);
> --
> 2.12.3
> 


Cheers, Andreas
diff mbox

Patch

diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 3852a3c79ac9..99693c6d5dae 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -110,14 +110,11 @@ 
  * 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
@@ -453,21 +450,23 @@  int dquot_commit(struct dquot *dquot)
 	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);
-		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 */
-	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);
-	else
+		up_write(&dqopt->dqio_sem);
+	} else
 		ret = -EIO;
-out_sem:
-	up_write(&dqopt->dqio_sem);
+out_lock:
+	mutex_unlock(&dquot->dq_lock);
 	return ret;
 }
 EXPORT_SYMBOL(dquot_commit);