Commit a0a133ff authored by Jan Kara's avatar Jan Kara Committed by Linus Torvalds

[PATCH] Fix minor quota race

It fixes a possible race between quotaoff and prune_icache.  The race could
lead to some forgotten pointers to quotas in inodes leading later to BUG
when invalidating quota structures.
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 3fec7922
...@@ -112,8 +112,10 @@ ...@@ -112,8 +112,10 @@
* operations on dquots don't hold dq_lock as they copy data under dq_data_lock * operations on dquots don't hold dq_lock as they copy data under dq_data_lock
* spinlock to internal buffers before writing. * spinlock to internal buffers before writing.
* *
* Lock ordering (including journal_lock) is following: * Lock ordering (including some related VFS locks) is the following:
* dqonoff_sem > journal_lock > dqptr_sem > dquot->dq_lock > dqio_sem * i_sem > dqonoff_sem > iprune_sem > journal_lock > dqptr_sem >
* > dquot->dq_lock > dqio_sem
* i_sem on quota files is special (it's below dqio_sem)
*/ */
spinlock_t dq_list_lock = SPIN_LOCK_UNLOCKED; spinlock_t dq_list_lock = SPIN_LOCK_UNLOCKED;
...@@ -728,17 +730,18 @@ static void put_dquot_list(struct list_head *tofree_head) ...@@ -728,17 +730,18 @@ static void put_dquot_list(struct list_head *tofree_head)
} }
} }
/* Function in inode.c - remove pointers to dquots in icache */
extern void remove_dquot_ref(struct super_block *, int, struct list_head *);
/* Gather all references from inodes and drop them */ /* Gather all references from inodes and drop them */
static void drop_dquot_ref(struct super_block *sb, int type) static void drop_dquot_ref(struct super_block *sb, int type)
{ {
LIST_HEAD(tofree_head); LIST_HEAD(tofree_head);
/* We need to be guarded against prune_icache to reach all the
* inodes - otherwise some can be on the local list of prune_icache */
down(&iprune_sem);
down_write(&sb_dqopt(sb)->dqptr_sem); down_write(&sb_dqopt(sb)->dqptr_sem);
remove_dquot_ref(sb, type, &tofree_head); remove_dquot_ref(sb, type, &tofree_head);
up_write(&sb_dqopt(sb)->dqptr_sem); up_write(&sb_dqopt(sb)->dqptr_sem);
up(&iprune_sem);
put_dquot_list(&tofree_head); put_dquot_list(&tofree_head);
} }
......
...@@ -90,7 +90,7 @@ spinlock_t inode_lock = SPIN_LOCK_UNLOCKED; ...@@ -90,7 +90,7 @@ spinlock_t inode_lock = SPIN_LOCK_UNLOCKED;
* from its final dispose_list, the struct super_block they refer to * from its final dispose_list, the struct super_block they refer to
* (for inode->i_sb->s_op) may already have been freed and reused. * (for inode->i_sb->s_op) may already have been freed and reused.
*/ */
static DECLARE_MUTEX(iprune_sem); DECLARE_MUTEX(iprune_sem);
/* /*
* Statistics gathering.. * Statistics gathering..
......
...@@ -1374,6 +1374,8 @@ extern void clear_inode(struct inode *); ...@@ -1374,6 +1374,8 @@ extern void clear_inode(struct inode *);
extern void destroy_inode(struct inode *); extern void destroy_inode(struct inode *);
extern struct inode *new_inode(struct super_block *); extern struct inode *new_inode(struct super_block *);
extern int remove_suid(struct dentry *); extern int remove_suid(struct dentry *);
extern void remove_dquot_ref(struct super_block *, int, struct list_head *);
extern struct semaphore iprune_sem;
extern void __insert_inode_hash(struct inode *, unsigned long hashval); extern void __insert_inode_hash(struct inode *, unsigned long hashval);
extern void remove_inode_hash(struct inode *); extern void remove_inode_hash(struct inode *);
......
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