From: Jan Kara 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" */ }; _