Commit 07157aac authored by Chris Mason's avatar Chris Mason Committed by David Woodhouse
Browse files

Btrfs: Add file data csums back in via hooks in the extent map code

parent 95e05289
Loading
Loading
Loading
Loading
+84 −10
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ void extent_map_tree_init(struct extent_map_tree *tree,
{
	tree->map.rb_node = NULL;
	tree->state.rb_node = NULL;
	tree->fill_delalloc = NULL;
	tree->ops = NULL;
	rwlock_init(&tree->lock);
	tree->mapping = mapping;
}
@@ -93,6 +93,7 @@ struct extent_state *alloc_extent_state(gfp_t mask)
		return state;
	state->state = 0;
	state->in_tree = 0;
	state->private = 0;
	atomic_set(&state->refs, 1);
	init_waitqueue_head(&state->wq);
	return state;
@@ -1034,6 +1035,61 @@ int unlock_range(struct extent_map_tree *tree, u64 start, u64 end)
}
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.
 * 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,
				   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 extent_map_tree *tree = bio->bi_private;
	u64 start;
	u64 end;
	int whole_page;
	int ret;

	if (bio->bi_size)
		return 1;
@@ -1208,6 +1265,11 @@ static int end_bio_extent_readpage(struct bio *bio,
		if (--bvec >= bio->bi_io_vec)
			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) {
			set_extent_uptodate(tree, start, end, GFP_ATOMIC);
			if (whole_page)
@@ -1388,9 +1450,16 @@ int extent_read_full_page(struct extent_map_tree *tree, struct page *page,
			continue;
		}

		ret = 0;
		if (tree->ops && tree->ops->readpage_io_hook) {
			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);
						 sector, iosize, page_offset,
						 bdev, end_bio_extent_readpage);
		}
		if (ret)
			SetPageError(page);
		cur = cur + iosize;
@@ -1462,7 +1531,7 @@ int extent_write_full_page(struct extent_map_tree *tree, struct page *page,
					       &delalloc_end,
					       128 * 1024 * 1024);
	if (nr_delalloc) {
		tree->fill_delalloc(inode, start, delalloc_end);
		tree->ops->fill_delalloc(inode, start, delalloc_end);
		if (delalloc_end >= page_end + 1) {
			clear_extent_bit(tree, page_end + 1, delalloc_end,
					 EXTENT_LOCKED | EXTENT_DELALLOC,
@@ -1528,12 +1597,17 @@ int extent_write_full_page(struct extent_map_tree *tree, struct page *page,
			continue;
		}
		clear_extent_dirty(tree, cur, cur + iosize - 1, GFP_NOFS);
		ret = tree->ops->writepage_io_hook(page, cur, cur + iosize - 1);
		if (ret)
			SetPageError(page);
		else {
			set_range_writeback(tree, cur, cur + iosize - 1);
		ret = submit_extent_page(WRITE, tree, page,
					 sector, iosize, page_offset, bdev,
			ret = submit_extent_page(WRITE, tree, page, sector,
						 iosize, page_offset, bdev,
						 end_bio_extent_writepage);
			if (ret)
				SetPageError(page);
		}
		cur = cur + iosize;
		page_offset += iosize;
		nr++;
+14 −1
Original line number Diff line number Diff line
@@ -6,12 +6,19 @@
#define EXTENT_MAP_INLINE (u64)-2
#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 rb_root map;
	struct rb_root state;
	struct address_space *mapping;
	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 */
@@ -36,6 +43,10 @@ struct extent_state {
	wait_queue_head_t wq;
	atomic_t refs;
	unsigned long state;

	/* for use by the FS */
	u64 private;

	struct list_head list;
};

@@ -89,4 +100,6 @@ int extent_commit_write(struct extent_map_tree *tree,
			struct inode *inode, struct page *page,
			unsigned from, unsigned to);
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
+95 −3
Original line number Diff line number Diff line
@@ -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_symlink_aops;
static struct file_operations btrfs_dir_file_operations;
static struct extent_map_ops btrfs_extent_map_ops;

static struct kmem_cache *btrfs_inode_cachep;
struct kmem_cache *btrfs_trans_handle_cachep;
@@ -103,6 +104,90 @@ out:
	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)
{
@@ -155,7 +240,7 @@ void btrfs_read_locked_inode(struct inode *inode)
	switch (inode->i_mode & S_IFMT) {
	case S_IFREG:
		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_op = &btrfs_file_inode_operations;
		break;
@@ -1148,7 +1233,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
		inode->i_op = &btrfs_file_inode_operations;
		extent_map_tree_init(&BTRFS_I(inode)->extent_tree,
				     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;
	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;
		extent_map_tree_init(&BTRFS_I(inode)->extent_tree,
				     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;
	btrfs_update_inode_block_group(trans, inode);
@@ -2362,6 +2447,13 @@ static struct file_operations btrfs_dir_file_operations = {
#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 = {
	.readpage	= btrfs_readpage,
	.writepage	= btrfs_writepage,