ext3/4 with synchronous writes gets wedged by Postfix
OK, that's probably the easiest way to do that, as much as I don't like it... Since iget() et.al. will not accept I_FREEING (will wait to go away and restart), and since we'd better have serialization between new/free on fs data structures anyway, we can afford simply skipping I_FREEING et.al. in insert_inode_locked(). We do that from new_inode, so it won't race with free_inode in any interesting ways and it won't race with iget (of any origin; nfsd or in case of fs corruption a lookup) since both still will wait for I_LOCK. Reviewed-by: "Theodore Ts'o" <tytso@mit.edu> Acked-by: Jan Kara <jack@suse.cz> Tested-by: David Watson <dbwatson@ukfsn.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
31
fs/inode.c
31
fs/inode.c
@@ -1053,13 +1053,22 @@ int insert_inode_locked(struct inode *inode)
|
|||||||
struct super_block *sb = inode->i_sb;
|
struct super_block *sb = inode->i_sb;
|
||||||
ino_t ino = inode->i_ino;
|
ino_t ino = inode->i_ino;
|
||||||
struct hlist_head *head = inode_hashtable + hash(sb, ino);
|
struct hlist_head *head = inode_hashtable + hash(sb, ino);
|
||||||
struct inode *old;
|
|
||||||
|
|
||||||
inode->i_state |= I_LOCK|I_NEW;
|
inode->i_state |= I_LOCK|I_NEW;
|
||||||
while (1) {
|
while (1) {
|
||||||
|
struct hlist_node *node;
|
||||||
|
struct inode *old = NULL;
|
||||||
spin_lock(&inode_lock);
|
spin_lock(&inode_lock);
|
||||||
old = find_inode_fast(sb, head, ino);
|
hlist_for_each_entry(old, node, head, i_hash) {
|
||||||
if (likely(!old)) {
|
if (old->i_ino != ino)
|
||||||
|
continue;
|
||||||
|
if (old->i_sb != sb)
|
||||||
|
continue;
|
||||||
|
if (old->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE))
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (likely(!node)) {
|
||||||
hlist_add_head(&inode->i_hash, head);
|
hlist_add_head(&inode->i_hash, head);
|
||||||
spin_unlock(&inode_lock);
|
spin_unlock(&inode_lock);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1081,14 +1090,24 @@ int insert_inode_locked4(struct inode *inode, unsigned long hashval,
|
|||||||
{
|
{
|
||||||
struct super_block *sb = inode->i_sb;
|
struct super_block *sb = inode->i_sb;
|
||||||
struct hlist_head *head = inode_hashtable + hash(sb, hashval);
|
struct hlist_head *head = inode_hashtable + hash(sb, hashval);
|
||||||
struct inode *old;
|
|
||||||
|
|
||||||
inode->i_state |= I_LOCK|I_NEW;
|
inode->i_state |= I_LOCK|I_NEW;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
struct hlist_node *node;
|
||||||
|
struct inode *old = NULL;
|
||||||
|
|
||||||
spin_lock(&inode_lock);
|
spin_lock(&inode_lock);
|
||||||
old = find_inode(sb, head, test, data);
|
hlist_for_each_entry(old, node, head, i_hash) {
|
||||||
if (likely(!old)) {
|
if (old->i_sb != sb)
|
||||||
|
continue;
|
||||||
|
if (!test(old, data))
|
||||||
|
continue;
|
||||||
|
if (old->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE))
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (likely(!node)) {
|
||||||
hlist_add_head(&inode->i_hash, head);
|
hlist_add_head(&inode->i_hash, head);
|
||||||
spin_unlock(&inode_lock);
|
spin_unlock(&inode_lock);
|
||||||
return 0;
|
return 0;
|
||||||
|
Reference in New Issue
Block a user