Loading fs/btrfs/extent_map.c +84 −10 Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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; Loading @@ -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) Loading Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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++; Loading fs/btrfs/extent_map.h +14 −1 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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; }; Loading Loading @@ -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 fs/btrfs/inode.c +95 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -103,6 +104,90 @@ static int run_delalloc_range(struct inode *inode, u64 start, u64 end) 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) { Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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, Loading Loading
fs/btrfs/extent_map.c +84 −10 Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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; Loading @@ -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) Loading Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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++; Loading
fs/btrfs/extent_map.h +14 −1 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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; }; Loading Loading @@ -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
fs/btrfs/inode.c +95 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -103,6 +104,90 @@ static int run_delalloc_range(struct inode *inode, u64 start, u64 end) 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) { Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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, Loading