Commit ed678f13 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Quota locking fixes

From: Jan Kara <jack@ucw.cz>

Change locking rules in quota code to fix lock ordering especially wrt
journal lock.  Also some unnecessary spinlocking is removed.  The locking
changes are mainly: dqptr_sem, dqio_sem are acquired only when transaction is
already started, dqonoff_sem before a transaction is started.  This change
requires some callbacks to ext3 (also implemented in this patch) to start
transaction before the locks are acquired.
parent a97de48b
...@@ -417,7 +417,7 @@ config QFMT_V1 ...@@ -417,7 +417,7 @@ config QFMT_V1
tristate "Old quota format support" tristate "Old quota format support"
depends on QUOTA depends on QUOTA
help help
This quota format was (is) used by kernels earlier than 2.4.??. If This quota format was (is) used by kernels earlier than 2.4.22. If
you have quota working and you don't want to convert to new quota you have quota working and you don't want to convert to new quota
format say Y here. format say Y here.
...@@ -426,8 +426,8 @@ config QFMT_V2 ...@@ -426,8 +426,8 @@ config QFMT_V2
depends on QUOTA depends on QUOTA
help help
This quota format allows using quotas with 32-bit UIDs/GIDs. If you This quota format allows using quotas with 32-bit UIDs/GIDs. If you
need this functionality say Y here. Note that you will need latest need this functionality say Y here. Note that you will need recent
quota utilities for new quota format with this kernel. quota utilities (>= 3.01) for new quota format with this kernel.
config QUOTACTL config QUOTACTL
bool bool
......
This diff is collapsed.
...@@ -1958,6 +1958,18 @@ int ext3_statfs (struct super_block * sb, struct kstatfs * buf) ...@@ -1958,6 +1958,18 @@ int ext3_statfs (struct super_block * sb, struct kstatfs * buf)
#define EXT3_V0_QFMT_BLOCKS 27 #define EXT3_V0_QFMT_BLOCKS 27
static int (*old_write_dquot)(struct dquot *dquot); static int (*old_write_dquot)(struct dquot *dquot);
static void (*old_drop_dquot)(struct inode *inode);
static int fmt_to_blocks(int fmt)
{
switch (fmt) {
case QFMT_VFS_OLD:
return EXT3_OLD_QFMT_BLOCKS;
case QFMT_VFS_V0:
return EXT3_V0_QFMT_BLOCKS;
}
return EXT3_MAX_TRANS_DATA;
}
static int ext3_write_dquot(struct dquot *dquot) static int ext3_write_dquot(struct dquot *dquot)
{ {
...@@ -1965,20 +1977,11 @@ static int ext3_write_dquot(struct dquot *dquot) ...@@ -1965,20 +1977,11 @@ static int ext3_write_dquot(struct dquot *dquot)
int ret; int ret;
int err; int err;
handle_t *handle; handle_t *handle;
struct quota_info *dqops = sb_dqopt(dquot->dq_sb); struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
struct inode *qinode; struct inode *qinode;
switch (dqops->info[dquot->dq_type].dqi_format->qf_fmt_id) { nblocks = fmt_to_blocks(dqopt->info[dquot->dq_type].dqi_format->qf_fmt_id);
case QFMT_VFS_OLD: qinode = dqopt->files[dquot->dq_type]->f_dentry->d_inode;
nblocks = EXT3_OLD_QFMT_BLOCKS;
break;
case QFMT_VFS_V0:
nblocks = EXT3_V0_QFMT_BLOCKS;
break;
default:
nblocks = EXT3_MAX_TRANS_DATA;
}
qinode = dqops->files[dquot->dq_type]->f_dentry->d_inode;
handle = ext3_journal_start(qinode, nblocks); handle = ext3_journal_start(qinode, nblocks);
if (IS_ERR(handle)) { if (IS_ERR(handle)) {
ret = PTR_ERR(handle); ret = PTR_ERR(handle);
...@@ -1991,6 +1994,28 @@ static int ext3_write_dquot(struct dquot *dquot) ...@@ -1991,6 +1994,28 @@ static int ext3_write_dquot(struct dquot *dquot)
out: out:
return ret; return ret;
} }
static void ext3_drop_dquot(struct inode *inode)
{
int nblocks, type;
struct quota_info *dqopt = sb_dqopt(inode->i_sb);
handle_t *handle;
for (type = 0; type < MAXQUOTAS; type++) {
if (sb_has_quota_enabled(inode->i_sb, type))
break;
}
if (type < MAXQUOTAS)
nblocks = fmt_to_blocks(dqopt->info[type].dqi_format->qf_fmt_id);
else
nblocks = 0; /* No quota => no drop */
handle = ext3_journal_start(inode, 2*nblocks);
if (IS_ERR(handle))
return;
old_drop_dquot(inode);
ext3_journal_stop(handle);
return;
}
#endif #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,
...@@ -2018,7 +2043,9 @@ static int __init init_ext3_fs(void) ...@@ -2018,7 +2043,9 @@ static int __init init_ext3_fs(void)
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
init_dquot_operations(&ext3_qops); init_dquot_operations(&ext3_qops);
old_write_dquot = ext3_qops.write_dquot; old_write_dquot = ext3_qops.write_dquot;
old_drop_dquot = ext3_qops.drop;
ext3_qops.write_dquot = ext3_write_dquot; ext3_qops.write_dquot = ext3_write_dquot;
ext3_qops.drop = ext3_drop_dquot;
#endif #endif
err = register_filesystem(&ext3_fs_type); err = register_filesystem(&ext3_fs_type);
if (err) if (err)
......
...@@ -1216,15 +1216,13 @@ EXPORT_SYMBOL(inode_needs_sync); ...@@ -1216,15 +1216,13 @@ EXPORT_SYMBOL(inode_needs_sync);
*/ */
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
/* Functions back in dquot.c */ /* Function back in dquot.c */
void put_dquot_list(struct list_head *);
int remove_inode_dquot_ref(struct inode *, int, struct list_head *); int remove_inode_dquot_ref(struct inode *, int, struct list_head *);
void remove_dquot_ref(struct super_block *sb, int type) void remove_dquot_ref(struct super_block *sb, int type, struct list_head *tofree_head)
{ {
struct inode *inode; struct inode *inode;
struct list_head *act_head; struct list_head *act_head;
LIST_HEAD(tofree_head);
if (!sb->dq_op) if (!sb->dq_op)
return; /* nothing to do */ return; /* nothing to do */
...@@ -1234,26 +1232,24 @@ void remove_dquot_ref(struct super_block *sb, int type) ...@@ -1234,26 +1232,24 @@ void remove_dquot_ref(struct super_block *sb, int type)
list_for_each(act_head, &inode_in_use) { list_for_each(act_head, &inode_in_use) {
inode = list_entry(act_head, struct inode, i_list); inode = list_entry(act_head, struct inode, i_list);
if (inode->i_sb == sb && IS_QUOTAINIT(inode)) if (inode->i_sb == sb && IS_QUOTAINIT(inode))
remove_inode_dquot_ref(inode, type, &tofree_head); remove_inode_dquot_ref(inode, type, tofree_head);
} }
list_for_each(act_head, &inode_unused) { list_for_each(act_head, &inode_unused) {
inode = list_entry(act_head, struct inode, i_list); inode = list_entry(act_head, struct inode, i_list);
if (inode->i_sb == sb && IS_QUOTAINIT(inode)) if (inode->i_sb == sb && IS_QUOTAINIT(inode))
remove_inode_dquot_ref(inode, type, &tofree_head); remove_inode_dquot_ref(inode, type, tofree_head);
} }
list_for_each(act_head, &sb->s_dirty) { list_for_each(act_head, &sb->s_dirty) {
inode = list_entry(act_head, struct inode, i_list); inode = list_entry(act_head, struct inode, i_list);
if (IS_QUOTAINIT(inode)) if (IS_QUOTAINIT(inode))
remove_inode_dquot_ref(inode, type, &tofree_head); remove_inode_dquot_ref(inode, type, tofree_head);
} }
list_for_each(act_head, &sb->s_io) { list_for_each(act_head, &sb->s_io) {
inode = list_entry(act_head, struct inode, i_list); inode = list_entry(act_head, struct inode, i_list);
if (IS_QUOTAINIT(inode)) if (IS_QUOTAINIT(inode))
remove_inode_dquot_ref(inode, type, &tofree_head); remove_inode_dquot_ref(inode, type, tofree_head);
} }
spin_unlock(&inode_lock); spin_unlock(&inode_lock);
put_dquot_list(&tofree_head);
} }
#endif #endif
......
...@@ -64,11 +64,8 @@ static __inline__ int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t ...@@ -64,11 +64,8 @@ static __inline__ int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t
if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA) if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA)
return 1; return 1;
} }
else { else
spin_lock(&dq_data_lock);
inode_add_bytes(inode, nr); inode_add_bytes(inode, nr);
spin_unlock(&dq_data_lock);
}
return 0; return 0;
} }
...@@ -87,11 +84,8 @@ static __inline__ int DQUOT_ALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) ...@@ -87,11 +84,8 @@ static __inline__ int DQUOT_ALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
if (inode->i_sb->dq_op->alloc_space(inode, nr, 0) == NO_QUOTA) if (inode->i_sb->dq_op->alloc_space(inode, nr, 0) == NO_QUOTA)
return 1; return 1;
} }
else { else
spin_lock(&dq_data_lock);
inode_add_bytes(inode, nr); inode_add_bytes(inode, nr);
spin_unlock(&dq_data_lock);
}
return 0; return 0;
} }
...@@ -117,11 +111,8 @@ static __inline__ void DQUOT_FREE_SPACE_NODIRTY(struct inode *inode, qsize_t nr) ...@@ -117,11 +111,8 @@ static __inline__ void DQUOT_FREE_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
{ {
if (sb_any_quota_enabled(inode->i_sb)) if (sb_any_quota_enabled(inode->i_sb))
inode->i_sb->dq_op->free_space(inode, nr); inode->i_sb->dq_op->free_space(inode, nr);
else { else
spin_lock(&dq_data_lock);
inode_sub_bytes(inode, nr); inode_sub_bytes(inode, nr);
spin_unlock(&dq_data_lock);
}
} }
static __inline__ void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr) static __inline__ void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr)
......
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