aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Viro <aviro@redhat.com>2005-06-23 03:09:01 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-06-23 12:45:17 -0400
commit991114c6fa6a21d1fa4d544abe78592352860c82 (patch)
treecc81f871756a70a3312552409ee5cacd6625aa1d
parentf972be33ce6a08b5f096ba013c7459a3a82f5f39 (diff)
[PATCH] fix for prune_icache()/forced final iput() races
Based on analysis and a patch from Russ Weight <rweight@us.ibm.com> There is a race condition that can occur if an inode is allocated and then released (using iput) during the ->fill_super functions. The race condition is between kswapd and mount. For most filesystems this can only happen in an error path when kswapd is running concurrently. For isofs, however, the error can occur in a more common code path (which is how the bug was found). The logic here is "we want final iput() to free inode *now* instead of letting it sit in cache if fs is going down or had not quite come up". The problem is with kswapd seeing such inodes in the middle of being killed and happily taking over. The clean solution would be to tell kswapd to leave those inodes alone and let our final iput deal with them. I.e. add a new flag (I_FORCED_FREEING), set it before write_inode_now() there and make prune_icache() leave those alone. Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/inode.c16
-rw-r--r--include/linux/fs.h1
2 files changed, 11 insertions, 6 deletions
diff --git a/fs/inode.c b/fs/inode.c
index 801fe7f36280..1f9a3a2b89bc 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -500,7 +500,7 @@ repeat:
500 continue; 500 continue;
501 if (!test(inode, data)) 501 if (!test(inode, data))
502 continue; 502 continue;
503 if (inode->i_state & (I_FREEING|I_CLEAR)) { 503 if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) {
504 __wait_on_freeing_inode(inode); 504 __wait_on_freeing_inode(inode);
505 goto repeat; 505 goto repeat;
506 } 506 }
@@ -525,7 +525,7 @@ repeat:
525 continue; 525 continue;
526 if (inode->i_sb != sb) 526 if (inode->i_sb != sb)
527 continue; 527 continue;
528 if (inode->i_state & (I_FREEING|I_CLEAR)) { 528 if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) {
529 __wait_on_freeing_inode(inode); 529 __wait_on_freeing_inode(inode);
530 goto repeat; 530 goto repeat;
531 } 531 }
@@ -727,7 +727,7 @@ EXPORT_SYMBOL(iunique);
727struct inode *igrab(struct inode *inode) 727struct inode *igrab(struct inode *inode)
728{ 728{
729 spin_lock(&inode_lock); 729 spin_lock(&inode_lock);
730 if (!(inode->i_state & I_FREEING)) 730 if (!(inode->i_state & (I_FREEING|I_WILL_FREE)))
731 __iget(inode); 731 __iget(inode);
732 else 732 else
733 /* 733 /*
@@ -1024,17 +1024,21 @@ static void generic_forget_inode(struct inode *inode)
1024 if (!(inode->i_state & (I_DIRTY|I_LOCK))) 1024 if (!(inode->i_state & (I_DIRTY|I_LOCK)))
1025 list_move(&inode->i_list, &inode_unused); 1025 list_move(&inode->i_list, &inode_unused);
1026 inodes_stat.nr_unused++; 1026 inodes_stat.nr_unused++;
1027 spin_unlock(&inode_lock); 1027 if (!sb || (sb->s_flags & MS_ACTIVE)) {
1028 if (!sb || (sb->s_flags & MS_ACTIVE)) 1028 spin_unlock(&inode_lock);
1029 return; 1029 return;
1030 }
1031 inode->i_state |= I_WILL_FREE;
1032 spin_unlock(&inode_lock);
1030 write_inode_now(inode, 1); 1033 write_inode_now(inode, 1);
1031 spin_lock(&inode_lock); 1034 spin_lock(&inode_lock);
1035 inode->i_state &= ~I_WILL_FREE;
1032 inodes_stat.nr_unused--; 1036 inodes_stat.nr_unused--;
1033 hlist_del_init(&inode->i_hash); 1037 hlist_del_init(&inode->i_hash);
1034 } 1038 }
1035 list_del_init(&inode->i_list); 1039 list_del_init(&inode->i_list);
1036 list_del_init(&inode->i_sb_list); 1040 list_del_init(&inode->i_sb_list);
1037 inode->i_state|=I_FREEING; 1041 inode->i_state |= I_FREEING;
1038 inodes_stat.nr_inodes--; 1042 inodes_stat.nr_inodes--;
1039 spin_unlock(&inode_lock); 1043 spin_unlock(&inode_lock);
1040 if (inode->i_data.nrpages) 1044 if (inode->i_data.nrpages)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index e5a8db00df29..3622e952e98c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1025,6 +1025,7 @@ struct super_operations {
1025#define I_FREEING 16 1025#define I_FREEING 16
1026#define I_CLEAR 32 1026#define I_CLEAR 32
1027#define I_NEW 64 1027#define I_NEW 64
1028#define I_WILL_FREE 128
1028 1029
1029#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES) 1030#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
1030 1031