Btrfs: Add file data csums back in via hooks in the extent map code
Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
parent
95e0528919
commit
07157aacb1
|
@ -57,7 +57,7 @@ void extent_map_tree_init(struct extent_map_tree *tree,
|
||||||
{
|
{
|
||||||
tree->map.rb_node = NULL;
|
tree->map.rb_node = NULL;
|
||||||
tree->state.rb_node = NULL;
|
tree->state.rb_node = NULL;
|
||||||
tree->fill_delalloc = NULL;
|
tree->ops = NULL;
|
||||||
rwlock_init(&tree->lock);
|
rwlock_init(&tree->lock);
|
||||||
tree->mapping = mapping;
|
tree->mapping = mapping;
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,7 @@ struct extent_state *alloc_extent_state(gfp_t mask)
|
||||||
return state;
|
return state;
|
||||||
state->state = 0;
|
state->state = 0;
|
||||||
state->in_tree = 0;
|
state->in_tree = 0;
|
||||||
|
state->private = 0;
|
||||||
atomic_set(&state->refs, 1);
|
atomic_set(&state->refs, 1);
|
||||||
init_waitqueue_head(&state->wq);
|
init_waitqueue_head(&state->wq);
|
||||||
return state;
|
return state;
|
||||||
|
@ -1034,6 +1035,61 @@ int unlock_range(struct extent_map_tree *tree, u64 start, u64 end)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(unlock_range);
|
EXPORT_SYMBOL(unlock_range);
|
||||||
|
|
||||||
|
int set_state_private(struct extent_map_tree *tree, u64 start, u64 private)
|
||||||
|
{
|
||||||
|
struct rb_node *node;
|
||||||
|
struct extent_state *state;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
write_lock_irq(&tree->lock);
|
||||||
|
/*
|
||||||
|
* this search will find all the extents that end after
|
||||||
|
* our range starts.
|
||||||
|
*/
|
||||||
|
node = tree_search(&tree->state, start);
|
||||||
|
if (!node || IS_ERR(node)) {
|
||||||
|
ret = -ENOENT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
state = rb_entry(node, struct extent_state, rb_node);
|
||||||
|
if (state->start != start) {
|
||||||
|
ret = -ENOENT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
state->private = private;
|
||||||
|
out:
|
||||||
|
write_unlock_irq(&tree->lock);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_state_private(struct extent_map_tree *tree, u64 start, u64 *private)
|
||||||
|
{
|
||||||
|
struct rb_node *node;
|
||||||
|
struct extent_state *state;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
read_lock_irq(&tree->lock);
|
||||||
|
/*
|
||||||
|
* this search will find all the extents that end after
|
||||||
|
* our range starts.
|
||||||
|
*/
|
||||||
|
node = tree_search(&tree->state, start);
|
||||||
|
if (!node || IS_ERR(node)) {
|
||||||
|
ret = -ENOENT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
state = rb_entry(node, struct extent_state, rb_node);
|
||||||
|
if (state->start != start) {
|
||||||
|
ret = -ENOENT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
*private = state->private;
|
||||||
|
out:
|
||||||
|
read_unlock_irq(&tree->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* searches a range in the state tree for a given mask.
|
* searches a range in the state tree for a given mask.
|
||||||
* If 'filled' == 1, this returns 1 only if ever extent in the tree
|
* If 'filled' == 1, this returns 1 only if ever extent in the tree
|
||||||
|
@ -1185,12 +1241,13 @@ static int end_bio_extent_writepage(struct bio *bio,
|
||||||
static int end_bio_extent_readpage(struct bio *bio,
|
static int end_bio_extent_readpage(struct bio *bio,
|
||||||
unsigned int bytes_done, int err)
|
unsigned int bytes_done, int err)
|
||||||
{
|
{
|
||||||
const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
|
int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
|
||||||
struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
|
struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
|
||||||
struct extent_map_tree *tree = bio->bi_private;
|
struct extent_map_tree *tree = bio->bi_private;
|
||||||
u64 start;
|
u64 start;
|
||||||
u64 end;
|
u64 end;
|
||||||
int whole_page;
|
int whole_page;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (bio->bi_size)
|
if (bio->bi_size)
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1208,6 +1265,11 @@ static int end_bio_extent_readpage(struct bio *bio,
|
||||||
if (--bvec >= bio->bi_io_vec)
|
if (--bvec >= bio->bi_io_vec)
|
||||||
prefetchw(&bvec->bv_page->flags);
|
prefetchw(&bvec->bv_page->flags);
|
||||||
|
|
||||||
|
if (uptodate && tree->ops && tree->ops->readpage_end_io_hook) {
|
||||||
|
ret = tree->ops->readpage_end_io_hook(page, start, end);
|
||||||
|
if (ret)
|
||||||
|
uptodate = 0;
|
||||||
|
}
|
||||||
if (uptodate) {
|
if (uptodate) {
|
||||||
set_extent_uptodate(tree, start, end, GFP_ATOMIC);
|
set_extent_uptodate(tree, start, end, GFP_ATOMIC);
|
||||||
if (whole_page)
|
if (whole_page)
|
||||||
|
@ -1388,9 +1450,16 @@ int extent_read_full_page(struct extent_map_tree *tree, struct page *page,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = submit_extent_page(READ, tree, page,
|
ret = 0;
|
||||||
sector, iosize, page_offset, bdev,
|
if (tree->ops && tree->ops->readpage_io_hook) {
|
||||||
end_bio_extent_readpage);
|
ret = tree->ops->readpage_io_hook(page, cur,
|
||||||
|
cur + iosize - 1);
|
||||||
|
}
|
||||||
|
if (!ret) {
|
||||||
|
ret = submit_extent_page(READ, tree, page,
|
||||||
|
sector, iosize, page_offset,
|
||||||
|
bdev, end_bio_extent_readpage);
|
||||||
|
}
|
||||||
if (ret)
|
if (ret)
|
||||||
SetPageError(page);
|
SetPageError(page);
|
||||||
cur = cur + iosize;
|
cur = cur + iosize;
|
||||||
|
@ -1462,7 +1531,7 @@ int extent_write_full_page(struct extent_map_tree *tree, struct page *page,
|
||||||
&delalloc_end,
|
&delalloc_end,
|
||||||
128 * 1024 * 1024);
|
128 * 1024 * 1024);
|
||||||
if (nr_delalloc) {
|
if (nr_delalloc) {
|
||||||
tree->fill_delalloc(inode, start, delalloc_end);
|
tree->ops->fill_delalloc(inode, start, delalloc_end);
|
||||||
if (delalloc_end >= page_end + 1) {
|
if (delalloc_end >= page_end + 1) {
|
||||||
clear_extent_bit(tree, page_end + 1, delalloc_end,
|
clear_extent_bit(tree, page_end + 1, delalloc_end,
|
||||||
EXTENT_LOCKED | EXTENT_DELALLOC,
|
EXTENT_LOCKED | EXTENT_DELALLOC,
|
||||||
|
@ -1528,12 +1597,17 @@ int extent_write_full_page(struct extent_map_tree *tree, struct page *page,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
clear_extent_dirty(tree, cur, cur + iosize - 1, GFP_NOFS);
|
clear_extent_dirty(tree, cur, cur + iosize - 1, GFP_NOFS);
|
||||||
set_range_writeback(tree, cur, cur + iosize - 1);
|
ret = tree->ops->writepage_io_hook(page, cur, cur + iosize - 1);
|
||||||
ret = submit_extent_page(WRITE, tree, page,
|
|
||||||
sector, iosize, page_offset, bdev,
|
|
||||||
end_bio_extent_writepage);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
SetPageError(page);
|
SetPageError(page);
|
||||||
|
else {
|
||||||
|
set_range_writeback(tree, cur, cur + iosize - 1);
|
||||||
|
ret = submit_extent_page(WRITE, tree, page, sector,
|
||||||
|
iosize, page_offset, bdev,
|
||||||
|
end_bio_extent_writepage);
|
||||||
|
if (ret)
|
||||||
|
SetPageError(page);
|
||||||
|
}
|
||||||
cur = cur + iosize;
|
cur = cur + iosize;
|
||||||
page_offset += iosize;
|
page_offset += iosize;
|
||||||
nr++;
|
nr++;
|
||||||
|
|
|
@ -6,12 +6,19 @@
|
||||||
#define EXTENT_MAP_INLINE (u64)-2
|
#define EXTENT_MAP_INLINE (u64)-2
|
||||||
#define EXTENT_MAP_DELALLOC (u64)-1
|
#define EXTENT_MAP_DELALLOC (u64)-1
|
||||||
|
|
||||||
|
struct extent_map_ops {
|
||||||
|
int (*fill_delalloc)(struct inode *inode, u64 start, u64 end);
|
||||||
|
int (*writepage_io_hook)(struct page *page, u64 start, u64 end);
|
||||||
|
int (*readpage_io_hook)(struct page *page, u64 start, u64 end);
|
||||||
|
int (*readpage_end_io_hook)(struct page *page, u64 start, u64 end);
|
||||||
|
};
|
||||||
|
|
||||||
struct extent_map_tree {
|
struct extent_map_tree {
|
||||||
struct rb_root map;
|
struct rb_root map;
|
||||||
struct rb_root state;
|
struct rb_root state;
|
||||||
struct address_space *mapping;
|
struct address_space *mapping;
|
||||||
rwlock_t lock;
|
rwlock_t lock;
|
||||||
int (*fill_delalloc)(struct inode *inode, u64 start, u64 end);
|
struct extent_map_ops *ops;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* note, this must start with the same fields as fs/extent_map.c:tree_entry */
|
/* note, this must start with the same fields as fs/extent_map.c:tree_entry */
|
||||||
|
@ -36,6 +43,10 @@ struct extent_state {
|
||||||
wait_queue_head_t wq;
|
wait_queue_head_t wq;
|
||||||
atomic_t refs;
|
atomic_t refs;
|
||||||
unsigned long state;
|
unsigned long state;
|
||||||
|
|
||||||
|
/* for use by the FS */
|
||||||
|
u64 private;
|
||||||
|
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,4 +100,6 @@ int extent_commit_write(struct extent_map_tree *tree,
|
||||||
struct inode *inode, struct page *page,
|
struct inode *inode, struct page *page,
|
||||||
unsigned from, unsigned to);
|
unsigned from, unsigned to);
|
||||||
int set_range_dirty(struct extent_map_tree *tree, u64 start, u64 end);
|
int set_range_dirty(struct extent_map_tree *tree, u64 start, u64 end);
|
||||||
|
int set_state_private(struct extent_map_tree *tree, u64 start, u64 private);
|
||||||
|
int get_state_private(struct extent_map_tree *tree, u64 start, u64 *private);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -52,6 +52,7 @@ static struct inode_operations btrfs_file_inode_operations;
|
||||||
static struct address_space_operations btrfs_aops;
|
static struct address_space_operations btrfs_aops;
|
||||||
static struct address_space_operations btrfs_symlink_aops;
|
static struct address_space_operations btrfs_symlink_aops;
|
||||||
static struct file_operations btrfs_dir_file_operations;
|
static struct file_operations btrfs_dir_file_operations;
|
||||||
|
static struct extent_map_ops btrfs_extent_map_ops;
|
||||||
|
|
||||||
static struct kmem_cache *btrfs_inode_cachep;
|
static struct kmem_cache *btrfs_inode_cachep;
|
||||||
struct kmem_cache *btrfs_trans_handle_cachep;
|
struct kmem_cache *btrfs_trans_handle_cachep;
|
||||||
|
@ -103,6 +104,90 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int btrfs_writepage_io_hook(struct page *page, u64 start, u64 end)
|
||||||
|
{
|
||||||
|
struct inode *inode = page->mapping->host;
|
||||||
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
|
struct btrfs_trans_handle *trans;
|
||||||
|
char *kaddr;
|
||||||
|
int ret;
|
||||||
|
u64 page_start = page->index << PAGE_CACHE_SHIFT;
|
||||||
|
size_t offset = start - page_start;
|
||||||
|
|
||||||
|
mutex_lock(&root->fs_info->fs_mutex);
|
||||||
|
trans = btrfs_start_transaction(root, 1);
|
||||||
|
btrfs_set_trans_block_group(trans, inode);
|
||||||
|
kaddr = kmap(page);
|
||||||
|
btrfs_csum_file_block(trans, root, inode->i_ino,
|
||||||
|
start, kaddr + offset, end - start + 1);
|
||||||
|
kunmap(page);
|
||||||
|
ret = btrfs_end_transaction(trans, root);
|
||||||
|
BUG_ON(ret);
|
||||||
|
mutex_unlock(&root->fs_info->fs_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int btrfs_readpage_io_hook(struct page *page, u64 start, u64 end)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct inode *inode = page->mapping->host;
|
||||||
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
|
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
|
||||||
|
struct btrfs_csum_item *item;
|
||||||
|
struct btrfs_path *path = NULL;
|
||||||
|
u64 private;
|
||||||
|
|
||||||
|
mutex_lock(&root->fs_info->fs_mutex);
|
||||||
|
path = btrfs_alloc_path();
|
||||||
|
item = btrfs_lookup_csum(NULL, root, path, inode->i_ino, start, 0);
|
||||||
|
if (IS_ERR(item)) {
|
||||||
|
ret = PTR_ERR(item);
|
||||||
|
/* a csum that isn't present is a preallocated region. */
|
||||||
|
if (ret == -ENOENT || ret == -EFBIG)
|
||||||
|
ret = 0;
|
||||||
|
private = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
memcpy((char *)&private, &item->csum, BTRFS_CRC32_SIZE);
|
||||||
|
set_state_private(em_tree, start, private);
|
||||||
|
out:
|
||||||
|
if (path)
|
||||||
|
btrfs_free_path(path);
|
||||||
|
mutex_unlock(&root->fs_info->fs_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int btrfs_readpage_end_io_hook(struct page *page, u64 start, u64 end)
|
||||||
|
{
|
||||||
|
char csum[BTRFS_CRC32_SIZE];
|
||||||
|
size_t offset = start - (page->index << PAGE_CACHE_SHIFT);
|
||||||
|
struct inode *inode = page->mapping->host;
|
||||||
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||||
|
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
|
||||||
|
char *kaddr;
|
||||||
|
u64 private;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = get_state_private(em_tree, start, &private);
|
||||||
|
kaddr = kmap_atomic(page, KM_IRQ0);
|
||||||
|
if (ret) {
|
||||||
|
goto zeroit;
|
||||||
|
}
|
||||||
|
ret = btrfs_csum_data(root, kaddr + offset, end - start + 1, csum);
|
||||||
|
BUG_ON(ret);
|
||||||
|
if (memcmp(csum, &private, BTRFS_CRC32_SIZE)) {
|
||||||
|
goto zeroit;
|
||||||
|
}
|
||||||
|
kunmap_atomic(kaddr, KM_IRQ0);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
zeroit:
|
||||||
|
printk("btrfs csum failed ino %lu off %llu\n",
|
||||||
|
page->mapping->host->i_ino, (unsigned long long)start);
|
||||||
|
memset(kaddr + offset, 1, end - start + 1); flush_dcache_page(page);
|
||||||
|
kunmap_atomic(kaddr, KM_IRQ0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void btrfs_read_locked_inode(struct inode *inode)
|
void btrfs_read_locked_inode(struct inode *inode)
|
||||||
{
|
{
|
||||||
|
@ -155,7 +240,7 @@ void btrfs_read_locked_inode(struct inode *inode)
|
||||||
switch (inode->i_mode & S_IFMT) {
|
switch (inode->i_mode & S_IFMT) {
|
||||||
case S_IFREG:
|
case S_IFREG:
|
||||||
inode->i_mapping->a_ops = &btrfs_aops;
|
inode->i_mapping->a_ops = &btrfs_aops;
|
||||||
BTRFS_I(inode)->extent_tree.fill_delalloc = run_delalloc_range;
|
BTRFS_I(inode)->extent_tree.ops = &btrfs_extent_map_ops;
|
||||||
inode->i_fop = &btrfs_file_operations;
|
inode->i_fop = &btrfs_file_operations;
|
||||||
inode->i_op = &btrfs_file_inode_operations;
|
inode->i_op = &btrfs_file_inode_operations;
|
||||||
break;
|
break;
|
||||||
|
@ -1148,7 +1233,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
|
||||||
inode->i_op = &btrfs_file_inode_operations;
|
inode->i_op = &btrfs_file_inode_operations;
|
||||||
extent_map_tree_init(&BTRFS_I(inode)->extent_tree,
|
extent_map_tree_init(&BTRFS_I(inode)->extent_tree,
|
||||||
inode->i_mapping, GFP_NOFS);
|
inode->i_mapping, GFP_NOFS);
|
||||||
BTRFS_I(inode)->extent_tree.fill_delalloc = run_delalloc_range;
|
BTRFS_I(inode)->extent_tree.ops = &btrfs_extent_map_ops;
|
||||||
}
|
}
|
||||||
dir->i_sb->s_dirt = 1;
|
dir->i_sb->s_dirt = 1;
|
||||||
btrfs_update_inode_block_group(trans, inode);
|
btrfs_update_inode_block_group(trans, inode);
|
||||||
|
@ -2286,7 +2371,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
|
||||||
inode->i_op = &btrfs_file_inode_operations;
|
inode->i_op = &btrfs_file_inode_operations;
|
||||||
extent_map_tree_init(&BTRFS_I(inode)->extent_tree,
|
extent_map_tree_init(&BTRFS_I(inode)->extent_tree,
|
||||||
inode->i_mapping, GFP_NOFS);
|
inode->i_mapping, GFP_NOFS);
|
||||||
BTRFS_I(inode)->extent_tree.fill_delalloc = run_delalloc_range;
|
BTRFS_I(inode)->extent_tree.ops = &btrfs_extent_map_ops;
|
||||||
}
|
}
|
||||||
dir->i_sb->s_dirt = 1;
|
dir->i_sb->s_dirt = 1;
|
||||||
btrfs_update_inode_block_group(trans, inode);
|
btrfs_update_inode_block_group(trans, inode);
|
||||||
|
@ -2362,6 +2447,13 @@ static struct file_operations btrfs_dir_file_operations = {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct extent_map_ops btrfs_extent_map_ops = {
|
||||||
|
.fill_delalloc = run_delalloc_range,
|
||||||
|
.writepage_io_hook = btrfs_writepage_io_hook,
|
||||||
|
.readpage_io_hook = btrfs_readpage_io_hook,
|
||||||
|
.readpage_end_io_hook = btrfs_readpage_end_io_hook,
|
||||||
|
};
|
||||||
|
|
||||||
static struct address_space_operations btrfs_aops = {
|
static struct address_space_operations btrfs_aops = {
|
||||||
.readpage = btrfs_readpage,
|
.readpage = btrfs_readpage,
|
||||||
.writepage = btrfs_writepage,
|
.writepage = btrfs_writepage,
|
||||||
|
|
Loading…
Reference in New Issue