Browse Source

quota: Fix possible deadlock during parallel quotaon and quotaoff

The following test script triggers a deadlock on ext2 filesystem:
while true; do quotaon /dev/hda >&/dev/null; usleep $RANDOM; done &
while true; do quotaoff /dev/hda >&/dev/null; usleep $RANDOM; done &

I found there is a potential deadlock between quotaon and quotaoff (or
quotasync). Basically, all of quotactl operations need to be protected by
dqonoff_mutex. vfs_quota_off and vfs_quota_sync also call sb->s_op->quota_write
that needs to grab the i_mutex of the quota file.  But in vfs_quota_on_inode
(called from quotaon operation), the current code tries to grab  the i_mutex of
the quota file first before getting quonoff_mutex.

Reverse the order in which we take locks in vfs_quota_on_inode().

Jan Kara: Changed changelog to be more readable, made lockdep happy with
  I_MUTEX_QUOTA.

Signed-off-by: Jiaying Zhang <jiayingz@google.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Jiaying Zhang 16 years ago
parent
commit
d01730d74d
1 changed files with 2 additions and 2 deletions
  1. 2 2
      fs/quota/dquot.c

+ 2 - 2
fs/quota/dquot.c

@@ -2042,8 +2042,8 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
 		 * changes */
 		 * changes */
 		invalidate_bdev(sb->s_bdev);
 		invalidate_bdev(sb->s_bdev);
 	}
 	}
-	mutex_lock(&inode->i_mutex);
 	mutex_lock(&dqopt->dqonoff_mutex);
 	mutex_lock(&dqopt->dqonoff_mutex);
+	mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
 	if (sb_has_quota_loaded(sb, type)) {
 	if (sb_has_quota_loaded(sb, type)) {
 		error = -EBUSY;
 		error = -EBUSY;
 		goto out_lock;
 		goto out_lock;
@@ -2094,7 +2094,6 @@ out_file_init:
 	dqopt->files[type] = NULL;
 	dqopt->files[type] = NULL;
 	iput(inode);
 	iput(inode);
 out_lock:
 out_lock:
-	mutex_unlock(&dqopt->dqonoff_mutex);
 	if (oldflags != -1) {
 	if (oldflags != -1) {
 		down_write(&dqopt->dqptr_sem);
 		down_write(&dqopt->dqptr_sem);
 		/* Set the flags back (in the case of accidental quotaon()
 		/* Set the flags back (in the case of accidental quotaon()
@@ -2104,6 +2103,7 @@ out_lock:
 		up_write(&dqopt->dqptr_sem);
 		up_write(&dqopt->dqptr_sem);
 	}
 	}
 	mutex_unlock(&inode->i_mutex);
 	mutex_unlock(&inode->i_mutex);
+	mutex_unlock(&dqopt->dqonoff_mutex);
 out_fmt:
 out_fmt:
 	put_quota_format(fmt);
 	put_quota_format(fmt);