diff options
Diffstat (limited to 'fs/btrfs')
50 files changed, 1095 insertions, 2683 deletions
diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index aa976ec..398cbd5 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -9,17 +9,12 @@ config BTRFS_FS select XOR_BLOCKS help - Btrfs is a general purpose copy-on-write filesystem with extents, - writable snapshotting, support for multiple devices and many more - features focused on fault tolerance, repair and easy administration. + Btrfs is a new filesystem with extents, writable snapshotting, + support for multiple devices and many more features. - The filesystem disk format is no longer unstable, and it's not - expected to change unless there are strong reasons to do so. If there - is a format change, file systems with a unchanged format will - continue to be mountable and usable by newer kernels. - - For more information, please see the web pages at - http://btrfs.wiki.kernel.org. + Btrfs is highly experimental, and THE DISK FORMAT IS NOT YET + FINALIZED. You should say N here unless you are interested in + testing Btrfs with non-critical data. To compile this file system support as a module, choose M here. The module will be called btrfs. @@ -64,8 +59,7 @@ config BTRFS_FS_RUN_SANITY_TESTS help This will run some basic sanity tests on the free space cache code to make sure it is acting as it should. These are mostly - regression tests and are only really interesting to btrfs - developers. + regression tests and are only really interesting to btrfs devlopers. If unsure, say N. diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index 1a44e42..a91a6a3 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile @@ -14,6 +14,4 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \ btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o -btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \ - tests/extent-buffer-tests.o tests/btrfs-tests.o \ - tests/extent-io-tests.o tests/inode-tests.o +btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c index 0890c83..e15d2b0 100644 --- a/fs/btrfs/acl.c +++ b/fs/btrfs/acl.c @@ -229,7 +229,7 @@ int btrfs_init_acl(struct btrfs_trans_handle *trans, if (ret > 0) { /* we need an acl */ ret = btrfs_set_acl(trans, inode, acl, ACL_TYPE_ACCESS); - } else if (ret < 0) { + } else { cache_no_acl(inode); } } else { diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c index c1e0b0c..08cc08f 100644 --- a/fs/btrfs/async-thread.c +++ b/fs/btrfs/async-thread.c @@ -262,7 +262,7 @@ static struct btrfs_work *get_next_work(struct btrfs_worker_thread *worker, struct btrfs_work *work = NULL; struct list_head *cur = NULL; - if (!list_empty(prio_head)) + if(!list_empty(prio_head)) cur = prio_head->next; smp_mb(); @@ -495,7 +495,6 @@ static int __btrfs_start_workers(struct btrfs_workers *workers) spin_lock_irq(&workers->lock); if (workers->stopping) { spin_unlock_irq(&workers->lock); - ret = -EINVAL; goto fail_kthread; } list_add_tail(&worker->worker_list, &workers->idle_list); diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 3775947..0552a59 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -185,9 +185,6 @@ static int __add_prelim_ref(struct list_head *head, u64 root_id, { struct __prelim_ref *ref; - if (root_id == BTRFS_DATA_RELOC_TREE_OBJECTID) - return 0; - ref = kmem_cache_alloc(btrfs_prelim_ref_cache, gfp_mask); if (!ref) return -ENOMEM; @@ -326,7 +323,8 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, eb = path->nodes[level]; while (!eb) { - if (WARN_ON(!level)) { + if (!level) { + WARN_ON(1); ret = 1; goto out; } @@ -1621,7 +1619,7 @@ static int iterate_inode_refs(u64 inum, struct btrfs_root *fs_root, btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); btrfs_release_path(path); - item = btrfs_item_nr(slot); + item = btrfs_item_nr(eb, slot); iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); for (cur = 0; cur < btrfs_item_size(eb, item); cur += len) { diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index ac0b39d..71f074e 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -19,7 +19,6 @@ #ifndef __BTRFS_I__ #define __BTRFS_I__ -#include <linux/hash.h> #include "extent_map.h" #include "extent_io.h" #include "ordered-data.h" @@ -180,25 +179,6 @@ static inline struct btrfs_inode *BTRFS_I(struct inode *inode) return container_of(inode, struct btrfs_inode, vfs_inode); } -static inline unsigned long btrfs_inode_hash(u64 objectid, - const struct btrfs_root *root) -{ - u64 h = objectid ^ (root->objectid * GOLDEN_RATIO_PRIME); - -#if BITS_PER_LONG == 32 - h = (h >> 32) ^ (h & 0xffffffff); -#endif - - return (unsigned long)h; -} - -static inline void btrfs_insert_inode_hash(struct inode *inode) -{ - unsigned long h = btrfs_inode_hash(inode->i_ino, BTRFS_I(inode)->root); - - __insert_inode_hash(inode, h); -} - static inline u64 btrfs_ino(struct inode *inode) { u64 ino = BTRFS_I(inode)->location.objectid; diff --git a/fs/btrfs/check-integrity.c b/fs/btrfs/check-integrity.c index 131d828..1c47be1 100644 --- a/fs/btrfs/check-integrity.c +++ b/fs/btrfs/check-integrity.c @@ -77,15 +77,6 @@ * the integrity of (super)-block write requests, do not * enable the config option BTRFS_FS_CHECK_INTEGRITY to * include and compile the integrity check tool. - * - * Expect millions of lines of information in the kernel log with an - * enabled check_int_print_mask. Therefore set LOG_BUF_SHIFT in the - * kernel config to at least 26 (which is 64MB). Usually the value is - * limited to 21 (which is 2MB) in init/Kconfig. The file needs to be - * changed like this before LOG_BUF_SHIFT can be set to a high value: - * config LOG_BUF_SHIFT - * int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" - * range 12 30 */ #include <linux/sched.h> @@ -133,7 +124,6 @@ #define BTRFSIC_PRINT_MASK_INITIAL_DATABASE 0x00000400 #define BTRFSIC_PRINT_MASK_NUM_COPIES 0x00000800 #define BTRFSIC_PRINT_MASK_TREE_WITH_ALL_MIRRORS 0x00001000 -#define BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH_VERBOSE 0x00002000 struct btrfsic_dev_state; struct btrfsic_state; @@ -333,6 +323,7 @@ static void btrfsic_release_block_ctx(struct btrfsic_block_data_ctx *block_ctx); static int btrfsic_read_block(struct btrfsic_state *state, struct btrfsic_block_data_ctx *block_ctx); static void btrfsic_dump_database(struct btrfsic_state *state); +static void btrfsic_complete_bio_end_io(struct bio *bio, int err); static int btrfsic_test_for_metadata(struct btrfsic_state *state, char **datav, unsigned int num_pages); static void btrfsic_process_written_block(struct btrfsic_dev_state *dev_state, @@ -1047,7 +1038,7 @@ leaf_item_out_of_bounce_error: disk_item_offset, sizeof(struct btrfs_item)); item_offset = btrfs_stack_item_offset(&disk_item); - item_size = btrfs_stack_item_size(&disk_item); + item_size = btrfs_stack_item_offset(&disk_item); disk_key = &disk_item.key; type = btrfs_disk_key_type(disk_key); @@ -1686,6 +1677,7 @@ static int btrfsic_read_block(struct btrfsic_state *state, for (i = 0; i < num_pages;) { struct bio *bio; unsigned int j; + DECLARE_COMPLETION_ONSTACK(complete); bio = btrfs_io_bio_alloc(GFP_NOFS, num_pages - i); if (!bio) { @@ -1696,6 +1688,8 @@ static int btrfsic_read_block(struct btrfsic_state *state, } bio->bi_bdev = block_ctx->dev->bdev; bio->bi_sector = dev_bytenr >> 9; + bio->bi_end_io = btrfsic_complete_bio_end_io; + bio->bi_private = &complete; for (j = i; j < num_pages; j++) { ret = bio_add_page(bio, block_ctx->pagev[j], @@ -1708,7 +1702,12 @@ static int btrfsic_read_block(struct btrfsic_state *state, "btrfsic: error, failed to add a single page!\n"); return -1; } - if (submit_bio_wait(READ, bio)) { + submit_bio(READ, bio); + + /* this will also unplug the queue */ + wait_for_completion(&complete); + + if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) { printk(KERN_INFO "btrfsic: read error at logical %llu dev %s!\n", block_ctx->start, block_ctx->dev->name); @@ -1731,6 +1730,11 @@ static int btrfsic_read_block(struct btrfsic_state *state, return block_ctx->len; } +static void btrfsic_complete_bio_end_io(struct bio *bio, int err) +{ + complete((struct completion *)bio->bi_private); +} + static void btrfsic_dump_database(struct btrfsic_state *state) { struct list_head *elem_all; @@ -1896,9 +1900,7 @@ again: dev_state, dev_bytenr); } - if (block->logical_bytenr != bytenr && - !(!block->is_metadata && - block->logical_bytenr == 0)) + if (block->logical_bytenr != bytenr) { printk(KERN_INFO "Written block @%llu (%s/%llu/%d)" " found in hash table, %c," @@ -1908,14 +1910,15 @@ again: block->mirror_num, btrfsic_get_block_type(state, block), block->logical_bytenr); - else if (state->print_mask & BTRFSIC_PRINT_MASK_VERBOSE) + block->logical_bytenr = bytenr; + } else if (state->print_mask & + BTRFSIC_PRINT_MASK_VERBOSE) printk(KERN_INFO "Written block @%llu (%s/%llu/%d)" " found in hash table, %c.\n", bytenr, dev_state->name, dev_bytenr, block->mirror_num, btrfsic_get_block_type(state, block)); - block->logical_bytenr = bytenr; } else { if (num_pages * PAGE_CACHE_SIZE < state->datablock_size) { @@ -2460,8 +2463,10 @@ static int btrfsic_process_written_superblock( } } - if (WARN_ON(-1 == btrfsic_check_all_ref_blocks(state, superblock, 0))) + if (-1 == btrfsic_check_all_ref_blocks(state, superblock, 0)) { + WARN_ON(1); btrfsic_dump_tree(state); + } return 0; } @@ -2901,7 +2906,7 @@ static void btrfsic_cmp_log_and_dev_bytenr(struct btrfsic_state *state, btrfsic_release_block_ctx(&block_ctx); } - if (WARN_ON(!match)) { + if (!match) { printk(KERN_INFO "btrfs: attempt to write M-block which contains logical bytenr that doesn't map to dev+physical bytenr of submit_bio," " buffer->log_bytenr=%llu, submit_bio(bdev=%s," " phys_bytenr=%llu)!\n", @@ -2918,6 +2923,7 @@ static void btrfsic_cmp_log_and_dev_bytenr(struct btrfsic_state *state, bytenr, block_ctx.dev->name, block_ctx.dev_bytenr, mirror_num); } + WARN_ON(1); } } @@ -2994,12 +3000,14 @@ int btrfsic_submit_bh(int rw, struct buffer_head *bh) return submit_bh(rw, bh); } -static void __btrfsic_submit_bio(int rw, struct bio *bio) +void btrfsic_submit_bio(int rw, struct bio *bio) { struct btrfsic_dev_state *dev_state; - if (!btrfsic_is_initialized) + if (!btrfsic_is_initialized) { + submit_bio(rw, bio); return; + } mutex_lock(&btrfsic_mutex); /* since btrfsic_submit_bio() is also called before @@ -3009,7 +3017,6 @@ static void __btrfsic_submit_bio(int rw, struct bio *bio) (rw & WRITE) && NULL != bio->bi_io_vec) { unsigned int i; u64 dev_bytenr; - u64 cur_bytenr; int bio_is_patched; char **mapped_datav; @@ -3028,7 +3035,6 @@ static void __btrfsic_submit_bio(int rw, struct bio *bio) GFP_NOFS); if (!mapped_datav) goto leave; - cur_bytenr = dev_bytenr; for (i = 0; i < bio->bi_vcnt; i++) { BUG_ON(bio->bi_io_vec[i].bv_len != PAGE_CACHE_SIZE); mapped_datav[i] = kmap(bio->bi_io_vec[i].bv_page); @@ -3040,13 +3046,16 @@ static void __btrfsic_submit_bio(int rw, struct bio *bio) kfree(mapped_datav); goto leave; } - if (dev_state->state->print_mask & - BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH_VERBOSE) + if ((BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH | + BTRFSIC_PRINT_MASK_VERBOSE) == + (dev_state->state->print_mask & + (BTRFSIC_PRINT_MASK_SUBMIT_BIO_BH | + BTRFSIC_PRINT_MASK_VERBOSE))) printk(KERN_INFO - "#%u: bytenr=%llu, len=%u, offset=%u\n", - i, cur_bytenr, bio->bi_io_vec[i].bv_len, + "#%u: page=%p, len=%u, offset=%u\n", + i, bio->bi_io_vec[i].bv_page, + bio->bi_io_vec[i].bv_len, bio->bi_io_vec[i].bv_offset); - cur_bytenr += bio->bi_io_vec[i].bv_len; } btrfsic_process_written_block(dev_state, dev_bytenr, mapped_datav, bio->bi_vcnt, @@ -3090,20 +3099,10 @@ static void __btrfsic_submit_bio(int rw, struct bio *bio) } leave: mutex_unlock(&btrfsic_mutex); -} -void btrfsic_submit_bio(int rw, struct bio *bio) -{ - __btrfsic_submit_bio(rw, bio); submit_bio(rw, bio); } -int btrfsic_submit_bio_wait(int rw, struct bio *bio) -{ - __btrfsic_submit_bio(rw, bio); - return submit_bio_wait(rw, bio); -} - int btrfsic_mount(struct btrfs_root *root, struct btrfs_fs_devices *fs_devices, int including_extent_data, u32 print_mask) diff --git a/fs/btrfs/check-integrity.h b/fs/btrfs/check-integrity.h index 13b8566..8b59175 100644 --- a/fs/btrfs/check-integrity.h +++ b/fs/btrfs/check-integrity.h @@ -22,11 +22,9 @@ #ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY int btrfsic_submit_bh(int rw, struct buffer_head *bh); void btrfsic_submit_bio(int rw, struct bio *bio); -int btrfsic_submit_bio_wait(int rw, struct bio *bio); #else #define btrfsic_submit_bh submit_bh #define btrfsic_submit_bio submit_bio -#define btrfsic_submit_bio_wait submit_bio_wait #endif int btrfsic_mount(struct btrfs_root *root, diff --git a/fs/btrfs/compat.h b/fs/btrfs/compat.h new file mode 100644 index 0000000..7c4503e --- /dev/null +++ b/fs/btrfs/compat.h @@ -0,0 +1,7 @@ +#ifndef _COMPAT_H_ +#define _COMPAT_H_ + +#define btrfs_drop_nlink(inode) drop_nlink(inode) +#define btrfs_inc_nlink(inode) inc_nlink(inode) + +#endif /* _COMPAT_H_ */ diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 1499b27..6aad98c 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -32,6 +32,7 @@ #include <linux/writeback.h> #include <linux/bit_spinlock.h> #include <linux/slab.h> +#include "compat.h" #include "ctree.h" #include "disk-io.h" #include "transaction.h" @@ -359,7 +360,7 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start, bdev = BTRFS_I(inode)->root->fs_info->fs_devices->latest_bdev; bio = compressed_bio_alloc(bdev, first_byte, GFP_NOFS); - if (!bio) { + if(!bio) { kfree(cb); return -ENOMEM; } diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 316136b..61b5bcd 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -274,7 +274,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans, else btrfs_set_header_owner(cow, new_root_objectid); - write_extent_buffer(cow, root->fs_info->fsid, btrfs_header_fsid(), + write_extent_buffer(cow, root->fs_info->fsid, btrfs_header_fsid(cow), BTRFS_FSID_SIZE); WARN_ON(btrfs_header_generation(buf) > trans->transid); @@ -996,7 +996,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, else btrfs_set_header_owner(cow, root->root_key.objectid); - write_extent_buffer(cow, root->fs_info->fsid, btrfs_header_fsid(), + write_extent_buffer(cow, root->fs_info->fsid, btrfs_header_fsid(cow), BTRFS_FSID_SIZE); ret = update_ref_for_cow(trans, root, buf, cow, &last_ref); @@ -1285,10 +1285,11 @@ get_old_root(struct btrfs_root *root, u64 time_seq) free_extent_buffer(eb_root); blocksize = btrfs_level_size(root, old_root->level); old = read_tree_block(root, logical, blocksize, 0); - if (WARN_ON(!old || !extent_buffer_uptodate(old))) { + if (!old || !extent_buffer_uptodate(old)) { free_extent_buffer(old); pr_warn("btrfs: failed to read tree block %llu from get_old_root\n", logical); + WARN_ON(1); } else { eb = btrfs_clone_extent_buffer(old); free_extent_buffer(old); @@ -2757,7 +2758,7 @@ int btrfs_search_old_slot(struct btrfs_root *root, struct btrfs_key *key, int level; int lowest_unlock = 1; u8 lowest_level = 0; - int prev_cmp = -1; + int prev_cmp; lowest_level = p->lowest_level; WARN_ON(p->nodes[0] != NULL); @@ -2768,6 +2769,7 @@ int btrfs_search_old_slot(struct btrfs_root *root, struct btrfs_key *key, } again: + prev_cmp = -1; b = get_old_root(root, time_seq); level = btrfs_header_level(b); p->locks[level] = BTRFS_READ_LOCK; @@ -2785,11 +2787,6 @@ again: */ btrfs_unlock_up_safe(p, level + 1); - /* - * Since we can unwind eb's we want to do a real search every - * time. - */ - prev_cmp = -1; ret = key_search(b, key, level, &prev_cmp, &slot); if (level != 0) { @@ -3151,7 +3148,7 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans, btrfs_set_header_backref_rev(c, BTRFS_MIXED_BACKREF_REV); btrfs_set_header_owner(c, root->root_key.objectid); - write_extent_buffer(c, root->fs_info->fsid, btrfs_header_fsid(), + write_extent_buffer(c, root->fs_info->fsid, btrfs_header_fsid(c), BTRFS_FSID_SIZE); write_extent_buffer(c, root->fs_info->chunk_tree_uuid, @@ -3290,7 +3287,7 @@ static noinline int split_node(struct btrfs_trans_handle *trans, btrfs_set_header_backref_rev(split, BTRFS_MIXED_BACKREF_REV); btrfs_set_header_owner(split, root->root_key.objectid); write_extent_buffer(split, root->fs_info->fsid, - btrfs_header_fsid(), BTRFS_FSID_SIZE); + btrfs_header_fsid(split), BTRFS_FSID_SIZE); write_extent_buffer(split, root->fs_info->chunk_tree_uuid, btrfs_header_chunk_tree_uuid(split), BTRFS_UUID_SIZE); @@ -3340,8 +3337,8 @@ static int leaf_space_used(struct extent_buffer *l, int start, int nr) if (!nr) return 0; btrfs_init_map_token(&token); - start_item = btrfs_item_nr(start); - end_item = btrfs_item_nr(end); + start_item = btrfs_item_nr(l, start); + end_item = btrfs_item_nr(l, end); data_len = btrfs_token_item_offset(l, start_item, &token) + btrfs_token_item_size(l, start_item, &token); data_len = data_len - btrfs_token_item_offset(l, end_item, &token); @@ -3409,7 +3406,7 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans, slot = path->slots[1]; i = left_nritems - 1; while (i >= nr) { - item = btrfs_item_nr(i); + item = btrfs_item_nr(left, i); if (!empty && push_items > 0) { if (path->slots[0] > i) @@ -3473,7 +3470,7 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans, btrfs_set_header_nritems(right, right_nritems); push_space = BTRFS_LEAF_DATA_SIZE(root); for (i = 0; i < right_nritems; i++) { - item = btrfs_item_nr(i); + item = btrfs_item_nr(right, i); push_space -= btrfs_token_item_size(right, item, &token); btrfs_set_token_item_offset(right, item, push_space, &token); } @@ -3615,7 +3612,7 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans, nr = min(right_nritems - 1, max_slot); for (i = 0; i < nr; i++) { - item = btrfs_item_nr(i); + item = btrfs_item_nr(right, i); if (!empty && push_items > 0) { if (path->slots[0] < i) @@ -3642,7 +3639,8 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans, ret = 1; goto out; } - WARN_ON(!empty && push_items == btrfs_header_nritems(right)); + if (!empty && push_items == btrfs_header_nritems(right)) + WARN_ON(1); /* push data from right to left */ copy_extent_buffer(left, right, @@ -3665,7 +3663,7 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans, for (i = old_left_nritems; i < old_left_nritems + push_items; i++) { u32 ioff; - item = btrfs_item_nr(i); + item = btrfs_item_nr(left, i); ioff = btrfs_token_item_offset(left, item, &token); btrfs_set_token_item_offset(left, item, @@ -3696,7 +3694,7 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans, btrfs_set_header_nritems(right, right_nritems); push_space = BTRFS_LEAF_DATA_SIZE(root); for (i = 0; i < right_nritems; i++) { - item = btrfs_item_nr(i); + item = btrfs_item_nr(right, i); push_space = push_space - btrfs_token_item_size(right, item, &token); @@ -3837,7 +3835,7 @@ static noinline void copy_for_split(struct btrfs_trans_handle *trans, btrfs_item_end_nr(l, mid); for (i = 0; i < nritems; i++) { - struct btrfs_item *item = btrfs_item_nr(i); + struct btrfs_item *item = btrfs_item_nr(right, i); u32 ioff; ioff = btrfs_token_item_offset(right, item, &token); @@ -4018,7 +4016,7 @@ again: data_size > BTRFS_LEAF_DATA_SIZE(root)) { if (data_size && !tried_avoid_double) goto push_for_double; - split = 2; + split = 2 ; } } } @@ -4044,7 +4042,7 @@ again: btrfs_set_header_owner(right, root->root_key.objectid); btrfs_set_header_level(right, 0); write_extent_buffer(right, root->fs_info->fsid, - btrfs_header_fsid(), BTRFS_FSID_SIZE); + btrfs_header_fsid(right), BTRFS_FSID_SIZE); write_extent_buffer(right, root->fs_info->chunk_tree_uuid, btrfs_header_chunk_tree_uuid(right), @@ -4179,7 +4177,7 @@ static noinline int split_item(struct btrfs_trans_handle *trans, btrfs_set_path_blocking(path); - item = btrfs_item_nr(path->slots[0]); + item = btrfs_item_nr(leaf, path->slots[0]); orig_offset = btrfs_item_offset(leaf, item); item_size = btrfs_item_size(leaf, item); @@ -4202,7 +4200,7 @@ static noinline int split_item(struct btrfs_trans_handle *trans, btrfs_cpu_key_to_disk(&disk_key, new_key); btrfs_set_item_key(leaf, &disk_key, slot); - new_item = btrfs_item_nr(slot); + new_item = btrfs_item_nr(leaf, slot); btrfs_set_item_offset(leaf, new_item, orig_offset); btrfs_set_item_size(leaf, new_item, item_size - split_offset); @@ -4341,7 +4339,7 @@ void btrfs_truncate_item(struct btrfs_root *root, struct btrfs_path *path, /* first correct the data pointers */ for (i = slot; i < nritems; i++) { u32 ioff; - item = btrfs_item_nr(i); + item = btrfs_item_nr(leaf, i); ioff = btrfs_token_item_offset(leaf, item, &token); btrfs_set_token_item_offset(leaf, item, @@ -4389,7 +4387,7 @@ void btrfs_truncate_item(struct btrfs_root *root, struct btrfs_path *path, fixup_low_keys(root, path, &disk_key, 1); } - item = btrfs_item_nr(slot); + item = btrfs_item_nr(leaf, slot); btrfs_set_item_size(leaf, item, new_size); btrfs_mark_buffer_dirty(leaf); @@ -4443,7 +4441,7 @@ void btrfs_extend_item(struct btrfs_root *root, struct btrfs_path *path, /* first correct the data pointers */ for (i = slot; i < nritems; i++) { u32 ioff; - item = btrfs_item_nr(i); + item = btrfs_item_nr(leaf, i); ioff = btrfs_token_item_offset(leaf, item, &token); btrfs_set_token_item_offset(leaf, item, @@ -4457,7 +4455,7 @@ void btrfs_extend_item(struct btrfs_root *root, struct btrfs_path *path, data_end = old_data; old_size = btrfs_item_size_nr(leaf, slot); - item = btrfs_item_nr(slot); + item = btrfs_item_nr(leaf, slot); btrfs_set_item_size(leaf, item, old_size + data_size); btrfs_mark_buffer_dirty(leaf); @@ -4516,7 +4514,7 @@ void setup_items_for_insert(struct btrfs_root *root, struct btrfs_path *path, for (i = slot; i < nritems; i++) { u32 ioff; - item = btrfs_item_nr( i); + item = btrfs_item_nr(leaf, i); ioff = btrfs_token_item_offset(leaf, item, &token); btrfs_set_token_item_offset(leaf, item, ioff - total_data, &token); @@ -4537,7 +4535,7 @@ void setup_items_for_insert(struct btrfs_root *root, struct btrfs_path *path, for (i = 0; i < nr; i++) { btrfs_cpu_key_to_disk(&disk_key, cpu_key + i); btrfs_set_item_key(leaf, &disk_key, slot + i); - item = btrfs_item_nr(slot + i); + item = btrfs_item_nr(leaf, slot + i); btrfs_set_token_item_offset(leaf, item, data_end - data_size[i], &token); data_end -= data_size[i]; @@ -4732,7 +4730,7 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, for (i = slot + nr; i < nritems; i++) { u32 ioff; - item = btrfs_item_nr(i); + item = btrfs_item_nr(leaf, i); ioff = btrfs_token_item_offset(leaf, item, &token); btrfs_set_token_item_offset(leaf, item, ioff + dsize, &token); @@ -4825,18 +4823,14 @@ static int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path) btrfs_item_key_to_cpu(path->nodes[0], &key, 0); - if (key.offset > 0) { + if (key.offset > 0) key.offset--; - } else if (key.type > 0) { + else if (key.type > 0) key.type--; - key.offset = (u64)-1; - } else if (key.objectid > 0) { + else if (key.objectid > 0) key.objectid--; - key.type = (u8)-1; - key.offset = (u64)-1; - } else { + else return 1; - } btrfs_release_path(path); ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); @@ -4872,6 +4866,7 @@ static int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path) * was nothing in the tree that matched the search criteria. */ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key, + struct btrfs_key *max_key, struct btrfs_path *path, u64 min_trans) { @@ -4916,8 +4911,10 @@ again: * If it is too old, old, skip to the next one. */ while (slot < nritems) { + u64 blockptr; u64 gen; + blockptr = btrfs_node_blockptr(cur, slot); gen = btrfs_node_ptr_generation(cur, slot); if (gen < min_trans) { slot++; diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 54ab861..0506f40 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -47,12 +47,6 @@ extern struct kmem_cache *btrfs_path_cachep; extern struct kmem_cache *btrfs_free_space_cachep; struct btrfs_ordered_sum; -#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS -#define STATIC noinline -#else -#define STATIC static noinline -#endif - #define BTRFS_MAGIC 0x4D5F53665248425FULL /* ascii _BHRfS_M, no null */ #define BTRFS_MAX_MIRRORS 3 @@ -1586,6 +1580,7 @@ struct btrfs_fs_info { atomic_t scrubs_paused; atomic_t scrub_cancel_req; wait_queue_head_t scrub_pause_wait; + struct rw_semaphore scrub_super_lock; int scrub_workers_refcnt; struct btrfs_workers scrub_workers; struct btrfs_workers scrub_wr_completion_workers; @@ -1729,9 +1724,7 @@ struct btrfs_root { int ref_cows; int track_dirty; int in_radix; -#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS - int dummy_root; -#endif + u64 defrag_trans_start; struct btrfs_key defrag_progress; struct btrfs_key defrag_max; @@ -2468,7 +2461,8 @@ static inline unsigned long btrfs_item_nr_offset(int nr) sizeof(struct btrfs_item) * nr; } -static inline struct btrfs_item *btrfs_item_nr(int nr) +static inline struct btrfs_item *btrfs_item_nr(struct extent_buffer *eb, + int nr) { return (struct btrfs_item *)btrfs_item_nr_offset(nr); } @@ -2481,30 +2475,30 @@ static inline u32 btrfs_item_end(struct extent_buffer *eb, static inline u32 btrfs_item_end_nr(struct extent_buffer *eb, int nr) { - return btrfs_item_end(eb, btrfs_item_nr(nr)); + return btrfs_item_end(eb, btrfs_item_nr(eb, nr)); } static inline u32 btrfs_item_offset_nr(struct extent_buffer *eb, int nr) { - return btrfs_item_offset(eb, btrfs_item_nr(nr)); + return btrfs_item_offset(eb, btrfs_item_nr(eb, nr)); } static inline u32 btrfs_item_size_nr(struct extent_buffer *eb, int nr) { - return btrfs_item_size(eb, btrfs_item_nr(nr)); + return btrfs_item_size(eb, btrfs_item_nr(eb, nr)); } static inline void btrfs_item_key(struct extent_buffer *eb, struct btrfs_disk_key *disk_key, int nr) { - struct btrfs_item *item = btrfs_item_nr(nr); + struct btrfs_item *item = btrfs_item_nr(eb, nr); read_eb_member(eb, item, struct btrfs_item, key, disk_key); } static inline void btrfs_set_item_key(struct extent_buffer *eb, struct btrfs_disk_key *disk_key, int nr) { - struct btrfs_item *item = btrfs_item_nr(nr); + struct btrfs_item *item = btrfs_item_nr(eb, nr); write_eb_member(eb, item, struct btrfs_item, key, disk_key); } @@ -2672,7 +2666,7 @@ static inline void btrfs_set_header_backref_rev(struct extent_buffer *eb, btrfs_set_header_flags(eb, flags); } -static inline unsigned long btrfs_header_fsid(void) +static inline unsigned long btrfs_header_fsid(struct extent_buffer *eb) { return offsetof(struct btrfs_header, fsid); } @@ -3111,6 +3105,11 @@ static inline u32 btrfs_level_size(struct btrfs_root *root, int level) ((unsigned long)(btrfs_leaf_data(leaf) + \ btrfs_item_offset_nr(leaf, slot))) +static inline struct dentry *fdentry(struct file *file) +{ + return file->f_path.dentry; +} + static inline bool btrfs_mixed_space_info(struct btrfs_space_info *space_info) { return ((space_info->flags & BTRFS_BLOCK_GROUP_METADATA) && @@ -3309,6 +3308,7 @@ int btrfs_find_next_key(struct btrfs_root *root, struct btrfs_path *path, struct btrfs_key *key, int lowest_level, u64 min_trans); int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key, + struct btrfs_key *max_key, struct btrfs_path *path, u64 min_trans); enum btrfs_compare_tree_result { @@ -3613,6 +3613,9 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, struct btrfs_ordered_sum *sums); int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode, struct bio *bio, u64 file_start, int contig); +int btrfs_csum_truncate(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct btrfs_path *path, + u64 isize); int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end, struct list_head *list, int search_commit); /* inode.c */ @@ -3672,7 +3675,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, u32 min_type); int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput); -int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput); +int btrfs_start_all_delalloc_inodes(struct btrfs_fs_info *fs_info, + int delay_iput); int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end, struct extent_state **cached_state); int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, @@ -3741,6 +3745,9 @@ void btrfs_cleanup_defrag_inodes(struct btrfs_fs_info *fs_info); int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync); void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end, int skip_pinned); +int btrfs_replace_extent_cache(struct inode *inode, struct extent_map *replace, + u64 start, u64 end, int skip_pinned, + int modified); extern const struct file_operations btrfs_file_operations; int __btrfs_drop_extents(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct inode *inode, @@ -3937,7 +3944,9 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, u64 end, struct btrfs_scrub_progress *progress, int readonly, int is_dev_replace); void btrfs_scrub_pause(struct btrfs_root *root); +void btrfs_scrub_pause_super(struct btrfs_root *root); void btrfs_scrub_continue(struct btrfs_root *root); +void btrfs_scrub_continue_super(struct btrfs_root *root); int btrfs_scrub_cancel(struct btrfs_fs_info *info); int btrfs_scrub_cancel_dev(struct btrfs_fs_info *info, struct btrfs_device *dev); @@ -4019,9 +4028,5 @@ static inline int btrfs_defrag_cancelled(struct btrfs_fs_info *fs_info) return signal_pending(current); } -/* Sanity test specific functions */ -#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS -void btrfs_test_destroy_inode(struct inode *inode); -#endif #endif diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 8d292fb..cbd9523 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -108,8 +108,8 @@ static struct btrfs_delayed_node *btrfs_get_delayed_node(struct inode *inode) return node; } btrfs_inode->delayed_node = node; - /* can be accessed and cached in the inode */ - atomic_add(2, &node->refs); + atomic_inc(&node->refs); /* can be accessed */ + atomic_inc(&node->refs); /* cached in the inode */ spin_unlock(&root->inode_lock); return node; } @@ -138,8 +138,8 @@ again: return ERR_PTR(-ENOMEM); btrfs_init_delayed_node(node, root, ino); - /* cached in the btrfs inode and can be accessed */ - atomic_add(2, &node->refs); + atomic_inc(&node->refs); /* cached in the btrfs inode */ + atomic_inc(&node->refs); /* can be accessed */ ret = radix_tree_preload(GFP_NOFS & ~__GFP_HIGHMEM); if (ret) { @@ -649,13 +649,14 @@ static int btrfs_delayed_inode_reserve_metadata( goto out; ret = btrfs_block_rsv_migrate(src_rsv, dst_rsv, num_bytes); - if (!WARN_ON(ret)) + if (!ret) goto out; /* * Ok this is a problem, let's just steal from the global rsv * since this really shouldn't happen that often. */ + WARN_ON(1); ret = btrfs_block_rsv_migrate(&root->fs_info->global_block_rsv, dst_rsv, num_bytes); goto out; @@ -770,13 +771,13 @@ static int btrfs_batch_insert_items(struct btrfs_root *root, */ btrfs_set_path_blocking(path); - keys = kmalloc_array(nitems, sizeof(struct btrfs_key), GFP_NOFS); + keys = kmalloc(sizeof(struct btrfs_key) * nitems, GFP_NOFS); if (!keys) { ret = -ENOMEM; goto out; } - data_size = kmalloc_array(nitems, sizeof(u32), GFP_NOFS); + data_size = kmalloc(sizeof(u32) * nitems, GFP_NOFS); if (!data_size) { ret = -ENOMEM; goto error; @@ -1173,10 +1174,8 @@ int btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans, mutex_unlock(&delayed_node->mutex); path = btrfs_alloc_path(); - if (!path) { - btrfs_release_delayed_node(delayed_node); + if (!path) return -ENOMEM; - } path->leave_spinning = 1; block_rsv = trans->block_rsv; diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 2cfc3df..9efb94e 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -26,6 +26,7 @@ #include <linux/kthread.h> #include <linux/math64.h> #include <asm/div64.h> +#include "compat.h" #include "ctree.h" #include "extent_map.h" #include "disk-io.h" @@ -37,6 +38,7 @@ #include "rcu-string.h" #include "dev-replace.h" +static u64 btrfs_get_seconds_since_1970(void); static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, int scrub_ret); static void btrfs_dev_replace_update_device_in_mapping_tree( @@ -294,6 +296,13 @@ void btrfs_after_dev_replace_commit(struct btrfs_fs_info *fs_info) dev_replace->cursor_left_last_write_of_item; } +static u64 btrfs_get_seconds_since_1970(void) +{ + struct timespec t = CURRENT_TIME_SEC; + + return t.tv_sec; +} + int btrfs_dev_replace_start(struct btrfs_root *root, struct btrfs_ioctl_dev_replace_args *args) { @@ -366,7 +375,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root, dev_replace->tgtdev = tgt_device; printk_in_rcu(KERN_INFO - "btrfs: dev_replace from %s (devid %llu) to %s started\n", + "btrfs: dev_replace from %s (devid %llu) to %s) started\n", src_device->missing ? "<missing disk>" : rcu_str_deref(src_device->name), src_device->devid, @@ -381,7 +390,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root, * go to the tgtdev as well (refer to btrfs_map_block()). */ dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED; - dev_replace->time_started = get_seconds(); + dev_replace->time_started = btrfs_get_seconds_since_1970(); dev_replace->cursor_left = 0; dev_replace->committed_cursor_left = 0; dev_replace->cursor_left_last_write_of_item = 0; @@ -391,7 +400,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root, args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR; btrfs_dev_replace_unlock(dev_replace); - btrfs_wait_ordered_roots(root->fs_info, -1); + btrfs_wait_all_ordered_extents(root->fs_info); /* force writing the updated state information to disk */ trans = btrfs_start_transaction(root, 0); @@ -461,12 +470,12 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, * flush all outstanding I/O and inode extent mappings before the * copy operation is declared as being finished */ - ret = btrfs_start_delalloc_roots(root->fs_info, 0); + ret = btrfs_start_all_delalloc_inodes(root->fs_info, 0); if (ret) { mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return ret; } - btrfs_wait_ordered_roots(root->fs_info, -1); + btrfs_wait_all_ordered_extents(root->fs_info); trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { @@ -484,7 +493,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, : BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED; dev_replace->tgtdev = NULL; dev_replace->srcdev = NULL; - dev_replace->time_stopped = get_seconds(); + dev_replace->time_stopped = btrfs_get_seconds_since_1970(); dev_replace->item_needs_writeback = 1; if (scrub_ret) { @@ -641,9 +650,6 @@ static u64 __btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info) u64 result; int ret; - if (fs_info->sb->s_flags & MS_RDONLY) - return -EROFS; - mutex_lock(&dev_replace->lock_finishing_cancel_unmount); btrfs_dev_replace_lock(dev_replace); switch (dev_replace->replace_state) { @@ -662,7 +668,7 @@ static u64 __btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info) break; } dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED; - dev_replace->time_stopped = get_seconds(); + dev_replace->time_stopped = btrfs_get_seconds_since_1970(); dev_replace->item_needs_writeback = 1; btrfs_dev_replace_unlock(dev_replace); btrfs_scrub_cancel(fs_info); @@ -697,7 +703,7 @@ void btrfs_dev_replace_suspend_for_unmount(struct btrfs_fs_info *fs_info) case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: dev_replace->replace_state = BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED; - dev_replace->time_stopped = get_seconds(); + dev_replace->time_stopped = btrfs_get_seconds_since_1970(); dev_replace->item_needs_writeback = 1; pr_info("btrfs: suspending dev_replace for unmount\n"); break; diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index c031ea3..79e594e 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -58,7 +58,7 @@ static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle return ERR_PTR(ret); WARN_ON(ret > 0); leaf = path->nodes[0]; - item = btrfs_item_nr(path->slots[0]); + item = btrfs_item_nr(leaf, path->slots[0]); ptr = btrfs_item_ptr(leaf, path->slots[0], char); BUG_ON(data_size > btrfs_item_size(leaf, item)); ptr += btrfs_item_size(leaf, item) - data_size; @@ -474,10 +474,8 @@ int verify_dir_item(struct btrfs_root *root, } /* BTRFS_MAX_XATTR_SIZE is the same for all dir items */ - if ((btrfs_dir_data_len(leaf, dir_item) + - btrfs_dir_name_len(leaf, dir_item)) > BTRFS_MAX_XATTR_SIZE(root)) { - printk(KERN_CRIT "btrfs: invalid dir item name + data len: %u + %u\n", - (unsigned)btrfs_dir_name_len(leaf, dir_item), + if (btrfs_dir_data_len(leaf, dir_item) > BTRFS_MAX_XATTR_SIZE(root)) { + printk(KERN_CRIT "btrfs: invalid dir item data len: %u\n", (unsigned)btrfs_dir_data_len(leaf, dir_item)); return 1; } diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 8072cfa..62176ad 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -33,6 +33,7 @@ #include <linux/uuid.h> #include <linux/semaphore.h> #include <asm/unaligned.h> +#include "compat.h" #include "ctree.h" #include "disk-io.h" #include "transaction.h" @@ -63,6 +64,7 @@ static void btrfs_destroy_ordered_operations(struct btrfs_transaction *t, static void btrfs_destroy_ordered_extents(struct btrfs_root *root); static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans, struct btrfs_root *root); +static void btrfs_evict_pending_snapshots(struct btrfs_transaction *t); static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root); static int btrfs_destroy_marked_extents(struct btrfs_root *root, struct extent_io_tree *dirty_pages, @@ -475,8 +477,14 @@ static int csum_dirty_buffer(struct btrfs_root *root, struct page *page) if (page != eb->pages[0]) return 0; found_start = btrfs_header_bytenr(eb); - if (WARN_ON(found_start != start || !PageUptodate(page))) + if (found_start != start) { + WARN_ON(1); return 0; + } + if (!PageUptodate(page)) { + WARN_ON(1); + return 0; + } csum_tree_block(root, eb, 0); return 0; } @@ -488,7 +496,7 @@ static int check_tree_block_fsid(struct btrfs_root *root, u8 fsid[BTRFS_UUID_SIZE]; int ret = 1; - read_extent_buffer(eb, fsid, btrfs_header_fsid(), BTRFS_FSID_SIZE); + read_extent_buffer(eb, fsid, btrfs_header_fsid(eb), BTRFS_FSID_SIZE); while (fs_devices) { if (!memcmp(fsid, fs_devices->fsid, BTRFS_FSID_SIZE)) { ret = 0; @@ -1097,7 +1105,8 @@ struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root, { struct inode *btree_inode = root->fs_info->btree_inode; struct extent_buffer *eb; - eb = find_extent_buffer(&BTRFS_I(btree_inode)->io_tree, bytenr); + eb = find_extent_buffer(&BTRFS_I(btree_inode)->io_tree, + bytenr, blocksize); return eb; } @@ -1220,18 +1229,14 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize, atomic_set(&root->refs, 1); root->log_transid = 0; root->last_log_commit = 0; - if (fs_info) - extent_io_tree_init(&root->dirty_log_pages, - fs_info->btree_inode->i_mapping); + extent_io_tree_init(&root->dirty_log_pages, + fs_info->btree_inode->i_mapping); memset(&root->root_key, 0, sizeof(root->root_key)); memset(&root->root_item, 0, sizeof(root->root_item)); memset(&root->defrag_progress, 0, sizeof(root->defrag_progress)); memset(&root->root_kobj, 0, sizeof(root->root_kobj)); - if (fs_info) - root->defrag_trans_start = fs_info->generation; - else - root->defrag_trans_start = 0; + root->defrag_trans_start = fs_info->generation; init_completion(&root->kobj_unregister); root->defrag_running = 0; root->root_key.objectid = objectid; @@ -1248,22 +1253,6 @@ static struct btrfs_root *btrfs_alloc_root(struct btrfs_fs_info *fs_info) return root; } -#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS -/* Should only be used by the testing infrastructure */ -struct btrfs_root *btrfs_alloc_dummy_root(void) -{ - struct btrfs_root *root; - - root = btrfs_alloc_root(NULL); - if (!root) - return ERR_PTR(-ENOMEM); - __setup_root(4096, 4096, 4096, 4096, root, NULL, 1); - root->dummy_root = 1; - - return root; -} -#endif - struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, u64 objectid) @@ -1303,7 +1292,7 @@ struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans, btrfs_set_header_owner(leaf, objectid); root->node = leaf; - write_extent_buffer(leaf, fs_info->fsid, btrfs_header_fsid(), + write_extent_buffer(leaf, fs_info->fsid, btrfs_header_fsid(leaf), BTRFS_FSID_SIZE); write_extent_buffer(leaf, fs_info->chunk_tree_uuid, btrfs_header_chunk_tree_uuid(leaf), @@ -1390,7 +1379,7 @@ static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans, root->node = leaf; write_extent_buffer(root->node, root->fs_info->fsid, - btrfs_header_fsid(), BTRFS_FSID_SIZE); + btrfs_header_fsid(root->node), BTRFS_FSID_SIZE); btrfs_mark_buffer_dirty(root->node); btrfs_tree_unlock(root->node); return root; @@ -1791,9 +1780,6 @@ sleep: wake_up_process(root->fs_info->cleaner_kthread); mutex_unlock(&root->fs_info->transaction_kthread_mutex); - if (unlikely(test_bit(BTRFS_FS_STATE_ERROR, - &root->fs_info->fs_state))) - btrfs_cleanup_transaction(root); if (!try_to_freeze()) { set_current_state(TASK_INTERRUPTIBLE); if (!kthread_should_stop() && @@ -2027,28 +2013,50 @@ static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info) btrfs_stop_workers(&fs_info->qgroup_rescan_workers); } -static void free_root_extent_buffers(struct btrfs_root *root) -{ - if (root) { - free_extent_buffer(root->node); - free_extent_buffer(root->commit_root); - root->node = NULL; - root->commit_root = NULL; - } -} - /* helper to cleanup tree roots */ static void free_root_pointers(struct btrfs_fs_info *info, int chunk_root) { - free_root_extent_buffers(info->tree_root); - - free_root_extent_buffers(info->dev_root); - free_root_extent_buffers(info->extent_root); - free_root_extent_buffers(info->csum_root); - free_root_extent_buffers(info->quota_root); - free_root_extent_buffers(info->uuid_root); - if (chunk_root) - free_root_extent_buffers(info->chunk_root); + free_extent_buffer(info->tree_root->node); + free_extent_buffer(info->tree_root->commit_root); + info->tree_root->node = NULL; + info->tree_root->commit_root = NULL; + + if (info->dev_root) { + free_extent_buffer(info->dev_root->node); + free_extent_buffer(info->dev_root->commit_root); + info->dev_root->node = NULL; + info->dev_root->commit_root = NULL; + } + if (info->extent_root) { + free_extent_buffer(info->extent_root->node); + free_extent_buffer(info->extent_root->commit_root); + info->extent_root->node = NULL; + info->extent_root->commit_root = NULL; + } + if (info->csum_root) { + free_extent_buffer(info->csum_root->node); + free_extent_buffer(info->csum_root->commit_root); + info->csum_root->node = NULL; + info->csum_root->commit_root = NULL; + } + if (info->quota_root) { + free_extent_buffer(info->quota_root->node); + free_extent_buffer(info->quota_root->commit_root); + info->quota_root->node = NULL; + info->quota_root->commit_root = NULL; + } + if (info->uuid_root) { + free_extent_buffer(info->uuid_root->node); + free_extent_buffer(info->uuid_root->commit_root); + info->uuid_root->node = NULL; + info->uuid_root->commit_root = NULL; + } + if (chunk_root) { + free_extent_buffer(info->chunk_root->node); + free_extent_buffer(info->chunk_root->commit_root); + info->chunk_root->node = NULL; + info->chunk_root->commit_root = NULL; + } } static void del_fs_roots(struct btrfs_fs_info *fs_info) @@ -2222,6 +2230,7 @@ int open_ctree(struct super_block *sb, atomic_set(&fs_info->scrubs_paused, 0); atomic_set(&fs_info->scrub_cancel_req, 0); init_waitqueue_head(&fs_info->scrub_pause_wait); + init_rwsem(&fs_info->scrub_super_lock); fs_info->scrub_workers_refcnt = 0; #ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY fs_info->check_integrity_print_mask = 0; @@ -2263,7 +2272,7 @@ int open_ctree(struct super_block *sb, sizeof(struct btrfs_key)); set_bit(BTRFS_INODE_DUMMY, &BTRFS_I(fs_info->btree_inode)->runtime_flags); - btrfs_insert_inode_hash(fs_info->btree_inode); + insert_inode_hash(fs_info->btree_inode); spin_lock_init(&fs_info->block_group_cache_lock); fs_info->block_group_cache_tree = RB_ROOT; @@ -2661,7 +2670,6 @@ retry_root_backup: btrfs_set_root_node(&tree_root->root_item, tree_root->node); tree_root->commit_root = btrfs_root_node(tree_root); - btrfs_set_root_refs(&tree_root->root_item, 1); location.objectid = BTRFS_EXTENT_TREE_OBJECTID; location.type = BTRFS_ROOT_ITEM_KEY; @@ -3440,7 +3448,10 @@ static int write_all_supers(struct btrfs_root *root, int max_mirrors) int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root *root, int max_mirrors) { - return write_all_supers(root, max_mirrors); + int ret; + + ret = write_all_supers(root, max_mirrors); + return ret; } /* Drop a fs root from the radix tree and free it. */ @@ -3517,6 +3528,7 @@ int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info) int btrfs_commit_super(struct btrfs_root *root) { struct btrfs_trans_handle *trans; + int ret; mutex_lock(&root->fs_info->cleaner_mutex); btrfs_run_delayed_iputs(root); @@ -3530,7 +3542,25 @@ int btrfs_commit_super(struct btrfs_root *root) trans = btrfs_join_transaction(root); if (IS_ERR(trans)) return PTR_ERR(trans); - return btrfs_commit_transaction(trans, root); + ret = btrfs_commit_transaction(trans, root); + if (ret) + return ret; + /* run commit again to drop the original snapshot */ + trans = btrfs_join_transaction(root); + if (IS_ERR(trans)) + return PTR_ERR(trans); + ret = btrfs_commit_transaction(trans, root); + if (ret) + return ret; + ret = btrfs_write_and_wait_transaction(NULL, root); + if (ret) { + btrfs_error(root->fs_info, ret, + "Failed to sync btree inode to disk."); + return ret; + } + + ret = write_ctree_super(NULL, root, 0); + return ret; } int close_ctree(struct btrfs_root *root) @@ -3584,12 +3614,12 @@ int close_ctree(struct btrfs_root *root) percpu_counter_sum(&fs_info->delalloc_bytes)); } - del_fs_roots(fs_info); - btrfs_free_block_groups(fs_info); btrfs_stop_all_workers(fs_info); + del_fs_roots(fs_info); + free_root_pointers(fs_info, 1); iput(fs_info->btree_inode); @@ -3639,20 +3669,10 @@ int btrfs_set_buffer_uptodate(struct extent_buffer *buf) void btrfs_mark_buffer_dirty(struct extent_buffer *buf) { - struct btrfs_root *root; + struct btrfs_root *root = BTRFS_I(buf->pages[0]->mapping->host)->root; u64 transid = btrfs_header_generation(buf); int was_dirty; -#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS - /* - * This is a fast path so only do this check if we have sanity tests - * enabled. Normal people shouldn't be marking dummy buffers as dirty - * outside of the sanity tests. - */ - if (unlikely(test_bit(EXTENT_BUFFER_DUMMY, &buf->bflags))) - return; -#endif - root = BTRFS_I(buf->pages[0]->mapping->host)->root; btrfs_assert_tree_locked(buf); if (transid != root->fs_info->generation) WARN(1, KERN_CRIT "btrfs transid mismatch buffer %llu, " @@ -3782,8 +3802,7 @@ static void btrfs_destroy_all_ordered_extents(struct btrfs_fs_info *fs_info) while (!list_empty(&splice)) { root = list_first_entry(&splice, struct btrfs_root, ordered_root); - list_move_tail(&root->ordered_root, - &fs_info->ordered_roots); + list_del_init(&root->ordered_root); btrfs_destroy_ordered_extents(root); @@ -3861,6 +3880,24 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans, return ret; } +static void btrfs_evict_pending_snapshots(struct btrfs_transaction *t) +{ + struct btrfs_pending_snapshot *snapshot; + struct list_head splice; + + INIT_LIST_HEAD(&splice); + + list_splice_init(&t->pending_snapshots, &splice); + + while (!list_empty(&splice)) { + snapshot = list_entry(splice.next, + struct btrfs_pending_snapshot, + list); + snapshot->error = -ECANCELED; + list_del_init(&snapshot->list); + } +} + static void btrfs_destroy_delalloc_inodes(struct btrfs_root *root) { struct btrfs_inode *btrfs_inode; @@ -3990,13 +4027,15 @@ again: void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans, struct btrfs_root *root) { - btrfs_destroy_ordered_operations(cur_trans, root); - btrfs_destroy_delayed_refs(cur_trans, root); + btrfs_block_rsv_release(root, &root->fs_info->trans_block_rsv, + cur_trans->dirty_pages.dirty_bytes); cur_trans->state = TRANS_STATE_COMMIT_START; wake_up(&root->fs_info->transaction_blocked_wait); + btrfs_evict_pending_snapshots(cur_trans); + cur_trans->state = TRANS_STATE_UNBLOCKED; wake_up(&root->fs_info->transaction_wait); @@ -4020,51 +4059,63 @@ void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans, static int btrfs_cleanup_transaction(struct btrfs_root *root) { struct btrfs_transaction *t; + LIST_HEAD(list); mutex_lock(&root->fs_info->transaction_kthread_mutex); spin_lock(&root->fs_info->trans_lock); - while (!list_empty(&root->fs_info->trans_list)) { - t = list_first_entry(&root->fs_info->trans_list, - struct btrfs_transaction, list); - if (t->state >= TRANS_STATE_COMMIT_START) { - atomic_inc(&t->use_count); - spin_unlock(&root->fs_info->trans_lock); - btrfs_wait_for_commit(root, t->transid); - btrfs_put_transaction(t); - spin_lock(&root->fs_info->trans_lock); - continue; - } - if (t == root->fs_info->running_transaction) { - t->state = TRANS_STATE_COMMIT_DOING; - spin_unlock(&root->fs_info->trans_lock); - /* - * We wait for 0 num_writers since we don't hold a trans - * handle open currently for this transaction. - */ - wait_event(t->writer_wait, - atomic_read(&t->num_writers) == 0); - } else { - spin_unlock(&root->fs_info->trans_lock); - } - btrfs_cleanup_one_transaction(t, root); + list_splice_init(&root->fs_info->trans_list, &list); + root->fs_info->running_transaction = NULL; + spin_unlock(&root->fs_info->trans_lock); - spin_lock(&root->fs_info->trans_lock); - if (t == root->fs_info->running_transaction) - root->fs_info->running_transaction = NULL; - list_del_init(&t->list); - spin_unlock(&root->fs_info->trans_lock); + while (!list_empty(&list)) { + t = list_entry(list.next, struct btrfs_transaction, list); - btrfs_put_transaction(t); - trace_btrfs_transaction_commit(root); - spin_lock(&root->fs_info->trans_lock); + btrfs_destroy_ordered_operations(t, root); + + btrfs_destroy_all_ordered_extents(root->fs_info); + + btrfs_destroy_delayed_refs(t, root); + + /* + * FIXME: cleanup wait for commit + * We needn't acquire the lock here, because we are during + * the umount, there is no other task which will change it. + */ + t->state = TRANS_STATE_COMMIT_START; + smp_mb(); + if (waitqueue_active(&root->fs_info->transaction_blocked_wait)) + wake_up(&root->fs_info->transaction_blocked_wait); + + btrfs_evict_pending_snapshots(t); + + t->state = TRANS_STATE_UNBLOCKED; + smp_mb(); + if (waitqueue_active(&root->fs_info->transaction_wait)) + wake_up(&root->fs_info->transaction_wait); + + btrfs_destroy_delayed_inodes(root); + btrfs_assert_delayed_root_empty(root); + + btrfs_destroy_all_delalloc_inodes(root->fs_info); + + btrfs_destroy_marked_extents(root, &t->dirty_pages, + EXTENT_DIRTY); + + btrfs_destroy_pinned_extent(root, + root->fs_info->pinned_extents); + + t->state = TRANS_STATE_COMPLETED; + smp_mb(); + if (waitqueue_active(&t->commit_wait)) + wake_up(&t->commit_wait); + + atomic_set(&t->use_count, 0); + list_del_init(&t->list); + memset(t, 0, sizeof(*t)); + kmem_cache_free(btrfs_transaction_cachep, t); } - spin_unlock(&root->fs_info->trans_lock); - btrfs_destroy_all_ordered_extents(root->fs_info); - btrfs_destroy_delayed_inodes(root); - btrfs_assert_delayed_root_empty(root); - btrfs_destroy_pinned_extent(root, root->fs_info->pinned_extents); - btrfs_destroy_all_delalloc_inodes(root->fs_info); + mutex_unlock(&root->fs_info->transaction_kthread_mutex); return 0; diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h index 53059df..5ce2a7d 100644 --- a/fs/btrfs/disk-io.h +++ b/fs/btrfs/disk-io.h @@ -86,10 +86,6 @@ void btrfs_drop_and_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root); void btrfs_free_fs_root(struct btrfs_root *root); -#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS -struct btrfs_root *btrfs_alloc_dummy_root(void); -#endif - /* * This function is used to grab the root, and avoid it is freed when we * access it. But it doesn't ensure that the tree is not dropped. diff --git a/fs/btrfs/export.c b/fs/btrfs/export.c index 41422a3..4b86916 100644 --- a/fs/btrfs/export.c +++ b/fs/btrfs/export.c @@ -5,6 +5,7 @@ #include "btrfs_inode.h" #include "print-tree.h" #include "export.h" +#include "compat.h" #define BTRFS_FID_SIZE_NON_CONNECTABLE (offsetof(struct btrfs_fid, \ parent_objectid) / 4) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 45d98d0..d58bef1 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -25,6 +25,7 @@ #include <linux/slab.h> #include <linux/ratelimit.h> #include <linux/percpu_counter.h> +#include "compat.h" #include "hash.h" #include "ctree.h" #include "disk-io.h" @@ -1550,8 +1551,9 @@ again: if (ret && !insert) { err = -ENOENT; goto out; - } else if (WARN_ON(ret)) { + } else if (ret) { err = -EIO; + WARN_ON(1); goto out; } @@ -1977,6 +1979,7 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, struct btrfs_extent_item *item; u64 refs; int ret; + int err = 0; path = btrfs_alloc_path(); if (!path) @@ -1989,9 +1992,14 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, path, bytenr, num_bytes, parent, root_objectid, owner, offset, refs_to_add, extent_op); - if (ret != -EAGAIN) + if (ret == 0) goto out; + if (ret != -EAGAIN) { + err = ret; + goto out; + } + leaf = path->nodes[0]; item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); refs = btrfs_extent_refs(leaf, item); @@ -2013,7 +2021,7 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, btrfs_abort_transaction(trans, root, ret); out: btrfs_free_path(path); - return ret; + return err; } static int run_delayed_data_ref(struct btrfs_trans_handle *trans, @@ -2129,28 +2137,15 @@ again: } if (ret > 0) { if (metadata) { - if (path->slots[0] > 0) { - path->slots[0]--; - btrfs_item_key_to_cpu(path->nodes[0], &key, - path->slots[0]); - if (key.objectid == node->bytenr && - key.type == BTRFS_EXTENT_ITEM_KEY && - key.offset == node->num_bytes) - ret = 0; - } - if (ret > 0) { - btrfs_release_path(path); - metadata = 0; + btrfs_release_path(path); + metadata = 0; - key.objectid = node->bytenr; - key.offset = node->num_bytes; - key.type = BTRFS_EXTENT_ITEM_KEY; - goto again; - } - } else { - err = -EIO; - goto out; + key.offset = node->num_bytes; + key.type = BTRFS_EXTENT_ITEM_KEY; + goto again; } + err = -EIO; + goto out; } leaf = path->nodes[0]; @@ -2239,12 +2234,8 @@ static int run_one_delayed_ref(struct btrfs_trans_handle *trans, { int ret = 0; - if (trans->aborted) { - if (insert_reserved) - btrfs_pin_extent(root, node->bytenr, - node->num_bytes, 1); + if (trans->aborted) return 0; - } if (btrfs_delayed_ref_is_head(node)) { struct btrfs_delayed_ref_head *head; @@ -2420,14 +2411,6 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, btrfs_free_delayed_extent_op(extent_op); if (ret) { - /* - * Need to reset must_insert_reserved if - * there was an error so the abort stuff - * can cleanup the reserved space - * properly. - */ - if (must_insert_reserved) - locked_ref->must_insert_reserved = 1; btrfs_debug(fs_info, "run_delayed_extent_op returned %d", ret); spin_lock(&delayed_refs->lock); btrfs_delayed_ref_unlock(locked_ref); @@ -3214,7 +3197,8 @@ again: if (ret) goto out_put; - ret = btrfs_truncate_free_space_cache(root, trans, inode); + ret = btrfs_truncate_free_space_cache(root, trans, path, + inode); if (ret) goto out_put; } @@ -3334,9 +3318,10 @@ again: last = cache->key.objectid + cache->key.offset; err = write_one_cache_group(trans, root, path, cache); - btrfs_put_block_group(cache); if (err) /* File system offline */ goto out; + + btrfs_put_block_group(cache); } while (1) { @@ -3620,9 +3605,10 @@ int btrfs_check_data_free_space(struct inode *inode, u64 bytes) /* make sure bytes are sectorsize aligned */ bytes = ALIGN(bytes, root->sectorsize); - if (btrfs_is_free_space_inode(inode)) { + if (root == root->fs_info->tree_root || + BTRFS_I(inode)->location.objectid == BTRFS_FREE_INO_OBJECTID) { + alloc_chunk = 0; committed = 1; - ASSERT(current->journal_info); } data_sinfo = fs_info->data_sinfo; @@ -3650,16 +3636,6 @@ again: spin_unlock(&data_sinfo->lock); alloc: alloc_target = btrfs_get_alloc_profile(root, 1); - /* - * It is ugly that we don't call nolock join - * transaction for the free space inode case here. - * But it is safe because we only do the data space - * reservation for the free space cache in the - * transaction context, the common join transaction - * just increase the counter of the current transaction - * handler, doesn't try to acquire the trans_lock of - * the fs. - */ trans = btrfs_join_transaction(root); if (IS_ERR(trans)) return PTR_ERR(trans); @@ -3705,9 +3681,6 @@ commit_trans: goto again; } - trace_btrfs_space_reservation(root->fs_info, - "space_info:enospc", - data_sinfo->flags, bytes, 1); return -ENOSPC; } data_sinfo->bytes_may_use += bytes; @@ -4016,26 +3989,12 @@ static void btrfs_writeback_inodes_sb_nr(struct btrfs_root *root, * the filesystem is readonly(all dirty pages are written to * the disk). */ - btrfs_start_delalloc_roots(root->fs_info, 0); + btrfs_start_all_delalloc_inodes(root->fs_info, 0); if (!current->journal_info) - btrfs_wait_ordered_roots(root->fs_info, -1); + btrfs_wait_all_ordered_extents(root->fs_info); } } -static inline int calc_reclaim_items_nr(struct btrfs_root *root, u64 to_reclaim) -{ - u64 bytes; - int nr; - - bytes = btrfs_calc_trans_metadata_size(root, 1); - nr = (int)div64_u64(to_reclaim, bytes); - if (!nr) - nr = 1; - return nr; -} - -#define EXTENT_SIZE_PER_ITEM (256 * 1024) - /* * shrink metadata reservation for delalloc */ @@ -4048,30 +4007,24 @@ static void shrink_delalloc(struct btrfs_root *root, u64 to_reclaim, u64 orig, u64 delalloc_bytes; u64 max_reclaim; long time_left; - unsigned long nr_pages; - int loops; - int items; + unsigned long nr_pages = (2 * 1024 * 1024) >> PAGE_CACHE_SHIFT; + int loops = 0; enum btrfs_reserve_flush_enum flush; - /* Calc the number of the pages we need flush for space reservation */ - items = calc_reclaim_items_nr(root, to_reclaim); - to_reclaim = items * EXTENT_SIZE_PER_ITEM; - trans = (struct btrfs_trans_handle *)current->journal_info; block_rsv = &root->fs_info->delalloc_block_rsv; space_info = block_rsv->space_info; + smp_mb(); delalloc_bytes = percpu_counter_sum_positive( &root->fs_info->delalloc_bytes); if (delalloc_bytes == 0) { if (trans) return; - if (wait_ordered) - btrfs_wait_ordered_roots(root->fs_info, items); + btrfs_wait_all_ordered_extents(root->fs_info); return; } - loops = 0; while (delalloc_bytes && loops < 3) { max_reclaim = min(delalloc_bytes, to_reclaim); nr_pages = max_reclaim >> PAGE_CACHE_SHIFT; @@ -4080,19 +4033,9 @@ static void shrink_delalloc(struct btrfs_root *root, u64 to_reclaim, u64 orig, * We need to wait for the async pages to actually start before * we do anything. */ - max_reclaim = atomic_read(&root->fs_info->async_delalloc_pages); - if (!max_reclaim) - goto skip_async; - - if (max_reclaim <= nr_pages) - max_reclaim = 0; - else - max_reclaim -= nr_pages; - wait_event(root->fs_info->async_submit_wait, - atomic_read(&root->fs_info->async_delalloc_pages) <= - (int)max_reclaim); -skip_async: + !atomic_read(&root->fs_info->async_delalloc_pages)); + if (!trans) flush = BTRFS_RESERVE_FLUSH_ALL; else @@ -4106,12 +4049,13 @@ skip_async: loops++; if (wait_ordered && !trans) { - btrfs_wait_ordered_roots(root->fs_info, items); + btrfs_wait_all_ordered_extents(root->fs_info); } else { time_left = schedule_timeout_killable(1); if (time_left) break; } + smp_mb(); delalloc_bytes = percpu_counter_sum_positive( &root->fs_info->delalloc_bytes); } @@ -4196,11 +4140,16 @@ static int flush_space(struct btrfs_root *root, switch (state) { case FLUSH_DELAYED_ITEMS_NR: case FLUSH_DELAYED_ITEMS: - if (state == FLUSH_DELAYED_ITEMS_NR) - nr = calc_reclaim_items_nr(root, num_bytes) * 2; - else - nr = -1; + if (state == FLUSH_DELAYED_ITEMS_NR) { + u64 bytes = btrfs_calc_trans_metadata_size(root, 1); + nr = (int)div64_u64(num_bytes, bytes); + if (!nr) + nr = 1; + nr *= 2; + } else { + nr = -1; + } trans = btrfs_join_transaction(root); if (IS_ERR(trans)) { ret = PTR_ERR(trans); @@ -4383,10 +4332,6 @@ out: !block_rsv_use_bytes(global_rsv, orig_bytes)) ret = 0; } - if (ret == -ENOSPC) - trace_btrfs_space_reservation(root->fs_info, - "space_info:enospc", - space_info->flags, orig_bytes, 1); if (flushing) { spin_lock(&space_info->lock); space_info->flush = 0; @@ -5041,7 +4986,7 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes) mutex_unlock(&BTRFS_I(inode)->delalloc_mutex); if (to_reserve) - trace_btrfs_space_reservation(root->fs_info, "delalloc", + trace_btrfs_space_reservation(root->fs_info,"delalloc", btrfs_ino(inode), to_reserve, 1); block_rsv_add_bytes(block_rsv, to_reserve, 1); @@ -5319,8 +5264,6 @@ static int pin_down_extent(struct btrfs_root *root, set_extent_dirty(root->fs_info->pinned_extents, bytenr, bytenr + num_bytes - 1, GFP_NOFS | __GFP_NOFAIL); - if (reserved) - trace_btrfs_reserved_extent_free(root, bytenr, num_bytes); return 0; } @@ -5775,8 +5718,9 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, } extent_slot = path->slots[0]; } - } else if (WARN_ON(ret == -ENOENT)) { + } else if (ret == -ENOENT) { btrfs_print_leaf(extent_root, path->nodes[0]); + WARN_ON(1); btrfs_err(info, "unable to find ref byte nr %llu parent %llu root %llu owner %llu offset %llu", bytenr, parent, root_objectid, owner_objectid, @@ -6023,7 +5967,6 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans, btrfs_add_free_space(cache, buf->start, buf->len); btrfs_update_reserved_bytes(cache, buf->len, RESERVE_FREE); - trace_btrfs_reserved_extent_free(root, buf->start, buf->len); pin = 0; } out: @@ -6651,6 +6594,8 @@ again: } } + trace_btrfs_reserved_extent_alloc(root, ins->objectid, ins->offset); + return ret; } @@ -6762,7 +6707,6 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans, ins->objectid, ins->offset); BUG(); } - trace_btrfs_reserved_extent_alloc(root, ins->objectid, ins->offset); return ret; } @@ -6787,18 +6731,13 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, size += sizeof(*block_info); path = btrfs_alloc_path(); - if (!path) { - btrfs_free_and_pin_reserved_extent(root, ins->objectid, - root->leafsize); + if (!path) return -ENOMEM; - } path->leave_spinning = 1; ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path, ins, size); if (ret) { - btrfs_free_and_pin_reserved_extent(root, ins->objectid, - root->leafsize); btrfs_free_path(path); return ret; } @@ -6840,8 +6779,6 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, ins->objectid, ins->offset); BUG(); } - - trace_btrfs_reserved_extent_alloc(root, ins->objectid, root->leafsize); return ret; } @@ -8046,7 +7983,7 @@ u64 btrfs_account_ro_block_groups_free_space(struct btrfs_space_info *sinfo) spin_lock(&sinfo->lock); - for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) + for(i = 0; i < BTRFS_NR_RAID_TYPES; i++) if (!list_empty(&sinfo->block_groups[i])) free_bytes += __btrfs_get_ro_block_group_free_space( &sinfo->block_groups[i]); @@ -8334,14 +8271,15 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info) release_global_block_rsv(info); - while (!list_empty(&info->space_info)) { + while(!list_empty(&info->space_info)) { space_info = list_entry(info->space_info.next, struct btrfs_space_info, list); if (btrfs_test_opt(info->tree_root, ENOSPC_DEBUG)) { - if (WARN_ON(space_info->bytes_pinned > 0 || + if (space_info->bytes_pinned > 0 || space_info->bytes_reserved > 0 || - space_info->bytes_may_use > 0)) { + space_info->bytes_may_use > 0) { + WARN_ON(1); dump_space_info(space_info, 0, 0); } } diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index ff43802..51731b7 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -13,13 +13,13 @@ #include <linux/cleancache.h> #include "extent_io.h" #include "extent_map.h" +#include "compat.h" #include "ctree.h" #include "btrfs_inode.h" #include "volumes.h" #include "check-integrity.h" #include "locking.h" #include "rcu-string.h" -#include "backref.h" static struct kmem_cache *extent_state_cache; static struct kmem_cache *extent_buffer_cache; @@ -1597,10 +1597,11 @@ done: * * 1 is returned if we find something, 0 if nothing was in the tree */ -STATIC u64 find_lock_delalloc_range(struct inode *inode, - struct extent_io_tree *tree, - struct page *locked_page, u64 *start, - u64 *end, u64 max_bytes) +static noinline u64 find_lock_delalloc_range(struct inode *inode, + struct extent_io_tree *tree, + struct page *locked_page, + u64 *start, u64 *end, + u64 max_bytes) { u64 delalloc_start; u64 delalloc_end; @@ -1739,8 +1740,10 @@ u64 count_range_bits(struct extent_io_tree *tree, u64 last = 0; int found = 0; - if (WARN_ON(search_end <= cur_start)) + if (search_end <= cur_start) { + WARN_ON(1); return 0; + } spin_lock(&tree->lock); if (cur_start == 0 && bits == EXTENT_DIRTY) { @@ -1952,6 +1955,11 @@ static int free_io_failure(struct inode *inode, struct io_failure_record *rec, return err; } +static void repair_io_failure_callback(struct bio *bio, int err) +{ + complete(bio->bi_private); +} + /* * this bypasses the standard btrfs submit functions deliberately, as * the standard behavior is to write all copies in a raid setup. here we only @@ -1968,13 +1976,13 @@ int repair_io_failure(struct btrfs_fs_info *fs_info, u64 start, { struct bio *bio; struct btrfs_device *dev; + DECLARE_COMPLETION_ONSTACK(compl); u64 map_length = 0; u64 sector; struct btrfs_bio *bbio = NULL; struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree; int ret; - ASSERT(!(fs_info->sb->s_flags & MS_RDONLY)); BUG_ON(!mirror_num); /* we can't repair anything in raid56 yet */ @@ -1984,6 +1992,8 @@ int repair_io_failure(struct btrfs_fs_info *fs_info, u64 start, bio = btrfs_io_bio_alloc(GFP_NOFS, 1); if (!bio) return -EIO; + bio->bi_private = &compl; + bio->bi_end_io = repair_io_failure_callback; bio->bi_size = 0; map_length = length; @@ -2004,8 +2014,10 @@ int repair_io_failure(struct btrfs_fs_info *fs_info, u64 start, } bio->bi_bdev = dev->bdev; bio_add_page(bio, page, length, start - page_offset(page)); + btrfsic_submit_bio(WRITE_SYNC, bio); + wait_for_completion(&compl); - if (btrfsic_submit_bio_wait(WRITE_SYNC, bio)) { + if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) { /* try to remap that extent elsewhere? */ bio_put(bio); btrfs_dev_stat_inc_and_print(dev, BTRFS_DEV_STAT_WRITE_ERRS); @@ -2027,9 +2039,6 @@ int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb, unsigned long i, num_pages = num_extent_pages(eb->start, eb->len); int ret = 0; - if (root->fs_info->sb->s_flags & MS_RDONLY) - return -EROFS; - for (i = 0; i < num_pages; i++) { struct page *p = extent_buffer_page(eb, i); ret = repair_io_failure(root->fs_info, start, PAGE_CACHE_SIZE, @@ -2051,12 +2060,12 @@ static int clean_io_failure(u64 start, struct page *page) u64 private; u64 private_failure; struct io_failure_record *failrec; - struct inode *inode = page->mapping->host; - struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; + struct btrfs_fs_info *fs_info; struct extent_state *state; int num_copies; int did_repair = 0; int ret; + struct inode *inode = page->mapping->host; private = 0; ret = count_range_bits(&BTRFS_I(inode)->io_failure_tree, &private, @@ -2079,8 +2088,6 @@ static int clean_io_failure(u64 start, struct page *page) did_repair = 1; goto out; } - if (fs_info->sb->s_flags & MS_RDONLY) - goto out; spin_lock(&BTRFS_I(inode)->io_tree.lock); state = find_first_extent_bit_state(&BTRFS_I(inode)->io_tree, @@ -2090,6 +2097,7 @@ static int clean_io_failure(u64 start, struct page *page) if (state && state->start <= failrec->start && state->end >= failrec->start + failrec->len - 1) { + fs_info = BTRFS_I(inode)->root->fs_info; num_copies = btrfs_num_copies(fs_info, failrec->logical, failrec->len); if (num_copies > 1) { @@ -3561,8 +3569,9 @@ retry: * but no sense in crashing the users box for something * we can survive anyway. */ - if (WARN_ON(!eb)) { + if (!eb) { spin_unlock(&mapping->private_lock); + WARN_ON(1); continue; } @@ -4029,7 +4038,7 @@ static struct extent_map *get_extent_skip_holes(struct inode *inode, if (offset >= last) return NULL; - while (1) { + while(1) { len = last - offset; if (len == 0) break; @@ -4053,19 +4062,6 @@ static struct extent_map *get_extent_skip_holes(struct inode *inode, return NULL; } -static noinline int count_ext_ref(u64 inum, u64 offset, u64 root_id, void *ctx) -{ - unsigned long cnt = *((unsigned long *)ctx); - - cnt++; - *((unsigned long *)ctx) = cnt; - - /* Now we're sure that the extent is shared. */ - if (cnt > 1) - return 1; - return 0; -} - int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len, get_extent_t *get_extent) { @@ -4132,7 +4128,7 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, last = found_key.offset; last_for_get_extent = last + 1; } - btrfs_release_path(path); + btrfs_free_path(path); /* * we might have some extents allocated but more delalloc past those @@ -4202,24 +4198,7 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, flags |= (FIEMAP_EXTENT_DELALLOC | FIEMAP_EXTENT_UNKNOWN); } else { - unsigned long ref_cnt = 0; - disko = em->block_start + offset_in_extent; - - /* - * As btrfs supports shared space, this information - * can be exported to userspace tools via - * flag FIEMAP_EXTENT_SHARED. - */ - ret = iterate_inodes_from_logical( - em->block_start, - BTRFS_I(inode)->root->fs_info, - path, count_ext_ref, &ref_cnt); - if (ret < 0 && ret != -ENOENT) - goto out_free; - - if (ref_cnt > 1) - flags |= FIEMAP_EXTENT_SHARED; } if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) flags |= FIEMAP_EXTENT_ENCODED; @@ -4251,7 +4230,6 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, out_free: free_extent_map(em); out: - btrfs_free_path(path); unlock_extent_cached(&BTRFS_I(inode)->io_tree, start, start + len - 1, &cached_state, GFP_NOFS); return ret; @@ -4477,23 +4455,6 @@ static void mark_extent_buffer_accessed(struct extent_buffer *eb) } } -struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree, - u64 start) -{ - struct extent_buffer *eb; - - rcu_read_lock(); - eb = radix_tree_lookup(&tree->buffer, start >> PAGE_CACHE_SHIFT); - if (eb && atomic_inc_not_zero(&eb->refs)) { - rcu_read_unlock(); - mark_extent_buffer_accessed(eb); - return eb; - } - rcu_read_unlock(); - - return NULL; -} - struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, u64 start, unsigned long len) { @@ -4507,10 +4468,14 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, int uptodate = 1; int ret; - - eb = find_extent_buffer(tree, start); - if (eb) + rcu_read_lock(); + eb = radix_tree_lookup(&tree->buffer, start >> PAGE_CACHE_SHIFT); + if (eb && atomic_inc_not_zero(&eb->refs)) { + rcu_read_unlock(); + mark_extent_buffer_accessed(eb); return eb; + } + rcu_read_unlock(); eb = __alloc_extent_buffer(tree, start, len, GFP_NOFS); if (!eb) @@ -4569,17 +4534,24 @@ again: spin_lock(&tree->buffer_lock); ret = radix_tree_insert(&tree->buffer, start >> PAGE_CACHE_SHIFT, eb); - spin_unlock(&tree->buffer_lock); - radix_tree_preload_end(); if (ret == -EEXIST) { - exists = find_extent_buffer(tree, start); - if (exists) - goto free_eb; - else + exists = radix_tree_lookup(&tree->buffer, + start >> PAGE_CACHE_SHIFT); + if (!atomic_inc_not_zero(&exists->refs)) { + spin_unlock(&tree->buffer_lock); + radix_tree_preload_end(); + exists = NULL; goto again; + } + spin_unlock(&tree->buffer_lock); + radix_tree_preload_end(); + mark_extent_buffer_accessed(exists); + goto free_eb; } /* add one reference for the tree */ check_buffer_tree_ref(eb); + spin_unlock(&tree->buffer_lock); + radix_tree_preload_end(); /* * there is a race where release page may have @@ -4610,6 +4582,23 @@ free_eb: return exists; } +struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree, + u64 start, unsigned long len) +{ + struct extent_buffer *eb; + + rcu_read_lock(); + eb = radix_tree_lookup(&tree->buffer, start >> PAGE_CACHE_SHIFT); + if (eb && atomic_inc_not_zero(&eb->refs)) { + rcu_read_unlock(); + mark_extent_buffer_accessed(eb); + return eb; + } + rcu_read_unlock(); + + return NULL; +} + static inline void btrfs_release_extent_buffer_rcu(struct rcu_head *head) { struct extent_buffer *eb = @@ -5073,6 +5062,23 @@ void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src, } } +static void move_pages(struct page *dst_page, struct page *src_page, + unsigned long dst_off, unsigned long src_off, + unsigned long len) +{ + char *dst_kaddr = page_address(dst_page); + if (dst_page == src_page) { + memmove(dst_kaddr + dst_off, dst_kaddr + src_off, len); + } else { + char *src_kaddr = page_address(src_page); + char *p = dst_kaddr + dst_off + len; + char *s = src_kaddr + src_off + len; + + while (len--) + *--p = *--s; + } +} + static inline bool areas_overlap(unsigned long src, unsigned long dst, unsigned long len) { unsigned long distance = (src > dst) ? src - dst : dst - src; @@ -5183,7 +5189,7 @@ void memmove_extent_buffer(struct extent_buffer *dst, unsigned long dst_offset, cur = min_t(unsigned long, len, src_off_in_page + 1); cur = min(cur, dst_off_in_page + 1); - copy_pages(extent_buffer_page(dst, dst_i), + move_pages(extent_buffer_page(dst, dst_i), extent_buffer_page(dst, src_i), dst_off_in_page - cur + 1, src_off_in_page - cur + 1, cur); diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 19620c5..6dbc645 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -271,7 +271,7 @@ struct extent_buffer *alloc_extent_buffer(struct extent_io_tree *tree, struct extent_buffer *alloc_dummy_extent_buffer(u64 start, unsigned long len); struct extent_buffer *btrfs_clone_extent_buffer(struct extent_buffer *src); struct extent_buffer *find_extent_buffer(struct extent_io_tree *tree, - u64 start); + u64 start, unsigned long len); void free_extent_buffer(struct extent_buffer *eb); void free_extent_buffer_stale(struct extent_buffer *eb); #define WAIT_NONE 0 @@ -345,10 +345,4 @@ int repair_io_failure(struct btrfs_fs_info *fs_info, u64 start, int end_extent_writepage(struct page *page, int err, u64 start, u64 end); int repair_eb_io_failure(struct btrfs_root *root, struct extent_buffer *eb, int mirror_num); -#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS -noinline u64 find_lock_delalloc_range(struct inode *inode, - struct extent_io_tree *tree, - struct page *locked_page, u64 *start, - u64 *end, u64 max_bytes); -#endif #endif diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h index 93fba71..61adc44 100644 --- a/fs/btrfs/extent_map.h +++ b/fs/btrfs/extent_map.h @@ -3,10 +3,10 @@ #include <linux/rbtree.h> -#define EXTENT_MAP_LAST_BYTE ((u64)-4) -#define EXTENT_MAP_HOLE ((u64)-3) -#define EXTENT_MAP_INLINE ((u64)-2) -#define EXTENT_MAP_DELALLOC ((u64)-1) +#define EXTENT_MAP_LAST_BYTE (u64)-4 +#define EXTENT_MAP_HOLE (u64)-3 +#define EXTENT_MAP_INLINE (u64)-2 +#define EXTENT_MAP_DELALLOC (u64)-1 /* bits for the flags field */ #define EXTENT_FLAG_PINNED 0 /* this entry not yet on disk, don't free it */ diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 6f38488..4f53159 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -329,9 +329,6 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end, u64 csum_end; u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy); - ASSERT(start == ALIGN(start, root->sectorsize) && - (end + 1) == ALIGN(end + 1, root->sectorsize)); - path = btrfs_alloc_path(); if (!path) return -ENOMEM; @@ -849,8 +846,10 @@ insert: path->leave_spinning = 0; if (ret < 0) goto fail_unlock; - if (WARN_ON(ret != 0)) + if (ret != 0) { + WARN_ON(1); goto fail_unlock; + } leaf = path->nodes[0]; csum: item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 82d0342..72da4df 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -39,6 +39,7 @@ #include "print-tree.h" #include "tree-log.h" #include "locking.h" +#include "compat.h" #include "volumes.h" static struct kmem_cache *btrfs_inode_defrag_cachep; @@ -369,7 +370,7 @@ int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info) u64 root_objectid = 0; atomic_inc(&fs_info->defrag_running); - while (1) { + while(1) { /* Pause the auto defragger. */ if (test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state)) @@ -1280,7 +1281,6 @@ again: } wait_on_page_writeback(pages[i]); } - faili = num_pages - 1; err = 0; if (start_pos < inode->i_size) { struct btrfs_ordered_extent *ordered; @@ -1299,10 +1299,8 @@ again: unlock_page(pages[i]); page_cache_release(pages[i]); } - err = btrfs_wait_ordered_range(inode, start_pos, - last_pos - start_pos); - if (err) - goto fail; + btrfs_wait_ordered_range(inode, start_pos, + last_pos - start_pos); goto again; } if (ordered) @@ -1811,13 +1809,8 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) atomic_inc(&root->log_batch); full_sync = test_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &BTRFS_I(inode)->runtime_flags); - if (full_sync) { - ret = btrfs_wait_ordered_range(inode, start, end - start + 1); - if (ret) { - mutex_unlock(&inode->i_mutex); - goto out; - } - } + if (full_sync) + btrfs_wait_ordered_range(inode, start, end - start + 1); atomic_inc(&root->log_batch); /* @@ -1883,20 +1876,27 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) mutex_unlock(&inode->i_mutex); if (ret != BTRFS_NO_LOG_SYNC) { - if (!ret) { + if (ret > 0) { + /* + * If we didn't already wait for ordered extents we need + * to do that now. + */ + if (!full_sync) + btrfs_wait_ordered_range(inode, start, + end - start + 1); + ret = btrfs_commit_transaction(trans, root); + } else { ret = btrfs_sync_log(trans, root); - if (!ret) { + if (ret == 0) { ret = btrfs_end_transaction(trans, root); - goto out; + } else { + if (!full_sync) + btrfs_wait_ordered_range(inode, start, + end - + start + 1); + ret = btrfs_commit_transaction(trans, root); } } - if (!full_sync) { - ret = btrfs_wait_ordered_range(inode, start, - end - start + 1); - if (ret) - goto out; - } - ret = btrfs_commit_transaction(trans, root); } else { ret = btrfs_end_transaction(trans, root); } @@ -2067,9 +2067,7 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len) bool same_page = ((offset >> PAGE_CACHE_SHIFT) == ((offset + len - 1) >> PAGE_CACHE_SHIFT)); - ret = btrfs_wait_ordered_range(inode, offset, len); - if (ret) - return ret; + btrfs_wait_ordered_range(inode, offset, len); mutex_lock(&inode->i_mutex); /* @@ -2138,12 +2136,8 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len) btrfs_put_ordered_extent(ordered); unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend, &cached_state, GFP_NOFS); - ret = btrfs_wait_ordered_range(inode, lockstart, - lockend - lockstart + 1); - if (ret) { - mutex_unlock(&inode->i_mutex); - return ret; - } + btrfs_wait_ordered_range(inode, lockstart, + lockend - lockstart + 1); } path = btrfs_alloc_path(); @@ -2314,10 +2308,7 @@ static long btrfs_fallocate(struct file *file, int mode, * wait for ordered IO before we have any locks. We'll loop again * below with the locks held. */ - ret = btrfs_wait_ordered_range(inode, alloc_start, - alloc_end - alloc_start); - if (ret) - goto out; + btrfs_wait_ordered_range(inode, alloc_start, alloc_end - alloc_start); locked_end = alloc_end - 1; while (1) { @@ -2341,10 +2332,8 @@ static long btrfs_fallocate(struct file *file, int mode, * we can't wait on the range with the transaction * running or with the extent lock held */ - ret = btrfs_wait_ordered_range(inode, alloc_start, - alloc_end - alloc_start); - if (ret) - goto out; + btrfs_wait_ordered_range(inode, alloc_start, + alloc_end - alloc_start); } else { if (ordered) btrfs_put_ordered_extent(ordered); @@ -2416,12 +2405,14 @@ out_reserve_fail: static int find_desired_extent(struct inode *inode, loff_t *offset, int whence) { struct btrfs_root *root = BTRFS_I(inode)->root; - struct extent_map *em = NULL; + struct extent_map *em; struct extent_state *cached_state = NULL; u64 lockstart = *offset; u64 lockend = i_size_read(inode); u64 start = *offset; + u64 orig_start = *offset; u64 len = i_size_read(inode); + u64 last_end = 0; int ret = 0; lockend = max_t(u64, root->sectorsize, lockend); @@ -2438,35 +2429,89 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence) lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend, 0, &cached_state); - while (start < inode->i_size) { + /* + * Delalloc is such a pain. If we have a hole and we have pending + * delalloc for a portion of the hole we will get back a hole that + * exists for the entire range since it hasn't been actually written + * yet. So to take care of this case we need to look for an extent just + * before the position we want in case there is outstanding delalloc + * going on here. + */ + if (whence == SEEK_HOLE && start != 0) { + if (start <= root->sectorsize) + em = btrfs_get_extent_fiemap(inode, NULL, 0, 0, + root->sectorsize, 0); + else + em = btrfs_get_extent_fiemap(inode, NULL, 0, + start - root->sectorsize, + root->sectorsize, 0); + if (IS_ERR(em)) { + ret = PTR_ERR(em); + goto out; + } + last_end = em->start + em->len; + if (em->block_start == EXTENT_MAP_DELALLOC) + last_end = min_t(u64, last_end, inode->i_size); + free_extent_map(em); + } + + while (1) { em = btrfs_get_extent_fiemap(inode, NULL, 0, start, len, 0); if (IS_ERR(em)) { ret = PTR_ERR(em); - em = NULL; break; } - if (whence == SEEK_HOLE && - (em->block_start == EXTENT_MAP_HOLE || - test_bit(EXTENT_FLAG_PREALLOC, &em->flags))) - break; - else if (whence == SEEK_DATA && - (em->block_start != EXTENT_MAP_HOLE && - !test_bit(EXTENT_FLAG_PREALLOC, &em->flags))) - break; + if (em->block_start == EXTENT_MAP_HOLE) { + if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) { + if (last_end <= orig_start) { + free_extent_map(em); + ret = -ENXIO; + break; + } + } + + if (whence == SEEK_HOLE) { + *offset = start; + free_extent_map(em); + break; + } + } else { + if (whence == SEEK_DATA) { + if (em->block_start == EXTENT_MAP_DELALLOC) { + if (start >= inode->i_size) { + free_extent_map(em); + ret = -ENXIO; + break; + } + } + + if (!test_bit(EXTENT_FLAG_PREALLOC, + &em->flags)) { + *offset = start; + free_extent_map(em); + break; + } + } + } start = em->start + em->len; + last_end = em->start + em->len; + + if (em->block_start == EXTENT_MAP_DELALLOC) + last_end = min_t(u64, last_end, inode->i_size); + + if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) { + free_extent_map(em); + ret = -ENXIO; + break; + } free_extent_map(em); - em = NULL; cond_resched(); } - free_extent_map(em); - if (!ret) { - if (whence == SEEK_DATA && start >= inode->i_size) - ret = -ENXIO; - else - *offset = min_t(loff_t, start, inode->i_size); - } + if (!ret) + *offset = min(*offset, inode->i_size); +out: unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend, &cached_state, GFP_NOFS); return ret; diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 057be95..b4f9904 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -218,6 +218,7 @@ int btrfs_check_trunc_cache_free_space(struct btrfs_root *root, int btrfs_truncate_free_space_cache(struct btrfs_root *root, struct btrfs_trans_handle *trans, + struct btrfs_path *path, struct inode *inode) { int ret = 0; @@ -1008,13 +1009,8 @@ static int __btrfs_write_out_cache(struct btrfs_root *root, struct inode *inode, if (ret) goto out; - ret = btrfs_wait_ordered_range(inode, 0, (u64)-1); - if (ret) { - clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, inode->i_size - 1, - EXTENT_DIRTY | EXTENT_DELALLOC, 0, 0, NULL, - GFP_NOFS); - goto out; - } + + btrfs_wait_ordered_range(inode, 0, (u64)-1); key.objectid = BTRFS_FREE_SPACE_OBJECTID; key.offset = offset; @@ -2280,7 +2276,7 @@ u64 btrfs_alloc_from_cluster(struct btrfs_block_group_cache *block_group, goto out; entry = rb_entry(node, struct btrfs_free_space, offset_index); - while (1) { + while(1) { if (entry->bytes < bytes && entry->bytes > *max_extent_size) *max_extent_size = entry->bytes; @@ -2971,15 +2967,19 @@ out: int btrfs_write_out_ino_cache(struct btrfs_root *root, struct btrfs_trans_handle *trans, - struct btrfs_path *path, - struct inode *inode) + struct btrfs_path *path) { struct btrfs_free_space_ctl *ctl = root->free_ino_ctl; + struct inode *inode; int ret; if (!btrfs_test_opt(root, INODE_MAP_CACHE)) return 0; + inode = lookup_free_ino_inode(root, path); + if (IS_ERR(inode)) + return 0; + ret = __btrfs_write_out_cache(root, inode, ctl, NULL, trans, path, 0); if (ret) { btrfs_delalloc_release_metadata(inode, inode->i_size); @@ -2990,6 +2990,7 @@ int btrfs_write_out_ino_cache(struct btrfs_root *root, #endif } + iput(inode); return ret; } diff --git a/fs/btrfs/free-space-cache.h b/fs/btrfs/free-space-cache.h index 0cf4977..e737f92 100644 --- a/fs/btrfs/free-space-cache.h +++ b/fs/btrfs/free-space-cache.h @@ -58,6 +58,7 @@ int btrfs_check_trunc_cache_free_space(struct btrfs_root *root, struct btrfs_block_rsv *rsv); int btrfs_truncate_free_space_cache(struct btrfs_root *root, struct btrfs_trans_handle *trans, + struct btrfs_path *path, struct inode *inode); int load_free_space_cache(struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group); @@ -75,8 +76,7 @@ int load_free_ino_cache(struct btrfs_fs_info *fs_info, struct btrfs_root *root); int btrfs_write_out_ino_cache(struct btrfs_root *root, struct btrfs_trans_handle *trans, - struct btrfs_path *path, - struct inode *inode); + struct btrfs_path *path); void btrfs_init_free_space_ctl(struct btrfs_block_group_cache *block_group); int __btrfs_add_free_space(struct btrfs_free_space_ctl *ctl, diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c index ec82fae..e0b7034 100644 --- a/fs/btrfs/inode-item.c +++ b/fs/btrfs/inode-item.c @@ -369,7 +369,7 @@ static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans, goto out; leaf = path->nodes[0]; - item = btrfs_item_nr(path->slots[0]); + item = btrfs_item_nr(leaf, path->slots[0]); ptr = (unsigned long)btrfs_item_ptr(leaf, path->slots[0], char); ptr += btrfs_item_size(leaf, item) - ins_len; extref = (struct btrfs_inode_extref *)ptr; diff --git a/fs/btrfs/inode-map.c b/fs/btrfs/inode-map.c index ab485e5..2c66ddb 100644 --- a/fs/btrfs/inode-map.c +++ b/fs/btrfs/inode-map.c @@ -78,8 +78,10 @@ again: btrfs_transaction_in_commit(fs_info)) { leaf = path->nodes[0]; - if (WARN_ON(btrfs_header_nritems(leaf) == 0)) + if (btrfs_header_nritems(leaf) == 0) { + WARN_ON(1); break; + } /* * Save the key so we can advances forward @@ -235,7 +237,7 @@ again: start_caching(root); if (objectid <= root->cache_progress || - objectid >= root->highest_objectid) + objectid > root->highest_objectid) __btrfs_add_free_space(ctl, objectid, 1); else __btrfs_add_free_space(pinned, objectid, 1); @@ -410,7 +412,8 @@ int btrfs_save_ino_cache(struct btrfs_root *root, return 0; /* Don't save inode cache if we are deleting this root */ - if (btrfs_root_refs(&root->root_item) == 0) + if (btrfs_root_refs(&root->root_item) == 0 && + root != root->fs_info->tree_root) return 0; if (!btrfs_test_opt(root, INODE_MAP_CACHE)) @@ -464,7 +467,7 @@ again: } if (i_size_read(inode) > 0) { - ret = btrfs_truncate_free_space_cache(root, trans, inode); + ret = btrfs_truncate_free_space_cache(root, trans, path, inode); if (ret) { if (ret != -ENOSPC) btrfs_abort_transaction(trans, root, ret); @@ -501,7 +504,7 @@ again: } btrfs_free_reserved_data_space(inode, prealloc); - ret = btrfs_write_out_ino_cache(root, trans, path, inode); + ret = btrfs_write_out_ino_cache(root, trans, path); out_put: iput(inode); out_release: diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f1a7744..51e3afa 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -43,6 +43,7 @@ #include <linux/btrfs.h> #include <linux/blkdev.h> #include <linux/posix_acl_xattr.h> +#include "compat.h" #include "ctree.h" #include "disk-io.h" #include "transaction.h" @@ -843,10 +844,7 @@ static noinline int cow_file_range(struct inode *inode, struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; int ret = 0; - if (btrfs_is_free_space_inode(inode)) { - WARN_ON_ONCE(1); - return -EINVAL; - } + BUG_ON(btrfs_is_free_space_inode(inode)); num_bytes = ALIGN(end - start + 1, blocksize); num_bytes = max(blocksize, num_bytes); @@ -1180,8 +1178,10 @@ static noinline int run_delalloc_nocow(struct inode *inode, while (1) { ret = btrfs_lookup_file_extent(trans, root, path, ino, cur_offset, 0); - if (ret < 0) + if (ret < 0) { + btrfs_abort_transaction(trans, root, ret); goto error; + } if (ret > 0 && path->slots[0] > 0 && check_prev) { leaf = path->nodes[0]; btrfs_item_key_to_cpu(leaf, &found_key, @@ -1195,8 +1195,10 @@ next_slot: leaf = path->nodes[0]; if (path->slots[0] >= btrfs_header_nritems(leaf)) { ret = btrfs_next_leaf(root, path); - if (ret < 0) + if (ret < 0) { + btrfs_abort_transaction(trans, root, ret); goto error; + } if (ret > 0) break; leaf = path->nodes[0]; @@ -1287,8 +1289,10 @@ out_check: ret = cow_file_range(inode, locked_page, cow_start, found_key.offset - 1, page_started, nr_written, 1); - if (ret) + if (ret) { + btrfs_abort_transaction(trans, root, ret); goto error; + } cow_start = (u64)-1; } @@ -1335,8 +1339,10 @@ out_check: BTRFS_DATA_RELOC_TREE_OBJECTID) { ret = btrfs_reloc_clone_csums(inode, cur_offset, num_bytes); - if (ret) + if (ret) { + btrfs_abort_transaction(trans, root, ret); goto error; + } } extent_clear_unlock_delalloc(inode, cur_offset, @@ -1358,8 +1364,10 @@ out_check: if (cow_start != (u64)-1) { ret = cow_file_range(inode, locked_page, cow_start, end, page_started, nr_written, 1); - if (ret) + if (ret) { + btrfs_abort_transaction(trans, root, ret); goto error; + } } error: @@ -1543,13 +1551,7 @@ static void btrfs_clear_bit_hook(struct inode *inode, spin_unlock(&BTRFS_I(inode)->lock); } - /* - * We don't reserve metadata space for space cache inodes so we - * don't need to call dellalloc_release_metadata if there is an - * error. - */ - if (*bits & EXTENT_DO_ACCOUNTING && - root != root->fs_info->tree_root) + if (*bits & EXTENT_DO_ACCOUNTING) btrfs_delalloc_release_metadata(inode, len); if (root->root_key.objectid != BTRFS_DATA_RELOC_TREE_OBJECTID @@ -2039,8 +2041,10 @@ static noinline int record_one_backref(u64 inum, u64 offset, u64 root_id, key.offset = offset; ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - if (WARN_ON(ret < 0)) + if (ret < 0) { + WARN_ON(1); return ret; + } ret = 0; while (1) { @@ -2129,8 +2133,7 @@ static noinline bool record_extent_backrefs(struct btrfs_path *path, old->extent_offset, fs_info, path, record_one_backref, old); - if (ret < 0 && ret != -ENOENT) - return false; + BUG_ON(ret < 0 && ret != -ENOENT); /* no backref to be processed for this extent */ if (!old->count) { @@ -2364,23 +2367,10 @@ out_unlock: return ret; } -static void free_sa_defrag_extent(struct new_sa_defrag_extent *new) -{ - struct old_sa_defrag_extent *old, *tmp; - - if (!new) - return; - - list_for_each_entry_safe(old, tmp, &new->head, list) { - list_del(&old->list); - kfree(old); - } - kfree(new); -} - static void relink_file_extents(struct new_sa_defrag_extent *new) { struct btrfs_path *path; + struct old_sa_defrag_extent *old, *tmp; struct sa_defrag_extent_backref *backref; struct sa_defrag_extent_backref *prev = NULL; struct inode *inode; @@ -2423,11 +2413,16 @@ static void relink_file_extents(struct new_sa_defrag_extent *new) kfree(prev); btrfs_free_path(path); -out: - free_sa_defrag_extent(new); + list_for_each_entry_safe(old, tmp, &new->head, list) { + list_del(&old->list); + kfree(old); + } +out: atomic_dec(&root->fs_info->defrag_running); wake_up(&root->fs_info->transaction_wait); + + kfree(new); } static struct new_sa_defrag_extent * @@ -2437,7 +2432,7 @@ record_old_file_extents(struct inode *inode, struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_path *path; struct btrfs_key key; - struct old_sa_defrag_extent *old; + struct old_sa_defrag_extent *old, *tmp; struct new_sa_defrag_extent *new; int ret; @@ -2485,7 +2480,7 @@ record_old_file_extents(struct inode *inode, if (slot >= btrfs_header_nritems(l)) { ret = btrfs_next_leaf(root, path); if (ret < 0) - goto out_free_path; + goto out_free_list; else if (ret > 0) break; continue; @@ -2514,7 +2509,7 @@ record_old_file_extents(struct inode *inode, old = kmalloc(sizeof(*old), GFP_NOFS); if (!old) - goto out_free_path; + goto out_free_list; offset = max(new->file_pos, key.offset); end = min(new->file_pos + new->len, key.offset + num_bytes); @@ -2536,10 +2531,15 @@ next: return new; +out_free_list: + list_for_each_entry_safe(old, tmp, &new->head, list) { + list_del(&old->list); + kfree(old); + } out_free_path: btrfs_free_path(path); out_kfree: - free_sa_defrag_extent(new); + kfree(new); return NULL; } @@ -2710,14 +2710,8 @@ out: btrfs_remove_ordered_extent(inode, ordered_extent); /* for snapshot-aware defrag */ - if (new) { - if (ret) { - free_sa_defrag_extent(new); - atomic_dec(&root->fs_info->defrag_running); - } else { - relink_file_extents(new); - } - } + if (new) + relink_file_extents(new); /* once for us */ btrfs_put_ordered_extent(ordered_extent); @@ -2975,7 +2969,6 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode) if (insert >= 1) { ret = btrfs_insert_orphan_item(trans, root, btrfs_ino(inode)); if (ret) { - atomic_dec(&root->orphan_inodes); if (reserve) { clear_bit(BTRFS_INODE_ORPHAN_META_RESERVED, &BTRFS_I(inode)->runtime_flags); @@ -3025,15 +3018,13 @@ static int btrfs_orphan_del(struct btrfs_trans_handle *trans, release_rsv = 1; spin_unlock(&root->orphan_lock); - if (delete_item) { - atomic_dec(&root->orphan_inodes); - if (trans) - ret = btrfs_del_orphan_item(trans, root, - btrfs_ino(inode)); - } + if (trans && delete_item) + ret = btrfs_del_orphan_item(trans, root, btrfs_ino(inode)); - if (release_rsv) + if (release_rsv) { btrfs_orphan_release_metadata(inode); + atomic_dec(&root->orphan_inodes); + } return ret; } @@ -3181,7 +3172,8 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) /* if we have links, this was a truncate, lets do that */ if (inode->i_nlink) { - if (WARN_ON(!S_ISREG(inode->i_mode))) { + if (!S_ISREG(inode->i_mode)) { + WARN_ON(1); iput(inode); continue; } @@ -3644,7 +3636,7 @@ int btrfs_unlink_inode(struct btrfs_trans_handle *trans, int ret; ret = __btrfs_unlink_inode(trans, root, dir, inode, name, name_len); if (!ret) { - drop_nlink(inode); + btrfs_drop_nlink(inode); ret = btrfs_update_inode(trans, root, inode); } return ret; @@ -4238,16 +4230,15 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size) while (1) { struct btrfs_ordered_extent *ordered; - + btrfs_wait_ordered_range(inode, hole_start, + block_end - hole_start); lock_extent_bits(io_tree, hole_start, block_end - 1, 0, &cached_state); - ordered = btrfs_lookup_ordered_range(inode, hole_start, - block_end - hole_start); + ordered = btrfs_lookup_ordered_extent(inode, hole_start); if (!ordered) break; unlock_extent_cached(io_tree, hole_start, block_end - 1, &cached_state, GFP_NOFS); - btrfs_start_ordered_extent(inode, ordered, 1); btrfs_put_ordered_extent(ordered); } @@ -4481,10 +4472,8 @@ void btrfs_evict_inode(struct inode *inode) trace_btrfs_inode_evict(inode); truncate_inode_pages(&inode->i_data, 0); - if (inode->i_nlink && - ((btrfs_root_refs(&root->root_item) != 0 && - root->root_key.objectid != BTRFS_ROOT_TREE_OBJECTID) || - btrfs_is_free_space_inode(inode))) + if (inode->i_nlink && (btrfs_root_refs(&root->root_item) != 0 || + btrfs_is_free_space_inode(inode))) goto no_delete; if (is_bad_inode(inode)) { @@ -4501,8 +4490,7 @@ void btrfs_evict_inode(struct inode *inode) } if (inode->i_nlink > 0) { - BUG_ON(btrfs_root_refs(&root->root_item) != 0 && - root->root_key.objectid != BTRFS_ROOT_TREE_OBJECTID); + BUG_ON(btrfs_root_refs(&root->root_item) != 0); goto no_delete; } @@ -4743,7 +4731,14 @@ static void inode_tree_del(struct inode *inode) } spin_unlock(&root->inode_lock); - if (empty && btrfs_root_refs(&root->root_item) == 0) { + /* + * Free space cache has inodes in the tree root, but the tree root has a + * root_refs of 0, so this could end up dropping the tree root as a + * snapshot, so we need the extra !root->fs_info->tree_root check to + * make sure we don't drop it. + */ + if (empty && btrfs_root_refs(&root->root_item) == 0 && + root != root->fs_info->tree_root) { synchronize_srcu(&root->fs_info->subvol_srcu); spin_lock(&root->inode_lock); empty = RB_EMPTY_ROOT(&root->inode_tree); @@ -4836,12 +4831,10 @@ static struct inode *btrfs_iget_locked(struct super_block *s, { struct inode *inode; struct btrfs_iget_args args; - unsigned long hashval = btrfs_inode_hash(objectid, root); - args.ino = objectid; args.root = root; - inode = iget5_locked(s, hashval, btrfs_find_actor, + inode = iget5_locked(s, objectid, btrfs_find_actor, btrfs_init_locked_inode, (void *)&args); return inode; @@ -5055,7 +5048,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx) continue; } - item = btrfs_item_nr(slot); + item = btrfs_item_nr(leaf, slot); btrfs_item_key_to_cpu(leaf, &found_key, slot); if (found_key.objectid != key.objectid) @@ -5461,7 +5454,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans, BTRFS_INODE_NODATASUM; } - btrfs_insert_inode_hash(inode); + insert_inode_hash(inode); inode_tree_add(inode); trace_btrfs_inode_new(inode); @@ -5737,7 +5730,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir, goto fail; } - inc_nlink(inode); + btrfs_inc_nlink(inode); inode_inc_iversion(inode); inode->i_ctime = CURRENT_TIME; ihold(inode); @@ -5867,7 +5860,7 @@ static noinline int uncompress_inline(struct btrfs_path *path, compress_type = btrfs_file_extent_compression(leaf, item); max_size = btrfs_file_extent_ram_bytes(leaf, item); inline_size = btrfs_file_extent_inline_item_len(leaf, - btrfs_item_nr(path->slots[0])); + btrfs_item_nr(leaf, path->slots[0])); tmp = kmalloc(inline_size, GFP_NOFS); if (!tmp) return -ENOMEM; @@ -5981,14 +5974,7 @@ again: found_type = btrfs_key_type(&found_key); if (found_key.objectid != objectid || found_type != BTRFS_EXTENT_DATA_KEY) { - /* - * If we backup past the first extent we want to move forward - * and see if there is an extent in front of us, otherwise we'll - * say there is a hole for our whole search range which can - * cause problems. - */ - extent_end = start; - goto next; + goto not_found; } found_type = btrfs_file_extent_type(leaf, item); @@ -6003,7 +5989,7 @@ again: size = btrfs_file_extent_inline_len(leaf, item); extent_end = ALIGN(extent_start + size, root->sectorsize); } -next: + if (start >= extent_end) { path->slots[0]++; if (path->slots[0] >= btrfs_header_nritems(leaf)) { @@ -6187,7 +6173,8 @@ insert: write_unlock(&em_tree->lock); out: - trace_btrfs_get_extent(root, em); + if (em) + trace_btrfs_get_extent(root, em); if (path) btrfs_free_path(path); @@ -6262,7 +6249,7 @@ struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *pag /* adjust the range_start to make sure it doesn't * go backwards from the start they passed in */ - range_start = max(start, range_start); + range_start = max(start,range_start); found = found_end - range_start; if (found > 0) { @@ -7066,7 +7053,7 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip, } } else { submit_len += bvec->bv_len; - nr_pages++; + nr_pages ++; bvec++; } } @@ -7235,9 +7222,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, * outstanding dirty pages are on disk. */ count = iov_length(iov, nr_segs); - ret = btrfs_wait_ordered_range(inode, offset, count); - if (ret) - return ret; + btrfs_wait_ordered_range(inode, offset, count); if (rw & WRITE) { /* @@ -7578,10 +7563,7 @@ static int btrfs_truncate(struct inode *inode) u64 mask = root->sectorsize - 1; u64 min_size = btrfs_calc_trunc_metadata_size(root, 1); - ret = btrfs_wait_ordered_range(inode, inode->i_size & (~mask), - (u64)-1); - if (ret) - return ret; + btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1); /* * Yes ladies and gentelment, this is indeed ugly. The fact is we have @@ -7805,14 +7787,6 @@ struct inode *btrfs_alloc_inode(struct super_block *sb) return inode; } -#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS -void btrfs_test_destroy_inode(struct inode *inode) -{ - btrfs_drop_extent_cache(inode, 0, (u64)-1, 0); - kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode)); -} -#endif - static void btrfs_i_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); @@ -7883,7 +7857,8 @@ int btrfs_drop_inode(struct inode *inode) return 1; /* the snap/subvol tree is on deleting */ - if (btrfs_root_refs(&root->root_item) == 0) + if (btrfs_root_refs(&root->root_item) == 0 && + root != root->fs_info->tree_root) return 1; else return generic_drop_inode(inode); @@ -8020,7 +7995,8 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (ret == -EEXIST) { /* we shouldn't get * eexist without a new_inode */ - if (WARN_ON(!new_inode)) { + if (!new_inode) { + WARN_ON(1); return ret; } } else { @@ -8168,24 +8144,18 @@ out_notrans: static void btrfs_run_delalloc_work(struct btrfs_work *work) { struct btrfs_delalloc_work *delalloc_work; - struct inode *inode; delalloc_work = container_of(work, struct btrfs_delalloc_work, work); - inode = delalloc_work->inode; - if (delalloc_work->wait) { - btrfs_wait_ordered_range(inode, 0, (u64)-1); - } else { - filemap_flush(inode->i_mapping); - if (test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT, - &BTRFS_I(inode)->runtime_flags)) - filemap_flush(inode->i_mapping); - } + if (delalloc_work->wait) + btrfs_wait_ordered_range(delalloc_work->inode, 0, (u64)-1); + else + filemap_flush(delalloc_work->inode->i_mapping); if (delalloc_work->delay_iput) - btrfs_add_delayed_iput(inode); + btrfs_add_delayed_iput(delalloc_work->inode); else - iput(inode); + iput(delalloc_work->inode); complete(&delalloc_work->completion); } @@ -8306,7 +8276,8 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput) return ret; } -int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput) +int btrfs_start_all_delalloc_inodes(struct btrfs_fs_info *fs_info, + int delay_iput) { struct btrfs_root *root; struct list_head splice; @@ -8366,14 +8337,14 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry, int err; int drop_inode = 0; u64 objectid; - u64 index = 0; + u64 index = 0 ; int name_len; int datasize; unsigned long ptr; struct btrfs_file_extent_item *ei; struct extent_buffer *leaf; - name_len = strlen(symname); + name_len = strlen(symname) + 1; if (name_len > BTRFS_MAX_INLINE_DATA_SIZE(root)) return -ENAMETOOLONG; @@ -8461,7 +8432,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry, inode->i_mapping->a_ops = &btrfs_symlink_aops; inode->i_mapping->backing_dev_info = &root->fs_info->bdi; inode_set_bytes(inode, name_len); - btrfs_i_size_write(inode, name_len); + btrfs_i_size_write(inode, name_len - 1); err = btrfs_update_inode(trans, root, inode); if (err) drop_inode = 1; @@ -8520,8 +8491,6 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, ins.offset, 0, 0, 0, BTRFS_FILE_EXTENT_PREALLOC); if (ret) { - btrfs_free_reserved_extent(root, ins.objectid, - ins.offset); btrfs_abort_transaction(trans, root, ret); if (own_trans) btrfs_end_transaction(trans, root); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a111622..9d46f60 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -44,6 +44,7 @@ #include <linux/uuid.h> #include <linux/btrfs.h> #include <linux/uaccess.h> +#include "compat.h" #include "ctree.h" #include "disk-io.h" #include "transaction.h" @@ -320,7 +321,7 @@ static int btrfs_ioctl_getversion(struct file *file, int __user *arg) static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg) { - struct btrfs_fs_info *fs_info = btrfs_sb(file_inode(file)->i_sb); + struct btrfs_fs_info *fs_info = btrfs_sb(fdentry(file)->d_sb); struct btrfs_device *device; struct request_queue *q; struct fstrim_range range; @@ -368,13 +369,9 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg) int btrfs_is_empty_uuid(u8 *uuid) { - int i; + static char empty_uuid[BTRFS_UUID_SIZE] = {0}; - for (i = 0; i < BTRFS_UUID_SIZE; i++) { - if (uuid[i]) - return 0; - } - return 1; + return !memcmp(uuid, empty_uuid, BTRFS_UUID_SIZE); } static noinline int create_subvol(struct inode *dir, @@ -439,7 +436,7 @@ static noinline int create_subvol(struct inode *dir, btrfs_set_header_backref_rev(leaf, BTRFS_MIXED_BACKREF_REV); btrfs_set_header_owner(leaf, objectid); - write_extent_buffer(leaf, root->fs_info->fsid, btrfs_header_fsid(), + write_extent_buffer(leaf, root->fs_info->fsid, btrfs_header_fsid(leaf), BTRFS_FSID_SIZE); write_extent_buffer(leaf, root->fs_info->chunk_tree_uuid, btrfs_header_chunk_tree_uuid(leaf), @@ -577,7 +574,7 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, if (ret) return ret; - btrfs_wait_ordered_extents(root, -1); + btrfs_wait_ordered_extents(root); pending_snapshot = kzalloc(sizeof(*pending_snapshot), GFP_NOFS); if (!pending_snapshot) @@ -691,7 +688,7 @@ static inline int btrfs_check_sticky(struct inode *dir, struct inode *inode) * nfs_async_unlink(). */ -static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) +static int btrfs_may_delete(struct inode *dir,struct dentry *victim,int isdir) { int error; @@ -845,6 +842,7 @@ static int find_new_extents(struct btrfs_root *root, { struct btrfs_path *path; struct btrfs_key min_key; + struct btrfs_key max_key; struct extent_buffer *leaf; struct btrfs_file_extent_item *extent; int type; @@ -859,10 +857,15 @@ static int find_new_extents(struct btrfs_root *root, min_key.type = BTRFS_EXTENT_DATA_KEY; min_key.offset = *off; + max_key.objectid = ino; + max_key.type = (u8)-1; + max_key.offset = (u64)-1; + path->keep_locks = 1; - while (1) { - ret = btrfs_search_forward(root, &min_key, path, newer_than); + while(1) { + ret = btrfs_search_forward(root, &min_key, &max_key, + path, newer_than); if (ret != 0) goto none; if (min_key.objectid != ino) @@ -1203,7 +1206,7 @@ int btrfs_defrag_file(struct inode *inode, struct file *file, ra = &file->f_ra; } - pages = kmalloc_array(max_cluster, sizeof(struct page *), + pages = kmalloc(sizeof(struct page *) * max_cluster, GFP_NOFS); if (!pages) { ret = -ENOMEM; @@ -1890,6 +1893,7 @@ static noinline int search_ioctl(struct inode *inode, { struct btrfs_root *root; struct btrfs_key key; + struct btrfs_key max_key; struct btrfs_path *path; struct btrfs_ioctl_search_key *sk = &args->key; struct btrfs_fs_info *info = BTRFS_I(inode)->root->fs_info; @@ -1921,10 +1925,15 @@ static noinline int search_ioctl(struct inode *inode, key.type = sk->min_type; key.offset = sk->min_offset; + max_key.objectid = sk->max_objectid; + max_key.type = sk->max_type; + max_key.offset = sk->max_offset; + path->keep_locks = 1; - while (1) { - ret = btrfs_search_forward(root, &key, path, sk->min_transid); + while(1) { + ret = btrfs_search_forward(root, &key, &max_key, path, + sk->min_transid); if (ret != 0) { if (ret > 0) ret = 0; @@ -2009,7 +2018,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, key.type = BTRFS_INODE_REF_KEY; key.offset = (u64)-1; - while (1) { + while(1) { ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); if (ret < 0) goto out; @@ -2038,7 +2047,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, } *(ptr + len) = '/'; - read_extent_buffer(l, ptr, (unsigned long)(iref + 1), len); + read_extent_buffer(l, ptr,(unsigned long)(iref + 1), len); if (key.offset == BTRFS_FIRST_FREE_OBJECTID) break; @@ -2049,7 +2058,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, dirid = key.objectid; } memmove(name, ptr, total_len); - name[total_len] = '\0'; + name[total_len]='\0'; ret = 0; out: btrfs_free_path(path); @@ -2089,7 +2098,7 @@ static noinline int btrfs_ioctl_ino_lookup(struct file *file, static noinline int btrfs_ioctl_snap_destroy(struct file *file, void __user *arg) { - struct dentry *parent = file->f_path.dentry; + struct dentry *parent = fdentry(file); struct dentry *dentry; struct inode *dir = parent->d_inode; struct inode *inode; @@ -2135,7 +2144,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, inode = dentry->d_inode; dest = BTRFS_I(inode)->root; - if (!capable(CAP_SYS_ADMIN)) { + if (!capable(CAP_SYS_ADMIN)){ /* * Regular user. Only allow this with a special mount * option, when the user has write+exec access to the @@ -2718,10 +2727,15 @@ static long btrfs_ioctl_file_extent_same(struct file *file, size = sizeof(tmp) + tmp.dest_count * sizeof(struct btrfs_ioctl_same_extent_info); - same = memdup_user((struct btrfs_ioctl_same_args __user *)argp, size); + same = kmalloc(size, GFP_NOFS); + if (!same) { + ret = -EFAULT; + goto out; + } - if (IS_ERR(same)) { - ret = PTR_ERR(same); + if (copy_from_user(same, + (struct btrfs_ioctl_same_args __user *)argp, size)) { + ret = -EFAULT; goto out; } @@ -3105,7 +3119,7 @@ out: static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, u64 off, u64 olen, u64 destoff) { - struct inode *inode = file_inode(file); + struct inode *inode = fdentry(file)->d_inode; struct btrfs_root *root = BTRFS_I(inode)->root; struct fd src_file; struct inode *src; @@ -3665,10 +3679,9 @@ static long btrfs_ioctl_dev_replace(struct btrfs_root *root, void __user *arg) switch (p->cmd) { case BTRFS_IOCTL_DEV_REPLACE_CMD_START: - if (root->fs_info->sb->s_flags & MS_RDONLY) { - ret = -EROFS; - goto out; - } + if (root->fs_info->sb->s_flags & MS_RDONLY) + return -EROFS; + if (atomic_xchg( &root->fs_info->mutually_exclusive_operation_running, 1)) { @@ -3694,7 +3707,7 @@ static long btrfs_ioctl_dev_replace(struct btrfs_root *root, void __user *arg) if (copy_to_user(arg, p, sizeof(*p))) ret = -EFAULT; -out: + kfree(p); return ret; } @@ -4304,7 +4317,7 @@ static long btrfs_ioctl_quota_rescan_status(struct file *file, void __user *arg) static long btrfs_ioctl_quota_rescan_wait(struct file *file, void __user *arg) { - struct btrfs_root *root = BTRFS_I(file_inode(file))->root; + struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -4544,15 +4557,9 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_logical_to_ino(root, argp); case BTRFS_IOC_SPACE_INFO: return btrfs_ioctl_space_info(root, argp); - case BTRFS_IOC_SYNC: { - int ret; - - ret = btrfs_start_delalloc_roots(root->fs_info, 0); - if (ret) - return ret; - ret = btrfs_sync_fs(file->f_dentry->d_sb, 1); - return ret; - } + case BTRFS_IOC_SYNC: + btrfs_sync_fs(file->f_dentry->d_sb, 1); + return 0; case BTRFS_IOC_START_SYNC: return btrfs_ioctl_start_sync(root, argp); case BTRFS_IOC_WAIT_SYNC: diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 69582d5..c702cb6 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -537,9 +537,7 @@ void btrfs_remove_ordered_extent(struct inode *inode, */ if (RB_EMPTY_ROOT(&tree->tree) && !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_DIRTY)) { - spin_lock(&root->fs_info->ordered_root_lock); list_del_init(&BTRFS_I(inode)->ordered_operations); - spin_unlock(&root->fs_info->ordered_root_lock); } if (!root->nr_ordered_extents) { @@ -565,11 +563,10 @@ static void btrfs_run_ordered_extent_work(struct btrfs_work *work) * wait for all the ordered extents in a root. This is done when balancing * space between drives. */ -int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr) +void btrfs_wait_ordered_extents(struct btrfs_root *root) { struct list_head splice, works; struct btrfs_ordered_extent *ordered, *next; - int count = 0; INIT_LIST_HEAD(&splice); INIT_LIST_HEAD(&works); @@ -577,7 +574,7 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr) mutex_lock(&root->fs_info->ordered_operations_mutex); spin_lock(&root->ordered_extent_lock); list_splice_init(&root->ordered_extents, &splice); - while (!list_empty(&splice) && nr) { + while (!list_empty(&splice)) { ordered = list_first_entry(&splice, struct btrfs_ordered_extent, root_extent_list); list_move_tail(&ordered->root_extent_list, @@ -592,11 +589,7 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr) cond_resched(); spin_lock(&root->ordered_extent_lock); - if (nr != -1) - nr--; - count++; } - list_splice_tail(&splice, &root->ordered_extents); spin_unlock(&root->ordered_extent_lock); list_for_each_entry_safe(ordered, next, &works, work_list) { @@ -606,21 +599,18 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr) cond_resched(); } mutex_unlock(&root->fs_info->ordered_operations_mutex); - - return count; } -void btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr) +void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info) { struct btrfs_root *root; struct list_head splice; - int done; INIT_LIST_HEAD(&splice); spin_lock(&fs_info->ordered_root_lock); list_splice_init(&fs_info->ordered_roots, &splice); - while (!list_empty(&splice) && nr) { + while (!list_empty(&splice)) { root = list_first_entry(&splice, struct btrfs_root, ordered_root); root = btrfs_grab_fs_root(root); @@ -629,16 +619,11 @@ void btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr) &fs_info->ordered_roots); spin_unlock(&fs_info->ordered_root_lock); - done = btrfs_wait_ordered_extents(root, nr); + btrfs_wait_ordered_extents(root); btrfs_put_fs_root(root); spin_lock(&fs_info->ordered_root_lock); - if (nr != -1) { - nr -= done; - WARN_ON(nr < 0); - } } - list_splice_tail(&splice, &fs_info->ordered_roots); spin_unlock(&fs_info->ordered_root_lock); } @@ -749,9 +734,8 @@ void btrfs_start_ordered_extent(struct inode *inode, /* * Used to wait on ordered extents across a large range of bytes. */ -int btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len) +void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len) { - int ret = 0; u64 end; u64 orig_end; struct btrfs_ordered_extent *ordered; @@ -767,9 +751,8 @@ int btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len) /* start IO across the range first to instantiate any delalloc * extents */ - ret = filemap_fdatawrite_range(inode->i_mapping, start, orig_end); - if (ret) - return ret; + filemap_fdatawrite_range(inode->i_mapping, start, orig_end); + /* * So with compression we will find and lock a dirty page and clear the * first one as dirty, setup an async extent, and immediately return @@ -785,15 +768,10 @@ int btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len) * right and you are wrong. */ if (test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT, - &BTRFS_I(inode)->runtime_flags)) { - ret = filemap_fdatawrite_range(inode->i_mapping, start, - orig_end); - if (ret) - return ret; - } - ret = filemap_fdatawait_range(inode->i_mapping, start, orig_end); - if (ret) - return ret; + &BTRFS_I(inode)->runtime_flags)) + filemap_fdatawrite_range(inode->i_mapping, start, orig_end); + + filemap_fdatawait_range(inode->i_mapping, start, orig_end); end = orig_end; while (1) { @@ -804,20 +782,17 @@ int btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len) btrfs_put_ordered_extent(ordered); break; } - if (ordered->file_offset + ordered->len <= start) { + if (ordered->file_offset + ordered->len < start) { btrfs_put_ordered_extent(ordered); break; } btrfs_start_ordered_extent(inode, ordered, 1); end = ordered->file_offset; - if (test_bit(BTRFS_ORDERED_IOERR, &ordered->flags)) - ret = -EIO; btrfs_put_ordered_extent(ordered); - if (ret || end == 0 || end == start) + if (end == 0 || end == start) break; end--; } - return ret; } /* @@ -1101,7 +1076,7 @@ void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans, * if this file hasn't been changed since the last transaction * commit, we can safely return without doing anything */ - if (last_mod <= root->fs_info->last_trans_committed) + if (last_mod < root->fs_info->last_trans_committed) return; spin_lock(&root->fs_info->ordered_root_lock); diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index 9b0450f..0c0b356 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -180,7 +180,7 @@ struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct inode *inode, u64 file_offset); void btrfs_start_ordered_extent(struct inode *inode, struct btrfs_ordered_extent *entry, int wait); -int btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len); +void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len); struct btrfs_ordered_extent * btrfs_lookup_first_ordered_extent(struct inode * inode, u64 file_offset); struct btrfs_ordered_extent *btrfs_lookup_ordered_range(struct inode *inode, @@ -195,8 +195,8 @@ int btrfs_run_ordered_operations(struct btrfs_trans_handle *trans, void btrfs_add_ordered_operation(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct inode *inode); -int btrfs_wait_ordered_extents(struct btrfs_root *root, int nr); -void btrfs_wait_ordered_roots(struct btrfs_fs_info *fs_info, int nr); +void btrfs_wait_ordered_extents(struct btrfs_root *root); +void btrfs_wait_all_ordered_extents(struct btrfs_fs_info *fs_info); void btrfs_get_logged_extents(struct btrfs_root *log, struct inode *inode); void btrfs_wait_logged_extents(struct btrfs_root *log, u64 transid); void btrfs_free_logged_extents(struct btrfs_root *log, u64 transid); diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c index 417053b..0088bed 100644 --- a/fs/btrfs/print-tree.c +++ b/fs/btrfs/print-tree.c @@ -193,7 +193,7 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l) btrfs_info(root->fs_info, "leaf %llu total ptrs %d free space %d", btrfs_header_bytenr(l), nr, btrfs_leaf_free_space(root, l)); for (i = 0 ; i < nr ; i++) { - item = btrfs_item_nr(i); + item = btrfs_item_nr(l, i); btrfs_item_key_to_cpu(l, &key, i); type = btrfs_key_type(&key); printk(KERN_INFO "\titem %d key (%llu %u %llu) itemoff %d " diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index 24ac218..d0ecfbd 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -33,6 +33,7 @@ #include <linux/raid/xor.h> #include <linux/vmalloc.h> #include <asm/div64.h> +#include "compat.h" #include "ctree.h" #include "extent_map.h" #include "disk-io.h" diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index ce459a7..4a35572 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -1383,7 +1383,6 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans, { struct btrfs_root *reloc_root; struct reloc_control *rc = root->fs_info->reloc_ctl; - struct btrfs_block_rsv *rsv; int clear_rsv = 0; int ret; @@ -1397,14 +1396,13 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans, root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) return 0; - if (!trans->reloc_reserved) { - rsv = trans->block_rsv; + if (!trans->block_rsv) { trans->block_rsv = rc->block_rsv; clear_rsv = 1; } reloc_root = create_reloc_root(trans, root, root->root_key.objectid); if (clear_rsv) - trans->block_rsv = rsv; + trans->block_rsv = NULL; ret = __add_reloc_root(reloc_root); BUG_ON(ret < 0); @@ -1777,7 +1775,8 @@ again: new_ptr_gen = 0; } - if (WARN_ON(new_bytenr > 0 && new_bytenr == old_bytenr)) { + if (new_bytenr > 0 && new_bytenr == old_bytenr) { + WARN_ON(1); ret = level; break; } @@ -2059,7 +2058,7 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc, LIST_HEAD(inode_list); struct btrfs_key key; struct btrfs_key next_key; - struct btrfs_trans_handle *trans = NULL; + struct btrfs_trans_handle *trans; struct btrfs_root *reloc_root; struct btrfs_root_item *root_item; struct btrfs_path *path; @@ -2108,19 +2107,18 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc, memset(&next_key, 0, sizeof(next_key)); while (1) { + trans = btrfs_start_transaction(root, 0); + BUG_ON(IS_ERR(trans)); + trans->block_rsv = rc->block_rsv; + ret = btrfs_block_rsv_refill(root, rc->block_rsv, min_reserved, BTRFS_RESERVE_FLUSH_ALL); if (ret) { - err = ret; - goto out; - } - trans = btrfs_start_transaction(root, 0); - if (IS_ERR(trans)) { - err = PTR_ERR(trans); - trans = NULL; - goto out; + BUG_ON(ret != -EAGAIN); + ret = btrfs_commit_transaction(trans, root); + BUG_ON(ret); + continue; } - trans->block_rsv = rc->block_rsv; replaced = 0; max_level = level; @@ -2166,7 +2164,6 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc, root_item->drop_level = level; btrfs_end_transaction_throttle(trans, root); - trans = NULL; btrfs_btree_balance_dirty(root); @@ -2195,8 +2192,7 @@ out: btrfs_update_reloc_root(trans, root); } - if (trans) - btrfs_end_transaction_throttle(trans, root); + btrfs_end_transaction_throttle(trans, root); btrfs_btree_balance_dirty(root); @@ -3262,7 +3258,7 @@ static int add_tree_block(struct reloc_control *rc, struct rb_node *rb_node; u32 item_size; int level = -1; - u64 generation; + int generation; eb = path->nodes[0]; item_size = btrfs_item_size_nr(eb, path->slots[0]); @@ -3411,6 +3407,7 @@ static int delete_block_group_cache(struct btrfs_fs_info *fs_info, struct inode *inode, u64 ino) { struct btrfs_key key; + struct btrfs_path *path; struct btrfs_root *root = fs_info->tree_root; struct btrfs_trans_handle *trans; int ret = 0; @@ -3435,14 +3432,22 @@ truncate: if (ret) goto out; + path = btrfs_alloc_path(); + if (!path) { + ret = -ENOMEM; + goto out; + } + trans = btrfs_join_transaction(root); if (IS_ERR(trans)) { + btrfs_free_path(path); ret = PTR_ERR(trans); goto out; } - ret = btrfs_truncate_free_space_cache(root, trans, inode); + ret = btrfs_truncate_free_space_cache(root, trans, path, inode); + btrfs_free_path(path); btrfs_end_transaction(trans, root); btrfs_btree_balance_dirty(root); out: @@ -3544,8 +3549,10 @@ static int find_data_references(struct reloc_control *rc, err = ret; goto out; } - if (WARN_ON(ret > 0)) + if (ret > 0) { + WARN_ON(1); goto out; + } leaf = path->nodes[0]; nritems = btrfs_header_nritems(leaf); @@ -3565,9 +3572,11 @@ static int find_data_references(struct reloc_control *rc, } btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); - if (WARN_ON(key.objectid != ref_objectid || - key.type != BTRFS_EXTENT_DATA_KEY)) + if (key.objectid != ref_objectid || + key.type != BTRFS_EXTENT_DATA_KEY) { + WARN_ON(1); break; + } fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); @@ -3992,6 +4001,16 @@ restart: } } + ret = btrfs_block_rsv_check(rc->extent_root, rc->block_rsv, 5); + if (ret < 0) { + if (ret != -ENOSPC) { + err = ret; + WARN_ON(1); + break; + } + rc->commit_transaction = 1; + } + if (rc->commit_transaction) { rc->commit_transaction = 0; ret = btrfs_commit_transaction(trans, rc->extent_root); @@ -4222,12 +4241,12 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start) printk(KERN_INFO "btrfs: relocating block group %llu flags %llu\n", rc->block_group->key.objectid, rc->block_group->flags); - ret = btrfs_start_delalloc_roots(fs_info, 0); + ret = btrfs_start_all_delalloc_inodes(fs_info, 0); if (ret < 0) { err = ret; goto out; } - btrfs_wait_ordered_roots(fs_info, -1); + btrfs_wait_all_ordered_extents(fs_info); while (1) { mutex_lock(&fs_info->cleaner_mutex); @@ -4245,12 +4264,7 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start) rc->extents_found); if (rc->stage == MOVE_DATA_EXTENTS && rc->found_file_extent) { - ret = btrfs_wait_ordered_range(rc->data_inode, 0, - (u64)-1); - if (ret) { - err = ret; - goto out; - } + btrfs_wait_ordered_range(rc->data_inode, 0, (u64)-1); invalidate_mapping_pages(rc->data_inode->i_mapping, 0, -1); rc->stage = UPDATE_DATA_PTRS; @@ -4467,7 +4481,6 @@ int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len) struct btrfs_root *root = BTRFS_I(inode)->root; int ret; u64 disk_bytenr; - u64 new_bytenr; LIST_HEAD(list); ordered = btrfs_lookup_ordered_extent(inode, file_pos); @@ -4479,24 +4492,13 @@ int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len) if (ret) goto out; + disk_bytenr = ordered->start; while (!list_empty(&list)) { sums = list_entry(list.next, struct btrfs_ordered_sum, list); list_del_init(&sums->list); - /* - * We need to offset the new_bytenr based on where the csum is. - * We need to do this because we will read in entire prealloc - * extents but we may have written to say the middle of the - * prealloc extent, so we need to make sure the csum goes with - * the right disk offset. - * - * We can do this because the data reloc inode refers strictly - * to the on disk bytes, so we don't have to worry about - * disk_len vs real len like with real inodes since it's all - * disk length. - */ - new_bytenr = ordered->start + (sums->bytenr - disk_bytenr); - sums->bytenr = new_bytenr; + sums->bytenr = disk_bytenr; + disk_bytenr += sums->len; btrfs_add_ordered_sum(inode, ordered, sums); } diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 1fd3f33..a18e0e2 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -208,6 +208,7 @@ static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info, int is_metadata, int have_csum, const u8 *csum, u64 generation, u16 csum_size); +static void scrub_complete_bio_end_io(struct bio *bio, int err); static int scrub_repair_block_from_good_copy(struct scrub_block *sblock_bad, struct scrub_block *sblock_good, int force_write); @@ -937,10 +938,8 @@ static int scrub_handle_errored_block(struct scrub_block *sblock_to_check) BTRFS_DEV_STAT_CORRUPTION_ERRS); } - if (sctx->readonly) { - ASSERT(!sctx->is_dev_replace); - goto out; - } + if (sctx->readonly && !sctx->is_dev_replace) + goto did_not_correct_error; if (!is_metadata && !have_csum) { struct scrub_fixup_nodatasum *fixup_nodatasum; @@ -1293,6 +1292,7 @@ static void scrub_recheck_block(struct btrfs_fs_info *fs_info, for (page_num = 0; page_num < sblock->page_count; page_num++) { struct bio *bio; struct scrub_page *page = sblock->pagev[page_num]; + DECLARE_COMPLETION_ONSTACK(complete); if (page->dev->bdev == NULL) { page->io_error = 1; @@ -1309,11 +1309,18 @@ static void scrub_recheck_block(struct btrfs_fs_info *fs_info, } bio->bi_bdev = page->dev->bdev; bio->bi_sector = page->physical >> 9; + bio->bi_end_io = scrub_complete_bio_end_io; + bio->bi_private = &complete; bio_add_page(bio, page->page, PAGE_SIZE, 0); - if (btrfsic_submit_bio_wait(READ, bio)) - sblock->no_io_error_seen = 0; + btrfsic_submit_bio(READ, bio); + + /* this will also unplug the queue */ + wait_for_completion(&complete); + page->io_error = !test_bit(BIO_UPTODATE, &bio->bi_flags); + if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) + sblock->no_io_error_seen = 0; bio_put(bio); } @@ -1382,6 +1389,11 @@ static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info, sblock->checksum_error = 1; } +static void scrub_complete_bio_end_io(struct bio *bio, int err) +{ + complete((struct completion *)bio->bi_private); +} + static int scrub_repair_block_from_good_copy(struct scrub_block *sblock_bad, struct scrub_block *sblock_good, int force_write) @@ -1416,6 +1428,7 @@ static int scrub_repair_page_from_good_copy(struct scrub_block *sblock_bad, sblock_bad->checksum_error || page_bad->io_error) { struct bio *bio; int ret; + DECLARE_COMPLETION_ONSTACK(complete); if (!page_bad->dev->bdev) { printk_ratelimited(KERN_WARNING @@ -1428,14 +1441,19 @@ static int scrub_repair_page_from_good_copy(struct scrub_block *sblock_bad, return -EIO; bio->bi_bdev = page_bad->dev->bdev; bio->bi_sector = page_bad->physical >> 9; + bio->bi_end_io = scrub_complete_bio_end_io; + bio->bi_private = &complete; ret = bio_add_page(bio, page_good->page, PAGE_SIZE, 0); if (PAGE_SIZE != ret) { bio_put(bio); return -EIO; } + btrfsic_submit_bio(WRITE, bio); - if (btrfsic_submit_bio_wait(WRITE, bio)) { + /* this will also unplug the queue */ + wait_for_completion(&complete); + if (!bio_flagged(bio, BIO_UPTODATE)) { btrfs_dev_stat_inc_and_print(page_bad->dev, BTRFS_DEV_STAT_WRITE_ERRS); btrfs_dev_replace_stats_inc( @@ -2699,6 +2717,8 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx, mutex_unlock(&fs_info->scrub_lock); wake_up(&fs_info->scrub_pause_wait); + dev_replace->cursor_left = dev_replace->cursor_right; + dev_replace->item_needs_writeback = 1; btrfs_put_block_group(cache); if (ret) break; @@ -2712,9 +2732,6 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx, break; } - dev_replace->cursor_left = dev_replace->cursor_right; - dev_replace->item_needs_writeback = 1; - key.offset = found_key.offset + length; btrfs_release_path(path); } @@ -2766,6 +2783,7 @@ static noinline_for_stack int scrub_workers_get(struct btrfs_fs_info *fs_info, { int ret = 0; + mutex_lock(&fs_info->scrub_lock); if (fs_info->scrub_workers_refcnt == 0) { if (is_dev_replace) btrfs_init_workers(&fs_info->scrub_workers, "scrub", 1, @@ -2795,17 +2813,21 @@ static noinline_for_stack int scrub_workers_get(struct btrfs_fs_info *fs_info, } ++fs_info->scrub_workers_refcnt; out: + mutex_unlock(&fs_info->scrub_lock); + return ret; } static noinline_for_stack void scrub_workers_put(struct btrfs_fs_info *fs_info) { + mutex_lock(&fs_info->scrub_lock); if (--fs_info->scrub_workers_refcnt == 0) { btrfs_stop_workers(&fs_info->scrub_workers); btrfs_stop_workers(&fs_info->scrub_wr_completion_workers); btrfs_stop_workers(&fs_info->scrub_nocow_workers); } WARN_ON(fs_info->scrub_workers_refcnt < 0); + mutex_unlock(&fs_info->scrub_lock); } int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, @@ -2866,18 +2888,23 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, return -EINVAL; } + ret = scrub_workers_get(fs_info, is_dev_replace); + if (ret) + return ret; mutex_lock(&fs_info->fs_devices->device_list_mutex); dev = btrfs_find_device(fs_info, devid, NULL, NULL); if (!dev || (dev->missing && !is_dev_replace)) { mutex_unlock(&fs_info->fs_devices->device_list_mutex); + scrub_workers_put(fs_info); return -ENODEV; } - mutex_lock(&fs_info->scrub_lock); + if (!dev->in_fs_metadata || dev->is_tgtdev_for_dev_replace) { mutex_unlock(&fs_info->scrub_lock); mutex_unlock(&fs_info->fs_devices->device_list_mutex); + scrub_workers_put(fs_info); return -EIO; } @@ -2888,17 +2915,10 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, btrfs_dev_replace_unlock(&fs_info->dev_replace); mutex_unlock(&fs_info->scrub_lock); mutex_unlock(&fs_info->fs_devices->device_list_mutex); + scrub_workers_put(fs_info); return -EINPROGRESS; } btrfs_dev_replace_unlock(&fs_info->dev_replace); - - ret = scrub_workers_get(fs_info, is_dev_replace); - if (ret) { - mutex_unlock(&fs_info->scrub_lock); - mutex_unlock(&fs_info->fs_devices->device_list_mutex); - return ret; - } - sctx = scrub_setup_ctx(dev, is_dev_replace); if (IS_ERR(sctx)) { mutex_unlock(&fs_info->scrub_lock); @@ -2911,15 +2931,13 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, atomic_inc(&fs_info->scrubs_running); mutex_unlock(&fs_info->scrub_lock); + mutex_unlock(&fs_info->fs_devices->device_list_mutex); if (!is_dev_replace) { - /* - * by holding device list mutex, we can - * kick off writing super in log tree sync. - */ + down_read(&fs_info->scrub_super_lock); ret = scrub_supers(sctx, dev); + up_read(&fs_info->scrub_super_lock); } - mutex_unlock(&fs_info->fs_devices->device_list_mutex); if (!ret) ret = scrub_enumerate_chunks(sctx, dev, start, end, @@ -2936,10 +2954,10 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, mutex_lock(&fs_info->scrub_lock); dev->scrub_device = NULL; - scrub_workers_put(fs_info); mutex_unlock(&fs_info->scrub_lock); scrub_free_ctx(sctx); + scrub_workers_put(fs_info); return ret; } @@ -2969,6 +2987,16 @@ void btrfs_scrub_continue(struct btrfs_root *root) wake_up(&fs_info->scrub_pause_wait); } +void btrfs_scrub_pause_super(struct btrfs_root *root) +{ + down_write(&root->fs_info->scrub_super_lock); +} + +void btrfs_scrub_continue_super(struct btrfs_root *root) +{ + up_write(&root->fs_info->scrub_super_lock); +} + int btrfs_scrub_cancel(struct btrfs_fs_info *fs_info) { mutex_lock(&fs_info->scrub_lock); @@ -3355,6 +3383,7 @@ static int write_page_nocow(struct scrub_ctx *sctx, struct bio *bio; struct btrfs_device *dev; int ret; + DECLARE_COMPLETION_ONSTACK(compl); dev = sctx->wr_ctx.tgtdev; if (!dev) @@ -3371,6 +3400,8 @@ static int write_page_nocow(struct scrub_ctx *sctx, spin_unlock(&sctx->stat_lock); return -ENOMEM; } + bio->bi_private = &compl; + bio->bi_end_io = scrub_complete_bio_end_io; bio->bi_size = 0; bio->bi_sector = physical_for_dev_replace >> 9; bio->bi_bdev = dev->bdev; @@ -3381,8 +3412,10 @@ leave_with_eio: btrfs_dev_stat_inc_and_print(dev, BTRFS_DEV_STAT_WRITE_ERRS); return -EIO; } + btrfsic_submit_bio(WRITE_SYNC, bio); + wait_for_completion(&compl); - if (btrfsic_submit_bio_wait(WRITE_SYNC, bio)) + if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) goto leave_with_eio; bio_put(bio); diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 6837fe8..e46e0ed 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -121,6 +121,7 @@ struct send_ctx { struct list_head name_cache_list; int name_cache_size; + struct file *cur_inode_filp; char *read_buf; }; @@ -564,8 +565,10 @@ static int begin_cmd(struct send_ctx *sctx, int cmd) { struct btrfs_cmd_header *hdr; - if (WARN_ON(!sctx->send_buf)) + if (!sctx->send_buf) { + WARN_ON(1); return -EINVAL; + } BUG_ON(sctx->send_size); @@ -788,7 +791,7 @@ static int iterate_inode_ref(struct btrfs_root *root, struct btrfs_path *path, if (found_key->type == BTRFS_INODE_REF_KEY) { ptr = (unsigned long)btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); - item = btrfs_item_nr(slot); + item = btrfs_item_nr(eb, slot); total = btrfs_item_size(eb, item); elem_size = sizeof(*iref); } else { @@ -902,7 +905,7 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path, eb = path->nodes[0]; slot = path->slots[0]; - item = btrfs_item_nr(slot); + item = btrfs_item_nr(eb, slot); di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item); cur = 0; len = 0; @@ -2117,6 +2120,77 @@ out: } /* + * Called for regular files when sending extents data. Opens a struct file + * to read from the file. + */ +static int open_cur_inode_file(struct send_ctx *sctx) +{ + int ret = 0; + struct btrfs_key key; + struct path path; + struct inode *inode; + struct dentry *dentry; + struct file *filp; + int new = 0; + + if (sctx->cur_inode_filp) + goto out; + + key.objectid = sctx->cur_ino; + key.type = BTRFS_INODE_ITEM_KEY; + key.offset = 0; + + inode = btrfs_iget(sctx->send_root->fs_info->sb, &key, sctx->send_root, + &new); + if (IS_ERR(inode)) { + ret = PTR_ERR(inode); + goto out; + } + + dentry = d_obtain_alias(inode); + inode = NULL; + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto out; + } + + path.mnt = sctx->mnt; + path.dentry = dentry; + filp = dentry_open(&path, O_RDONLY | O_LARGEFILE, current_cred()); + dput(dentry); + dentry = NULL; + if (IS_ERR(filp)) { + ret = PTR_ERR(filp); + goto out; + } + sctx->cur_inode_filp = filp; + +out: + /* + * no xxxput required here as every vfs op + * does it by itself on failure + */ + return ret; +} + +/* + * Closes the struct file that was created in open_cur_inode_file + */ +static int close_cur_inode_file(struct send_ctx *sctx) +{ + int ret = 0; + + if (!sctx->cur_inode_filp) + goto out; + + ret = filp_close(sctx->cur_inode_filp, NULL); + sctx->cur_inode_filp = NULL; + +out: + return ret; +} + +/* * Sends a BTRFS_SEND_C_SUBVOL command/item to userspace */ static int send_subvol_begin(struct send_ctx *sctx) @@ -3548,72 +3622,6 @@ out: return ret; } -static ssize_t fill_read_buf(struct send_ctx *sctx, u64 offset, u32 len) -{ - struct btrfs_root *root = sctx->send_root; - struct btrfs_fs_info *fs_info = root->fs_info; - struct inode *inode; - struct page *page; - char *addr; - struct btrfs_key key; - pgoff_t index = offset >> PAGE_CACHE_SHIFT; - pgoff_t last_index; - unsigned pg_offset = offset & ~PAGE_CACHE_MASK; - ssize_t ret = 0; - - key.objectid = sctx->cur_ino; - key.type = BTRFS_INODE_ITEM_KEY; - key.offset = 0; - - inode = btrfs_iget(fs_info->sb, &key, root, NULL); - if (IS_ERR(inode)) - return PTR_ERR(inode); - - if (offset + len > i_size_read(inode)) { - if (offset > i_size_read(inode)) - len = 0; - else - len = offset - i_size_read(inode); - } - if (len == 0) - goto out; - - last_index = (offset + len - 1) >> PAGE_CACHE_SHIFT; - while (index <= last_index) { - unsigned cur_len = min_t(unsigned, len, - PAGE_CACHE_SIZE - pg_offset); - page = find_or_create_page(inode->i_mapping, index, GFP_NOFS); - if (!page) { - ret = -ENOMEM; - break; - } - - if (!PageUptodate(page)) { - btrfs_readpage(NULL, page); - lock_page(page); - if (!PageUptodate(page)) { - unlock_page(page); - page_cache_release(page); - ret = -EIO; - break; - } - } - - addr = kmap(page); - memcpy(sctx->read_buf + ret, addr + pg_offset, cur_len); - kunmap(page); - unlock_page(page); - page_cache_release(page); - index++; - pg_offset = 0; - len -= cur_len; - ret += cur_len; - } -out: - iput(inode); - return ret; -} - /* * Read some bytes from the current inode/file and send a write command to * user space. @@ -3622,20 +3630,35 @@ static int send_write(struct send_ctx *sctx, u64 offset, u32 len) { int ret = 0; struct fs_path *p; - ssize_t num_read = 0; + loff_t pos = offset; + int num_read = 0; + mm_segment_t old_fs; p = fs_path_alloc(); if (!p) return -ENOMEM; + /* + * vfs normally only accepts user space buffers for security reasons. + * we only read from the file and also only provide the read_buf buffer + * to vfs. As this buffer does not come from a user space call, it's + * ok to temporary allow kernel space buffers. + */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + verbose_printk("btrfs: send_write offset=%llu, len=%d\n", offset, len); - num_read = fill_read_buf(sctx, offset, len); - if (num_read <= 0) { - if (num_read < 0) - ret = num_read; + ret = open_cur_inode_file(sctx); + if (ret < 0) + goto out; + + ret = vfs_read(sctx->cur_inode_filp, sctx->read_buf, len, &pos); + if (ret < 0) + goto out; + num_read = ret; + if (!num_read) goto out; - } ret = begin_cmd(sctx, BTRFS_SEND_C_WRITE); if (ret < 0) @@ -3654,6 +3677,7 @@ verbose_printk("btrfs: send_write offset=%llu, len=%d\n", offset, len); tlv_put_failure: out: fs_path_free(p); + set_fs(old_fs); if (ret < 0) return ret; return num_read; @@ -3902,16 +3926,16 @@ static int is_extent_unchanged(struct send_ctx *sctx, while (key.offset < ekey->offset + left_len) { ei = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item); right_type = btrfs_file_extent_type(eb, ei); - if (right_type != BTRFS_FILE_EXTENT_REG) { - ret = 0; - goto out; - } - right_disknr = btrfs_file_extent_disk_bytenr(eb, ei); right_len = btrfs_file_extent_num_bytes(eb, ei); right_offset = btrfs_file_extent_offset(eb, ei); right_gen = btrfs_file_extent_generation(eb, ei); + if (right_type != BTRFS_FILE_EXTENT_REG) { + ret = 0; + goto out; + } + /* * Are we at extent 8? If yes, we know the extent is changed. * This may only happen on the first iteration. @@ -4198,6 +4222,10 @@ static int changed_inode(struct send_ctx *sctx, u64 left_gen = 0; u64 right_gen = 0; + ret = close_cur_inode_file(sctx); + if (ret < 0) + goto out; + sctx->cur_ino = key->objectid; sctx->cur_inode_new_gen = 0; @@ -4658,6 +4686,11 @@ static int send_subvol(struct send_ctx *sctx) } out: + if (!ret) + ret = close_cur_inode_file(sctx); + else + close_cur_inode_file(sctx); + free_recorded_refs(sctx); return ret; } diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 2d8ac1b..e913328 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -42,6 +42,7 @@ #include <linux/cleancache.h> #include <linux/ratelimit.h> #include <linux/btrfs.h> +#include "compat.h" #include "delayed-inode.h" #include "ctree.h" #include "disk-io.h" @@ -920,7 +921,7 @@ int btrfs_sync_fs(struct super_block *sb, int wait) return 0; } - btrfs_wait_ordered_roots(fs_info, -1); + btrfs_wait_all_ordered_extents(fs_info); trans = btrfs_attach_transaction_barrier(root); if (IS_ERR(trans)) { @@ -1329,12 +1330,6 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) * this also happens on 'umount -rf' or on shutdown, when * the filesystem is busy. */ - - /* wait for the uuid_scan task to finish */ - down(&fs_info->uuid_tree_rescan_sem); - /* avoid complains from lockdep et al. */ - up(&fs_info->uuid_tree_rescan_sem); - sb->s_flags |= MS_RDONLY; btrfs_dev_replace_suspend_for_unmount(fs_info); @@ -1470,7 +1465,7 @@ static int btrfs_calc_avail_data_space(struct btrfs_root *root, u64 *free_bytes) nr_devices = fs_info->fs_devices->open_devices; BUG_ON(!nr_devices); - devices_info = kmalloc_array(nr_devices, sizeof(*devices_info), + devices_info = kmalloc(sizeof(*devices_info) * nr_devices, GFP_NOFS); if (!devices_info) return -ENOMEM; @@ -1794,25 +1789,7 @@ static void btrfs_print_info(void) static int btrfs_run_sanity_tests(void) { - int ret; - - ret = btrfs_init_test_fs(); - if (ret) - return ret; - - ret = btrfs_test_free_space_cache(); - if (ret) - goto out; - ret = btrfs_test_extent_buffer_operations(); - if (ret) - goto out; - ret = btrfs_test_extent_io(); - if (ret) - goto out; - ret = btrfs_test_inodes(); -out: - btrfs_destroy_test_fs(); - return ret; + return btrfs_test_free_space_cache(); } static int __init init_btrfs_fs(void) diff --git a/fs/btrfs/tests/btrfs-tests.c b/fs/btrfs/tests/btrfs-tests.c deleted file mode 100644 index 757ef00..0000000 --- a/fs/btrfs/tests/btrfs-tests.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2013 Fusion IO. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License v2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 021110-1307, USA. - */ - -#include <linux/fs.h> -#include <linux/mount.h> -#include <linux/magic.h> -#include "btrfs-tests.h" -#include "../ctree.h" - -static struct vfsmount *test_mnt = NULL; - -static const struct super_operations btrfs_test_super_ops = { - .alloc_inode = btrfs_alloc_inode, - .destroy_inode = btrfs_test_destroy_inode, -}; - -static struct dentry *btrfs_test_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) -{ - return mount_pseudo(fs_type, "btrfs_test:", &btrfs_test_super_ops, - NULL, BTRFS_TEST_MAGIC); -} - -static struct file_system_type test_type = { - .name = "btrfs_test_fs", - .mount = btrfs_test_mount, - .kill_sb = kill_anon_super, -}; - -struct inode *btrfs_new_test_inode(void) -{ - return new_inode(test_mnt->mnt_sb); -} - -int btrfs_init_test_fs(void) -{ - int ret; - - ret = register_filesystem(&test_type); - if (ret) { - printk(KERN_ERR "btrfs: cannot register test file system\n"); - return ret; - } - - test_mnt = kern_mount(&test_type); - if (IS_ERR(test_mnt)) { - printk(KERN_ERR "btrfs: cannot mount test file system\n"); - unregister_filesystem(&test_type); - return ret; - } - return 0; -} - -void btrfs_destroy_test_fs(void) -{ - kern_unmount(test_mnt); - unregister_filesystem(&test_type); -} diff --git a/fs/btrfs/tests/btrfs-tests.h b/fs/btrfs/tests/btrfs-tests.h index b353bc8..5808776 100644 --- a/fs/btrfs/tests/btrfs-tests.h +++ b/fs/btrfs/tests/btrfs-tests.h @@ -24,36 +24,11 @@ #define test_msg(fmt, ...) pr_info("btrfs: selftest: " fmt, ##__VA_ARGS__) int btrfs_test_free_space_cache(void); -int btrfs_test_extent_buffer_operations(void); -int btrfs_test_extent_io(void); -int btrfs_test_inodes(void); -int btrfs_init_test_fs(void); -void btrfs_destroy_test_fs(void); -struct inode *btrfs_new_test_inode(void); #else static inline int btrfs_test_free_space_cache(void) { return 0; } -static inline int btrfs_test_extent_buffer_operations(void) -{ - return 0; -} -static inline int btrfs_init_test_fs(void) -{ - return 0; -} -static inline void btrfs_destroy_test_fs(void) -{ -} -static inline int btrfs_test_extent_io(void) -{ - return 0; -} -static inline int btrfs_test_inodes(void) -{ - return 0; -} #endif #endif diff --git a/fs/btrfs/tests/extent-buffer-tests.c b/fs/btrfs/tests/extent-buffer-tests.c deleted file mode 100644 index cc286ce..0000000 --- a/fs/btrfs/tests/extent-buffer-tests.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2013 Fusion IO. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License v2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 021110-1307, USA. - */ - -#include <linux/slab.h> -#include "btrfs-tests.h" -#include "../ctree.h" -#include "../extent_io.h" -#include "../disk-io.h" - -static int test_btrfs_split_item(void) -{ - struct btrfs_path *path; - struct btrfs_root *root; - struct extent_buffer *eb; - struct btrfs_item *item; - char *value = "mary had a little lamb"; - char *split1 = "mary had a little"; - char *split2 = " lamb"; - char *split3 = "mary"; - char *split4 = " had a little"; - char buf[32]; - struct btrfs_key key; - u32 value_len = strlen(value); - int ret = 0; - - test_msg("Running btrfs_split_item tests\n"); - - root = btrfs_alloc_dummy_root(); - if (IS_ERR(root)) { - test_msg("Could not allocate root\n"); - return PTR_ERR(root); - } - - path = btrfs_alloc_path(); - if (!path) { - test_msg("Could not allocate path\n"); - kfree(root); - return -ENOMEM; - } - - path->nodes[0] = eb = alloc_dummy_extent_buffer(0, 4096); - if (!eb) { - test_msg("Could not allocate dummy buffer\n"); - ret = -ENOMEM; - goto out; - } - path->slots[0] = 0; - - key.objectid = 0; - key.type = BTRFS_EXTENT_CSUM_KEY; - key.offset = 0; - - setup_items_for_insert(root, path, &key, &value_len, value_len, - value_len + sizeof(struct btrfs_item), 1); - item = btrfs_item_nr(0); - write_extent_buffer(eb, value, btrfs_item_ptr_offset(eb, 0), - value_len); - - key.offset = 3; - - /* - * Passing NULL trans here should be safe because we have plenty of - * space in this leaf to split the item without having to split the - * leaf. - */ - ret = btrfs_split_item(NULL, root, path, &key, 17); - if (ret) { - test_msg("Split item failed %d\n", ret); - goto out; - } - - /* - * Read the first slot, it should have the original key and contain only - * 'mary had a little' - */ - btrfs_item_key_to_cpu(eb, &key, 0); - if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || - key.offset != 0) { - test_msg("Invalid key at slot 0\n"); - ret = -EINVAL; - goto out; - } - - item = btrfs_item_nr(0); - if (btrfs_item_size(eb, item) != strlen(split1)) { - test_msg("Invalid len in the first split\n"); - ret = -EINVAL; - goto out; - } - - read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 0), - strlen(split1)); - if (memcmp(buf, split1, strlen(split1))) { - test_msg("Data in the buffer doesn't match what it should " - "in the first split have='%.*s' want '%s'\n", - (int)strlen(split1), buf, split1); - ret = -EINVAL; - goto out; - } - - btrfs_item_key_to_cpu(eb, &key, 1); - if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || - key.offset != 3) { - test_msg("Invalid key at slot 1\n"); - ret = -EINVAL; - goto out; - } - - item = btrfs_item_nr(1); - if (btrfs_item_size(eb, item) != strlen(split2)) { - test_msg("Invalid len in the second split\n"); - ret = -EINVAL; - goto out; - } - - read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 1), - strlen(split2)); - if (memcmp(buf, split2, strlen(split2))) { - test_msg("Data in the buffer doesn't match what it should " - "in the second split\n"); - ret = -EINVAL; - goto out; - } - - key.offset = 1; - /* Do it again so we test memmoving the other items in the leaf */ - ret = btrfs_split_item(NULL, root, path, &key, 4); - if (ret) { - test_msg("Second split item failed %d\n", ret); - goto out; - } - - btrfs_item_key_to_cpu(eb, &key, 0); - if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || - key.offset != 0) { - test_msg("Invalid key at slot 0\n"); - ret = -EINVAL; - goto out; - } - - item = btrfs_item_nr(0); - if (btrfs_item_size(eb, item) != strlen(split3)) { - test_msg("Invalid len in the first split\n"); - ret = -EINVAL; - goto out; - } - - read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 0), - strlen(split3)); - if (memcmp(buf, split3, strlen(split3))) { - test_msg("Data in the buffer doesn't match what it should " - "in the third split"); - ret = -EINVAL; - goto out; - } - - btrfs_item_key_to_cpu(eb, &key, 1); - if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || - key.offset != 1) { - test_msg("Invalid key at slot 1\n"); - ret = -EINVAL; - goto out; - } - - item = btrfs_item_nr(1); - if (btrfs_item_size(eb, item) != strlen(split4)) { - test_msg("Invalid len in the second split\n"); - ret = -EINVAL; - goto out; - } - - read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 1), - strlen(split4)); - if (memcmp(buf, split4, strlen(split4))) { - test_msg("Data in the buffer doesn't match what it should " - "in the fourth split\n"); - ret = -EINVAL; - goto out; - } - - btrfs_item_key_to_cpu(eb, &key, 2); - if (key.objectid != 0 || key.type != BTRFS_EXTENT_CSUM_KEY || - key.offset != 3) { - test_msg("Invalid key at slot 2\n"); - ret = -EINVAL; - goto out; - } - - item = btrfs_item_nr(2); - if (btrfs_item_size(eb, item) != strlen(split2)) { - test_msg("Invalid len in the second split\n"); - ret = -EINVAL; - goto out; - } - - read_extent_buffer(eb, buf, btrfs_item_ptr_offset(eb, 2), - strlen(split2)); - if (memcmp(buf, split2, strlen(split2))) { - test_msg("Data in the buffer doesn't match what it should " - "in the last chunk\n"); - ret = -EINVAL; - goto out; - } -out: - btrfs_free_path(path); - kfree(root); - return ret; -} - -int btrfs_test_extent_buffer_operations(void) -{ - test_msg("Running extent buffer operation tests"); - return test_btrfs_split_item(); -} diff --git a/fs/btrfs/tests/extent-io-tests.c b/fs/btrfs/tests/extent-io-tests.c deleted file mode 100644 index 7e99c2f..0000000 --- a/fs/btrfs/tests/extent-io-tests.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (C) 2013 Fusion IO. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License v2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 021110-1307, USA. - */ - -#include <linux/pagemap.h> -#include <linux/sched.h> -#include "btrfs-tests.h" -#include "../extent_io.h" - -#define PROCESS_UNLOCK (1 << 0) -#define PROCESS_RELEASE (1 << 1) -#define PROCESS_TEST_LOCKED (1 << 2) - -static noinline int process_page_range(struct inode *inode, u64 start, u64 end, - unsigned long flags) -{ - int ret; - struct page *pages[16]; - unsigned long index = start >> PAGE_CACHE_SHIFT; - unsigned long end_index = end >> PAGE_CACHE_SHIFT; - unsigned long nr_pages = end_index - index + 1; - int i; - int count = 0; - int loops = 0; - - while (nr_pages > 0) { - ret = find_get_pages_contig(inode->i_mapping, index, - min_t(unsigned long, nr_pages, - ARRAY_SIZE(pages)), pages); - for (i = 0; i < ret; i++) { - if (flags & PROCESS_TEST_LOCKED && - !PageLocked(pages[i])) - count++; - if (flags & PROCESS_UNLOCK && PageLocked(pages[i])) - unlock_page(pages[i]); - page_cache_release(pages[i]); - if (flags & PROCESS_RELEASE) - page_cache_release(pages[i]); - } - nr_pages -= ret; - index += ret; - cond_resched(); - loops++; - if (loops > 100000) { - printk(KERN_ERR "stuck in a loop, start %Lu, end %Lu, nr_pages %lu, ret %d\n", start, end, nr_pages, ret); - break; - } - } - return count; -} - -static int test_find_delalloc(void) -{ - struct inode *inode; - struct extent_io_tree tmp; - struct page *page; - struct page *locked_page = NULL; - unsigned long index = 0; - u64 total_dirty = 256 * 1024 * 1024; - u64 max_bytes = 128 * 1024 * 1024; - u64 start, end, test_start; - u64 found; - int ret = -EINVAL; - - inode = btrfs_new_test_inode(); - if (!inode) { - test_msg("Failed to allocate test inode\n"); - return -ENOMEM; - } - - extent_io_tree_init(&tmp, &inode->i_data); - - /* - * First go through and create and mark all of our pages dirty, we pin - * everything to make sure our pages don't get evicted and screw up our - * test. - */ - for (index = 0; index < (total_dirty >> PAGE_CACHE_SHIFT); index++) { - page = find_or_create_page(inode->i_mapping, index, GFP_NOFS); - if (!page) { - test_msg("Failed to allocate test page\n"); - ret = -ENOMEM; - goto out; - } - SetPageDirty(page); - if (index) { - unlock_page(page); - } else { - page_cache_get(page); - locked_page = page; - } - } - - /* Test this scenario - * |--- delalloc ---| - * |--- search ---| - */ - set_extent_delalloc(&tmp, 0, 4095, NULL, GFP_NOFS); - start = 0; - end = 0; - found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, - &end, max_bytes); - if (!found) { - test_msg("Should have found at least one delalloc\n"); - goto out_bits; - } - if (start != 0 || end != 4095) { - test_msg("Expected start 0 end 4095, got start %Lu end %Lu\n", - start, end); - goto out_bits; - } - unlock_extent(&tmp, start, end); - unlock_page(locked_page); - page_cache_release(locked_page); - - /* - * Test this scenario - * - * |--- delalloc ---| - * |--- search ---| - */ - test_start = 64 * 1024 * 1024; - locked_page = find_lock_page(inode->i_mapping, - test_start >> PAGE_CACHE_SHIFT); - if (!locked_page) { - test_msg("Couldn't find the locked page\n"); - goto out_bits; - } - set_extent_delalloc(&tmp, 4096, max_bytes - 1, NULL, GFP_NOFS); - start = test_start; - end = 0; - found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, - &end, max_bytes); - if (!found) { - test_msg("Couldn't find delalloc in our range\n"); - goto out_bits; - } - if (start != test_start || end != max_bytes - 1) { - test_msg("Expected start %Lu end %Lu, got start %Lu, end " - "%Lu\n", test_start, max_bytes - 1, start, end); - goto out_bits; - } - if (process_page_range(inode, start, end, - PROCESS_TEST_LOCKED | PROCESS_UNLOCK)) { - test_msg("There were unlocked pages in the range\n"); - goto out_bits; - } - unlock_extent(&tmp, start, end); - /* locked_page was unlocked above */ - page_cache_release(locked_page); - - /* - * Test this scenario - * |--- delalloc ---| - * |--- search ---| - */ - test_start = max_bytes + 4096; - locked_page = find_lock_page(inode->i_mapping, test_start >> - PAGE_CACHE_SHIFT); - if (!locked_page) { - test_msg("Could'nt find the locked page\n"); - goto out_bits; - } - start = test_start; - end = 0; - found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, - &end, max_bytes); - if (found) { - test_msg("Found range when we shouldn't have\n"); - goto out_bits; - } - if (end != (u64)-1) { - test_msg("Did not return the proper end offset\n"); - goto out_bits; - } - - /* - * Test this scenario - * [------- delalloc -------| - * [max_bytes]|-- search--| - * - * We are re-using our test_start from above since it works out well. - */ - set_extent_delalloc(&tmp, max_bytes, total_dirty - 1, NULL, GFP_NOFS); - start = test_start; - end = 0; - found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, - &end, max_bytes); - if (!found) { - test_msg("Didn't find our range\n"); - goto out_bits; - } - if (start != test_start || end != total_dirty - 1) { - test_msg("Expected start %Lu end %Lu, got start %Lu end %Lu\n", - test_start, total_dirty - 1, start, end); - goto out_bits; - } - if (process_page_range(inode, start, end, - PROCESS_TEST_LOCKED | PROCESS_UNLOCK)) { - test_msg("Pages in range were not all locked\n"); - goto out_bits; - } - unlock_extent(&tmp, start, end); - - /* - * Now to test where we run into a page that is no longer dirty in the - * range we want to find. - */ - page = find_get_page(inode->i_mapping, (max_bytes + (1 * 1024 * 1024)) - >> PAGE_CACHE_SHIFT); - if (!page) { - test_msg("Couldn't find our page\n"); - goto out_bits; - } - ClearPageDirty(page); - page_cache_release(page); - - /* We unlocked it in the previous test */ - lock_page(locked_page); - start = test_start; - end = 0; - /* - * Currently if we fail to find dirty pages in the delalloc range we - * will adjust max_bytes down to PAGE_CACHE_SIZE and then re-search. If - * this changes at any point in the future we will need to fix this - * tests expected behavior. - */ - found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, - &end, max_bytes); - if (!found) { - test_msg("Didn't find our range\n"); - goto out_bits; - } - if (start != test_start && end != test_start + PAGE_CACHE_SIZE - 1) { - test_msg("Expected start %Lu end %Lu, got start %Lu end %Lu\n", - test_start, test_start + PAGE_CACHE_SIZE - 1, start, - end); - goto out_bits; - } - if (process_page_range(inode, start, end, PROCESS_TEST_LOCKED | - PROCESS_UNLOCK)) { - test_msg("Pages in range were not all locked\n"); - goto out_bits; - } - ret = 0; -out_bits: - clear_extent_bits(&tmp, 0, total_dirty - 1, - (unsigned long)-1, GFP_NOFS); -out: - if (locked_page) - page_cache_release(locked_page); - process_page_range(inode, 0, total_dirty - 1, - PROCESS_UNLOCK | PROCESS_RELEASE); - iput(inode); - return ret; -} - -int btrfs_test_extent_io(void) -{ - test_msg("Running find delalloc tests\n"); - return test_find_delalloc(); -} diff --git a/fs/btrfs/tests/inode-tests.c b/fs/btrfs/tests/inode-tests.c deleted file mode 100644 index 397d1f9..0000000 --- a/fs/btrfs/tests/inode-tests.c +++ /dev/null @@ -1,955 +0,0 @@ -/* - * Copyright (C) 2013 Fusion IO. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License v2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 021110-1307, USA. - */ - -#include "btrfs-tests.h" -#include "../ctree.h" -#include "../btrfs_inode.h" -#include "../disk-io.h" -#include "../extent_io.h" -#include "../volumes.h" - -static struct btrfs_fs_info *alloc_dummy_fs_info(void) -{ - struct btrfs_fs_info *fs_info = kzalloc(sizeof(struct btrfs_fs_info), - GFP_NOFS); - if (!fs_info) - return fs_info; - fs_info->fs_devices = kzalloc(sizeof(struct btrfs_fs_devices), - GFP_NOFS); - if (!fs_info->fs_devices) { - kfree(fs_info); - return NULL; - } - return fs_info; -} -static void free_dummy_root(struct btrfs_root *root) -{ - if (!root) - return; - if (root->fs_info) { - kfree(root->fs_info->fs_devices); - kfree(root->fs_info); - } - if (root->node) - free_extent_buffer(root->node); - kfree(root); -} - -static void insert_extent(struct btrfs_root *root, u64 start, u64 len, - u64 ram_bytes, u64 offset, u64 disk_bytenr, - u64 disk_len, u32 type, u8 compression, int slot) -{ - struct btrfs_path path; - struct btrfs_file_extent_item *fi; - struct extent_buffer *leaf = root->node; - struct btrfs_key key; - u32 value_len = sizeof(struct btrfs_file_extent_item); - - if (type == BTRFS_FILE_EXTENT_INLINE) - value_len += len; - memset(&path, 0, sizeof(path)); - - path.nodes[0] = leaf; - path.slots[0] = slot; - - key.objectid = BTRFS_FIRST_FREE_OBJECTID; - key.type = BTRFS_EXTENT_DATA_KEY; - key.offset = start; - - setup_items_for_insert(root, &path, &key, &value_len, value_len, - value_len + sizeof(struct btrfs_item), 1); - fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); - btrfs_set_file_extent_generation(leaf, fi, 1); - btrfs_set_file_extent_type(leaf, fi, type); - btrfs_set_file_extent_disk_bytenr(leaf, fi, disk_bytenr); - btrfs_set_file_extent_disk_num_bytes(leaf, fi, disk_len); - btrfs_set_file_extent_offset(leaf, fi, offset); - btrfs_set_file_extent_num_bytes(leaf, fi, len); - btrfs_set_file_extent_ram_bytes(leaf, fi, ram_bytes); - btrfs_set_file_extent_compression(leaf, fi, compression); - btrfs_set_file_extent_encryption(leaf, fi, 0); - btrfs_set_file_extent_other_encoding(leaf, fi, 0); -} - -static void insert_inode_item_key(struct btrfs_root *root) -{ - struct btrfs_path path; - struct extent_buffer *leaf = root->node; - struct btrfs_key key; - u32 value_len = 0; - - memset(&path, 0, sizeof(path)); - - path.nodes[0] = leaf; - path.slots[0] = 0; - - key.objectid = BTRFS_INODE_ITEM_KEY; - key.type = BTRFS_INODE_ITEM_KEY; - key.offset = 0; - - setup_items_for_insert(root, &path, &key, &value_len, value_len, - value_len + sizeof(struct btrfs_item), 1); -} - -/* - * Build the most complicated map of extents the earth has ever seen. We want - * this so we can test all of the corner cases of btrfs_get_extent. Here is a - * diagram of how the extents will look though this may not be possible we still - * want to make sure everything acts normally (the last number is not inclusive) - * - * [0 - 5][5 - 6][6 - 10][10 - 4096][ 4096 - 8192 ][8192 - 12288] - * [hole ][inline][ hole ][ regular ][regular1 split][ hole ] - * - * [ 12288 - 20480][20480 - 24576][ 24576 - 28672 ][28672 - 36864][36864 - 45056] - * [regular1 split][ prealloc1 ][prealloc1 written][ prealloc1 ][ compressed ] - * - * [45056 - 49152][49152-53248][53248-61440][61440-65536][ 65536+81920 ] - * [ compressed1 ][ regular ][compressed1][ regular ][ hole but no extent] - * - * [81920-86016] - * [ regular ] - */ -static void setup_file_extents(struct btrfs_root *root) -{ - int slot = 0; - u64 disk_bytenr = 1 * 1024 * 1024; - u64 offset = 0; - - /* First we want a hole */ - insert_extent(root, offset, 5, 5, 0, 0, 0, BTRFS_FILE_EXTENT_REG, 0, - slot); - slot++; - offset += 5; - - /* - * Now we want an inline extent, I don't think this is possible but hey - * why not? Also keep in mind if we have an inline extent it counts as - * the whole first page. If we were to expand it we would have to cow - * and we wouldn't have an inline extent anymore. - */ - insert_extent(root, offset, 1, 1, 0, 0, 0, BTRFS_FILE_EXTENT_INLINE, 0, - slot); - slot++; - offset = 4096; - - /* Now another hole */ - insert_extent(root, offset, 4, 4, 0, 0, 0, BTRFS_FILE_EXTENT_REG, 0, - slot); - slot++; - offset += 4; - - /* Now for a regular extent */ - insert_extent(root, offset, 4095, 4095, 0, disk_bytenr, 4096, - BTRFS_FILE_EXTENT_REG, 0, slot); - slot++; - disk_bytenr += 4096; - offset += 4095; - - /* - * Now for 3 extents that were split from a hole punch so we test - * offsets properly. - */ - insert_extent(root, offset, 4096, 16384, 0, disk_bytenr, 16384, - BTRFS_FILE_EXTENT_REG, 0, slot); - slot++; - offset += 4096; - insert_extent(root, offset, 4096, 4096, 0, 0, 0, BTRFS_FILE_EXTENT_REG, - 0, slot); - slot++; - offset += 4096; - insert_extent(root, offset, 8192, 16384, 8192, disk_bytenr, 16384, - BTRFS_FILE_EXTENT_REG, 0, slot); - slot++; - offset += 8192; - disk_bytenr += 16384; - - /* Now for a unwritten prealloc extent */ - insert_extent(root, offset, 4096, 4096, 0, disk_bytenr, 4096, - BTRFS_FILE_EXTENT_PREALLOC, 0, slot); - slot++; - offset += 4096; - - /* - * We want to jack up disk_bytenr a little more so the em stuff doesn't - * merge our records. - */ - disk_bytenr += 8192; - - /* - * Now for a partially written prealloc extent, basically the same as - * the hole punch example above. Ram_bytes never changes when you mark - * extents written btw. - */ - insert_extent(root, offset, 4096, 16384, 0, disk_bytenr, 16384, - BTRFS_FILE_EXTENT_PREALLOC, 0, slot); - slot++; - offset += 4096; - insert_extent(root, offset, 4096, 16384, 4096, disk_bytenr, 16384, - BTRFS_FILE_EXTENT_REG, 0, slot); - slot++; - offset += 4096; - insert_extent(root, offset, 8192, 16384, 8192, disk_bytenr, 16384, - BTRFS_FILE_EXTENT_PREALLOC, 0, slot); - slot++; - offset += 8192; - disk_bytenr += 16384; - - /* Now a normal compressed extent */ - insert_extent(root, offset, 8192, 8192, 0, disk_bytenr, 4096, - BTRFS_FILE_EXTENT_REG, BTRFS_COMPRESS_ZLIB, slot); - slot++; - offset += 8192; - /* No merges */ - disk_bytenr += 8192; - - /* Now a split compressed extent */ - insert_extent(root, offset, 4096, 16384, 0, disk_bytenr, 4096, - BTRFS_FILE_EXTENT_REG, BTRFS_COMPRESS_ZLIB, slot); - slot++; - offset += 4096; - insert_extent(root, offset, 4096, 4096, 0, disk_bytenr + 4096, 4096, - BTRFS_FILE_EXTENT_REG, 0, slot); - slot++; - offset += 4096; - insert_extent(root, offset, 8192, 16384, 8192, disk_bytenr, 4096, - BTRFS_FILE_EXTENT_REG, BTRFS_COMPRESS_ZLIB, slot); - slot++; - offset += 8192; - disk_bytenr += 8192; - - /* Now extents that have a hole but no hole extent */ - insert_extent(root, offset, 4096, 4096, 0, disk_bytenr, 4096, - BTRFS_FILE_EXTENT_REG, 0, slot); - slot++; - offset += 16384; - disk_bytenr += 4096; - insert_extent(root, offset, 4096, 4096, 0, disk_bytenr, 4096, - BTRFS_FILE_EXTENT_REG, 0, slot); -} - -static unsigned long prealloc_only = 0; -static unsigned long compressed_only = 0; -static unsigned long vacancy_only = 0; - -static noinline int test_btrfs_get_extent(void) -{ - struct inode *inode = NULL; - struct btrfs_root *root = NULL; - struct extent_map *em = NULL; - u64 orig_start; - u64 disk_bytenr; - u64 offset; - int ret = -ENOMEM; - - inode = btrfs_new_test_inode(); - if (!inode) { - test_msg("Couldn't allocate inode\n"); - return ret; - } - - BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY; - BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID; - BTRFS_I(inode)->location.offset = 0; - - root = btrfs_alloc_dummy_root(); - if (IS_ERR(root)) { - test_msg("Couldn't allocate root\n"); - goto out; - } - - /* - * We do this since btrfs_get_extent wants to assign em->bdev to - * root->fs_info->fs_devices->latest_bdev. - */ - root->fs_info = alloc_dummy_fs_info(); - if (!root->fs_info) { - test_msg("Couldn't allocate dummy fs info\n"); - goto out; - } - - root->node = alloc_dummy_extent_buffer(0, 4096); - if (!root->node) { - test_msg("Couldn't allocate dummy buffer\n"); - goto out; - } - - /* - * We will just free a dummy node if it's ref count is 2 so we need an - * extra ref so our searches don't accidently release our page. - */ - extent_buffer_get(root->node); - btrfs_set_header_nritems(root->node, 0); - btrfs_set_header_level(root->node, 0); - ret = -EINVAL; - - /* First with no extents */ - BTRFS_I(inode)->root = root; - em = btrfs_get_extent(inode, NULL, 0, 0, 4096, 0); - if (IS_ERR(em)) { - em = NULL; - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start != EXTENT_MAP_HOLE) { - test_msg("Expected a hole, got %llu\n", em->block_start); - goto out; - } - if (!test_bit(EXTENT_FLAG_VACANCY, &em->flags)) { - test_msg("Vacancy flag wasn't set properly\n"); - goto out; - } - free_extent_map(em); - btrfs_drop_extent_cache(inode, 0, (u64)-1, 0); - - /* - * All of the magic numbers are based on the mapping setup in - * setup_file_extents, so if you change anything there you need to - * update the comment and update the expected values below. - */ - setup_file_extents(root); - - em = btrfs_get_extent(inode, NULL, 0, 0, (u64)-1, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start != EXTENT_MAP_HOLE) { - test_msg("Expected a hole, got %llu\n", em->block_start); - goto out; - } - if (em->start != 0 || em->len != 5) { - test_msg("Unexpected extent wanted start 0 len 5, got start " - "%llu len %llu\n", em->start, em->len); - goto out; - } - if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); - goto out; - } - offset = em->start + em->len; - free_extent_map(em); - - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start != EXTENT_MAP_INLINE) { - test_msg("Expected an inline, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 4091) { - test_msg("Unexpected extent wanted start %llu len 1, got start " - "%llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); - goto out; - } - /* - * We don't test anything else for inline since it doesn't get set - * unless we have a page for it to write into. Maybe we should change - * this? - */ - offset = em->start + em->len; - free_extent_map(em); - - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start != EXTENT_MAP_HOLE) { - test_msg("Expected a hole, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 4) { - test_msg("Unexpected extent wanted start %llu len 4, got start " - "%llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); - goto out; - } - offset = em->start + em->len; - free_extent_map(em); - - /* Regular extent */ - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 4095) { - test_msg("Unexpected extent wanted start %llu len 4095, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); - goto out; - } - if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, - em->orig_start); - goto out; - } - offset = em->start + em->len; - free_extent_map(em); - - /* The next 3 are split extents */ - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 4096) { - test_msg("Unexpected extent wanted start %llu len 4096, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); - goto out; - } - if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, - em->orig_start); - goto out; - } - disk_bytenr = em->block_start; - orig_start = em->start; - offset = em->start + em->len; - free_extent_map(em); - - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start != EXTENT_MAP_HOLE) { - test_msg("Expected a hole, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 4096) { - test_msg("Unexpected extent wanted start %llu len 4096, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); - goto out; - } - offset = em->start + em->len; - free_extent_map(em); - - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 8192) { - test_msg("Unexpected extent wanted start %llu len 8192, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); - goto out; - } - if (em->orig_start != orig_start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", - orig_start, em->orig_start); - goto out; - } - disk_bytenr += (em->start - orig_start); - if (em->block_start != disk_bytenr) { - test_msg("Wrong block start, want %llu, have %llu\n", - disk_bytenr, em->block_start); - goto out; - } - offset = em->start + em->len; - free_extent_map(em); - - /* Prealloc extent */ - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 4096) { - test_msg("Unexpected extent wanted start %llu len 4096, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != prealloc_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", - prealloc_only, em->flags); - goto out; - } - if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, - em->orig_start); - goto out; - } - offset = em->start + em->len; - free_extent_map(em); - - /* The next 3 are a half written prealloc extent */ - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 4096) { - test_msg("Unexpected extent wanted start %llu len 4096, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != prealloc_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", - prealloc_only, em->flags); - goto out; - } - if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, - em->orig_start); - goto out; - } - disk_bytenr = em->block_start; - orig_start = em->start; - offset = em->start + em->len; - free_extent_map(em); - - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start >= EXTENT_MAP_HOLE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 4096) { - test_msg("Unexpected extent wanted start %llu len 4096, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); - goto out; - } - if (em->orig_start != orig_start) { - test_msg("Unexpected orig offset, wanted %llu, have %llu\n", - orig_start, em->orig_start); - goto out; - } - if (em->block_start != (disk_bytenr + (em->start - em->orig_start))) { - test_msg("Unexpected block start, wanted %llu, have %llu\n", - disk_bytenr + (em->start - em->orig_start), - em->block_start); - goto out; - } - offset = em->start + em->len; - free_extent_map(em); - - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 8192) { - test_msg("Unexpected extent wanted start %llu len 8192, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != prealloc_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", - prealloc_only, em->flags); - goto out; - } - if (em->orig_start != orig_start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", orig_start, - em->orig_start); - goto out; - } - if (em->block_start != (disk_bytenr + (em->start - em->orig_start))) { - test_msg("Unexpected block start, wanted %llu, have %llu\n", - disk_bytenr + (em->start - em->orig_start), - em->block_start); - goto out; - } - offset = em->start + em->len; - free_extent_map(em); - - /* Now for the compressed extent */ - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 8192) { - test_msg("Unexpected extent wanted start %llu len 8192, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != compressed_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", - compressed_only, em->flags); - goto out; - } - if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", - em->start, em->orig_start); - goto out; - } - if (em->compress_type != BTRFS_COMPRESS_ZLIB) { - test_msg("Unexpected compress type, wanted %d, got %d\n", - BTRFS_COMPRESS_ZLIB, em->compress_type); - goto out; - } - offset = em->start + em->len; - free_extent_map(em); - - /* Split compressed extent */ - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 4096) { - test_msg("Unexpected extent wanted start %llu len 4096, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != compressed_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", - compressed_only, em->flags); - goto out; - } - if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", - em->start, em->orig_start); - goto out; - } - if (em->compress_type != BTRFS_COMPRESS_ZLIB) { - test_msg("Unexpected compress type, wanted %d, got %d\n", - BTRFS_COMPRESS_ZLIB, em->compress_type); - goto out; - } - disk_bytenr = em->block_start; - orig_start = em->start; - offset = em->start + em->len; - free_extent_map(em); - - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 4096) { - test_msg("Unexpected extent wanted start %llu len 4096, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); - goto out; - } - if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, - em->orig_start); - goto out; - } - offset = em->start + em->len; - free_extent_map(em); - - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start != disk_bytenr) { - test_msg("Block start does not match, want %llu got %llu\n", - disk_bytenr, em->block_start); - goto out; - } - if (em->start != offset || em->len != 8192) { - test_msg("Unexpected extent wanted start %llu len 8192, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != compressed_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", - compressed_only, em->flags); - goto out; - } - if (em->orig_start != orig_start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", - em->start, orig_start); - goto out; - } - if (em->compress_type != BTRFS_COMPRESS_ZLIB) { - test_msg("Unexpected compress type, wanted %d, got %d\n", - BTRFS_COMPRESS_ZLIB, em->compress_type); - goto out; - } - offset = em->start + em->len; - free_extent_map(em); - - /* A hole between regular extents but no hole extent */ - em = btrfs_get_extent(inode, NULL, 0, offset + 6, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 4096) { - test_msg("Unexpected extent wanted start %llu len 4096, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); - goto out; - } - if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, - em->orig_start); - goto out; - } - offset = em->start + em->len; - free_extent_map(em); - - em = btrfs_get_extent(inode, NULL, 0, offset, 4096 * 1024, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start != EXTENT_MAP_HOLE) { - test_msg("Expected a hole extent, got %llu\n", em->block_start); - goto out; - } - /* - * Currently we just return a length that we requested rather than the - * length of the actual hole, if this changes we'll have to change this - * test. - */ - if (em->start != offset || em->len != 12288) { - test_msg("Unexpected extent wanted start %llu len 12288, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != vacancy_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", - vacancy_only, em->flags); - goto out; - } - if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, - em->orig_start); - goto out; - } - offset = em->start + em->len; - free_extent_map(em); - - em = btrfs_get_extent(inode, NULL, 0, offset, 4096, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); - goto out; - } - if (em->start != offset || em->len != 4096) { - test_msg("Unexpected extent wanted start %llu len 4096, got " - "start %llu len %llu\n", offset, em->start, em->len); - goto out; - } - if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); - goto out; - } - if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, - em->orig_start); - goto out; - } - ret = 0; -out: - if (!IS_ERR(em)) - free_extent_map(em); - iput(inode); - free_dummy_root(root); - return ret; -} - -static int test_hole_first(void) -{ - struct inode *inode = NULL; - struct btrfs_root *root = NULL; - struct extent_map *em = NULL; - int ret = -ENOMEM; - - inode = btrfs_new_test_inode(); - if (!inode) { - test_msg("Couldn't allocate inode\n"); - return ret; - } - - BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY; - BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID; - BTRFS_I(inode)->location.offset = 0; - - root = btrfs_alloc_dummy_root(); - if (IS_ERR(root)) { - test_msg("Couldn't allocate root\n"); - goto out; - } - - root->fs_info = alloc_dummy_fs_info(); - if (!root->fs_info) { - test_msg("Couldn't allocate dummy fs info\n"); - goto out; - } - - root->node = alloc_dummy_extent_buffer(0, 4096); - if (!root->node) { - test_msg("Couldn't allocate dummy buffer\n"); - goto out; - } - - extent_buffer_get(root->node); - btrfs_set_header_nritems(root->node, 0); - btrfs_set_header_level(root->node, 0); - BTRFS_I(inode)->root = root; - ret = -EINVAL; - - /* - * Need a blank inode item here just so we don't confuse - * btrfs_get_extent. - */ - insert_inode_item_key(root); - insert_extent(root, 4096, 4096, 4096, 0, 4096, 4096, - BTRFS_FILE_EXTENT_REG, 0, 1); - em = btrfs_get_extent(inode, NULL, 0, 0, 8192, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start != EXTENT_MAP_HOLE) { - test_msg("Expected a hole, got %llu\n", em->block_start); - goto out; - } - if (em->start != 0 || em->len != 4096) { - test_msg("Unexpected extent wanted start 0 len 4096, got start " - "%llu len %llu\n", em->start, em->len); - goto out; - } - if (em->flags != vacancy_only) { - test_msg("Wrong flags, wanted %lu, have %lu\n", vacancy_only, - em->flags); - goto out; - } - free_extent_map(em); - - em = btrfs_get_extent(inode, NULL, 0, 4096, 8192, 0); - if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); - goto out; - } - if (em->block_start != 4096) { - test_msg("Expected a real extent, got %llu\n", em->block_start); - goto out; - } - if (em->start != 4096 || em->len != 4096) { - test_msg("Unexpected extent wanted start 4096 len 4096, got " - "start %llu len %llu\n", em->start, em->len); - goto out; - } - if (em->flags != 0) { - test_msg("Unexpected flags set, wanted 0 got %lu\n", - em->flags); - goto out; - } - ret = 0; -out: - if (!IS_ERR(em)) - free_extent_map(em); - iput(inode); - free_dummy_root(root); - return ret; -} - -int btrfs_test_inodes(void) -{ - int ret; - - set_bit(EXTENT_FLAG_COMPRESSED, &compressed_only); - set_bit(EXTENT_FLAG_VACANCY, &vacancy_only); - set_bit(EXTENT_FLAG_PREALLOC, &prealloc_only); - - test_msg("Running btrfs_get_extent tests\n"); - ret = test_btrfs_get_extent(); - if (ret) - return ret; - test_msg("Running hole first btrfs_get_extent test\n"); - return test_hole_first(); -} diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index c6a872a..8c81bdc 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -57,7 +57,7 @@ static unsigned int btrfs_blocked_trans_types[TRANS_STATE_MAX] = { __TRANS_JOIN_NOLOCK), }; -void btrfs_put_transaction(struct btrfs_transaction *transaction) +static void put_transaction(struct btrfs_transaction *transaction) { WARN_ON(atomic_read(&transaction->use_count) == 0); if (atomic_dec_and_test(&transaction->use_count)) { @@ -332,7 +332,7 @@ static void wait_current_trans(struct btrfs_root *root) wait_event(root->fs_info->transaction_wait, cur_trans->state >= TRANS_STATE_UNBLOCKED || cur_trans->aborted); - btrfs_put_transaction(cur_trans); + put_transaction(cur_trans); } else { spin_unlock(&root->fs_info->trans_lock); } @@ -353,17 +353,6 @@ static int may_wait_transaction(struct btrfs_root *root, int type) return 0; } -static inline bool need_reserve_reloc_root(struct btrfs_root *root) -{ - if (!root->fs_info->reloc_ctl || - !root->ref_cows || - root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID || - root->reloc_root) - return false; - - return true; -} - static struct btrfs_trans_handle * start_transaction(struct btrfs_root *root, u64 num_items, unsigned int type, enum btrfs_reserve_flush_enum flush) @@ -371,9 +360,8 @@ start_transaction(struct btrfs_root *root, u64 num_items, unsigned int type, struct btrfs_trans_handle *h; struct btrfs_transaction *cur_trans; u64 num_bytes = 0; - u64 qgroup_reserved = 0; - bool reloc_reserved = false; int ret; + u64 qgroup_reserved = 0; if (test_bit(BTRFS_FS_STATE_ERROR, &root->fs_info->fs_state)) return ERR_PTR(-EROFS); @@ -402,14 +390,6 @@ start_transaction(struct btrfs_root *root, u64 num_items, unsigned int type, } num_bytes = btrfs_calc_trans_metadata_size(root, num_items); - /* - * Do the reservation for the relocation root creation - */ - if (unlikely(need_reserve_reloc_root(root))) { - num_bytes += root->nodesize; - reloc_reserved = true; - } - ret = btrfs_block_rsv_add(root, &root->fs_info->trans_block_rsv, num_bytes, flush); @@ -471,7 +451,6 @@ again: h->delayed_ref_elem.seq = 0; h->type = type; h->allocating_chunk = false; - h->reloc_reserved = false; INIT_LIST_HEAD(&h->qgroup_ref_list); INIT_LIST_HEAD(&h->new_bgs); @@ -487,7 +466,6 @@ again: h->transid, num_bytes, 1); h->block_rsv = &root->fs_info->trans_block_rsv; h->bytes_reserved = num_bytes; - h->reloc_reserved = reloc_reserved; } h->qgroup_reserved = qgroup_reserved; @@ -632,7 +610,7 @@ int btrfs_wait_for_commit(struct btrfs_root *root, u64 transid) } wait_for_commit(root, cur_trans); - btrfs_put_transaction(cur_trans); + put_transaction(cur_trans); out: return ret; } @@ -757,7 +735,7 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, smp_mb(); if (waitqueue_active(&cur_trans->writer_wait)) wake_up(&cur_trans->writer_wait); - btrfs_put_transaction(cur_trans); + put_transaction(cur_trans); if (current->journal_info == trans) current->journal_info = NULL; @@ -766,10 +744,8 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, btrfs_run_delayed_iputs(root); if (trans->aborted || - test_bit(BTRFS_FS_STATE_ERROR, &root->fs_info->fs_state)) { - wake_up_process(info->transaction_kthread); + test_bit(BTRFS_FS_STATE_ERROR, &root->fs_info->fs_state)) err = -EIO; - } assert_qgroups_uptodate(trans); kmem_cache_free(btrfs_trans_handle_cachep, trans); @@ -972,19 +948,16 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans, return ret; ret = btrfs_run_dev_stats(trans, root->fs_info); - if (ret) - return ret; + WARN_ON(ret); ret = btrfs_run_dev_replace(trans, root->fs_info); - if (ret) - return ret; + WARN_ON(ret); + ret = btrfs_run_qgroups(trans, root->fs_info); - if (ret) - return ret; + BUG_ON(ret); /* run_qgroups might have added some more refs */ ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1); - if (ret) - return ret; + BUG_ON(ret); while (!list_empty(&fs_info->dirty_cowonly_roots)) { next = fs_info->dirty_cowonly_roots.next; @@ -1480,7 +1453,7 @@ static void do_async_commit(struct work_struct *work) * We've got freeze protection passed with the transaction. * Tell lockdep about it. */ - if (ac->newtrans->type & __TRANS_FREEZABLE) + if (ac->newtrans->type < TRANS_JOIN_NOLOCK) rwsem_acquire_read( &ac->root->fs_info->sb->s_writers.lock_map[SB_FREEZE_FS-1], 0, 1, _THIS_IP_); @@ -1521,7 +1494,7 @@ int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans, * Tell lockdep we've released the freeze rwsem, since the * async commit thread will be the one to unlock it. */ - if (ac->newtrans->type & __TRANS_FREEZABLE) + if (trans->type < TRANS_JOIN_NOLOCK) rwsem_release( &root->fs_info->sb->s_writers.lock_map[SB_FREEZE_FS-1], 1, _THIS_IP_); @@ -1537,7 +1510,7 @@ int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans, if (current->journal_info == trans) current->journal_info = NULL; - btrfs_put_transaction(cur_trans); + put_transaction(cur_trans); return 0; } @@ -1579,10 +1552,8 @@ static void cleanup_transaction(struct btrfs_trans_handle *trans, root->fs_info->running_transaction = NULL; spin_unlock(&root->fs_info->trans_lock); - if (trans->type & __TRANS_FREEZABLE) - sb_end_intwrite(root->fs_info->sb); - btrfs_put_transaction(cur_trans); - btrfs_put_transaction(cur_trans); + put_transaction(cur_trans); + put_transaction(cur_trans); trace_btrfs_transaction_commit(root); @@ -1600,19 +1571,15 @@ static int btrfs_flush_all_pending_stuffs(struct btrfs_trans_handle *trans, int ret; ret = btrfs_run_delayed_items(trans, root); + if (ret) + return ret; + /* * running the delayed items may have added new refs. account * them now so that they hinder processing of more delayed refs * as little as possible. */ - if (ret) { - btrfs_delayed_refs_qgroup_accounting(trans, root->fs_info); - return ret; - } - - ret = btrfs_delayed_refs_qgroup_accounting(trans, root->fs_info); - if (ret) - return ret; + btrfs_delayed_refs_qgroup_accounting(trans, root->fs_info); /* * rename don't use btrfs_join_transaction, so, once we @@ -1629,14 +1596,14 @@ static int btrfs_flush_all_pending_stuffs(struct btrfs_trans_handle *trans, static inline int btrfs_start_delalloc_flush(struct btrfs_fs_info *fs_info) { if (btrfs_test_opt(fs_info->tree_root, FLUSHONCOMMIT)) - return btrfs_start_delalloc_roots(fs_info, 1); + return btrfs_start_all_delalloc_inodes(fs_info, 1); return 0; } static inline void btrfs_wait_delalloc_flush(struct btrfs_fs_info *fs_info) { if (btrfs_test_opt(fs_info->tree_root, FLUSHONCOMMIT)) - btrfs_wait_ordered_roots(fs_info, -1); + btrfs_wait_all_ordered_extents(fs_info); } int btrfs_commit_transaction(struct btrfs_trans_handle *trans, @@ -1702,7 +1669,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, wait_for_commit(root, cur_trans); - btrfs_put_transaction(cur_trans); + put_transaction(cur_trans); return ret; } @@ -1719,7 +1686,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, wait_for_commit(root, prev_trans); - btrfs_put_transaction(prev_trans); + put_transaction(prev_trans); } else { spin_unlock(&root->fs_info->trans_lock); } @@ -1918,8 +1885,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, list_del_init(&cur_trans->list); spin_unlock(&root->fs_info->trans_lock); - btrfs_put_transaction(cur_trans); - btrfs_put_transaction(cur_trans); + put_transaction(cur_trans); + put_transaction(cur_trans); if (trans->type & __TRANS_FREEZABLE) sb_end_intwrite(root->fs_info->sb); diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index 7657d11..5c2af84 100644 --- a/fs/btrfs/transaction.h +++ b/fs/btrfs/transaction.h @@ -92,7 +92,6 @@ struct btrfs_trans_handle { short aborted; short adding_csums; bool allocating_chunk; - bool reloc_reserved; unsigned int type; /* * this root is only needed to validate that the root passed to @@ -167,5 +166,4 @@ int btrfs_wait_marked_extents(struct btrfs_root *root, struct extent_io_tree *dirty_pages, int mark); int btrfs_transaction_blocked(struct btrfs_fs_info *info); int btrfs_transaction_in_commit(struct btrfs_fs_info *info); -void btrfs_put_transaction(struct btrfs_transaction *transaction); #endif diff --git a/fs/btrfs/tree-defrag.c b/fs/btrfs/tree-defrag.c index 76928ca..94e05c1 100644 --- a/fs/btrfs/tree-defrag.c +++ b/fs/btrfs/tree-defrag.c @@ -37,6 +37,7 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, int ret = 0; int wret; int level; + int is_extent = 0; int next_key_ret = 0; u64 last_ret = 0; u64 min_trans = 0; @@ -49,7 +50,7 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, goto out; } - if (root->ref_cows == 0) + if (root->ref_cows == 0 && !is_extent) goto out; if (btrfs_test_opt(root, SSD)) @@ -84,7 +85,7 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, path->keep_locks = 1; - ret = btrfs_search_forward(root, &key, path, min_trans); + ret = btrfs_search_forward(root, &key, NULL, path, min_trans); if (ret < 0) goto out; if (ret > 0) { diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 9f7fc51..79f057c 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -26,6 +26,7 @@ #include "locking.h" #include "print-tree.h" #include "backref.h" +#include "compat.h" #include "tree-log.h" #include "hash.h" @@ -935,7 +936,7 @@ again: parent_objectid, victim_name, victim_name_len)) { - inc_nlink(inode); + btrfs_inc_nlink(inode); btrfs_release_path(path); ret = btrfs_unlink_inode(trans, root, dir, @@ -1005,7 +1006,7 @@ again: victim_parent = read_one_inode(root, parent_objectid); if (victim_parent) { - inc_nlink(inode); + btrfs_inc_nlink(inode); btrfs_release_path(path); ret = btrfs_unlink_inode(trans, root, @@ -1112,11 +1113,11 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, struct extent_buffer *eb, int slot, struct btrfs_key *key) { - struct inode *dir = NULL; - struct inode *inode = NULL; + struct inode *dir; + struct inode *inode; unsigned long ref_ptr; unsigned long ref_end; - char *name = NULL; + char *name; int namelen; int ret; int search_done = 0; @@ -1149,15 +1150,13 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, * care of the rest */ dir = read_one_inode(root, parent_objectid); - if (!dir) { - ret = -ENOENT; - goto out; - } + if (!dir) + return -ENOENT; inode = read_one_inode(root, inode_objectid); if (!inode) { - ret = -EIO; - goto out; + iput(dir); + return -EIO; } while (ref_ptr < ref_end) { @@ -1170,16 +1169,14 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, */ if (!dir) dir = read_one_inode(root, parent_objectid); - if (!dir) { - ret = -ENOENT; - goto out; - } + if (!dir) + return -ENOENT; } else { ret = ref_get_fields(eb, ref_ptr, &namelen, &name, &ref_index); } if (ret) - goto out; + return ret; /* if we already have a perfect match, we're done */ if (!inode_in_dir(root, path, btrfs_ino(dir), btrfs_ino(inode), @@ -1199,11 +1196,12 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, parent_objectid, ref_index, name, namelen, &search_done); - if (ret) { - if (ret == 1) - ret = 0; + if (ret == 1) { + ret = 0; goto out; } + if (ret) + goto out; } /* insert our name */ @@ -1217,7 +1215,6 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, ref_ptr = (unsigned long)(ref_ptr + ref_struct_size) + namelen; kfree(name); - name = NULL; if (log_ref_ver) { iput(dir); dir = NULL; @@ -1228,7 +1225,6 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, ret = overwrite_item(trans, root, path, eb, slot, key); out: btrfs_release_path(path); - kfree(name); iput(dir); iput(inode); return ret; @@ -1311,7 +1307,6 @@ static int count_inode_refs(struct btrfs_root *root, break; path->slots[0]--; } -process_slot: btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); if (key.objectid != ino || @@ -1332,10 +1327,6 @@ process_slot: if (key.offset == 0) break; - if (path->slots[0] > 0) { - path->slots[0]--; - goto process_slot; - } key.offset--; btrfs_release_path(path); } @@ -1489,7 +1480,7 @@ static noinline int link_to_fixup_dir(struct btrfs_trans_handle *trans, if (!inode->i_nlink) set_nlink(inode, 1); else - inc_nlink(inode); + btrfs_inc_nlink(inode); ret = btrfs_update_inode(trans, root, inode); } else if (ret == -EEXIST) { ret = 0; @@ -1832,7 +1823,7 @@ again: dir_key->offset, name, name_len, 0); } - if (!log_di || (IS_ERR(log_di) && PTR_ERR(log_di) == -ENOENT)) { + if (IS_ERR_OR_NULL(log_di)) { btrfs_dir_item_key_to_cpu(eb, di, &location); btrfs_release_path(path); btrfs_release_path(log_path); @@ -1850,7 +1841,7 @@ again: goto out; } - inc_nlink(inode); + btrfs_inc_nlink(inode); ret = btrfs_unlink_inode(trans, root, dir, inode, name, name_len); if (!ret) @@ -1869,9 +1860,6 @@ again: goto again; ret = 0; goto out; - } else if (IS_ERR(log_di)) { - kfree(name); - return PTR_ERR(log_di); } btrfs_release_path(log_path); kfree(name); @@ -2130,7 +2118,8 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans, WARN_ON(*level >= BTRFS_MAX_LEVEL); cur = path->nodes[*level]; - WARN_ON(btrfs_header_level(cur) != *level); + if (btrfs_header_level(cur) != *level) + WARN_ON(1); if (path->slots[*level] >= btrfs_header_nritems(cur)) @@ -2162,13 +2151,11 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans, return ret; } - if (trans) { - btrfs_tree_lock(next); - btrfs_set_lock_blocking(next); - clean_tree_block(trans, root, next); - btrfs_wait_tree_block_writeback(next); - btrfs_tree_unlock(next); - } + btrfs_tree_lock(next); + btrfs_set_lock_blocking(next); + clean_tree_block(trans, root, next); + btrfs_wait_tree_block_writeback(next); + btrfs_tree_unlock(next); WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID); @@ -2240,13 +2227,11 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans, next = path->nodes[*level]; - if (trans) { - btrfs_tree_lock(next); - btrfs_set_lock_blocking(next); - clean_tree_block(trans, root, next); - btrfs_wait_tree_block_writeback(next); - btrfs_tree_unlock(next); - } + btrfs_tree_lock(next); + btrfs_set_lock_blocking(next); + clean_tree_block(trans, root, next); + btrfs_wait_tree_block_writeback(next); + btrfs_tree_unlock(next); WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID); ret = btrfs_free_and_pin_reserved_extent(root, @@ -2316,13 +2301,11 @@ static int walk_log_tree(struct btrfs_trans_handle *trans, next = path->nodes[orig_level]; - if (trans) { - btrfs_tree_lock(next); - btrfs_set_lock_blocking(next); - clean_tree_block(trans, log, next); - btrfs_wait_tree_block_writeback(next); - btrfs_tree_unlock(next); - } + btrfs_tree_lock(next); + btrfs_set_lock_blocking(next); + clean_tree_block(trans, log, next); + btrfs_wait_tree_block_writeback(next); + btrfs_tree_unlock(next); WARN_ON(log->root_key.objectid != BTRFS_TREE_LOG_OBJECTID); @@ -2588,7 +2571,9 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, * the running transaction open, so a full commit can't hop * in and cause problems either. */ + btrfs_scrub_pause_super(root); ret = write_ctree_super(trans, root->fs_info->tree_root, 1); + btrfs_scrub_continue_super(root); if (ret) { btrfs_abort_transaction(trans, root, ret); goto out_wake_log_root; @@ -2623,10 +2608,13 @@ static void free_log_tree(struct btrfs_trans_handle *trans, .process_func = process_one_buffer }; - ret = walk_log_tree(trans, log, &wc); - /* I don't think this can happen but just in case */ - if (ret) - btrfs_abort_transaction(trans, log, ret); + if (trans) { + ret = walk_log_tree(trans, log, &wc); + + /* I don't think this can happen but just in case */ + if (ret) + btrfs_abort_transaction(trans, log, ret); + } while (1) { ret = find_first_extent_bit(&log->dirty_log_pages, @@ -2879,6 +2867,7 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans, u64 min_offset, u64 *last_offset_ret) { struct btrfs_key min_key; + struct btrfs_key max_key; struct btrfs_root *log = root->log_root; struct extent_buffer *src; int err = 0; @@ -2890,6 +2879,9 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans, u64 ino = btrfs_ino(inode); log = root->log_root; + max_key.objectid = ino; + max_key.offset = (u64)-1; + max_key.type = key_type; min_key.objectid = ino; min_key.type = key_type; @@ -2897,7 +2889,8 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans, path->keep_locks = 1; - ret = btrfs_search_forward(root, &min_key, path, trans->transid); + ret = btrfs_search_forward(root, &min_key, &max_key, + path, trans->transid); /* * we didn't find anything from this transaction, see if there @@ -2950,8 +2943,10 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans, /* find the first key from this transaction again */ ret = btrfs_search_slot(NULL, root, &min_key, path, 0, 0); - if (WARN_ON(ret != 0)) + if (ret != 0) { + WARN_ON(1); goto done; + } /* * we have a block from this transaction, log every item in it @@ -3177,10 +3172,11 @@ static int log_inode_item(struct btrfs_trans_handle *trans, struct inode *inode) { struct btrfs_inode_item *inode_item; + struct btrfs_key key; int ret; - ret = btrfs_insert_empty_item(trans, log, path, - &BTRFS_I(inode)->location, + memcpy(&key, &BTRFS_I(inode)->location, sizeof(key)); + ret = btrfs_insert_empty_item(trans, log, path, &key, sizeof(*inode_item)); if (ret && ret != -EEXIST) return ret; @@ -3379,7 +3375,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans, btrfs_set_token_file_extent_type(leaf, fi, BTRFS_FILE_EXTENT_REG, &token); - if (em->block_start == EXTENT_MAP_HOLE) + if (em->block_start == 0) skip_csum = true; } @@ -3421,6 +3417,11 @@ static int log_one_extent(struct btrfs_trans_handle *trans, if (skip_csum) return 0; + if (em->compress_type) { + csum_offset = 0; + csum_len = block_len; + } + /* * First check and see if our csums are on our outstanding ordered * extents. @@ -3504,13 +3505,8 @@ unlocked: if (!mod_len || ret) return ret; - if (em->compress_type) { - csum_offset = 0; - csum_len = block_len; - } else { - csum_offset = mod_start - em->start; - csum_len = mod_len; - } + csum_offset = mod_start - em->start; + csum_len = mod_len; /* block start is already adjusted for the file extent offset. */ ret = btrfs_lookup_csums_range(log->fs_info->csum_root, @@ -3697,8 +3693,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, ret = btrfs_truncate_inode_items(trans, log, inode, 0, 0); } else if (test_and_clear_bit(BTRFS_INODE_COPY_EVERYTHING, - &BTRFS_I(inode)->runtime_flags) || - inode_only == LOG_INODE_EXISTS) { + &BTRFS_I(inode)->runtime_flags)) { if (inode_only == LOG_INODE_ALL) fast_search = true; max_key.type = BTRFS_XATTR_ITEM_KEY; @@ -3724,7 +3719,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans, while (1) { ins_nr = 0; - ret = btrfs_search_forward(root, &min_key, + ret = btrfs_search_forward(root, &min_key, &max_key, path, trans->transid); if (ret != 0) break; @@ -3774,14 +3769,14 @@ next_slot: } btrfs_release_path(path); - if (min_key.offset < (u64)-1) { + if (min_key.offset < (u64)-1) min_key.offset++; - } else if (min_key.type < max_key.type) { + else if (min_key.type < (u8)-1) min_key.type++; - min_key.offset = 0; - } else { + else if (min_key.objectid < (u64)-1) + min_key.objectid++; + else break; - } } if (ins_nr) { ret = copy_items(trans, inode, dst_path, src, ins_start_slot, @@ -3802,7 +3797,7 @@ log_extents: err = ret; goto out_unlock; } - } else if (inode_only == LOG_INODE_ALL) { + } else { struct extent_map_tree *tree = &BTRFS_I(inode)->extent_tree; struct extent_map *em, *n; diff --git a/fs/btrfs/uuid-tree.c b/fs/btrfs/uuid-tree.c index fbda900..dd0dea3 100644 --- a/fs/btrfs/uuid-tree.c +++ b/fs/btrfs/uuid-tree.c @@ -260,6 +260,7 @@ int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info, { struct btrfs_root *root = fs_info->uuid_root; struct btrfs_key key; + struct btrfs_key max_key; struct btrfs_path *path; int ret = 0; struct extent_buffer *leaf; @@ -276,10 +277,13 @@ int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info, key.objectid = 0; key.type = 0; key.offset = 0; + max_key.objectid = (u64)-1; + max_key.type = (u8)-1; + max_key.offset = (u64)-1; again_search_slot: path->keep_locks = 1; - ret = btrfs_search_forward(root, &key, path, 0); + ret = btrfs_search_forward(root, &key, &max_key, path, 0); if (ret) { if (ret > 0) ret = 0; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 92303f4..043b215 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -28,6 +28,7 @@ #include <linux/raid/pq.h> #include <linux/semaphore.h> #include <asm/div64.h> +#include "compat.h" #include "ctree.h" #include "extent_map.h" #include "disk-io.h" @@ -665,8 +666,7 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) if (device->bdev) fs_devices->open_devices--; - if (device->writeable && - device->devid != BTRFS_DEV_REPLACE_DEVID) { + if (device->writeable && !device->is_tgtdev_for_dev_replace) { list_del_init(&device->dev_alloc_list); fs_devices->rw_devices--; } @@ -2041,7 +2041,6 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) device->in_fs_metadata = 1; device->is_tgtdev_for_dev_replace = 0; device->mode = FMODE_EXCL; - device->dev_stats_valid = 1; set_blocksize(device->bdev, 4096); if (seeding_dev) { @@ -2209,7 +2208,6 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path, device->in_fs_metadata = 1; device->is_tgtdev_for_dev_replace = 1; device->mode = FMODE_EXCL; - device->dev_stats_valid = 1; set_blocksize(device->bdev, 4096); device->fs_devices = fs_info->fs_devices; list_add(&device->dev_list, &fs_info->fs_devices->devices); @@ -2552,7 +2550,8 @@ again: failed = 0; retried = true; goto again; - } else if (WARN_ON(failed && retried)) { + } else if (failed && retried) { + WARN_ON(1); ret = -ENOSPC; } error: @@ -3424,9 +3423,6 @@ int btrfs_pause_balance(struct btrfs_fs_info *fs_info) int btrfs_cancel_balance(struct btrfs_fs_info *fs_info) { - if (fs_info->sb->s_flags & MS_RDONLY) - return -EROFS; - mutex_lock(&fs_info->balance_mutex); if (!fs_info->balance_ctl) { mutex_unlock(&fs_info->balance_mutex); @@ -3492,7 +3488,7 @@ static int btrfs_uuid_scan_kthread(void *data) path->keep_locks = 1; while (1) { - ret = btrfs_search_forward(root, &key, path, 0); + ret = btrfs_search_forward(root, &key, &max_key, path, 0); if (ret) { if (ret > 0) ret = 0; @@ -4492,7 +4488,6 @@ int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len) btrfs_crit(fs_info, "Invalid mapping for %Lu-%Lu, got " "%Lu-%Lu\n", logical, logical+len, em->start, em->start + em->len); - free_extent_map(em); return 1; } @@ -4673,7 +4668,6 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw, btrfs_crit(fs_info, "found a bad mapping, wanted %Lu, " "found %Lu-%Lu\n", logical, em->start, em->start + em->len); - free_extent_map(em); return -EINVAL; } @@ -4901,7 +4895,7 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw, num_stripes = map->num_stripes; max_errors = nr_parity_stripes(map); - raid_map = kmalloc_array(num_stripes, sizeof(u64), + raid_map = kmalloc(sizeof(u64) * num_stripes, GFP_NOFS); if (!raid_map) { ret = -ENOMEM; @@ -5394,15 +5388,17 @@ static int bio_size_ok(struct block_device *bdev, struct bio *bio, { struct bio_vec *prev; struct request_queue *q = bdev_get_queue(bdev); - unsigned int max_sectors = queue_max_sectors(q); + unsigned short max_sectors = queue_max_sectors(q); struct bvec_merge_data bvm = { .bi_bdev = bdev, .bi_sector = sector, .bi_rw = bio->bi_rw, }; - if (WARN_ON(bio->bi_vcnt == 0)) + if (bio->bi_vcnt == 0) { + WARN_ON(1); return 1; + } prev = &bio->bi_io_vec[bio->bi_vcnt - 1]; if (bio_sectors(bio) > max_sectors) @@ -5635,8 +5631,10 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info, struct btrfs_device *dev; u64 tmp; - if (WARN_ON(!devid && !fs_info)) + if (!devid && !fs_info) { + WARN_ON(1); return ERR_PTR(-EINVAL); + } dev = __alloc_device(); if (IS_ERR(dev)) diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 8b3cd14..b72f540 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -43,8 +43,9 @@ struct btrfs_device { /* WRITE_SYNC bios */ struct btrfs_pending_bios pending_sync_bios; - u64 generation; int running_pending; + u64 generation; + int writeable; int in_fs_metadata; int missing; @@ -52,11 +53,11 @@ struct btrfs_device { int is_tgtdev_for_dev_replace; spinlock_t io_lock; - /* the mode sent to blkdev_get */ - fmode_t mode; struct block_device *bdev; + /* the mode sent to blkdev_get */ + fmode_t mode; struct rcu_string *name; @@ -77,21 +78,16 @@ struct btrfs_device { /* optimal io width for this device */ u32 io_width; - /* type and info about this device */ - u64 type; /* minimal io size for this device */ u32 sector_size; + /* type and info about this device */ + u64 type; /* physical drive uuid (or lvm uuid) */ u8 uuid[BTRFS_UUID_SIZE]; - /* for sending down flush barriers */ - int nobarriers; - struct bio *flush_bio; - struct completion flush_wait; - /* per-device scrub information */ struct scrub_ctx *scrub_device; @@ -107,6 +103,10 @@ struct btrfs_device { struct radix_tree_root reada_zones; struct radix_tree_root reada_extents; + /* for sending down flush barriers */ + struct bio *flush_bio; + struct completion flush_wait; + int nobarriers; /* disk I/O failure stats. For detailed description refer to * enum btrfs_dev_stat_values in ioctl.h */ @@ -132,9 +132,7 @@ struct btrfs_fs_devices { /* all of the devices in the FS, protected by a mutex * so we can safely walk it to write out the supers without - * worrying about add/remove by the multi-device code. - * Scrubbing super can kick off supers writing by holding - * this mutex lock. + * worrying about add/remove by the multi-device code */ struct mutex device_list_mutex; struct list_head devices; |