diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2005-07-12 16:58:10 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-12 19:00:59 -0400 |
commit | 168a9fd6a1bf91041adf9909f6c72cf747f0ca8c (patch) | |
tree | 65b4dc843f34f0837b10f4fbbc1763f5aae87b7b /fs | |
parent | 3b6bfcdb116f2cc2cab921fcac6d39d4022952d2 (diff) |
[PATCH] __wait_on_freeing_inode fix
This patch fixes queer behavior in __wait_on_freeing_inode().
If I_LOCK was not set it called yield(), effectively busy waiting for the
removal of the inode from the hash. This change was introduced within
"[PATCH] eliminate inode waitqueue hashtable" Changeset 1.1938.166.16 last
october by wli.
The solution is to restore the old behavior, of unconditionally waiting on
the waitqueue. It doesn't matter if I_LOCK is not set initally, the task
will go to sleep, and wake up when wake_up_inode() is called from
generic_delete_inode() after removing the inode from the hash chain.
Comment is also updated to better reflect current behavior.
This condition is very hard to trigger normally (simultaneous clear_inode()
with iget()) so probably only heavy stress testing can reveal any change of
behavior.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/inode.c | 26 |
1 files changed, 9 insertions, 17 deletions
diff --git a/fs/inode.c b/fs/inode.c index 6d695037a0a3..0116d06731c2 100644 --- a/fs/inode.c +++ b/fs/inode.c | |||
@@ -1244,29 +1244,21 @@ int inode_wait(void *word) | |||
1244 | } | 1244 | } |
1245 | 1245 | ||
1246 | /* | 1246 | /* |
1247 | * If we try to find an inode in the inode hash while it is being deleted, we | 1247 | * If we try to find an inode in the inode hash while it is being |
1248 | * have to wait until the filesystem completes its deletion before reporting | 1248 | * deleted, we have to wait until the filesystem completes its |
1249 | * that it isn't found. This is because iget will immediately call | 1249 | * deletion before reporting that it isn't found. This function waits |
1250 | * ->read_inode, and we want to be sure that evidence of the deletion is found | 1250 | * until the deletion _might_ have completed. Callers are responsible |
1251 | * by ->read_inode. | 1251 | * to recheck inode state. |
1252 | * | ||
1253 | * It doesn't matter if I_LOCK is not set initially, a call to | ||
1254 | * wake_up_inode() after removing from the hash list will DTRT. | ||
1255 | * | ||
1252 | * This is called with inode_lock held. | 1256 | * This is called with inode_lock held. |
1253 | */ | 1257 | */ |
1254 | static void __wait_on_freeing_inode(struct inode *inode) | 1258 | static void __wait_on_freeing_inode(struct inode *inode) |
1255 | { | 1259 | { |
1256 | wait_queue_head_t *wq; | 1260 | wait_queue_head_t *wq; |
1257 | DEFINE_WAIT_BIT(wait, &inode->i_state, __I_LOCK); | 1261 | DEFINE_WAIT_BIT(wait, &inode->i_state, __I_LOCK); |
1258 | |||
1259 | /* | ||
1260 | * I_FREEING and I_CLEAR are cleared in process context under | ||
1261 | * inode_lock, so we have to give the tasks who would clear them | ||
1262 | * a chance to run and acquire inode_lock. | ||
1263 | */ | ||
1264 | if (!(inode->i_state & I_LOCK)) { | ||
1265 | spin_unlock(&inode_lock); | ||
1266 | yield(); | ||
1267 | spin_lock(&inode_lock); | ||
1268 | return; | ||
1269 | } | ||
1270 | wq = bit_waitqueue(&inode->i_state, __I_LOCK); | 1262 | wq = bit_waitqueue(&inode->i_state, __I_LOCK); |
1271 | prepare_to_wait(wq, &wait.wait, TASK_UNINTERRUPTIBLE); | 1263 | prepare_to_wait(wq, &wait.wait, TASK_UNINTERRUPTIBLE); |
1272 | spin_unlock(&inode_lock); | 1264 | spin_unlock(&inode_lock); |