Commit 784b4e29 authored by Chris Mason's avatar Chris Mason

Btrfs: add migrate page for metadata inode

Migrate page will directly call the btrfs btree writepage function,
which isn't actually allowed.

Our writepage assumes that you have locked the extent_buffer and
flagged the block as written.  Without doing these steps, we can
corrupt metadata blocks.

A later commit will remove the btree writepage function since
it is really only safely used internally by btrfs.  We
use writepages for everything else.
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 6418c961
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/crc32c.h> #include <linux/crc32c.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/migrate.h>
#include "compat.h" #include "compat.h"
#include "ctree.h" #include "ctree.h"
#include "disk-io.h" #include "disk-io.h"
...@@ -355,6 +356,8 @@ static int csum_dirty_buffer(struct btrfs_root *root, struct page *page) ...@@ -355,6 +356,8 @@ static int csum_dirty_buffer(struct btrfs_root *root, struct page *page)
ret = btree_read_extent_buffer_pages(root, eb, start + PAGE_CACHE_SIZE, ret = btree_read_extent_buffer_pages(root, eb, start + PAGE_CACHE_SIZE,
btrfs_header_generation(eb)); btrfs_header_generation(eb));
BUG_ON(ret); BUG_ON(ret);
WARN_ON(!btrfs_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN));
found_start = btrfs_header_bytenr(eb); found_start = btrfs_header_bytenr(eb);
if (found_start != start) { if (found_start != start) {
WARN_ON(1); WARN_ON(1);
...@@ -693,6 +696,26 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio, ...@@ -693,6 +696,26 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
__btree_submit_bio_done); __btree_submit_bio_done);
} }
static int btree_migratepage(struct address_space *mapping,
struct page *newpage, struct page *page)
{
/*
* we can't safely write a btree page from here,
* we haven't done the locking hook
*/
if (PageDirty(page))
return -EAGAIN;
/*
* Buffers may be managed in a filesystem specific way.
* We must have no buffers or drop them.
*/
if (page_has_private(page) &&
!try_to_release_page(page, GFP_KERNEL))
return -EAGAIN;
return migrate_page(mapping, newpage, page);
}
static int btree_writepage(struct page *page, struct writeback_control *wbc) static int btree_writepage(struct page *page, struct writeback_control *wbc)
{ {
struct extent_io_tree *tree; struct extent_io_tree *tree;
...@@ -707,8 +730,7 @@ static int btree_writepage(struct page *page, struct writeback_control *wbc) ...@@ -707,8 +730,7 @@ static int btree_writepage(struct page *page, struct writeback_control *wbc)
} }
redirty_page_for_writepage(wbc, page); redirty_page_for_writepage(wbc, page);
eb = btrfs_find_tree_block(root, page_offset(page), eb = btrfs_find_tree_block(root, page_offset(page), PAGE_CACHE_SIZE);
PAGE_CACHE_SIZE);
WARN_ON(!eb); WARN_ON(!eb);
was_dirty = test_and_set_bit(EXTENT_BUFFER_DIRTY, &eb->bflags); was_dirty = test_and_set_bit(EXTENT_BUFFER_DIRTY, &eb->bflags);
...@@ -799,6 +821,7 @@ static const struct address_space_operations btree_aops = { ...@@ -799,6 +821,7 @@ static const struct address_space_operations btree_aops = {
.releasepage = btree_releasepage, .releasepage = btree_releasepage,
.invalidatepage = btree_invalidatepage, .invalidatepage = btree_invalidatepage,
.sync_page = block_sync_page, .sync_page = block_sync_page,
.migratepage = btree_migratepage,
}; };
int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize, int readahead_tree_block(struct btrfs_root *root, u64 bytenr, u32 blocksize,
......
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