diff options
author | Wu Fengguang <fengguang.wu@intel.com> | 2009-04-03 04:35:14 +0000 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-05-02 10:24:01 -0700 |
commit | cf42ed344ad0d6c0fc12e29b817823af106b4720 (patch) | |
tree | 0a91d42f07c379fd2fd110b19b64e9cdd60931ad /fs/dquot.c | |
parent | e655a1eaa988bc55739f47d62ab0658c1364458a (diff) |
vfs: skip I_CLEAR state inodes
upstream commit: b6fac63cc1f52ec27f29fe6c6c8494a2ffac33fd
clear_inode() will switch inode state from I_FREEING to I_CLEAR, and do so
_outside_ of inode_lock. So any I_FREEING testing is incomplete without a
coupled testing of I_CLEAR.
So add I_CLEAR tests to drop_pagecache_sb(), generic_sync_sb_inodes() and
add_dquot_ref().
Masayoshi MIZUMA discovered the bug in drop_pagecache_sb() and Jan Kara
reminds fixing the other two cases.
Masayoshi MIZUMA has a nice panic flow:
=====================================================================
[process A] | [process B]
| |
| prune_icache() | drop_pagecache()
| spin_lock(&inode_lock) | drop_pagecache_sb()
| inode->i_state |= I_FREEING; | |
| spin_unlock(&inode_lock) | V
| | | spin_lock(&inode_lock)
| V | |
| dispose_list() | |
| list_del() | |
| clear_inode() | |
| inode->i_state = I_CLEAR | |
| | | V
| | | if (inode->i_state & (I_FREEING|I_WILL_FREE))
| | | continue; <==== NOT MATCH
| | |
| | | (DANGER from here on! Accessing disposing inode!)
| | |
| | | __iget()
| | | list_move() <===== PANIC on poisoned list !!
V V |
(time)
=====================================================================
Reported-by: Masayoshi MIZUMA <m.mizuma@jp.fujitsu.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Wu Fengguang <fengguang.wu@intel.com>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
[chrisw: backport to 2.6.29]
Signed-off-by: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/dquot.c')
-rw-r--r-- | fs/dquot.c | 2 |
1 files changed, 1 insertions, 1 deletions
diff --git a/fs/dquot.c b/fs/dquot.c index 8ec4d6cc763..150fc961da4 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -726,7 +726,7 @@ static void add_dquot_ref(struct super_block *sb, int type) continue; if (!dqinit_needed(inode, type)) continue; - if (inode->i_state & (I_FREEING|I_WILL_FREE)) + if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) continue; __iget(inode); |