btrfs: fix possible deadlock by clearing __GFP_FS flag
authorMiao Xie <miaox@cn.fujitsu.com>
Sun, 27 Mar 2011 08:07:36 +0000 (16:07 +0800)
committerroot <Chris Mason chris.mason@oracle.com>
Mon, 28 Mar 2011 09:37:58 +0000 (05:37 -0400)
Using the GFP_HIGHUSER_MOVABLE flag to allocate the metadata's page may cause
deadlock.
  Task1
  open()
    ...
    btrfs_search_slot()
      ...
      btrfs_cow_block()
...
alloc_page()
  wait for reclaiming
shrink_slab()
  ...
  shrink_icache_memory()
    ...
    btrfs_evict_inode()
      ...
      btrfs_search_slot()

If the path is locked by task1, the deadlock happens.

So the btree's page cache is different with the file's page cache, it can not
allocate pages by GFP_HIGHUSER_MOVABLE flag, we must clear __GFP_FS flag in
GFP_HIGHUSER_MOVABLE flag.

Reported-by: Itaru Kitayama <kitayama@cl.bb4u.ne.jp>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/disk-io.c
fs/btrfs/inode.c

index b3fc8475870f8677d612866fa88d50eefef57176..5cf3aa7b125c2686304b4bc2ec0449af33566166 100644 (file)
@@ -1724,6 +1724,8 @@ struct btrfs_root *open_ctree(struct super_block *sb,
                goto fail_bdi;
        }
 
+       fs_info->btree_inode->i_mapping->flags &= ~__GFP_FS;
+
        INIT_RADIX_TREE(&fs_info->fs_roots_radix, GFP_ATOMIC);
        INIT_LIST_HEAD(&fs_info->trans_list);
        INIT_LIST_HEAD(&fs_info->dead_roots);
index 04babaf31a335ef295806af0cc3968be438e7cbb..06274186b290de73cf46cbcf884fce40899a9e8d 100644 (file)
@@ -2536,6 +2536,8 @@ static void btrfs_read_locked_inode(struct inode *inode)
        BTRFS_I(inode)->flags = btrfs_inode_flags(leaf, inode_item);
 
        alloc_group_block = btrfs_inode_block_group(leaf, inode_item);
+       if (location.objectid == BTRFS_FREE_SPACE_OBJECTID)
+               inode->i_mapping->flags &= ~__GFP_FS;
 
        /*
         * try to precache a NULL acl entry for files that don't have
@@ -4084,7 +4086,6 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
                BTRFS_I(inode)->root = root;
                memcpy(&BTRFS_I(inode)->location, location, sizeof(*location));
                btrfs_read_locked_inode(inode);
-
                inode_tree_add(inode);
                unlock_new_inode(inode);
                if (new)