From: Jan Kara Fixes a deadlock-causing lock-ranking bug between dqio_sem and journal_start(). It sets up the needed infrastructure so that the quota code's sync_dquot() operation can call into ext3 and arrange for the transaction start to be nested outside the taking of dqio_sem. fs/dquot.c | 12 ++++++++-- fs/ext3/super.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/quota.h | 2 + 3 files changed, 70 insertions(+), 2 deletions(-) diff -puN fs/dquot.c~ext3-quota-deadlock-fix fs/dquot.c --- 25/fs/dquot.c~ext3-quota-deadlock-fix 2003-04-12 00:30:08.000000000 -0700 +++ 25-akpm/fs/dquot.c 2003-04-12 00:30:08.000000000 -0700 @@ -326,7 +326,7 @@ restart: if (!dquot_dirty(dquot)) continue; spin_unlock(&dq_list_lock); - commit_dqblk(dquot); + sb->dq_op->sync_dquot(dquot); goto restart; } spin_unlock(&dq_list_lock); @@ -1072,9 +1072,16 @@ struct dquot_operations dquot_operations .alloc_inode = dquot_alloc_inode, .free_space = dquot_free_space, .free_inode = dquot_free_inode, - .transfer = dquot_transfer + .transfer = dquot_transfer, + .sync_dquot = commit_dqblk }; +/* Function used by filesystems for initializing the dquot_operations structure */ +void init_dquot_operations(struct dquot_operations *fsdqops) +{ + memcpy(fsdqops, &dquot_operations, sizeof(dquot_operations)); +} + static inline void set_enable_flags(struct quota_info *dqopt, int type) { switch (type) { @@ -1432,3 +1439,4 @@ EXPORT_SYMBOL(unregister_quota_format); EXPORT_SYMBOL(dqstats); EXPORT_SYMBOL(dq_list_lock); EXPORT_SYMBOL(dq_data_lock); +EXPORT_SYMBOL(init_dquot_operations); diff -puN fs/ext3/super.c~ext3-quota-deadlock-fix fs/ext3/super.c --- 25/fs/ext3/super.c~ext3-quota-deadlock-fix 2003-04-12 00:30:08.000000000 -0700 +++ 25-akpm/fs/ext3/super.c 2003-04-12 00:56:43.000000000 -0700 @@ -566,6 +566,8 @@ static void ext3_clear_inode(struct inod # define ext3_clear_inode NULL #endif +static struct dquot_operations ext3_qops; + static struct super_operations ext3_sops = { .alloc_inode = ext3_alloc_inode, .destroy_inode = ext3_destroy_inode, @@ -1337,6 +1339,7 @@ static int ext3_fill_super (struct super */ sb->s_op = &ext3_sops; sb->s_export_op = &ext3_export_ops; + sb->dq_op = &ext3_qops; INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */ sb->s_root = 0; @@ -1977,6 +1980,56 @@ int ext3_statfs (struct super_block * sb return 0; } +/* Helper function for writing quotas on sync - we need to start transaction before quota file + * is locked for write. Otherwise the are possible deadlocks: + * Process 1 Process 2 + * ext3_create() quota_sync() + * journal_start() write_dquot() + * DQUOT_INIT() down(dqio_sem) + * down(dqio_sem) journal_start() + * + */ + +#ifdef CONFIG_QUOTA + +#define EXT3_OLD_QFMT_BLOCKS 2 +#define EXT3_V0_QFMT_BLOCKS 6 + +static int (*old_sync_dquot)(struct dquot *dquot); + +static int ext3_sync_dquot(struct dquot *dquot) +{ + int nblocks, ret; + handle_t *handle; + struct quota_info *dqops = sb_dqopt(dquot->dq_sb); + struct inode *qinode; + + switch (dqops->info[dquot->dq_type].dqi_format->qf_fmt_id) { + case QFMT_VFS_OLD: + nblocks = EXT3_OLD_QFMT_BLOCKS; + break; + case QFMT_VFS_V0: + nblocks = EXT3_V0_QFMT_BLOCKS; + break; + default: + nblocks = EXT3_MAX_TRANS_DATA; + } + lock_kernel(); + qinode = dqops->files[dquot->dq_type]->f_dentry->d_inode; + handle = ext3_journal_start(qinode, nblocks); + if (IS_ERR(handle)) { + unlock_kernel(); + return PTR_ERR(handle); + } + unlock_kernel(); + ret = old_sync_dquot(dquot); + lock_kernel(); + ret = ext3_journal_stop(handle); + unlock_kernel(); + return ret; +} +#endif + static struct super_block *ext3_get_sb(struct file_system_type *fs_type, int flags, char *dev_name, void *data) { @@ -1999,6 +2052,11 @@ static int __init init_ext3_fs(void) err = init_inodecache(); if (err) goto out1; +#ifdef CONFIG_QUOTA + init_dquot_operations(&ext3_qops); + old_sync_dquot = ext3_qops.sync_dquot; + ext3_qops.sync_dquot = ext3_sync_dquot; +#endif err = register_filesystem(&ext3_fs_type); if (err) goto out; diff -puN include/linux/quota.h~ext3-quota-deadlock-fix include/linux/quota.h --- 25/include/linux/quota.h~ext3-quota-deadlock-fix 2003-04-12 00:30:08.000000000 -0700 +++ 25-akpm/include/linux/quota.h 2003-04-12 00:30:08.000000000 -0700 @@ -250,6 +250,7 @@ struct dquot_operations { void (*free_space) (struct inode *, qsize_t); void (*free_inode) (const struct inode *, unsigned long); int (*transfer) (struct inode *, struct iattr *); + int (*sync_dquot) (struct dquot *); }; /* Operations handling requests from userspace */ @@ -303,6 +304,7 @@ struct quota_info { int register_quota_format(struct quota_format_type *fmt); void unregister_quota_format(struct quota_format_type *fmt); +void init_dquot_operations(struct dquot_operations *fsdqops); #else _