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: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
@@ -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)
|
|||||||
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,
|
|||||||
__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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 = {
|
|||||||
.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,
|
||||||
|
Reference in New Issue
Block a user