aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2011-11-01 10:08:06 -0400
committerChris Mason <chris.mason@oracle.com>2011-11-06 03:03:48 -0500
commit01d658f2ca3c85c1ffb20b306e30d16197000ce7 (patch)
treeeda62586bb95967a4001f8fb70e6955b531cf801
parente688b7252f784c2479d559f9f70ca8354752c5e7 (diff)
Btrfs: make sure to flush queued bios if write_cache_pages waits
write_cache_pages tries to build up a large bio to stuff down the pipe. But if it needs to wait for a page lock, it needs to make sure and send down any pending writes so we don't deadlock with anyone who has the page lock and is waiting for writeback of things inside the bio. Dave Sterba triggered this as a deadlock between the autodefrag code and the extent write_cache_pages Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r--fs/btrfs/disk-io.c13
-rw-r--r--fs/btrfs/disk-io.h2
-rw-r--r--fs/btrfs/extent_io.c14
-rw-r--r--fs/btrfs/extent_io.h3
4 files changed, 22 insertions, 10 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 51372a521167..6f58911ece0d 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -2735,7 +2735,8 @@ int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid)
2735 return ret; 2735 return ret;
2736} 2736}
2737 2737
2738int btree_lock_page_hook(struct page *page) 2738static int btree_lock_page_hook(struct page *page, void *data,
2739 void (*flush_fn)(void *))
2739{ 2740{
2740 struct inode *inode = page->mapping->host; 2741 struct inode *inode = page->mapping->host;
2741 struct btrfs_root *root = BTRFS_I(inode)->root; 2742 struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -2752,7 +2753,10 @@ int btree_lock_page_hook(struct page *page)
2752 if (!eb) 2753 if (!eb)
2753 goto out; 2754 goto out;
2754 2755
2755 btrfs_tree_lock(eb); 2756 if (!btrfs_try_tree_write_lock(eb)) {
2757 flush_fn(data);
2758 btrfs_tree_lock(eb);
2759 }
2756 btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN); 2760 btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
2757 2761
2758 if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)) { 2762 if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)) {
@@ -2767,7 +2771,10 @@ int btree_lock_page_hook(struct page *page)
2767 btrfs_tree_unlock(eb); 2771 btrfs_tree_unlock(eb);
2768 free_extent_buffer(eb); 2772 free_extent_buffer(eb);
2769out: 2773out:
2770 lock_page(page); 2774 if (!trylock_page(page)) {
2775 flush_fn(data);
2776 lock_page(page);
2777 }
2771 return 0; 2778 return 0;
2772} 2779}
2773 2780
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index bec3ea4bd67f..e678539c8519 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -83,8 +83,6 @@ int btrfs_init_log_root_tree(struct btrfs_trans_handle *trans,
83 struct btrfs_fs_info *fs_info); 83 struct btrfs_fs_info *fs_info);
84int btrfs_add_log_tree(struct btrfs_trans_handle *trans, 84int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
85 struct btrfs_root *root); 85 struct btrfs_root *root);
86int btree_lock_page_hook(struct page *page);
87
88 86
89#ifdef CONFIG_DEBUG_LOCK_ALLOC 87#ifdef CONFIG_DEBUG_LOCK_ALLOC
90void btrfs_init_lockdep(void); 88void btrfs_init_lockdep(void);
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index f284d4e5f447..b40ba75f4483 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -2613,10 +2613,16 @@ retry:
2613 * swizzled back from swapper_space to tmpfs file 2613 * swizzled back from swapper_space to tmpfs file
2614 * mapping 2614 * mapping
2615 */ 2615 */
2616 if (tree->ops && tree->ops->write_cache_pages_lock_hook) 2616 if (tree->ops &&
2617 tree->ops->write_cache_pages_lock_hook(page); 2617 tree->ops->write_cache_pages_lock_hook) {
2618 else 2618 tree->ops->write_cache_pages_lock_hook(page,
2619 lock_page(page); 2619 data, flush_fn);
2620 } else {
2621 if (!trylock_page(page)) {
2622 flush_fn(data);
2623 lock_page(page);
2624 }
2625 }
2620 2626
2621 if (unlikely(page->mapping != mapping)) { 2627 if (unlikely(page->mapping != mapping)) {
2622 unlock_page(page); 2628 unlock_page(page);
diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h
index 325a346369da..cbd4824a7c94 100644
--- a/fs/btrfs/extent_io.h
+++ b/fs/btrfs/extent_io.h
@@ -86,7 +86,8 @@ struct extent_io_ops {
86 struct extent_state *other); 86 struct extent_state *other);
87 void (*split_extent_hook)(struct inode *inode, 87 void (*split_extent_hook)(struct inode *inode,
88 struct extent_state *orig, u64 split); 88 struct extent_state *orig, u64 split);
89 int (*write_cache_pages_lock_hook)(struct page *page); 89 int (*write_cache_pages_lock_hook)(struct page *page, void *data,
90 void (*flush_fn)(void *));
90}; 91};
91 92
92struct extent_io_tree { 93struct extent_io_tree {