Commit 36b4f825 authored by Andrew Morton's avatar Andrew Morton Committed by James Bottomley

[PATCH] Fix deadlock with ext3+quota

From: Jan Kara <jack@ucw.cz>

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.
parent edf20d3a
...@@ -326,7 +326,7 @@ static int vfs_quota_sync(struct super_block *sb, int type) ...@@ -326,7 +326,7 @@ static int vfs_quota_sync(struct super_block *sb, int type)
if (!dquot_dirty(dquot)) if (!dquot_dirty(dquot))
continue; continue;
spin_unlock(&dq_list_lock); spin_unlock(&dq_list_lock);
commit_dqblk(dquot); sb->dq_op->sync_dquot(dquot);
goto restart; goto restart;
} }
spin_unlock(&dq_list_lock); spin_unlock(&dq_list_lock);
...@@ -1072,9 +1072,16 @@ struct dquot_operations dquot_operations = { ...@@ -1072,9 +1072,16 @@ struct dquot_operations dquot_operations = {
.alloc_inode = dquot_alloc_inode, .alloc_inode = dquot_alloc_inode,
.free_space = dquot_free_space, .free_space = dquot_free_space,
.free_inode = dquot_free_inode, .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) static inline void set_enable_flags(struct quota_info *dqopt, int type)
{ {
switch (type) { switch (type) {
...@@ -1432,3 +1439,4 @@ EXPORT_SYMBOL(unregister_quota_format); ...@@ -1432,3 +1439,4 @@ EXPORT_SYMBOL(unregister_quota_format);
EXPORT_SYMBOL(dqstats); EXPORT_SYMBOL(dqstats);
EXPORT_SYMBOL(dq_list_lock); EXPORT_SYMBOL(dq_list_lock);
EXPORT_SYMBOL(dq_data_lock); EXPORT_SYMBOL(dq_data_lock);
EXPORT_SYMBOL(init_dquot_operations);
...@@ -566,6 +566,8 @@ static void ext3_clear_inode(struct inode *inode) ...@@ -566,6 +566,8 @@ static void ext3_clear_inode(struct inode *inode)
# define ext3_clear_inode NULL # define ext3_clear_inode NULL
#endif #endif
static struct dquot_operations ext3_qops;
static struct super_operations ext3_sops = { static struct super_operations ext3_sops = {
.alloc_inode = ext3_alloc_inode, .alloc_inode = ext3_alloc_inode,
.destroy_inode = ext3_destroy_inode, .destroy_inode = ext3_destroy_inode,
...@@ -1337,6 +1339,7 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) ...@@ -1337,6 +1339,7 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent)
*/ */
sb->s_op = &ext3_sops; sb->s_op = &ext3_sops;
sb->s_export_op = &ext3_export_ops; sb->s_export_op = &ext3_export_ops;
sb->dq_op = &ext3_qops;
INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */ INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */
sb->s_root = 0; sb->s_root = 0;
...@@ -1977,6 +1980,56 @@ int ext3_statfs (struct super_block * sb, struct statfs * buf) ...@@ -1977,6 +1980,56 @@ int ext3_statfs (struct super_block * sb, struct statfs * buf)
return 0; 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, static struct super_block *ext3_get_sb(struct file_system_type *fs_type,
int flags, char *dev_name, void *data) int flags, char *dev_name, void *data)
{ {
...@@ -1999,6 +2052,11 @@ static int __init init_ext3_fs(void) ...@@ -1999,6 +2052,11 @@ static int __init init_ext3_fs(void)
err = init_inodecache(); err = init_inodecache();
if (err) if (err)
goto out1; 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); err = register_filesystem(&ext3_fs_type);
if (err) if (err)
goto out; goto out;
......
...@@ -250,6 +250,7 @@ struct dquot_operations { ...@@ -250,6 +250,7 @@ struct dquot_operations {
void (*free_space) (struct inode *, qsize_t); void (*free_space) (struct inode *, qsize_t);
void (*free_inode) (const struct inode *, unsigned long); void (*free_inode) (const struct inode *, unsigned long);
int (*transfer) (struct inode *, struct iattr *); int (*transfer) (struct inode *, struct iattr *);
int (*sync_dquot) (struct dquot *);
}; };
/* Operations handling requests from userspace */ /* Operations handling requests from userspace */
...@@ -303,6 +304,7 @@ struct quota_info { ...@@ -303,6 +304,7 @@ struct quota_info {
int register_quota_format(struct quota_format_type *fmt); int register_quota_format(struct quota_format_type *fmt);
void unregister_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 #else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment