Commit 01d658f2 authored by Chris Mason's avatar Chris Mason

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: default avatarChris Mason <chris.mason@oracle.com>
parent e688b725
...@@ -2735,7 +2735,8 @@ int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid) ...@@ -2735,7 +2735,8 @@ int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid)
return ret; return ret;
} }
int btree_lock_page_hook(struct page *page) static int btree_lock_page_hook(struct page *page, void *data,
void (*flush_fn)(void *))
{ {
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
...@@ -2752,7 +2753,10 @@ int btree_lock_page_hook(struct page *page) ...@@ -2752,7 +2753,10 @@ int btree_lock_page_hook(struct page *page)
if (!eb) if (!eb)
goto out; goto out;
btrfs_tree_lock(eb); if (!btrfs_try_tree_write_lock(eb)) {
flush_fn(data);
btrfs_tree_lock(eb);
}
btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN); btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)) { if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)) {
...@@ -2767,7 +2771,10 @@ int btree_lock_page_hook(struct page *page) ...@@ -2767,7 +2771,10 @@ int btree_lock_page_hook(struct page *page)
btrfs_tree_unlock(eb); btrfs_tree_unlock(eb);
free_extent_buffer(eb); free_extent_buffer(eb);
out: out:
lock_page(page); if (!trylock_page(page)) {
flush_fn(data);
lock_page(page);
}
return 0; return 0;
} }
......
...@@ -83,8 +83,6 @@ int btrfs_init_log_root_tree(struct btrfs_trans_handle *trans, ...@@ -83,8 +83,6 @@ int btrfs_init_log_root_tree(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info); struct btrfs_fs_info *fs_info);
int btrfs_add_log_tree(struct btrfs_trans_handle *trans, int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
struct btrfs_root *root); struct btrfs_root *root);
int btree_lock_page_hook(struct page *page);
#ifdef CONFIG_DEBUG_LOCK_ALLOC #ifdef CONFIG_DEBUG_LOCK_ALLOC
void btrfs_init_lockdep(void); void btrfs_init_lockdep(void);
......
...@@ -2613,10 +2613,16 @@ static int extent_write_cache_pages(struct extent_io_tree *tree, ...@@ -2613,10 +2613,16 @@ static int extent_write_cache_pages(struct extent_io_tree *tree,
* swizzled back from swapper_space to tmpfs file * swizzled back from swapper_space to tmpfs file
* mapping * mapping
*/ */
if (tree->ops && tree->ops->write_cache_pages_lock_hook) if (tree->ops &&
tree->ops->write_cache_pages_lock_hook(page); tree->ops->write_cache_pages_lock_hook) {
else tree->ops->write_cache_pages_lock_hook(page,
lock_page(page); data, flush_fn);
} else {
if (!trylock_page(page)) {
flush_fn(data);
lock_page(page);
}
}
if (unlikely(page->mapping != mapping)) { if (unlikely(page->mapping != mapping)) {
unlock_page(page); unlock_page(page);
......
...@@ -86,7 +86,8 @@ struct extent_io_ops { ...@@ -86,7 +86,8 @@ struct extent_io_ops {
struct extent_state *other); struct extent_state *other);
void (*split_extent_hook)(struct inode *inode, void (*split_extent_hook)(struct inode *inode,
struct extent_state *orig, u64 split); struct extent_state *orig, u64 split);
int (*write_cache_pages_lock_hook)(struct page *page); int (*write_cache_pages_lock_hook)(struct page *page, void *data,
void (*flush_fn)(void *));
}; };
struct extent_io_tree { struct extent_io_tree {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment