From: Jan Kara <jack@ucw.cz>

Attached patch should fix reported deadlock in journalled quota code. 
quotactl() call was violating the locking rules and didn't start transaction
when it should.


---

 25-akpm/fs/dquot.c            |    6 ++++--
 25-akpm/fs/ext3/super.c       |   36 ++++++++++++++++++++++++++++++++++++
 25-akpm/include/linux/quota.h |    2 ++
 3 files changed, 42 insertions(+), 2 deletions(-)

diff -puN fs/dquot.c~fix-deadlock-in-journalled-quota fs/dquot.c
--- 25/fs/dquot.c~fix-deadlock-in-journalled-quota	Wed Apr 28 15:09:05 2004
+++ 25-akpm/fs/dquot.c	Wed Apr 28 15:09:05 2004
@@ -529,7 +529,7 @@ we_slept:
 	clear_dquot_dirty(dquot);
 	if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
 		spin_unlock(&dq_list_lock);
-		dquot_release(dquot);
+		dquot->dq_sb->dq_op->release_dquot(dquot);
 		goto we_slept;
 	}
 	atomic_dec(&dquot->dq_count);
@@ -605,7 +605,7 @@ we_slept:
 	 * finished or it will be canceled due to dq_count > 1 test */
 	wait_on_dquot(dquot);
 	/* Read the dquot and instantiate it (everything done only if needed) */
-	if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && dquot_acquire(dquot) < 0) {
+	if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && sb->dq_op->acquire_dquot(dquot) < 0) {
 		dqput(dquot);
 		return NODQUOT;
 	}
@@ -1259,6 +1259,8 @@ struct dquot_operations dquot_operations
 	.free_inode	= dquot_free_inode,
 	.transfer	= dquot_transfer,
 	.write_dquot	= dquot_commit,
+	.acquire_dquot	= dquot_acquire,
+	.release_dquot	= dquot_release,
 	.mark_dirty	= dquot_mark_dquot_dirty,
 	.write_info	= dquot_commit_info
 };
diff -puN fs/ext3/super.c~fix-deadlock-in-journalled-quota fs/ext3/super.c
--- 25/fs/ext3/super.c~fix-deadlock-in-journalled-quota	Wed Apr 28 15:09:05 2004
+++ 25-akpm/fs/ext3/super.c	Wed Apr 28 15:09:55 2004
@@ -521,6 +521,8 @@ static void ext3_clear_inode(struct inod
 static int ext3_dquot_initialize(struct inode *inode, int type);
 static int ext3_dquot_drop(struct inode *inode);
 static int ext3_write_dquot(struct dquot *dquot);
+static int ext3_acquire_dquot(struct dquot *dquot);
+static int ext3_release_dquot(struct dquot *dquot);
 static int ext3_mark_dquot_dirty(struct dquot *dquot);
 static int ext3_write_info(struct super_block *sb, int type);
 static int ext3_quota_on(struct super_block *sb, int type, int format_id, char *path);
@@ -536,6 +538,8 @@ static struct dquot_operations ext3_quot
 	.free_inode	= dquot_free_inode,
 	.transfer	= dquot_transfer,
 	.write_dquot	= ext3_write_dquot,
+	.acquire_dquot	= ext3_acquire_dquot,
+	.release_dquot	= ext3_release_dquot,
 	.mark_dirty	= ext3_mark_dquot_dirty,
 	.write_info	= ext3_write_info
 };
@@ -2178,6 +2182,38 @@ static int ext3_write_dquot(struct dquot
 	err = ext3_journal_stop(handle);
 	if (!ret)
 		ret = err;
+	return ret;
+}
+
+static int ext3_acquire_dquot(struct dquot *dquot)
+{
+	int ret, err;
+	handle_t *handle;
+
+	handle = ext3_journal_start(dquot_to_inode(dquot),
+					EXT3_QUOTA_INIT_BLOCKS);
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+	ret = dquot_acquire(dquot);
+	err = ext3_journal_stop(handle);
+	if (!ret)
+		ret = err;
+	return ret;
+}
+
+static int ext3_release_dquot(struct dquot *dquot)
+{
+	int ret, err;
+	handle_t *handle;
+
+	handle = ext3_journal_start(dquot_to_inode(dquot),
+					EXT3_QUOTA_INIT_BLOCKS);
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+	ret = dquot_release(dquot);
+	err = ext3_journal_stop(handle);
+	if (!ret)
+		ret = err;
 	return ret;
 }
 
diff -puN include/linux/quota.h~fix-deadlock-in-journalled-quota include/linux/quota.h
--- 25/include/linux/quota.h~fix-deadlock-in-journalled-quota	Wed Apr 28 15:09:05 2004
+++ 25-akpm/include/linux/quota.h	Wed Apr 28 15:09:05 2004
@@ -250,6 +250,8 @@ struct dquot_operations {
 	int (*free_inode) (const struct inode *, unsigned long);
 	int (*transfer) (struct inode *, struct iattr *);
 	int (*write_dquot) (struct dquot *);		/* Ordinary dquot write */
+	int (*acquire_dquot) (struct dquot *);		/* Quota is going to be created on disk */
+	int (*release_dquot) (struct dquot *);		/* Quota is going to be deleted from disk */
 	int (*mark_dirty) (struct dquot *);		/* Dquot is marked dirty */
 	int (*write_info) (struct super_block *, int);	/* Write of quota "superblock" */
 };

_