diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 234bae55b85d2b5c4421c115e2a87bbfe969ed1b..7e075343daa508b7e7c0c6f8746ff2d2ff4a5653 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -19,17 +19,17 @@ * ordered operations list so that we make sure to flush out any * new data the application may have written before commit. */ -#define BTRFS_INODE_ORDERED_DATA_CLOSE 0 -#define BTRFS_INODE_ORPHAN_META_RESERVED 1 -#define BTRFS_INODE_DUMMY 2 -#define BTRFS_INODE_IN_DEFRAG 3 -#define BTRFS_INODE_HAS_ORPHAN_ITEM 4 -#define BTRFS_INODE_HAS_ASYNC_EXTENT 5 -#define BTRFS_INODE_NEEDS_FULL_SYNC 6 -#define BTRFS_INODE_COPY_EVERYTHING 7 -#define BTRFS_INODE_IN_DELALLOC_LIST 8 -#define BTRFS_INODE_READDIO_NEED_LOCK 9 -#define BTRFS_INODE_HAS_PROPS 10 +enum { + BTRFS_INODE_ORDERED_DATA_CLOSE = 0, + BTRFS_INODE_DUMMY, + BTRFS_INODE_IN_DEFRAG, + BTRFS_INODE_HAS_ASYNC_EXTENT, + BTRFS_INODE_NEEDS_FULL_SYNC, + BTRFS_INODE_COPY_EVERYTHING, + BTRFS_INODE_IN_DELALLOC_LIST, + BTRFS_INODE_READDIO_NEED_LOCK, + BTRFS_INODE_HAS_PROPS, +}; /* in memory btrfs inode */ struct btrfs_inode { diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 1061575a7d2520ba15b250e424ad39bea9da15f6..d3e447b45bf793abd7ca555af7e9e6ad40b2da2d 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -990,12 +990,7 @@ static void __free_workspace(int type, struct list_head *workspace, btrfs_compress_op[idx]->free_workspace(workspace); atomic_dec(total_ws); wake: - /* - * Make sure counter is updated before we wake up waiters. - */ - smp_mb(); - if (waitqueue_active(ws_wait)) - wake_up(ws_wait); + cond_wake_up(ws_wait); } static void free_workspace(int type, struct list_head *ws) diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h index cc605f7b23fbf3bf0f0ed56ac625daa9982b149a..ddda9b80bf2044edc3ae210bb401ce8e728fdbe2 100644 --- a/fs/btrfs/compression.h +++ b/fs/btrfs/compression.h @@ -6,6 +6,8 @@ #ifndef BTRFS_COMPRESSION_H #define BTRFS_COMPRESSION_H +#include + /* * We want to make sure that amount of RAM required to uncompress an extent is * reasonable, so we limit the total size in ram of a compressed extent to diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 8c68961925b1482517bfe2c96635f5ee1cc79694..4bc326df472ef4c81af658cb08ea77bae20c2916 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -2330,7 +2330,7 @@ static noinline void unlock_up(struct btrfs_path *path, int level, no_skips = 1; t = path->nodes[i]; - if (i >= lowest_unlock && i > skip_level && path->locks[i]) { + if (i >= lowest_unlock && i > skip_level) { btrfs_tree_unlock_rw(t, path->locks[i]); path->locks[i] = 0; if (write_lock_level && @@ -2432,7 +2432,6 @@ read_block_for_search(struct btrfs_root *root, struct btrfs_path *p, btrfs_unlock_up_safe(p, level + 1); btrfs_set_path_blocking(p); - free_extent_buffer(tmp); if (p->reada != READA_NONE) reada_for_search(fs_info, p, level, slot, key->objectid); @@ -2446,7 +2445,7 @@ read_block_for_search(struct btrfs_root *root, struct btrfs_path *p, * and give up so that our caller doesn't loop forever * on our EAGAINs. */ - if (!btrfs_buffer_uptodate(tmp, 0, 0)) + if (!extent_buffer_uptodate(tmp)) ret = -EIO; free_extent_buffer(tmp); } else { @@ -2599,6 +2598,78 @@ int btrfs_find_item(struct btrfs_root *fs_root, struct btrfs_path *path, return 0; } +static struct extent_buffer *btrfs_search_slot_get_root(struct btrfs_root *root, + struct btrfs_path *p, + int write_lock_level) +{ + struct btrfs_fs_info *fs_info = root->fs_info; + struct extent_buffer *b; + int root_lock; + int level = 0; + + /* We try very hard to do read locks on the root */ + root_lock = BTRFS_READ_LOCK; + + if (p->search_commit_root) { + /* The commit roots are read only so we always do read locks */ + if (p->need_commit_sem) + down_read(&fs_info->commit_root_sem); + b = root->commit_root; + extent_buffer_get(b); + level = btrfs_header_level(b); + if (p->need_commit_sem) + up_read(&fs_info->commit_root_sem); + /* + * Ensure that all callers have set skip_locking when + * p->search_commit_root = 1. + */ + ASSERT(p->skip_locking == 1); + + goto out; + } + + if (p->skip_locking) { + b = btrfs_root_node(root); + level = btrfs_header_level(b); + goto out; + } + + /* + * If the level is set to maximum, we can skip trying to get the read + * lock. + */ + if (write_lock_level < BTRFS_MAX_LEVEL) { + /* + * We don't know the level of the root node until we actually + * have it read locked + */ + b = btrfs_read_lock_root_node(root); + level = btrfs_header_level(b); + if (level > write_lock_level) + goto out; + + /* Whoops, must trade for write lock */ + btrfs_tree_read_unlock(b); + free_extent_buffer(b); + } + + b = btrfs_lock_root_node(root); + root_lock = BTRFS_WRITE_LOCK; + + /* The level might have changed, check again */ + level = btrfs_header_level(b); + +out: + p->nodes[level] = b; + if (!p->skip_locking) + p->locks[level] = root_lock; + /* + * Callers are responsible for dropping b's references. + */ + return b; +} + + /* * btrfs_search_slot - look for a key in a tree and perform necessary * modifications to preserve tree invariants. @@ -2635,7 +2706,6 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root *root, int err; int level; int lowest_unlock = 1; - int root_lock; /* everything at write_lock_level or lower must be write locked */ int write_lock_level = 0; u8 lowest_level = 0; @@ -2673,50 +2743,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root *root, again: prev_cmp = -1; - /* - * we try very hard to do read locks on the root - */ - root_lock = BTRFS_READ_LOCK; - level = 0; - if (p->search_commit_root) { - /* - * the commit roots are read only - * so we always do read locks - */ - if (p->need_commit_sem) - down_read(&fs_info->commit_root_sem); - b = root->commit_root; - extent_buffer_get(b); - level = btrfs_header_level(b); - if (p->need_commit_sem) - up_read(&fs_info->commit_root_sem); - if (!p->skip_locking) - btrfs_tree_read_lock(b); - } else { - if (p->skip_locking) { - b = btrfs_root_node(root); - level = btrfs_header_level(b); - } else { - /* we don't know the level of the root node - * until we actually have it read locked - */ - b = btrfs_read_lock_root_node(root); - level = btrfs_header_level(b); - if (level <= write_lock_level) { - /* whoops, must trade for write lock */ - btrfs_tree_read_unlock(b); - free_extent_buffer(b); - b = btrfs_lock_root_node(root); - root_lock = BTRFS_WRITE_LOCK; - - /* the level might have changed, check again */ - level = btrfs_header_level(b); - } - } - } - p->nodes[level] = b; - if (!p->skip_locking) - p->locks[level] = root_lock; + b = btrfs_search_slot_get_root(root, p, write_lock_level); while (b) { level = btrfs_header_level(b); diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 0d422c9908b8085f531a752a4bd6d5bf1b430e02..f4bf7874c24a4d63609a30a966d14f9db0a323e0 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -739,6 +739,12 @@ struct btrfs_delayed_root; */ #define BTRFS_FS_NEED_ASYNC_COMMIT 17 +/* + * Indicate that balance has been set up from the ioctl and is in the main + * phase. The fs_info::balance_ctl is initialized. + */ +#define BTRFS_FS_BALANCE_RUNNING 18 + struct btrfs_fs_info { u8 fsid[BTRFS_FSID_SIZE]; u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; @@ -838,7 +844,6 @@ struct btrfs_fs_info { struct mutex transaction_kthread_mutex; struct mutex cleaner_mutex; struct mutex chunk_mutex; - struct mutex volume_mutex; /* * this is taken to make sure we don't set block groups ro after @@ -1004,7 +1009,6 @@ struct btrfs_fs_info { /* restriper state */ spinlock_t balance_lock; struct mutex balance_mutex; - atomic_t balance_running; atomic_t balance_pause_req; atomic_t balance_cancel_req; struct btrfs_balance_control *balance_ctl; @@ -1219,9 +1223,6 @@ struct btrfs_root { spinlock_t log_extents_lock[2]; struct list_head logged_list[2]; - spinlock_t orphan_lock; - atomic_t orphan_inodes; - struct btrfs_block_rsv *orphan_block_rsv; int orphan_cleanup_state; spinlock_t inode_lock; @@ -2764,13 +2765,9 @@ void btrfs_delalloc_release_space(struct inode *inode, void btrfs_free_reserved_data_space_noquota(struct inode *inode, u64 start, u64 len); void btrfs_trans_release_chunk_metadata(struct btrfs_trans_handle *trans); -int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans, - struct btrfs_inode *inode); -void btrfs_orphan_release_metadata(struct btrfs_inode *inode); int btrfs_subvolume_reserve_metadata(struct btrfs_root *root, struct btrfs_block_rsv *rsv, - int nitems, - u64 *qgroup_reserved, bool use_global_rsv); + int nitems, bool use_global_rsv); void btrfs_subvolume_release_metadata(struct btrfs_fs_info *fs_info, struct btrfs_block_rsv *rsv); void btrfs_delalloc_release_extents(struct btrfs_inode *inode, u64 num_bytes, @@ -2828,7 +2825,7 @@ void btrfs_wait_for_snapshot_creation(struct btrfs_root *root); void check_system_chunk(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, const u64 type); u64 add_new_free_space(struct btrfs_block_group_cache *block_group, - struct btrfs_fs_info *info, u64 start, u64 end); + u64 start, u64 end); /* ctree.c */ int btrfs_bin_search(struct extent_buffer *eb, const struct btrfs_key *key, @@ -3042,11 +3039,9 @@ void btrfs_update_root_times(struct btrfs_trans_handle *trans, struct btrfs_root *root); /* uuid-tree.c */ -int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, u8 *uuid, u8 type, +int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, u8 *uuid, u8 type, u64 subid); -int btrfs_uuid_tree_rem(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, u8 *uuid, u8 type, +int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, u8 *uuid, u8 type, u64 subid); int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info, int (*check_func)(struct btrfs_fs_info *, u8 *, u8, @@ -3163,18 +3158,6 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode, struct extent_map *em); /* inode.c */ -struct btrfs_delalloc_work { - struct inode *inode; - int delay_iput; - struct completion completion; - struct list_head list; - struct btrfs_work work; -}; - -struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode, - int delay_iput); -void btrfs_wait_and_free_delalloc_work(struct btrfs_delalloc_work *work); - struct extent_map *btrfs_get_extent_fiemap(struct btrfs_inode *inode, struct page *page, size_t pg_offset, u64 start, u64 len, int create); @@ -3193,10 +3176,7 @@ int btrfs_unlink_inode(struct btrfs_trans_handle *trans, int btrfs_add_link(struct btrfs_trans_handle *trans, struct btrfs_inode *parent_inode, struct btrfs_inode *inode, const char *name, int name_len, int add_backref, u64 index); -int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - struct inode *dir, u64 objectid, - const char *name, int name_len); +int btrfs_delete_subvolume(struct inode *dir, struct dentry *dentry); int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len, int front); int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, @@ -3204,9 +3184,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, struct inode *inode, u64 new_size, 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 nr); +int btrfs_start_delalloc_inodes(struct btrfs_root *root); +int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int nr); int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end, unsigned int extra_bits, struct extent_state **cached_state, int dedupe); @@ -3240,10 +3219,7 @@ int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans, int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct btrfs_inode *inode); int btrfs_orphan_cleanup(struct btrfs_root *root); -void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans, - struct btrfs_root *root); int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size); -void btrfs_invalidate_inodes(struct btrfs_root *root); void btrfs_add_delayed_iput(struct inode *inode); void btrfs_run_delayed_iputs(struct btrfs_fs_info *fs_info); int btrfs_prealloc_file_range(struct inode *inode, int mode, @@ -3262,14 +3238,14 @@ void btrfs_test_inode_set_ops(struct inode *inode); long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); long btrfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); int btrfs_ioctl_get_supported_features(void __user *arg); -void btrfs_update_iflags(struct inode *inode); +void btrfs_sync_inode_flags_to_i_flags(struct inode *inode); int btrfs_is_empty_uuid(u8 *uuid); int btrfs_defrag_file(struct inode *inode, struct file *file, struct btrfs_ioctl_defrag_range_args *range, u64 newer_than, unsigned long max_pages); void btrfs_get_block_group_info(struct list_head *groups_list, struct btrfs_ioctl_space_info *space); -void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock, +void btrfs_update_ioctl_balance_args(struct btrfs_fs_info *fs_info, struct btrfs_ioctl_balance_args *bargs); ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen, struct file *dst_file, u64 dst_loff); @@ -3767,4 +3743,26 @@ static inline int btrfs_is_testing(struct btrfs_fs_info *fs_info) return 0; } +static inline void cond_wake_up(struct wait_queue_head *wq) +{ + /* + * This implies a full smp_mb barrier, see comments for + * waitqueue_active why. + */ + if (wq_has_sleeper(wq)) + wake_up(wq); +} + +static inline void cond_wake_up_nomb(struct wait_queue_head *wq) +{ + /* + * Special case for conditional wakeup where the barrier required for + * waitqueue_active is implied by some of the preceding code. Eg. one + * of such atomic operations (atomic_dec_and_return, ...), or a + * unlock/lock sequence, etc. + */ + if (waitqueue_active(wq)) + wake_up(wq); +} + #endif diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index a8d492dbd3e7c100715011a9ddad8a22d82932dd..fe6caa7e698bedf4d86c4fe427a3a49a227dcbc6 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -460,13 +460,10 @@ static void finish_one_item(struct btrfs_delayed_root *delayed_root) { int seq = atomic_inc_return(&delayed_root->items_seq); - /* - * atomic_dec_return implies a barrier for waitqueue_active - */ + /* atomic_dec_return implies a barrier */ if ((atomic_dec_return(&delayed_root->items) < - BTRFS_DELAYED_BACKGROUND || seq % BTRFS_DELAYED_BATCH == 0) && - waitqueue_active(&delayed_root->wait)) - wake_up(&delayed_root->wait); + BTRFS_DELAYED_BACKGROUND || seq % BTRFS_DELAYED_BATCH == 0)) + cond_wake_up_nomb(&delayed_root->wait); } static void __btrfs_remove_delayed_item(struct btrfs_delayed_item *delayed_item) diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index e1b0651686f7c4e988766d927c669306560c468d..03dec673d12abc4b5d753f1cc8ae585f4e6978f6 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -286,10 +286,10 @@ static bool merge_ref(struct btrfs_trans_handle *trans, } void btrfs_merge_delayed_refs(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_delayed_ref_root *delayed_refs, struct btrfs_delayed_ref_head *head) { + struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_delayed_ref_node *ref; struct rb_node *node; u64 seq = 0; @@ -323,9 +323,7 @@ void btrfs_merge_delayed_refs(struct btrfs_trans_handle *trans, } } -int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info, - struct btrfs_delayed_ref_root *delayed_refs, - u64 seq) +int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info, u64 seq) { struct seq_list *elem; int ret = 0; @@ -336,10 +334,9 @@ int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info, struct seq_list, list); if (seq >= elem->seq) { btrfs_debug(fs_info, - "holding back delayed_ref %#x.%x, lowest is %#x.%x (%p)", + "holding back delayed_ref %#x.%x, lowest is %#x.%x", (u32)(seq >> 32), (u32)seq, - (u32)(elem->seq >> 32), (u32)elem->seq, - delayed_refs); + (u32)(elem->seq >> 32), (u32)elem->seq); ret = 1; } } @@ -529,33 +526,20 @@ update_existing_head_ref(struct btrfs_delayed_ref_root *delayed_refs, spin_unlock(&existing->lock); } -/* - * helper function to actually insert a head node into the rbtree. - * this does all the dirty work in terms of maintaining the correct - * overall modification count. - */ -static noinline struct btrfs_delayed_ref_head * -add_delayed_ref_head(struct btrfs_fs_info *fs_info, - struct btrfs_trans_handle *trans, - struct btrfs_delayed_ref_head *head_ref, - struct btrfs_qgroup_extent_record *qrecord, - u64 bytenr, u64 num_bytes, u64 ref_root, u64 reserved, - int action, int is_data, int is_system, - int *qrecord_inserted_ret, - int *old_ref_mod, int *new_ref_mod) - +static void init_delayed_ref_head(struct btrfs_delayed_ref_head *head_ref, + struct btrfs_qgroup_extent_record *qrecord, + u64 bytenr, u64 num_bytes, u64 ref_root, + u64 reserved, int action, bool is_data, + bool is_system) { - struct btrfs_delayed_ref_head *existing; - struct btrfs_delayed_ref_root *delayed_refs; int count_mod = 1; int must_insert_reserved = 0; - int qrecord_inserted = 0; /* If reserved is provided, it must be a data extent. */ BUG_ON(!is_data && reserved); /* - * the head node stores the sum of all the mods, so dropping a ref + * The head node stores the sum of all the mods, so dropping a ref * should drop the sum in the head node by one. */ if (action == BTRFS_UPDATE_DELAYED_HEAD) @@ -564,12 +548,11 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, count_mod = -1; /* - * BTRFS_ADD_DELAYED_EXTENT means that we need to update - * the reserved accounting when the extent is finally added, or - * if a later modification deletes the delayed ref without ever - * inserting the extent into the extent allocation tree. - * ref->must_insert_reserved is the flag used to record - * that accounting mods are required. + * BTRFS_ADD_DELAYED_EXTENT means that we need to update the reserved + * accounting when the extent is finally added, or if a later + * modification deletes the delayed ref without ever inserting the + * extent into the extent allocation tree. ref->must_insert_reserved + * is the flag used to record that accounting mods are required. * * Once we record must_insert_reserved, switch the action to * BTRFS_ADD_DELAYED_REF because other special casing is not required. @@ -579,8 +562,6 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, else must_insert_reserved = 0; - delayed_refs = &trans->transaction->delayed_refs; - refcount_set(&head_ref->refs, 1); head_ref->bytenr = bytenr; head_ref->num_bytes = num_bytes; @@ -598,7 +579,6 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, spin_lock_init(&head_ref->lock); mutex_init(&head_ref->mutex); - /* Record qgroup extent info if provided */ if (qrecord) { if (ref_root && reserved) { head_ref->qgroup_ref_root = ref_root; @@ -608,20 +588,44 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, qrecord->bytenr = bytenr; qrecord->num_bytes = num_bytes; qrecord->old_roots = NULL; + } +} + +/* + * helper function to actually insert a head node into the rbtree. + * this does all the dirty work in terms of maintaining the correct + * overall modification count. + */ +static noinline struct btrfs_delayed_ref_head * +add_delayed_ref_head(struct btrfs_trans_handle *trans, + struct btrfs_delayed_ref_head *head_ref, + struct btrfs_qgroup_extent_record *qrecord, + int action, int *qrecord_inserted_ret, + int *old_ref_mod, int *new_ref_mod) +{ + struct btrfs_delayed_ref_head *existing; + struct btrfs_delayed_ref_root *delayed_refs; + int qrecord_inserted = 0; - if(btrfs_qgroup_trace_extent_nolock(fs_info, + delayed_refs = &trans->transaction->delayed_refs; + + /* Record qgroup extent info if provided */ + if (qrecord) { + if (btrfs_qgroup_trace_extent_nolock(trans->fs_info, delayed_refs, qrecord)) kfree(qrecord); else qrecord_inserted = 1; } - trace_add_delayed_ref_head(fs_info, head_ref, action); + trace_add_delayed_ref_head(trans->fs_info, head_ref, action); existing = htree_insert(&delayed_refs->href_root, &head_ref->href_node); if (existing) { - WARN_ON(ref_root && reserved && existing->qgroup_ref_root + WARN_ON(qrecord && head_ref->qgroup_ref_root + && head_ref->qgroup_reserved + && existing->qgroup_ref_root && existing->qgroup_reserved); update_existing_head_ref(delayed_refs, existing, head_ref, old_ref_mod); @@ -634,8 +638,8 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, } else { if (old_ref_mod) *old_ref_mod = 0; - if (is_data && count_mod < 0) - delayed_refs->pending_csums += num_bytes; + if (head_ref->is_data && head_ref->ref_mod < 0) + delayed_refs->pending_csums += head_ref->num_bytes; delayed_refs->num_heads++; delayed_refs->num_heads_ready++; atomic_inc(&delayed_refs->num_entries); @@ -645,90 +649,48 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, *qrecord_inserted_ret = qrecord_inserted; if (new_ref_mod) *new_ref_mod = head_ref->total_ref_mod; - return head_ref; -} - -/* - * helper to insert a delayed tree ref into the rbtree. - */ -static noinline void -add_delayed_tree_ref(struct btrfs_fs_info *fs_info, - struct btrfs_trans_handle *trans, - struct btrfs_delayed_ref_head *head_ref, - struct btrfs_delayed_ref_node *ref, u64 bytenr, - u64 num_bytes, u64 parent, u64 ref_root, int level, - int action) -{ - struct btrfs_delayed_tree_ref *full_ref; - struct btrfs_delayed_ref_root *delayed_refs; - u64 seq = 0; - int ret; - - if (action == BTRFS_ADD_DELAYED_EXTENT) - action = BTRFS_ADD_DELAYED_REF; - if (is_fstree(ref_root)) - seq = atomic64_read(&fs_info->tree_mod_seq); - delayed_refs = &trans->transaction->delayed_refs; - - /* first set the basic ref node struct up */ - refcount_set(&ref->refs, 1); - ref->bytenr = bytenr; - ref->num_bytes = num_bytes; - ref->ref_mod = 1; - ref->action = action; - ref->is_head = 0; - ref->in_tree = 1; - ref->seq = seq; - RB_CLEAR_NODE(&ref->ref_node); - INIT_LIST_HEAD(&ref->add_list); - - full_ref = btrfs_delayed_node_to_tree_ref(ref); - full_ref->parent = parent; - full_ref->root = ref_root; - if (parent) - ref->type = BTRFS_SHARED_BLOCK_REF_KEY; - else - ref->type = BTRFS_TREE_BLOCK_REF_KEY; - full_ref->level = level; - - trace_add_delayed_tree_ref(fs_info, ref, full_ref, action); - - ret = insert_delayed_ref(trans, delayed_refs, head_ref, ref); - - /* - * XXX: memory should be freed at the same level allocated. - * But bad practice is anywhere... Follow it now. Need cleanup. - */ - if (ret > 0) - kmem_cache_free(btrfs_delayed_tree_ref_cachep, full_ref); + return head_ref; } /* - * helper to insert a delayed data ref into the rbtree. + * init_delayed_ref_common - Initialize the structure which represents a + * modification to a an extent. + * + * @fs_info: Internal to the mounted filesystem mount structure. + * + * @ref: The structure which is going to be initialized. + * + * @bytenr: The logical address of the extent for which a modification is + * going to be recorded. + * + * @num_bytes: Size of the extent whose modification is being recorded. + * + * @ref_root: The id of the root where this modification has originated, this + * can be either one of the well-known metadata trees or the + * subvolume id which references this extent. + * + * @action: Can be one of BTRFS_ADD_DELAYED_REF/BTRFS_DROP_DELAYED_REF or + * BTRFS_ADD_DELAYED_EXTENT + * + * @ref_type: Holds the type of the extent which is being recorded, can be + * one of BTRFS_SHARED_BLOCK_REF_KEY/BTRFS_TREE_BLOCK_REF_KEY + * when recording a metadata extent or BTRFS_SHARED_DATA_REF_KEY/ + * BTRFS_EXTENT_DATA_REF_KEY when recording data extent */ -static noinline void -add_delayed_data_ref(struct btrfs_fs_info *fs_info, - struct btrfs_trans_handle *trans, - struct btrfs_delayed_ref_head *head_ref, - struct btrfs_delayed_ref_node *ref, u64 bytenr, - u64 num_bytes, u64 parent, u64 ref_root, u64 owner, - u64 offset, int action) +static void init_delayed_ref_common(struct btrfs_fs_info *fs_info, + struct btrfs_delayed_ref_node *ref, + u64 bytenr, u64 num_bytes, u64 ref_root, + int action, u8 ref_type) { - struct btrfs_delayed_data_ref *full_ref; - struct btrfs_delayed_ref_root *delayed_refs; u64 seq = 0; - int ret; if (action == BTRFS_ADD_DELAYED_EXTENT) action = BTRFS_ADD_DELAYED_REF; - delayed_refs = &trans->transaction->delayed_refs; - if (is_fstree(ref_root)) seq = atomic64_read(&fs_info->tree_mod_seq); - /* first set the basic ref node struct up */ refcount_set(&ref->refs, 1); ref->bytenr = bytenr; ref->num_bytes = num_bytes; @@ -737,25 +699,9 @@ add_delayed_data_ref(struct btrfs_fs_info *fs_info, ref->is_head = 0; ref->in_tree = 1; ref->seq = seq; + ref->type = ref_type; RB_CLEAR_NODE(&ref->ref_node); INIT_LIST_HEAD(&ref->add_list); - - full_ref = btrfs_delayed_node_to_data_ref(ref); - full_ref->parent = parent; - full_ref->root = ref_root; - if (parent) - ref->type = BTRFS_SHARED_DATA_REF_KEY; - else - ref->type = BTRFS_EXTENT_DATA_REF_KEY; - - full_ref->objectid = owner; - full_ref->offset = offset; - - trace_add_delayed_data_ref(fs_info, ref, full_ref, action); - - ret = insert_delayed_ref(trans, delayed_refs, head_ref, ref); - if (ret > 0) - kmem_cache_free(btrfs_delayed_data_ref_cachep, full_ref); } /* @@ -775,13 +721,25 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, struct btrfs_delayed_ref_root *delayed_refs; struct btrfs_qgroup_extent_record *record = NULL; int qrecord_inserted; - int is_system = (ref_root == BTRFS_CHUNK_TREE_OBJECTID); + bool is_system = (ref_root == BTRFS_CHUNK_TREE_OBJECTID); + int ret; + u8 ref_type; BUG_ON(extent_op && extent_op->is_data); ref = kmem_cache_alloc(btrfs_delayed_tree_ref_cachep, GFP_NOFS); if (!ref) return -ENOMEM; + if (parent) + ref_type = BTRFS_SHARED_BLOCK_REF_KEY; + else + ref_type = BTRFS_TREE_BLOCK_REF_KEY; + init_delayed_ref_common(fs_info, &ref->node, bytenr, num_bytes, + ref_root, action, ref_type); + ref->root = ref_root; + ref->parent = parent; + ref->level = level; + head_ref = kmem_cache_alloc(btrfs_delayed_ref_head_cachep, GFP_NOFS); if (!head_ref) goto free_ref; @@ -793,6 +751,8 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, goto free_head_ref; } + init_delayed_ref_head(head_ref, record, bytenr, num_bytes, + ref_root, 0, action, false, is_system); head_ref->extent_op = extent_op; delayed_refs = &trans->transaction->delayed_refs; @@ -802,15 +762,19 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, * insert both the head node and the new ref without dropping * the spin lock */ - head_ref = add_delayed_ref_head(fs_info, trans, head_ref, record, - bytenr, num_bytes, 0, 0, action, 0, - is_system, &qrecord_inserted, + head_ref = add_delayed_ref_head(trans, head_ref, record, + action, &qrecord_inserted, old_ref_mod, new_ref_mod); - add_delayed_tree_ref(fs_info, trans, head_ref, &ref->node, bytenr, - num_bytes, parent, ref_root, level, action); + ret = insert_delayed_ref(trans, delayed_refs, head_ref, &ref->node); spin_unlock(&delayed_refs->lock); + trace_add_delayed_tree_ref(fs_info, &ref->node, ref, + action == BTRFS_ADD_DELAYED_EXTENT ? + BTRFS_ADD_DELAYED_REF : action); + if (ret > 0) + kmem_cache_free(btrfs_delayed_tree_ref_cachep, ref); + if (qrecord_inserted) btrfs_qgroup_trace_extent_post(fs_info, record); @@ -839,11 +803,25 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, struct btrfs_delayed_ref_root *delayed_refs; struct btrfs_qgroup_extent_record *record = NULL; int qrecord_inserted; + int ret; + u8 ref_type; ref = kmem_cache_alloc(btrfs_delayed_data_ref_cachep, GFP_NOFS); if (!ref) return -ENOMEM; + if (parent) + ref_type = BTRFS_SHARED_DATA_REF_KEY; + else + ref_type = BTRFS_EXTENT_DATA_REF_KEY; + init_delayed_ref_common(fs_info, &ref->node, bytenr, num_bytes, + ref_root, action, ref_type); + ref->root = ref_root; + ref->parent = parent; + ref->objectid = owner; + ref->offset = offset; + + head_ref = kmem_cache_alloc(btrfs_delayed_ref_head_cachep, GFP_NOFS); if (!head_ref) { kmem_cache_free(btrfs_delayed_data_ref_cachep, ref); @@ -861,6 +839,8 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, } } + init_delayed_ref_head(head_ref, record, bytenr, num_bytes, ref_root, + reserved, action, true, false); head_ref->extent_op = NULL; delayed_refs = &trans->transaction->delayed_refs; @@ -870,16 +850,20 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, * insert both the head node and the new ref without dropping * the spin lock */ - head_ref = add_delayed_ref_head(fs_info, trans, head_ref, record, - bytenr, num_bytes, ref_root, reserved, - action, 1, 0, &qrecord_inserted, + head_ref = add_delayed_ref_head(trans, head_ref, record, + action, &qrecord_inserted, old_ref_mod, new_ref_mod); - add_delayed_data_ref(fs_info, trans, head_ref, &ref->node, bytenr, - num_bytes, parent, ref_root, owner, offset, - action); + ret = insert_delayed_ref(trans, delayed_refs, head_ref, &ref->node); spin_unlock(&delayed_refs->lock); + trace_add_delayed_data_ref(trans->fs_info, &ref->node, ref, + action == BTRFS_ADD_DELAYED_EXTENT ? + BTRFS_ADD_DELAYED_REF : action); + if (ret > 0) + kmem_cache_free(btrfs_delayed_data_ref_cachep, ref); + + if (qrecord_inserted) return btrfs_qgroup_trace_extent_post(fs_info, record); return 0; @@ -897,19 +881,16 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info, if (!head_ref) return -ENOMEM; + init_delayed_ref_head(head_ref, NULL, bytenr, num_bytes, 0, 0, + BTRFS_UPDATE_DELAYED_HEAD, extent_op->is_data, + false); head_ref->extent_op = extent_op; delayed_refs = &trans->transaction->delayed_refs; spin_lock(&delayed_refs->lock); - /* - * extent_ops just modify the flags of an extent and they don't result - * in ref count changes, hence it's safe to pass false/0 for is_system - * argument - */ - add_delayed_ref_head(fs_info, trans, head_ref, NULL, bytenr, - num_bytes, 0, 0, BTRFS_UPDATE_DELAYED_HEAD, - extent_op->is_data, 0, NULL, NULL, NULL); + add_delayed_ref_head(trans, head_ref, NULL, BTRFS_UPDATE_DELAYED_HEAD, + NULL, NULL, NULL); spin_unlock(&delayed_refs->lock); return 0; diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h index 7f00db50bd242d0a29f35c430339950b90588697..ea1aecb6a50dd977c471d1cf58611406b347954c 100644 --- a/fs/btrfs/delayed-ref.h +++ b/fs/btrfs/delayed-ref.h @@ -251,7 +251,6 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info, u64 bytenr, u64 num_bytes, struct btrfs_delayed_extent_op *extent_op); void btrfs_merge_delayed_refs(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_delayed_ref_root *delayed_refs, struct btrfs_delayed_ref_head *head); @@ -269,9 +268,7 @@ static inline void btrfs_delayed_ref_unlock(struct btrfs_delayed_ref_head *head) struct btrfs_delayed_ref_head * btrfs_select_ref_head(struct btrfs_trans_handle *trans); -int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info, - struct btrfs_delayed_ref_root *delayed_refs, - u64 seq); +int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info, u64 seq); /* * helper functions to cast a node into its container diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index f82be266ba4ba6dfb8fe3eb4ec2160beb080d77a..e2ba0419297a51e4ff53fc7522eb2bd780b56840 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -33,8 +33,6 @@ static void btrfs_dev_replace_update_device_in_mapping_tree( struct btrfs_device *srcdev, struct btrfs_device *tgtdev); static int btrfs_dev_replace_kthread(void *data); -static int btrfs_dev_replace_continue_on_mount(struct btrfs_fs_info *fs_info); - int btrfs_init_dev_replace(struct btrfs_fs_info *fs_info) { @@ -178,6 +176,105 @@ int btrfs_init_dev_replace(struct btrfs_fs_info *fs_info) return ret; } +/* + * Initialize a new device for device replace target from a given source dev + * and path. + * + * Return 0 and new device in @device_out, otherwise return < 0 + */ +static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, + const char *device_path, + struct btrfs_device *srcdev, + struct btrfs_device **device_out) +{ + struct btrfs_device *device; + struct block_device *bdev; + struct list_head *devices; + struct rcu_string *name; + u64 devid = BTRFS_DEV_REPLACE_DEVID; + int ret = 0; + + *device_out = NULL; + if (fs_info->fs_devices->seeding) { + btrfs_err(fs_info, "the filesystem is a seed filesystem!"); + return -EINVAL; + } + + bdev = blkdev_get_by_path(device_path, FMODE_WRITE | FMODE_EXCL, + fs_info->bdev_holder); + if (IS_ERR(bdev)) { + btrfs_err(fs_info, "target device %s is invalid!", device_path); + return PTR_ERR(bdev); + } + + filemap_write_and_wait(bdev->bd_inode->i_mapping); + + devices = &fs_info->fs_devices->devices; + list_for_each_entry(device, devices, dev_list) { + if (device->bdev == bdev) { + btrfs_err(fs_info, + "target device is in the filesystem!"); + ret = -EEXIST; + goto error; + } + } + + + if (i_size_read(bdev->bd_inode) < + btrfs_device_get_total_bytes(srcdev)) { + btrfs_err(fs_info, + "target device is smaller than source device!"); + ret = -EINVAL; + goto error; + } + + + device = btrfs_alloc_device(NULL, &devid, NULL); + if (IS_ERR(device)) { + ret = PTR_ERR(device); + goto error; + } + + name = rcu_string_strdup(device_path, GFP_KERNEL); + if (!name) { + btrfs_free_device(device); + ret = -ENOMEM; + goto error; + } + rcu_assign_pointer(device->name, name); + + mutex_lock(&fs_info->fs_devices->device_list_mutex); + set_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state); + device->generation = 0; + device->io_width = fs_info->sectorsize; + device->io_align = fs_info->sectorsize; + device->sector_size = fs_info->sectorsize; + device->total_bytes = btrfs_device_get_total_bytes(srcdev); + device->disk_total_bytes = btrfs_device_get_disk_total_bytes(srcdev); + device->bytes_used = btrfs_device_get_bytes_used(srcdev); + device->commit_total_bytes = srcdev->commit_total_bytes; + device->commit_bytes_used = device->bytes_used; + device->fs_info = fs_info; + device->bdev = bdev; + set_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state); + set_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state); + device->mode = FMODE_EXCL; + device->dev_stats_valid = 1; + set_blocksize(device->bdev, BTRFS_BDEV_BLOCKSIZE); + device->fs_devices = fs_info->fs_devices; + list_add(&device->dev_list, &fs_info->fs_devices->devices); + fs_info->fs_devices->num_devices++; + fs_info->fs_devices->open_devices++; + mutex_unlock(&fs_info->fs_devices->device_list_mutex); + + *device_out = device; + return 0; + +error: + blkdev_put(bdev, FMODE_EXCL); + return ret; +} + /* * called from commit_transaction. Writes changed device replace state to * disk. @@ -317,18 +414,13 @@ int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info, struct btrfs_device *tgt_device = NULL; struct btrfs_device *src_device = NULL; - /* the disk copy procedure reuses the scrub code */ - mutex_lock(&fs_info->volume_mutex); ret = btrfs_find_device_by_devspec(fs_info, srcdevid, srcdev_name, &src_device); - if (ret) { - mutex_unlock(&fs_info->volume_mutex); + if (ret) return ret; - } ret = btrfs_init_dev_replace_tgtdev(fs_info, tgtdev_name, src_device, &tgt_device); - mutex_unlock(&fs_info->volume_mutex); if (ret) return ret; @@ -360,7 +452,6 @@ int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info, dev_replace->cont_reading_from_srcdev_mode = read_src; WARN_ON(!src_device); dev_replace->srcdev = src_device; - WARN_ON(!tgt_device); dev_replace->tgtdev = tgt_device; btrfs_info_in_rcu(fs_info, @@ -503,7 +594,7 @@ 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(fs_info, 0, -1); + ret = btrfs_start_delalloc_roots(fs_info, -1); if (ret) { mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return ret; @@ -518,7 +609,6 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, ret = btrfs_commit_transaction(trans); WARN_ON(ret); - mutex_lock(&uuid_mutex); /* keep away write_all_supers() during the finishing procedure */ mutex_lock(&fs_info->fs_devices->device_list_mutex); mutex_lock(&fs_info->chunk_mutex); @@ -545,7 +635,6 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, btrfs_dev_replace_write_unlock(dev_replace); mutex_unlock(&fs_info->chunk_mutex); mutex_unlock(&fs_info->fs_devices->device_list_mutex); - mutex_unlock(&uuid_mutex); btrfs_rm_dev_replace_blocked(fs_info); if (tgt_device) btrfs_destroy_dev_replace_tgtdev(fs_info, tgt_device); @@ -596,7 +685,6 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, */ mutex_unlock(&fs_info->chunk_mutex); mutex_unlock(&fs_info->fs_devices->device_list_mutex); - mutex_unlock(&uuid_mutex); /* replace the sysfs entry */ btrfs_sysfs_rm_device_link(fs_info->fs_devices, src_device); @@ -800,7 +888,17 @@ int btrfs_resume_dev_replace_async(struct btrfs_fs_info *fs_info) } btrfs_dev_replace_write_unlock(dev_replace); - WARN_ON(test_and_set_bit(BTRFS_FS_EXCL_OP, &fs_info->flags)); + /* + * This could collide with a paused balance, but the exclusive op logic + * should never allow both to start and pause. We don't want to allow + * dev-replace to start anyway. + */ + if (test_and_set_bit(BTRFS_FS_EXCL_OP, &fs_info->flags)) { + btrfs_info(fs_info, + "cannot resume dev-replace, other exclusive operation running"); + return 0; + } + task = kthread_run(btrfs_dev_replace_kthread, fs_info, "btrfs-devrepl"); return PTR_ERR_OR_ZERO(task); } @@ -810,6 +908,7 @@ static int btrfs_dev_replace_kthread(void *data) struct btrfs_fs_info *fs_info = data; struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; u64 progress; + int ret; progress = btrfs_dev_replace_progress(fs_info); progress = div_u64(progress, 10); @@ -820,23 +919,14 @@ static int btrfs_dev_replace_kthread(void *data) btrfs_dev_name(dev_replace->tgtdev), (unsigned int)progress); - btrfs_dev_replace_continue_on_mount(fs_info); - clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); - - return 0; -} - -static int btrfs_dev_replace_continue_on_mount(struct btrfs_fs_info *fs_info) -{ - struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; - int ret; - ret = btrfs_scrub_dev(fs_info, dev_replace->srcdev->devid, dev_replace->committed_cursor_left, btrfs_device_get_total_bytes(dev_replace->srcdev), &dev_replace->scrub_progress, 0, 1); ret = btrfs_dev_replace_finishing(fs_info, ret); WARN_ON(ret); + + clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); return 0; } @@ -916,9 +1006,9 @@ void btrfs_dev_replace_clear_lock_blocking( ASSERT(atomic_read(&dev_replace->read_locks) > 0); ASSERT(atomic_read(&dev_replace->blocking_readers) > 0); read_lock(&dev_replace->lock); - if (atomic_dec_and_test(&dev_replace->blocking_readers) && - waitqueue_active(&dev_replace->read_lock_wq)) - wake_up(&dev_replace->read_lock_wq); + /* Barrier implied by atomic_dec_and_test */ + if (atomic_dec_and_test(&dev_replace->blocking_readers)) + cond_wake_up_nomb(&dev_replace->read_lock_wq); } void btrfs_bio_counter_inc_noblocked(struct btrfs_fs_info *fs_info) @@ -929,9 +1019,7 @@ void btrfs_bio_counter_inc_noblocked(struct btrfs_fs_info *fs_info) void btrfs_bio_counter_sub(struct btrfs_fs_info *fs_info, s64 amount) { percpu_counter_sub(&fs_info->bio_counter, amount); - - if (waitqueue_active(&fs_info->replace_wait)) - wake_up(&fs_info->replace_wait); + cond_wake_up_nomb(&fs_info->replace_wait); } void btrfs_bio_counter_inc_blocked(struct btrfs_fs_info *fs_info) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index c3504b4d281b5cd76bb0861b781e228bcec4b3bb..205092dc939082d48cabc43d7d4a4340a1feffb4 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -55,7 +55,6 @@ static const struct extent_io_ops btree_extent_io_ops; static void end_workqueue_fn(struct btrfs_work *work); static void free_fs_root(struct btrfs_root *root); -static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info); static void btrfs_destroy_ordered_extents(struct btrfs_root *root); static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans, struct btrfs_fs_info *fs_info); @@ -416,7 +415,7 @@ static int btrfs_check_super_csum(struct btrfs_fs_info *fs_info, static int verify_level_key(struct btrfs_fs_info *fs_info, struct extent_buffer *eb, int level, - struct btrfs_key *first_key) + struct btrfs_key *first_key, u64 parent_transid) { int found_level; struct btrfs_key found_key; @@ -454,10 +453,11 @@ static int verify_level_key(struct btrfs_fs_info *fs_info, if (ret) { WARN_ON(1); btrfs_err(fs_info, -"tree first key mismatch detected, bytenr=%llu key expected=(%llu, %u, %llu) has=(%llu, %u, %llu)", - eb->start, first_key->objectid, first_key->type, - first_key->offset, found_key.objectid, - found_key.type, found_key.offset); +"tree first key mismatch detected, bytenr=%llu parent_transid=%llu key expected=(%llu,%u,%llu) has=(%llu,%u,%llu)", + eb->start, parent_transid, first_key->objectid, + first_key->type, first_key->offset, + found_key.objectid, found_key.type, + found_key.offset); } #endif return ret; @@ -493,7 +493,7 @@ static int btree_read_extent_buffer_pages(struct btrfs_fs_info *fs_info, parent_transid, 0)) ret = -EIO; else if (verify_level_key(fs_info, eb, level, - first_key)) + first_key, parent_transid)) ret = -EUCLEAN; else break; @@ -1185,7 +1185,6 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info, root->inode_tree = RB_ROOT; INIT_RADIX_TREE(&root->delayed_nodes_tree, GFP_ATOMIC); root->block_rsv = NULL; - root->orphan_block_rsv = NULL; INIT_LIST_HEAD(&root->dirty_list); INIT_LIST_HEAD(&root->root_list); @@ -1195,7 +1194,6 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info, INIT_LIST_HEAD(&root->ordered_root); INIT_LIST_HEAD(&root->logged_list[0]); INIT_LIST_HEAD(&root->logged_list[1]); - spin_lock_init(&root->orphan_lock); spin_lock_init(&root->inode_lock); spin_lock_init(&root->delalloc_lock); spin_lock_init(&root->ordered_extent_lock); @@ -1216,7 +1214,6 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info, atomic_set(&root->log_commit[1], 0); atomic_set(&root->log_writers, 0); atomic_set(&root->log_batch, 0); - atomic_set(&root->orphan_inodes, 0); refcount_set(&root->refs, 1); atomic_set(&root->will_be_snapshotted, 0); root->log_transid = 0; @@ -2164,7 +2161,6 @@ static void btrfs_init_balance(struct btrfs_fs_info *fs_info) { spin_lock_init(&fs_info->balance_lock); mutex_init(&fs_info->balance_mutex); - atomic_set(&fs_info->balance_running, 0); atomic_set(&fs_info->balance_pause_req, 0); atomic_set(&fs_info->balance_cancel_req, 0); fs_info->balance_ctl = NULL; @@ -2442,6 +2438,211 @@ static int btrfs_read_roots(struct btrfs_fs_info *fs_info) return ret; } +/* + * Real super block validation + * NOTE: super csum type and incompat features will not be checked here. + * + * @sb: super block to check + * @mirror_num: the super block number to check its bytenr: + * 0 the primary (1st) sb + * 1, 2 2nd and 3rd backup copy + * -1 skip bytenr check + */ +static int validate_super(struct btrfs_fs_info *fs_info, + struct btrfs_super_block *sb, int mirror_num) +{ + u64 nodesize = btrfs_super_nodesize(sb); + u64 sectorsize = btrfs_super_sectorsize(sb); + int ret = 0; + + if (btrfs_super_magic(sb) != BTRFS_MAGIC) { + btrfs_err(fs_info, "no valid FS found"); + ret = -EINVAL; + } + if (btrfs_super_flags(sb) & ~BTRFS_SUPER_FLAG_SUPP) { + btrfs_err(fs_info, "unrecognized or unsupported super flag: %llu", + btrfs_super_flags(sb) & ~BTRFS_SUPER_FLAG_SUPP); + ret = -EINVAL; + } + if (btrfs_super_root_level(sb) >= BTRFS_MAX_LEVEL) { + btrfs_err(fs_info, "tree_root level too big: %d >= %d", + btrfs_super_root_level(sb), BTRFS_MAX_LEVEL); + ret = -EINVAL; + } + if (btrfs_super_chunk_root_level(sb) >= BTRFS_MAX_LEVEL) { + btrfs_err(fs_info, "chunk_root level too big: %d >= %d", + btrfs_super_chunk_root_level(sb), BTRFS_MAX_LEVEL); + ret = -EINVAL; + } + if (btrfs_super_log_root_level(sb) >= BTRFS_MAX_LEVEL) { + btrfs_err(fs_info, "log_root level too big: %d >= %d", + btrfs_super_log_root_level(sb), BTRFS_MAX_LEVEL); + ret = -EINVAL; + } + + /* + * Check sectorsize and nodesize first, other check will need it. + * Check all possible sectorsize(4K, 8K, 16K, 32K, 64K) here. + */ + if (!is_power_of_2(sectorsize) || sectorsize < 4096 || + sectorsize > BTRFS_MAX_METADATA_BLOCKSIZE) { + btrfs_err(fs_info, "invalid sectorsize %llu", sectorsize); + ret = -EINVAL; + } + /* Only PAGE SIZE is supported yet */ + if (sectorsize != PAGE_SIZE) { + btrfs_err(fs_info, + "sectorsize %llu not supported yet, only support %lu", + sectorsize, PAGE_SIZE); + ret = -EINVAL; + } + if (!is_power_of_2(nodesize) || nodesize < sectorsize || + nodesize > BTRFS_MAX_METADATA_BLOCKSIZE) { + btrfs_err(fs_info, "invalid nodesize %llu", nodesize); + ret = -EINVAL; + } + if (nodesize != le32_to_cpu(sb->__unused_leafsize)) { + btrfs_err(fs_info, "invalid leafsize %u, should be %llu", + le32_to_cpu(sb->__unused_leafsize), nodesize); + ret = -EINVAL; + } + + /* Root alignment check */ + if (!IS_ALIGNED(btrfs_super_root(sb), sectorsize)) { + btrfs_warn(fs_info, "tree_root block unaligned: %llu", + btrfs_super_root(sb)); + ret = -EINVAL; + } + if (!IS_ALIGNED(btrfs_super_chunk_root(sb), sectorsize)) { + btrfs_warn(fs_info, "chunk_root block unaligned: %llu", + btrfs_super_chunk_root(sb)); + ret = -EINVAL; + } + if (!IS_ALIGNED(btrfs_super_log_root(sb), sectorsize)) { + btrfs_warn(fs_info, "log_root block unaligned: %llu", + btrfs_super_log_root(sb)); + ret = -EINVAL; + } + + if (memcmp(fs_info->fsid, sb->dev_item.fsid, BTRFS_FSID_SIZE) != 0) { + btrfs_err(fs_info, + "dev_item UUID does not match fsid: %pU != %pU", + fs_info->fsid, sb->dev_item.fsid); + ret = -EINVAL; + } + + /* + * Hint to catch really bogus numbers, bitflips or so, more exact checks are + * done later + */ + if (btrfs_super_bytes_used(sb) < 6 * btrfs_super_nodesize(sb)) { + btrfs_err(fs_info, "bytes_used is too small %llu", + btrfs_super_bytes_used(sb)); + ret = -EINVAL; + } + if (!is_power_of_2(btrfs_super_stripesize(sb))) { + btrfs_err(fs_info, "invalid stripesize %u", + btrfs_super_stripesize(sb)); + ret = -EINVAL; + } + if (btrfs_super_num_devices(sb) > (1UL << 31)) + btrfs_warn(fs_info, "suspicious number of devices: %llu", + btrfs_super_num_devices(sb)); + if (btrfs_super_num_devices(sb) == 0) { + btrfs_err(fs_info, "number of devices is 0"); + ret = -EINVAL; + } + + if (mirror_num >= 0 && + btrfs_super_bytenr(sb) != btrfs_sb_offset(mirror_num)) { + btrfs_err(fs_info, "super offset mismatch %llu != %u", + btrfs_super_bytenr(sb), BTRFS_SUPER_INFO_OFFSET); + ret = -EINVAL; + } + + /* + * Obvious sys_chunk_array corruptions, it must hold at least one key + * and one chunk + */ + if (btrfs_super_sys_array_size(sb) > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) { + btrfs_err(fs_info, "system chunk array too big %u > %u", + btrfs_super_sys_array_size(sb), + BTRFS_SYSTEM_CHUNK_ARRAY_SIZE); + ret = -EINVAL; + } + if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key) + + sizeof(struct btrfs_chunk)) { + btrfs_err(fs_info, "system chunk array too small %u < %zu", + btrfs_super_sys_array_size(sb), + sizeof(struct btrfs_disk_key) + + sizeof(struct btrfs_chunk)); + ret = -EINVAL; + } + + /* + * The generation is a global counter, we'll trust it more than the others + * but it's still possible that it's the one that's wrong. + */ + if (btrfs_super_generation(sb) < btrfs_super_chunk_root_generation(sb)) + btrfs_warn(fs_info, + "suspicious: generation < chunk_root_generation: %llu < %llu", + btrfs_super_generation(sb), + btrfs_super_chunk_root_generation(sb)); + if (btrfs_super_generation(sb) < btrfs_super_cache_generation(sb) + && btrfs_super_cache_generation(sb) != (u64)-1) + btrfs_warn(fs_info, + "suspicious: generation < cache_generation: %llu < %llu", + btrfs_super_generation(sb), + btrfs_super_cache_generation(sb)); + + return ret; +} + +/* + * Validation of super block at mount time. + * Some checks already done early at mount time, like csum type and incompat + * flags will be skipped. + */ +static int btrfs_validate_mount_super(struct btrfs_fs_info *fs_info) +{ + return validate_super(fs_info, fs_info->super_copy, 0); +} + +/* + * Validation of super block at write time. + * Some checks like bytenr check will be skipped as their values will be + * overwritten soon. + * Extra checks like csum type and incompat flags will be done here. + */ +static int btrfs_validate_write_super(struct btrfs_fs_info *fs_info, + struct btrfs_super_block *sb) +{ + int ret; + + ret = validate_super(fs_info, sb, -1); + if (ret < 0) + goto out; + if (btrfs_super_csum_type(sb) != BTRFS_CSUM_TYPE_CRC32) { + ret = -EUCLEAN; + btrfs_err(fs_info, "invalid csum type, has %u want %u", + btrfs_super_csum_type(sb), BTRFS_CSUM_TYPE_CRC32); + goto out; + } + if (btrfs_super_incompat_flags(sb) & ~BTRFS_FEATURE_INCOMPAT_SUPP) { + ret = -EUCLEAN; + btrfs_err(fs_info, + "invalid incompat flags, has 0x%llx valid mask 0x%llx", + btrfs_super_incompat_flags(sb), + (unsigned long long)BTRFS_FEATURE_INCOMPAT_SUPP); + goto out; + } +out: + if (ret < 0) + btrfs_err(fs_info, + "super block corruption detected before writing it to disk"); + return ret; +} + int open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_devices, char *options) @@ -2601,7 +2802,6 @@ int open_ctree(struct super_block *sb, mutex_init(&fs_info->chunk_mutex); mutex_init(&fs_info->transaction_kthread_mutex); mutex_init(&fs_info->cleaner_mutex); - mutex_init(&fs_info->volume_mutex); mutex_init(&fs_info->ro_block_group_mutex); init_rwsem(&fs_info->commit_root_sem); init_rwsem(&fs_info->cleanup_work_sem); @@ -2668,7 +2868,7 @@ int open_ctree(struct super_block *sb, memcpy(fs_info->fsid, fs_info->super_copy->fsid, BTRFS_FSID_SIZE); - ret = btrfs_check_super_valid(fs_info); + ret = btrfs_validate_mount_super(fs_info); if (ret) { btrfs_err(fs_info, "superblock contains fatal errors"); err = -EINVAL; @@ -3523,7 +3723,7 @@ int btrfs_get_num_tolerated_disk_barrier_failures(u64 flags) for (raid_type = 0; raid_type < BTRFS_NR_RAID_TYPES; raid_type++) { if (raid_type == BTRFS_RAID_SINGLE) continue; - if (!(flags & btrfs_raid_group[raid_type])) + if (!(flags & btrfs_raid_array[raid_type].bg_flag)) continue; min_tolerated = min(min_tolerated, btrfs_raid_array[raid_type]. @@ -3603,6 +3803,14 @@ int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors) flags = btrfs_super_flags(sb); btrfs_set_super_flags(sb, flags | BTRFS_HEADER_FLAG_WRITTEN); + ret = btrfs_validate_write_super(fs_info, sb); + if (ret < 0) { + mutex_unlock(&fs_info->fs_devices->device_list_mutex); + btrfs_handle_fs_error(fs_info, -EUCLEAN, + "unexpected superblock corruption detected"); + return -EUCLEAN; + } + ret = write_dev_supers(dev, sb, max_mirrors); if (ret) total_errors++; @@ -3674,8 +3882,6 @@ static void free_fs_root(struct btrfs_root *root) { iput(root->ino_cache_inode); WARN_ON(!RB_EMPTY_ROOT(&root->inode_tree)); - btrfs_free_block_rsv(root->fs_info, root->orphan_block_rsv); - root->orphan_block_rsv = NULL; if (root->anon_dev) free_anon_bdev(root->anon_dev); if (root->subv_writers) @@ -3766,7 +3972,6 @@ int btrfs_commit_super(struct btrfs_fs_info *fs_info) void close_ctree(struct btrfs_fs_info *fs_info) { - struct btrfs_root *root = fs_info->tree_root; int ret; set_bit(BTRFS_FS_CLOSING_START, &fs_info->flags); @@ -3862,9 +4067,6 @@ void close_ctree(struct btrfs_fs_info *fs_info) btrfs_free_stripe_hash_table(fs_info); btrfs_free_ref_cache(fs_info); - __btrfs_free_block_rsv(root->orphan_block_rsv); - root->orphan_block_rsv = NULL; - while (!list_empty(&fs_info->pinned_chunks)) { struct extent_map *em; @@ -3975,155 +4177,6 @@ int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid, int level, level, first_key); } -static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info) -{ - struct btrfs_super_block *sb = fs_info->super_copy; - u64 nodesize = btrfs_super_nodesize(sb); - u64 sectorsize = btrfs_super_sectorsize(sb); - int ret = 0; - - if (btrfs_super_magic(sb) != BTRFS_MAGIC) { - btrfs_err(fs_info, "no valid FS found"); - ret = -EINVAL; - } - if (btrfs_super_flags(sb) & ~BTRFS_SUPER_FLAG_SUPP) { - btrfs_err(fs_info, "unrecognized or unsupported super flag: %llu", - btrfs_super_flags(sb) & ~BTRFS_SUPER_FLAG_SUPP); - ret = -EINVAL; - } - if (btrfs_super_root_level(sb) >= BTRFS_MAX_LEVEL) { - btrfs_err(fs_info, "tree_root level too big: %d >= %d", - btrfs_super_root_level(sb), BTRFS_MAX_LEVEL); - ret = -EINVAL; - } - if (btrfs_super_chunk_root_level(sb) >= BTRFS_MAX_LEVEL) { - btrfs_err(fs_info, "chunk_root level too big: %d >= %d", - btrfs_super_chunk_root_level(sb), BTRFS_MAX_LEVEL); - ret = -EINVAL; - } - if (btrfs_super_log_root_level(sb) >= BTRFS_MAX_LEVEL) { - btrfs_err(fs_info, "log_root level too big: %d >= %d", - btrfs_super_log_root_level(sb), BTRFS_MAX_LEVEL); - ret = -EINVAL; - } - - /* - * Check sectorsize and nodesize first, other check will need it. - * Check all possible sectorsize(4K, 8K, 16K, 32K, 64K) here. - */ - if (!is_power_of_2(sectorsize) || sectorsize < 4096 || - sectorsize > BTRFS_MAX_METADATA_BLOCKSIZE) { - btrfs_err(fs_info, "invalid sectorsize %llu", sectorsize); - ret = -EINVAL; - } - /* Only PAGE SIZE is supported yet */ - if (sectorsize != PAGE_SIZE) { - btrfs_err(fs_info, - "sectorsize %llu not supported yet, only support %lu", - sectorsize, PAGE_SIZE); - ret = -EINVAL; - } - if (!is_power_of_2(nodesize) || nodesize < sectorsize || - nodesize > BTRFS_MAX_METADATA_BLOCKSIZE) { - btrfs_err(fs_info, "invalid nodesize %llu", nodesize); - ret = -EINVAL; - } - if (nodesize != le32_to_cpu(sb->__unused_leafsize)) { - btrfs_err(fs_info, "invalid leafsize %u, should be %llu", - le32_to_cpu(sb->__unused_leafsize), nodesize); - ret = -EINVAL; - } - - /* Root alignment check */ - if (!IS_ALIGNED(btrfs_super_root(sb), sectorsize)) { - btrfs_warn(fs_info, "tree_root block unaligned: %llu", - btrfs_super_root(sb)); - ret = -EINVAL; - } - if (!IS_ALIGNED(btrfs_super_chunk_root(sb), sectorsize)) { - btrfs_warn(fs_info, "chunk_root block unaligned: %llu", - btrfs_super_chunk_root(sb)); - ret = -EINVAL; - } - if (!IS_ALIGNED(btrfs_super_log_root(sb), sectorsize)) { - btrfs_warn(fs_info, "log_root block unaligned: %llu", - btrfs_super_log_root(sb)); - ret = -EINVAL; - } - - if (memcmp(fs_info->fsid, sb->dev_item.fsid, BTRFS_FSID_SIZE) != 0) { - btrfs_err(fs_info, - "dev_item UUID does not match fsid: %pU != %pU", - fs_info->fsid, sb->dev_item.fsid); - ret = -EINVAL; - } - - /* - * Hint to catch really bogus numbers, bitflips or so, more exact checks are - * done later - */ - if (btrfs_super_bytes_used(sb) < 6 * btrfs_super_nodesize(sb)) { - btrfs_err(fs_info, "bytes_used is too small %llu", - btrfs_super_bytes_used(sb)); - ret = -EINVAL; - } - if (!is_power_of_2(btrfs_super_stripesize(sb))) { - btrfs_err(fs_info, "invalid stripesize %u", - btrfs_super_stripesize(sb)); - ret = -EINVAL; - } - if (btrfs_super_num_devices(sb) > (1UL << 31)) - btrfs_warn(fs_info, "suspicious number of devices: %llu", - btrfs_super_num_devices(sb)); - if (btrfs_super_num_devices(sb) == 0) { - btrfs_err(fs_info, "number of devices is 0"); - ret = -EINVAL; - } - - if (btrfs_super_bytenr(sb) != BTRFS_SUPER_INFO_OFFSET) { - btrfs_err(fs_info, "super offset mismatch %llu != %u", - btrfs_super_bytenr(sb), BTRFS_SUPER_INFO_OFFSET); - ret = -EINVAL; - } - - /* - * Obvious sys_chunk_array corruptions, it must hold at least one key - * and one chunk - */ - if (btrfs_super_sys_array_size(sb) > BTRFS_SYSTEM_CHUNK_ARRAY_SIZE) { - btrfs_err(fs_info, "system chunk array too big %u > %u", - btrfs_super_sys_array_size(sb), - BTRFS_SYSTEM_CHUNK_ARRAY_SIZE); - ret = -EINVAL; - } - if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key) - + sizeof(struct btrfs_chunk)) { - btrfs_err(fs_info, "system chunk array too small %u < %zu", - btrfs_super_sys_array_size(sb), - sizeof(struct btrfs_disk_key) - + sizeof(struct btrfs_chunk)); - ret = -EINVAL; - } - - /* - * The generation is a global counter, we'll trust it more than the others - * but it's still possible that it's the one that's wrong. - */ - if (btrfs_super_generation(sb) < btrfs_super_chunk_root_generation(sb)) - btrfs_warn(fs_info, - "suspicious: generation < chunk_root_generation: %llu < %llu", - btrfs_super_generation(sb), - btrfs_super_chunk_root_generation(sb)); - if (btrfs_super_generation(sb) < btrfs_super_cache_generation(sb) - && btrfs_super_cache_generation(sb) != (u64)-1) - btrfs_warn(fs_info, - "suspicious: generation < cache_generation: %llu < %llu", - btrfs_super_generation(sb), - btrfs_super_cache_generation(sb)); - - return ret; -} - static void btrfs_error_commit_super(struct btrfs_fs_info *fs_info) { /* cleanup FS via transaction */ diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 51b5e2da708c4e77566cb686aea81b4a1cfa3497..3d9fe58c0080db89f4933f162f8bf7f8d6df1c85 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -66,10 +66,8 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans, u64 flags, u64 owner, u64 offset, struct btrfs_key *ins, int ref_mod); static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, - u64 parent, u64 root_objectid, - u64 flags, struct btrfs_disk_key *key, - int level, struct btrfs_key *ins); + struct btrfs_delayed_ref_node *node, + struct btrfs_delayed_extent_op *extent_op); static int do_chunk_alloc(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, u64 flags, int force); @@ -256,7 +254,7 @@ static int exclude_super_stripes(struct btrfs_fs_info *fs_info, for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) { bytenr = btrfs_sb_offset(i); ret = btrfs_rmap_block(fs_info, cache->key.objectid, - bytenr, 0, &logical, &nr, &stripe_len); + bytenr, &logical, &nr, &stripe_len); if (ret) return ret; @@ -343,8 +341,9 @@ static void fragment_free_space(struct btrfs_block_group_cache *block_group) * since their free space will be released as soon as the transaction commits. */ u64 add_new_free_space(struct btrfs_block_group_cache *block_group, - struct btrfs_fs_info *info, u64 start, u64 end) + u64 start, u64 end) { + struct btrfs_fs_info *info = block_group->fs_info; u64 extent_start, extent_end, size, total_added = 0; int ret; @@ -489,8 +488,7 @@ static int load_extent_tree_free(struct btrfs_caching_control *caching_ctl) if (key.type == BTRFS_EXTENT_ITEM_KEY || key.type == BTRFS_METADATA_ITEM_KEY) { - total_found += add_new_free_space(block_group, - fs_info, last, + total_found += add_new_free_space(block_group, last, key.objectid); if (key.type == BTRFS_METADATA_ITEM_KEY) last = key.objectid + @@ -508,7 +506,7 @@ static int load_extent_tree_free(struct btrfs_caching_control *caching_ctl) } ret = 0; - total_found += add_new_free_space(block_group, fs_info, last, + total_found += add_new_free_space(block_group, last, block_group->key.objectid + block_group->key.offset); caching_ctl->progress = (u64)-1; @@ -744,12 +742,12 @@ static struct btrfs_space_info *__find_space_info(struct btrfs_fs_info *info, } static void add_pinned_bytes(struct btrfs_fs_info *fs_info, s64 num_bytes, - u64 owner, u64 root_objectid) + bool metadata, u64 root_objectid) { struct btrfs_space_info *space_info; u64 flags; - if (owner < BTRFS_FIRST_FREE_OBJECTID) { + if (metadata) { if (root_objectid == BTRFS_CHUNK_TREE_OBJECTID) flags = BTRFS_BLOCK_GROUP_SYSTEM; else @@ -2200,8 +2198,11 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, &old_ref_mod, &new_ref_mod); } - if (ret == 0 && old_ref_mod < 0 && new_ref_mod >= 0) - add_pinned_bytes(fs_info, -num_bytes, owner, root_objectid); + if (ret == 0 && old_ref_mod < 0 && new_ref_mod >= 0) { + bool metadata = owner < BTRFS_FIRST_FREE_OBJECTID; + + add_pinned_bytes(fs_info, -num_bytes, metadata, root_objectid); + } return ret; } @@ -2428,10 +2429,8 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans, { int ret = 0; struct btrfs_delayed_tree_ref *ref; - struct btrfs_key ins; u64 parent = 0; u64 ref_root = 0; - bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA); ref = btrfs_delayed_node_to_tree_ref(node); trace_run_delayed_tree_ref(fs_info, node, ref, node->action); @@ -2440,15 +2439,6 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans, parent = ref->parent; ref_root = ref->root; - ins.objectid = node->bytenr; - if (skinny_metadata) { - ins.offset = ref->level; - ins.type = BTRFS_METADATA_ITEM_KEY; - } else { - ins.offset = node->num_bytes; - ins.type = BTRFS_EXTENT_ITEM_KEY; - } - if (node->ref_mod != 1) { btrfs_err(fs_info, "btree block(%llu) has %d references rather than 1: action %d ref_root %llu parent %llu", @@ -2458,11 +2448,7 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans, } if (node->action == BTRFS_ADD_DELAYED_REF && insert_reserved) { BUG_ON(!extent_op || !extent_op->update_flags); - ret = alloc_reserved_tree_block(trans, fs_info, - parent, ref_root, - extent_op->flags_to_set, - &extent_op->key, - ref->level, &ins); + ret = alloc_reserved_tree_block(trans, node, extent_op); } else if (node->action == BTRFS_ADD_DELAYED_REF) { ret = __btrfs_inc_extent_ref(trans, fs_info, node, parent, ref_root, @@ -2594,8 +2580,8 @@ static int cleanup_ref_head(struct btrfs_trans_handle *trans, delayed_refs->num_heads--; rb_erase(&head->href_node, &delayed_refs->href_root); RB_CLEAR_NODE(&head->href_node); - spin_unlock(&delayed_refs->lock); spin_unlock(&head->lock); + spin_unlock(&delayed_refs->lock); atomic_dec(&delayed_refs->num_entries); trace_run_delayed_ref_head(fs_info, head, 0); @@ -2700,17 +2686,12 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, * insert_inline_extent_backref()). */ spin_lock(&locked_ref->lock); - btrfs_merge_delayed_refs(trans, fs_info, delayed_refs, - locked_ref); + btrfs_merge_delayed_refs(trans, delayed_refs, locked_ref); - /* - * locked_ref is the head node, so we have to go one - * node back for any delayed ref updates - */ ref = select_delayed_ref(locked_ref); if (ref && ref->seq && - btrfs_check_delayed_seq(fs_info, delayed_refs, ref->seq)) { + btrfs_check_delayed_seq(fs_info, ref->seq)) { spin_unlock(&locked_ref->lock); unselect_delayed_ref_head(delayed_refs, locked_ref); locked_ref = NULL; @@ -3291,7 +3272,7 @@ int btrfs_cross_ref_exist(struct btrfs_root *root, u64 objectid, u64 offset, path = btrfs_alloc_path(); if (!path) - return -ENOENT; + return -ENOMEM; do { ret = check_committed_ref(root, path, objectid, @@ -4026,8 +4007,7 @@ static const char *alloc_name(u64 flags) }; } -static int create_space_info(struct btrfs_fs_info *info, u64 flags, - struct btrfs_space_info **new) +static int create_space_info(struct btrfs_fs_info *info, u64 flags) { struct btrfs_space_info *space_info; @@ -4065,7 +4045,6 @@ static int create_space_info(struct btrfs_fs_info *info, u64 flags, return ret; } - *new = space_info; list_add_rcu(&space_info->list, &info->space_info); if (flags & BTRFS_BLOCK_GROUP_DATA) info->data_sinfo = space_info; @@ -4122,7 +4101,7 @@ static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags) * returns target flags in extended format or 0 if restripe for this * chunk_type is not in progress * - * should be called with either volume_mutex or balance_lock held + * should be called with balance_lock held */ static u64 get_restripe_target(struct btrfs_fs_info *fs_info, u64 flags) { @@ -4178,7 +4157,7 @@ static u64 btrfs_reduce_alloc_profile(struct btrfs_fs_info *fs_info, u64 flags) /* First, mask out the RAID levels which aren't possible */ for (raid_type = 0; raid_type < BTRFS_NR_RAID_TYPES; raid_type++) { if (num_devices >= btrfs_raid_array[raid_type].devs_min) - allowed |= btrfs_raid_group[raid_type]; + allowed |= btrfs_raid_array[raid_type].bg_flag; } allowed &= flags; @@ -4341,7 +4320,7 @@ int btrfs_alloc_data_chunk_ondemand(struct btrfs_inode *inode, u64 bytes) need_commit--; if (need_commit > 0) { - btrfs_start_delalloc_roots(fs_info, 0, -1); + btrfs_start_delalloc_roots(fs_info, -1); btrfs_wait_ordered_roots(fs_info, U64_MAX, 0, (u64)-1); } @@ -4678,12 +4657,14 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans, trans->allocating_chunk = false; spin_lock(&space_info->lock); - if (ret < 0 && ret != -ENOSPC) - goto out; - if (ret) - space_info->full = 1; - else + if (ret < 0) { + if (ret == -ENOSPC) + space_info->full = 1; + else + goto out; + } else { ret = 1; + } space_info->force_alloc = CHUNK_ALLOC_NO_FORCE; out: @@ -4792,7 +4773,7 @@ static void btrfs_writeback_inodes_sb_nr(struct btrfs_fs_info *fs_info, * the filesystem is readonly(all dirty pages are written to * the disk). */ - btrfs_start_delalloc_roots(fs_info, 0, nr_items); + btrfs_start_delalloc_roots(fs_info, nr_items); if (!current->journal_info) btrfs_wait_ordered_roots(fs_info, nr_items, 0, (u64)-1); } @@ -5949,44 +5930,6 @@ void btrfs_trans_release_chunk_metadata(struct btrfs_trans_handle *trans) trans->chunk_bytes_reserved = 0; } -/* Can only return 0 or -ENOSPC */ -int btrfs_orphan_reserve_metadata(struct btrfs_trans_handle *trans, - struct btrfs_inode *inode) -{ - struct btrfs_fs_info *fs_info = btrfs_sb(inode->vfs_inode.i_sb); - struct btrfs_root *root = inode->root; - /* - * We always use trans->block_rsv here as we will have reserved space - * for our orphan when starting the transaction, using get_block_rsv() - * here will sometimes make us choose the wrong block rsv as we could be - * doing a reloc inode for a non refcounted root. - */ - struct btrfs_block_rsv *src_rsv = trans->block_rsv; - struct btrfs_block_rsv *dst_rsv = root->orphan_block_rsv; - - /* - * We need to hold space in order to delete our orphan item once we've - * added it, so this takes the reservation so we can release it later - * when we are truly done with the orphan item. - */ - u64 num_bytes = btrfs_calc_trans_metadata_size(fs_info, 1); - - trace_btrfs_space_reservation(fs_info, "orphan", btrfs_ino(inode), - num_bytes, 1); - return btrfs_block_rsv_migrate(src_rsv, dst_rsv, num_bytes, 1); -} - -void btrfs_orphan_release_metadata(struct btrfs_inode *inode) -{ - struct btrfs_fs_info *fs_info = btrfs_sb(inode->vfs_inode.i_sb); - struct btrfs_root *root = inode->root; - u64 num_bytes = btrfs_calc_trans_metadata_size(fs_info, 1); - - trace_btrfs_space_reservation(fs_info, "orphan", btrfs_ino(inode), - num_bytes, 0); - btrfs_block_rsv_release(fs_info, root->orphan_block_rsv, num_bytes); -} - /* * btrfs_subvolume_reserve_metadata() - reserve space for subvolume operation * root: the root of the parent directory @@ -6004,7 +5947,6 @@ void btrfs_orphan_release_metadata(struct btrfs_inode *inode) int btrfs_subvolume_reserve_metadata(struct btrfs_root *root, struct btrfs_block_rsv *rsv, int items, - u64 *qgroup_reserved, bool use_global_rsv) { u64 num_bytes; @@ -6022,8 +5964,6 @@ int btrfs_subvolume_reserve_metadata(struct btrfs_root *root, num_bytes = 0; } - *qgroup_reserved = num_bytes; - num_bytes = btrfs_calc_trans_metadata_size(fs_info, items); rsv->space_info = __find_space_info(fs_info, BTRFS_BLOCK_GROUP_METADATA); @@ -6033,8 +5973,8 @@ int btrfs_subvolume_reserve_metadata(struct btrfs_root *root, if (ret == -ENOSPC && use_global_rsv) ret = btrfs_block_rsv_migrate(global_rsv, rsv, num_bytes, 1); - if (ret && *qgroup_reserved) - btrfs_qgroup_free_meta_prealloc(root, *qgroup_reserved); + if (ret && num_bytes) + btrfs_qgroup_free_meta_prealloc(root, num_bytes); return ret; } @@ -6354,6 +6294,7 @@ static int update_block_group(struct btrfs_trans_handle *trans, spin_lock(&info->unused_bgs_lock); if (list_empty(&cache->bg_list)) { btrfs_get_block_group(cache); + trace_btrfs_add_unused_block_group(cache); list_add_tail(&cache->bg_list, &info->unused_bgs); } @@ -6511,6 +6452,7 @@ int btrfs_exclude_logged_extents(struct btrfs_fs_info *fs_info, struct btrfs_key key; int found_type; int i; + int ret = 0; if (!btrfs_fs_incompat(fs_info, MIXED_GROUPS)) return 0; @@ -6527,10 +6469,12 @@ int btrfs_exclude_logged_extents(struct btrfs_fs_info *fs_info, continue; key.objectid = btrfs_file_extent_disk_bytenr(eb, item); key.offset = btrfs_file_extent_disk_num_bytes(eb, item); - __exclude_logged_extent(fs_info, key.objectid, key.offset); + ret = __exclude_logged_extent(fs_info, key.objectid, key.offset); + if (ret) + break; } - return 0; + return ret; } static void @@ -7122,7 +7066,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, } } - ret = add_to_free_space_tree(trans, info, bytenr, num_bytes); + ret = add_to_free_space_tree(trans, bytenr, num_bytes); if (ret) { btrfs_abort_transaction(trans, ret); goto out; @@ -7266,7 +7210,7 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans, } out: if (pin) - add_pinned_bytes(fs_info, buf->len, btrfs_header_level(buf), + add_pinned_bytes(fs_info, buf->len, true, root->root_key.objectid); if (last_ref) { @@ -7320,8 +7264,11 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, &old_ref_mod, &new_ref_mod); } - if (ret == 0 && old_ref_mod >= 0 && new_ref_mod < 0) - add_pinned_bytes(fs_info, num_bytes, owner, root_objectid); + if (ret == 0 && old_ref_mod >= 0 && new_ref_mod < 0) { + bool metadata = owner < BTRFS_FIRST_FREE_OBJECTID; + + add_pinned_bytes(fs_info, num_bytes, metadata, root_objectid); + } return ret; } @@ -7373,24 +7320,6 @@ wait_block_group_cache_done(struct btrfs_block_group_cache *cache) return ret; } -static const char *btrfs_raid_type_names[BTRFS_NR_RAID_TYPES] = { - [BTRFS_RAID_RAID10] = "raid10", - [BTRFS_RAID_RAID1] = "raid1", - [BTRFS_RAID_DUP] = "dup", - [BTRFS_RAID_RAID0] = "raid0", - [BTRFS_RAID_SINGLE] = "single", - [BTRFS_RAID_RAID5] = "raid5", - [BTRFS_RAID_RAID6] = "raid6", -}; - -static const char *get_raid_name(enum btrfs_raid_types type) -{ - if (type >= BTRFS_NR_RAID_TYPES) - return NULL; - - return btrfs_raid_type_names[type]; -} - enum btrfs_loop_type { LOOP_CACHING_NOWAIT = 0, LOOP_CACHING_WAIT = 1, @@ -7662,7 +7591,7 @@ static noinline int find_free_extent(struct btrfs_fs_info *fs_info, if (offset) { /* we have a block, we're done */ spin_unlock(&last_ptr->refill_lock); - trace_btrfs_reserve_extent_cluster(fs_info, + trace_btrfs_reserve_extent_cluster( used_block_group, search_start, num_bytes); if (used_block_group != block_group) { @@ -7735,7 +7664,7 @@ static noinline int find_free_extent(struct btrfs_fs_info *fs_info, if (offset) { /* we found one, proceed */ spin_unlock(&last_ptr->refill_lock); - trace_btrfs_reserve_extent_cluster(fs_info, + trace_btrfs_reserve_extent_cluster( block_group, search_start, num_bytes); goto checks; @@ -7835,8 +7764,7 @@ static noinline int find_free_extent(struct btrfs_fs_info *fs_info, ins->objectid = search_start; ins->offset = num_bytes; - trace_btrfs_reserve_extent(fs_info, block_group, - search_start, num_bytes); + trace_btrfs_reserve_extent(block_group, search_start, num_bytes); btrfs_release_block_group(block_group, delalloc); break; loop: @@ -8184,8 +8112,7 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans, btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_free_path(path); - ret = remove_from_free_space_tree(trans, fs_info, ins->objectid, - ins->offset); + ret = remove_from_free_space_tree(trans, ins->objectid, ins->offset); if (ret) return ret; @@ -8200,37 +8127,52 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans, } static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, - u64 parent, u64 root_objectid, - u64 flags, struct btrfs_disk_key *key, - int level, struct btrfs_key *ins) + struct btrfs_delayed_ref_node *node, + struct btrfs_delayed_extent_op *extent_op) { + struct btrfs_fs_info *fs_info = trans->fs_info; int ret; struct btrfs_extent_item *extent_item; + struct btrfs_key extent_key; struct btrfs_tree_block_info *block_info; struct btrfs_extent_inline_ref *iref; struct btrfs_path *path; struct extent_buffer *leaf; + struct btrfs_delayed_tree_ref *ref; u32 size = sizeof(*extent_item) + sizeof(*iref); - u64 num_bytes = ins->offset; + u64 num_bytes; + u64 flags = extent_op->flags_to_set; bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA); - if (!skinny_metadata) + ref = btrfs_delayed_node_to_tree_ref(node); + + extent_key.objectid = node->bytenr; + if (skinny_metadata) { + extent_key.offset = ref->level; + extent_key.type = BTRFS_METADATA_ITEM_KEY; + num_bytes = fs_info->nodesize; + } else { + extent_key.offset = node->num_bytes; + extent_key.type = BTRFS_EXTENT_ITEM_KEY; size += sizeof(*block_info); + num_bytes = node->num_bytes; + } path = btrfs_alloc_path(); if (!path) { - btrfs_free_and_pin_reserved_extent(fs_info, ins->objectid, + btrfs_free_and_pin_reserved_extent(fs_info, + extent_key.objectid, fs_info->nodesize); return -ENOMEM; } path->leave_spinning = 1; ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path, - ins, size); + &extent_key, size); if (ret) { btrfs_free_path(path); - btrfs_free_and_pin_reserved_extent(fs_info, ins->objectid, + btrfs_free_and_pin_reserved_extent(fs_info, + extent_key.objectid, fs_info->nodesize); return ret; } @@ -8245,42 +8187,41 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, if (skinny_metadata) { iref = (struct btrfs_extent_inline_ref *)(extent_item + 1); - num_bytes = fs_info->nodesize; } else { block_info = (struct btrfs_tree_block_info *)(extent_item + 1); - btrfs_set_tree_block_key(leaf, block_info, key); - btrfs_set_tree_block_level(leaf, block_info, level); + btrfs_set_tree_block_key(leaf, block_info, &extent_op->key); + btrfs_set_tree_block_level(leaf, block_info, ref->level); iref = (struct btrfs_extent_inline_ref *)(block_info + 1); } - if (parent > 0) { + if (node->type == BTRFS_SHARED_BLOCK_REF_KEY) { BUG_ON(!(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)); btrfs_set_extent_inline_ref_type(leaf, iref, BTRFS_SHARED_BLOCK_REF_KEY); - btrfs_set_extent_inline_ref_offset(leaf, iref, parent); + btrfs_set_extent_inline_ref_offset(leaf, iref, ref->parent); } else { btrfs_set_extent_inline_ref_type(leaf, iref, BTRFS_TREE_BLOCK_REF_KEY); - btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid); + btrfs_set_extent_inline_ref_offset(leaf, iref, ref->root); } btrfs_mark_buffer_dirty(leaf); btrfs_free_path(path); - ret = remove_from_free_space_tree(trans, fs_info, ins->objectid, + ret = remove_from_free_space_tree(trans, extent_key.objectid, num_bytes); if (ret) return ret; - ret = update_block_group(trans, fs_info, ins->objectid, + ret = update_block_group(trans, fs_info, extent_key.objectid, fs_info->nodesize, 1); if (ret) { /* -ENOENT, logic error */ btrfs_err(fs_info, "update block group failed for %llu %llu", - ins->objectid, ins->offset); + extent_key.objectid, extent_key.offset); BUG(); } - trace_btrfs_reserved_extent_alloc(fs_info, ins->objectid, + trace_btrfs_reserved_extent_alloc(fs_info, extent_key.objectid, fs_info->nodesize); return ret; } @@ -10173,8 +10114,7 @@ int btrfs_read_block_groups(struct btrfs_fs_info *info) } else if (btrfs_block_group_used(&cache->item) == 0) { cache->last_byte_to_unpin = (u64)-1; cache->cached = BTRFS_CACHE_FINISHED; - add_new_free_space(cache, info, - found_key.objectid, + add_new_free_space(cache, found_key.objectid, found_key.objectid + found_key.offset); free_excluded_extents(info, cache); @@ -10204,6 +10144,7 @@ int btrfs_read_block_groups(struct btrfs_fs_info *info) /* Should always be true but just in case. */ if (list_empty(&cache->bg_list)) { btrfs_get_block_group(cache); + trace_btrfs_add_unused_block_group(cache); list_add_tail(&cache->bg_list, &info->unused_bgs); } @@ -10269,7 +10210,7 @@ void btrfs_create_pending_block_groups(struct btrfs_trans_handle *trans) key.offset); if (ret) btrfs_abort_transaction(trans, ret); - add_block_group_free_space(trans, fs_info, block_group); + add_block_group_free_space(trans, block_group); /* already aborted the transaction if it failed. */ next: list_del_init(&block_group->bg_list); @@ -10310,7 +10251,7 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans, return ret; } - add_new_free_space(cache, fs_info, chunk_offset, chunk_offset + size); + add_new_free_space(cache, chunk_offset, chunk_offset + size); free_excluded_extents(fs_info, cache); @@ -10391,6 +10332,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, BUG_ON(!block_group); BUG_ON(!block_group->ro); + trace_btrfs_remove_block_group(block_group); /* * Free the reserved super bytes from this block group before * remove it. @@ -10648,7 +10590,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, mutex_unlock(&fs_info->chunk_mutex); - ret = remove_block_group_free_space(trans, fs_info, block_group); + ret = remove_block_group_free_space(trans, block_group); if (ret) goto out; @@ -10755,6 +10697,7 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info) * the ro check in case balance is currently acting on * this block group. */ + trace_btrfs_skip_unused_block_group(block_group); spin_unlock(&block_group->lock); up_write(&space_info->groups_sem); goto next; @@ -10877,7 +10820,6 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info) int btrfs_init_space_info(struct btrfs_fs_info *fs_info) { - struct btrfs_space_info *space_info; struct btrfs_super_block *disk_super; u64 features; u64 flags; @@ -10893,21 +10835,21 @@ int btrfs_init_space_info(struct btrfs_fs_info *fs_info) mixed = 1; flags = BTRFS_BLOCK_GROUP_SYSTEM; - ret = create_space_info(fs_info, flags, &space_info); + ret = create_space_info(fs_info, flags); if (ret) goto out; if (mixed) { flags = BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA; - ret = create_space_info(fs_info, flags, &space_info); + ret = create_space_info(fs_info, flags); } else { flags = BTRFS_BLOCK_GROUP_METADATA; - ret = create_space_info(fs_info, flags, &space_info); + ret = create_space_info(fs_info, flags); if (ret) goto out; flags = BTRFS_BLOCK_GROUP_DATA; - ret = create_space_info(fs_info, flags, &space_info); + ret = create_space_info(fs_info, flags); } out: return ret; @@ -11092,12 +11034,7 @@ int btrfs_trim_fs(struct btrfs_fs_info *fs_info, struct fstrim_range *range) void btrfs_end_write_no_snapshotting(struct btrfs_root *root) { percpu_counter_dec(&root->subv_writers->counter); - /* - * Make sure counter is updated before we wake up waiters. - */ - smp_mb(); - if (waitqueue_active(&root->subv_writers->wait)) - wake_up(&root->subv_writers->wait); + cond_wake_up(&root->subv_writers->wait); } int btrfs_start_write_no_snapshotting(struct btrfs_root *root) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 56d32bb462f941d6890f8a6b80226ee3f1381eb0..51fc015c7d2c69bd198e94c3ffc4b2ef267f7ef2 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4106,14 +4106,13 @@ int extent_write_locked_range(struct inode *inode, u64 start, u64 end, return ret; } -int extent_writepages(struct extent_io_tree *tree, - struct address_space *mapping, +int extent_writepages(struct address_space *mapping, struct writeback_control *wbc) { int ret = 0; struct extent_page_data epd = { .bio = NULL, - .tree = tree, + .tree = &BTRFS_I(mapping->host)->io_tree, .extent_locked = 0, .sync_io = wbc->sync_mode == WB_SYNC_ALL, }; @@ -4123,9 +4122,8 @@ int extent_writepages(struct extent_io_tree *tree, return ret; } -int extent_readpages(struct extent_io_tree *tree, - struct address_space *mapping, - struct list_head *pages, unsigned nr_pages) +int extent_readpages(struct address_space *mapping, struct list_head *pages, + unsigned nr_pages) { struct bio *bio = NULL; unsigned page_idx; @@ -4133,6 +4131,7 @@ int extent_readpages(struct extent_io_tree *tree, struct page *pagepool[16]; struct page *page; struct extent_map *em_cached = NULL; + struct extent_io_tree *tree = &BTRFS_I(mapping->host)->io_tree; int nr = 0; u64 prev_em_start = (u64)-1; @@ -4199,8 +4198,7 @@ int extent_invalidatepage(struct extent_io_tree *tree, * are locked or under IO and drops the related state bits if it is safe * to drop the page. */ -static int try_release_extent_state(struct extent_map_tree *map, - struct extent_io_tree *tree, +static int try_release_extent_state(struct extent_io_tree *tree, struct page *page, gfp_t mask) { u64 start = page_offset(page); @@ -4235,13 +4233,13 @@ static int try_release_extent_state(struct extent_map_tree *map, * in the range corresponding to the page, both state records and extent * map records are removed */ -int try_release_extent_mapping(struct extent_map_tree *map, - struct extent_io_tree *tree, struct page *page, - gfp_t mask) +int try_release_extent_mapping(struct page *page, gfp_t mask) { struct extent_map *em; u64 start = page_offset(page); u64 end = start + PAGE_SIZE - 1; + struct extent_io_tree *tree = &BTRFS_I(page->mapping->host)->io_tree; + struct extent_map_tree *map = &BTRFS_I(page->mapping->host)->extent_tree; if (gfpflags_allow_blocking(mask) && page->mapping->host->i_size > SZ_16M) { @@ -4275,7 +4273,7 @@ int try_release_extent_mapping(struct extent_map_tree *map, free_extent_map(em); } } - return try_release_extent_state(map, tree, page, mask); + return try_release_extent_state(tree, page, mask); } /* @@ -5617,46 +5615,6 @@ void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src, } } -void le_bitmap_set(u8 *map, unsigned int start, int len) -{ - u8 *p = map + BIT_BYTE(start); - const unsigned int size = start + len; - int bits_to_set = BITS_PER_BYTE - (start % BITS_PER_BYTE); - u8 mask_to_set = BITMAP_FIRST_BYTE_MASK(start); - - while (len - bits_to_set >= 0) { - *p |= mask_to_set; - len -= bits_to_set; - bits_to_set = BITS_PER_BYTE; - mask_to_set = ~0; - p++; - } - if (len) { - mask_to_set &= BITMAP_LAST_BYTE_MASK(size); - *p |= mask_to_set; - } -} - -void le_bitmap_clear(u8 *map, unsigned int start, int len) -{ - u8 *p = map + BIT_BYTE(start); - const unsigned int size = start + len; - int bits_to_clear = BITS_PER_BYTE - (start % BITS_PER_BYTE); - u8 mask_to_clear = BITMAP_FIRST_BYTE_MASK(start); - - while (len - bits_to_clear >= 0) { - *p &= ~mask_to_clear; - len -= bits_to_clear; - bits_to_clear = BITS_PER_BYTE; - mask_to_clear = ~0; - p++; - } - if (len) { - mask_to_clear &= BITMAP_LAST_BYTE_MASK(size); - *p &= ~mask_to_clear; - } -} - /* * eb_bitmap_offset() - calculate the page and offset of the byte containing the * given bit number diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index a53009694b164dad592013603b8d83bb6df999e9..0bfd4aeb822dd95f42e1cbe9ecc76fb5dc06a41d 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -79,14 +79,6 @@ #define BITMAP_LAST_BYTE_MASK(nbits) \ (BYTE_MASK >> (-(nbits) & (BITS_PER_BYTE - 1))) -static inline int le_test_bit(int nr, const u8 *addr) -{ - return 1U & (addr[BIT_BYTE(nr)] >> (nr & (BITS_PER_BYTE-1))); -} - -void le_bitmap_set(u8 *map, unsigned int start, int len); -void le_bitmap_clear(u8 *map, unsigned int start, int len); - struct extent_state; struct btrfs_root; struct btrfs_inode; @@ -278,9 +270,7 @@ typedef struct extent_map *(get_extent_t)(struct btrfs_inode *inode, int create); void extent_io_tree_init(struct extent_io_tree *tree, void *private_data); -int try_release_extent_mapping(struct extent_map_tree *map, - struct extent_io_tree *tree, struct page *page, - gfp_t mask); +int try_release_extent_mapping(struct page *page, gfp_t mask); int try_release_extent_buffer(struct page *page); int lock_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, struct extent_state **cached); @@ -421,14 +411,12 @@ int extent_invalidatepage(struct extent_io_tree *tree, int extent_write_full_page(struct page *page, struct writeback_control *wbc); int extent_write_locked_range(struct inode *inode, u64 start, u64 end, int mode); -int extent_writepages(struct extent_io_tree *tree, - struct address_space *mapping, +int extent_writepages(struct address_space *mapping, struct writeback_control *wbc); int btree_write_cache_pages(struct address_space *mapping, struct writeback_control *wbc); -int extent_readpages(struct extent_io_tree *tree, - struct address_space *mapping, - struct list_head *pages, unsigned nr_pages); +int extent_readpages(struct address_space *mapping, struct list_head *pages, + unsigned nr_pages); int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len); void set_page_extent_mapped(struct page *page); diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 1b8a078f92eb0b25400e596566331aaf9647150d..6648d55e5339622c1a835873fe58d01d7aa24f30 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -518,6 +518,7 @@ static noinline int merge_extent_mapping(struct extent_map_tree *em_tree, /** * btrfs_add_extent_mapping - add extent mapping into em_tree + * @fs_info - used for tracepoint * @em_tree - the extent tree into which we want to insert the extent mapping * @em_in - extent we are inserting * @start - start of the logical range btrfs_get_extent() is requesting @@ -535,7 +536,8 @@ static noinline int merge_extent_mapping(struct extent_map_tree *em_tree, * Return 0 on success, otherwise -EEXIST. * */ -int btrfs_add_extent_mapping(struct extent_map_tree *em_tree, +int btrfs_add_extent_mapping(struct btrfs_fs_info *fs_info, + struct extent_map_tree *em_tree, struct extent_map **em_in, u64 start, u64 len) { int ret; @@ -553,7 +555,7 @@ int btrfs_add_extent_mapping(struct extent_map_tree *em_tree, existing = search_extent_mapping(em_tree, start, len); - trace_btrfs_handle_em_exist(existing, em, start, len); + trace_btrfs_handle_em_exist(fs_info, existing, em, start, len); /* * existing will always be non-NULL, since there must be diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h index 5fcb80a6ce370b2669c12dc1a6b7401697019c74..25d985e7532a6d5cad742d29b91fb15a40d4c938 100644 --- a/fs/btrfs/extent_map.h +++ b/fs/btrfs/extent_map.h @@ -92,7 +92,8 @@ int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len, u64 gen void clear_em_logging(struct extent_map_tree *tree, struct extent_map *em); struct extent_map *search_extent_mapping(struct extent_map_tree *tree, u64 start, u64 len); -int btrfs_add_extent_mapping(struct extent_map_tree *em_tree, +int btrfs_add_extent_mapping(struct btrfs_fs_info *fs_info, + struct extent_map_tree *em_tree, struct extent_map **em_in, u64 start, u64 len); #endif diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index e5b569bebc731e816622b40f0c5f531b3d45b295..d5f80cb300bea03a2a417fd5cc03d55c960f8314 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -253,10 +253,8 @@ int btrfs_truncate_free_space_cache(struct btrfs_trans_handle *trans, truncate_pagecache(inode, 0); /* - * We don't need an orphan item because truncating the free space cache - * will never be split across transactions. - * We don't need to check for -EAGAIN because we're a free space - * cache inode + * We skip the throttling logic for free space cache inodes, so we don't + * need to check for -EAGAIN. */ ret = btrfs_truncate_inode_items(trans, root, inode, 0, BTRFS_EXTENT_DATA_KEY); diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 32a0f6cb55948aac654bc30e986adbeeaf5b451b..b5950aacd6975170c59dc3de29ed216eb9d49ffc 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -12,7 +12,6 @@ #include "transaction.h" static int __add_block_group_free_space(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path); @@ -45,11 +44,10 @@ void set_free_space_tree_thresholds(struct btrfs_block_group_cache *cache) } static int add_new_free_space_info(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path) { - struct btrfs_root *root = fs_info->free_space_root; + struct btrfs_root *root = trans->fs_info->free_space_root; struct btrfs_free_space_info *info; struct btrfs_key key; struct extent_buffer *leaf; @@ -138,10 +136,11 @@ static inline u32 free_space_bitmap_size(u64 size, u32 sectorsize) return DIV_ROUND_UP((u32)div_u64(size, sectorsize), BITS_PER_BYTE); } -static u8 *alloc_bitmap(u32 bitmap_size) +static unsigned long *alloc_bitmap(u32 bitmap_size) { - u8 *ret; + unsigned long *ret; unsigned int nofs_flag; + u32 bitmap_rounded_size = round_up(bitmap_size, sizeof(unsigned long)); /* * GFP_NOFS doesn't work with kvmalloc(), but we really can't recurse @@ -152,21 +151,42 @@ static u8 *alloc_bitmap(u32 bitmap_size) * know that recursion is unsafe. */ nofs_flag = memalloc_nofs_save(); - ret = kvzalloc(bitmap_size, GFP_KERNEL); + ret = kvzalloc(bitmap_rounded_size, GFP_KERNEL); memalloc_nofs_restore(nofs_flag); return ret; } +static void le_bitmap_set(unsigned long *map, unsigned int start, int len) +{ + u8 *p = ((u8 *)map) + BIT_BYTE(start); + const unsigned int size = start + len; + int bits_to_set = BITS_PER_BYTE - (start % BITS_PER_BYTE); + u8 mask_to_set = BITMAP_FIRST_BYTE_MASK(start); + + while (len - bits_to_set >= 0) { + *p |= mask_to_set; + len -= bits_to_set; + bits_to_set = BITS_PER_BYTE; + mask_to_set = ~0; + p++; + } + if (len) { + mask_to_set &= BITMAP_LAST_BYTE_MASK(size); + *p |= mask_to_set; + } +} + int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path) { + struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_root *root = fs_info->free_space_root; struct btrfs_free_space_info *info; struct btrfs_key key, found_key; struct extent_buffer *leaf; - u8 *bitmap, *bitmap_cursor; + unsigned long *bitmap; + char *bitmap_cursor; u64 start, end; u64 bitmap_range, i; u32 bitmap_size, flags, expected_extent_count; @@ -255,7 +275,7 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans, goto out; } - bitmap_cursor = bitmap; + bitmap_cursor = (char *)bitmap; bitmap_range = fs_info->sectorsize * BTRFS_FREE_SPACE_BITMAP_BITS; i = start; while (i < end) { @@ -296,21 +316,18 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans, } int convert_free_space_to_extents(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path) { + struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_root *root = fs_info->free_space_root; struct btrfs_free_space_info *info; struct btrfs_key key, found_key; struct extent_buffer *leaf; - u8 *bitmap; + unsigned long *bitmap; u64 start, end; - /* Initialize to silence GCC. */ - u64 extent_start = 0; - u64 offset; u32 bitmap_size, flags, expected_extent_count; - int prev_bit = 0, bit, bitnr; + unsigned long nrbits, start_bit, end_bit; u32 extent_count = 0; int done = 0, nr; int ret; @@ -348,7 +365,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans, break; } else if (found_key.type == BTRFS_FREE_SPACE_BITMAP_KEY) { unsigned long ptr; - u8 *bitmap_cursor; + char *bitmap_cursor; u32 bitmap_pos, data_size; ASSERT(found_key.objectid >= start); @@ -358,7 +375,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans, bitmap_pos = div_u64(found_key.objectid - start, fs_info->sectorsize * BITS_PER_BYTE); - bitmap_cursor = bitmap + bitmap_pos; + bitmap_cursor = ((char *)bitmap) + bitmap_pos; data_size = free_space_bitmap_size(found_key.offset, fs_info->sectorsize); @@ -392,32 +409,16 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans, btrfs_mark_buffer_dirty(leaf); btrfs_release_path(path); - offset = start; - bitnr = 0; - while (offset < end) { - bit = !!le_test_bit(bitnr, bitmap); - if (prev_bit == 0 && bit == 1) { - extent_start = offset; - } else if (prev_bit == 1 && bit == 0) { - key.objectid = extent_start; - key.type = BTRFS_FREE_SPACE_EXTENT_KEY; - key.offset = offset - extent_start; - - ret = btrfs_insert_empty_item(trans, root, path, &key, 0); - if (ret) - goto out; - btrfs_release_path(path); + nrbits = div_u64(block_group->key.offset, block_group->fs_info->sectorsize); + start_bit = find_next_bit_le(bitmap, nrbits, 0); - extent_count++; - } - prev_bit = bit; - offset += fs_info->sectorsize; - bitnr++; - } - if (prev_bit == 1) { - key.objectid = extent_start; + while (start_bit < nrbits) { + end_bit = find_next_zero_bit_le(bitmap, nrbits, start_bit); + ASSERT(start_bit < end_bit); + + key.objectid = start + start_bit * block_group->fs_info->sectorsize; key.type = BTRFS_FREE_SPACE_EXTENT_KEY; - key.offset = end - extent_start; + key.offset = (end_bit - start_bit) * block_group->fs_info->sectorsize; ret = btrfs_insert_empty_item(trans, root, path, &key, 0); if (ret) @@ -425,6 +426,8 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans, btrfs_release_path(path); extent_count++; + + start_bit = find_next_bit_le(bitmap, nrbits, end_bit); } if (extent_count != expected_extent_count) { @@ -446,7 +449,6 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans, } static int update_free_space_extent_count(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, int new_extents) @@ -459,7 +461,8 @@ static int update_free_space_extent_count(struct btrfs_trans_handle *trans, if (new_extents == 0) return 0; - info = search_free_space_info(trans, fs_info, block_group, path, 1); + info = search_free_space_info(trans, trans->fs_info, block_group, path, + 1); if (IS_ERR(info)) { ret = PTR_ERR(info); goto out; @@ -474,12 +477,10 @@ static int update_free_space_extent_count(struct btrfs_trans_handle *trans, if (!(flags & BTRFS_FREE_SPACE_USING_BITMAPS) && extent_count > block_group->bitmap_high_thresh) { - ret = convert_free_space_to_bitmaps(trans, fs_info, block_group, - path); + ret = convert_free_space_to_bitmaps(trans, block_group, path); } else if ((flags & BTRFS_FREE_SPACE_USING_BITMAPS) && extent_count < block_group->bitmap_low_thresh) { - ret = convert_free_space_to_extents(trans, fs_info, block_group, - path); + ret = convert_free_space_to_extents(trans, block_group, path); } out: @@ -576,12 +577,11 @@ static int free_space_next_bitmap(struct btrfs_trans_handle *trans, * the bitmap. */ static int modify_free_space_bitmap(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, u64 start, u64 size, int remove) { - struct btrfs_root *root = fs_info->free_space_root; + struct btrfs_root *root = block_group->fs_info->free_space_root; struct btrfs_key key; u64 end = start + size; u64 cur_start, cur_size; @@ -682,7 +682,7 @@ static int modify_free_space_bitmap(struct btrfs_trans_handle *trans, } btrfs_release_path(path); - ret = update_free_space_extent_count(trans, fs_info, block_group, path, + ret = update_free_space_extent_count(trans, block_group, path, new_extents); out: @@ -690,12 +690,11 @@ static int modify_free_space_bitmap(struct btrfs_trans_handle *trans, } static int remove_free_space_extent(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, u64 start, u64 size) { - struct btrfs_root *root = fs_info->free_space_root; + struct btrfs_root *root = trans->fs_info->free_space_root; struct btrfs_key key; u64 found_start, found_end; u64 end = start + size; @@ -769,7 +768,7 @@ static int remove_free_space_extent(struct btrfs_trans_handle *trans, } btrfs_release_path(path); - ret = update_free_space_extent_count(trans, fs_info, block_group, path, + ret = update_free_space_extent_count(trans, block_group, path, new_extents); out: @@ -777,7 +776,6 @@ static int remove_free_space_extent(struct btrfs_trans_handle *trans, } int __remove_from_free_space_tree(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, u64 start, u64 size) { @@ -786,36 +784,35 @@ int __remove_from_free_space_tree(struct btrfs_trans_handle *trans, int ret; if (block_group->needs_free_space) { - ret = __add_block_group_free_space(trans, fs_info, block_group, - path); + ret = __add_block_group_free_space(trans, block_group, path); if (ret) return ret; } - info = search_free_space_info(NULL, fs_info, block_group, path, 0); + info = search_free_space_info(NULL, trans->fs_info, block_group, path, + 0); if (IS_ERR(info)) return PTR_ERR(info); flags = btrfs_free_space_flags(path->nodes[0], info); btrfs_release_path(path); if (flags & BTRFS_FREE_SPACE_USING_BITMAPS) { - return modify_free_space_bitmap(trans, fs_info, block_group, - path, start, size, 1); + return modify_free_space_bitmap(trans, block_group, path, + start, size, 1); } else { - return remove_free_space_extent(trans, fs_info, block_group, - path, start, size); + return remove_free_space_extent(trans, block_group, path, + start, size); } } int remove_from_free_space_tree(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, u64 start, u64 size) { struct btrfs_block_group_cache *block_group; struct btrfs_path *path; int ret; - if (!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) + if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE)) return 0; path = btrfs_alloc_path(); @@ -824,7 +821,7 @@ int remove_from_free_space_tree(struct btrfs_trans_handle *trans, goto out; } - block_group = btrfs_lookup_block_group(fs_info, start); + block_group = btrfs_lookup_block_group(trans->fs_info, start); if (!block_group) { ASSERT(0); ret = -ENOENT; @@ -832,8 +829,8 @@ int remove_from_free_space_tree(struct btrfs_trans_handle *trans, } mutex_lock(&block_group->free_space_lock); - ret = __remove_from_free_space_tree(trans, fs_info, block_group, path, - start, size); + ret = __remove_from_free_space_tree(trans, block_group, path, start, + size); mutex_unlock(&block_group->free_space_lock); btrfs_put_block_group(block_group); @@ -845,12 +842,11 @@ int remove_from_free_space_tree(struct btrfs_trans_handle *trans, } static int add_free_space_extent(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, u64 start, u64 size) { - struct btrfs_root *root = fs_info->free_space_root; + struct btrfs_root *root = trans->fs_info->free_space_root; struct btrfs_key key, new_key; u64 found_start, found_end; u64 end = start + size; @@ -965,7 +961,7 @@ static int add_free_space_extent(struct btrfs_trans_handle *trans, goto out; btrfs_release_path(path); - ret = update_free_space_extent_count(trans, fs_info, block_group, path, + ret = update_free_space_extent_count(trans, block_group, path, new_extents); out: @@ -973,17 +969,16 @@ static int add_free_space_extent(struct btrfs_trans_handle *trans, } int __add_to_free_space_tree(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, u64 start, u64 size) { + struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_free_space_info *info; u32 flags; int ret; if (block_group->needs_free_space) { - ret = __add_block_group_free_space(trans, fs_info, block_group, - path); + ret = __add_block_group_free_space(trans, block_group, path); if (ret) return ret; } @@ -995,23 +990,22 @@ int __add_to_free_space_tree(struct btrfs_trans_handle *trans, btrfs_release_path(path); if (flags & BTRFS_FREE_SPACE_USING_BITMAPS) { - return modify_free_space_bitmap(trans, fs_info, block_group, - path, start, size, 0); + return modify_free_space_bitmap(trans, block_group, path, + start, size, 0); } else { - return add_free_space_extent(trans, fs_info, block_group, path, - start, size); + return add_free_space_extent(trans, block_group, path, start, + size); } } int add_to_free_space_tree(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, u64 start, u64 size) { struct btrfs_block_group_cache *block_group; struct btrfs_path *path; int ret; - if (!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) + if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE)) return 0; path = btrfs_alloc_path(); @@ -1020,7 +1014,7 @@ int add_to_free_space_tree(struct btrfs_trans_handle *trans, goto out; } - block_group = btrfs_lookup_block_group(fs_info, start); + block_group = btrfs_lookup_block_group(trans->fs_info, start); if (!block_group) { ASSERT(0); ret = -ENOENT; @@ -1028,8 +1022,7 @@ int add_to_free_space_tree(struct btrfs_trans_handle *trans, } mutex_lock(&block_group->free_space_lock); - ret = __add_to_free_space_tree(trans, fs_info, block_group, path, start, - size); + ret = __add_to_free_space_tree(trans, block_group, path, start, size); mutex_unlock(&block_group->free_space_lock); btrfs_put_block_group(block_group); @@ -1046,10 +1039,9 @@ int add_to_free_space_tree(struct btrfs_trans_handle *trans, * through the normal add/remove hooks. */ static int populate_free_space_tree(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group) { - struct btrfs_root *extent_root = fs_info->extent_root; + struct btrfs_root *extent_root = trans->fs_info->extent_root; struct btrfs_path *path, *path2; struct btrfs_key key; u64 start, end; @@ -1066,7 +1058,7 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans, return -ENOMEM; } - ret = add_new_free_space_info(trans, fs_info, block_group, path2); + ret = add_new_free_space_info(trans, block_group, path2); if (ret) goto out; @@ -1099,7 +1091,7 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans, break; if (start < key.objectid) { - ret = __add_to_free_space_tree(trans, fs_info, + ret = __add_to_free_space_tree(trans, block_group, path2, start, key.objectid - @@ -1109,7 +1101,7 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans, } start = key.objectid; if (key.type == BTRFS_METADATA_ITEM_KEY) - start += fs_info->nodesize; + start += trans->fs_info->nodesize; else start += key.offset; } else if (key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) { @@ -1124,8 +1116,8 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans, break; } if (start < end) { - ret = __add_to_free_space_tree(trans, fs_info, block_group, - path2, start, end - start); + ret = __add_to_free_space_tree(trans, block_group, path2, + start, end - start); if (ret) goto out_locked; } @@ -1165,7 +1157,7 @@ int btrfs_create_free_space_tree(struct btrfs_fs_info *fs_info) while (node) { block_group = rb_entry(node, struct btrfs_block_group_cache, cache_node); - ret = populate_free_space_tree(trans, fs_info, block_group); + ret = populate_free_space_tree(trans, block_group); if (ret) goto abort; node = rb_next(node); @@ -1269,7 +1261,6 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info) } static int __add_block_group_free_space(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path) { @@ -1277,19 +1268,19 @@ static int __add_block_group_free_space(struct btrfs_trans_handle *trans, block_group->needs_free_space = 0; - ret = add_new_free_space_info(trans, fs_info, block_group, path); + ret = add_new_free_space_info(trans, block_group, path); if (ret) return ret; - return __add_to_free_space_tree(trans, fs_info, block_group, path, + return __add_to_free_space_tree(trans, block_group, path, block_group->key.objectid, block_group->key.offset); } int add_block_group_free_space(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group) { + struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_path *path = NULL; int ret = 0; @@ -1306,7 +1297,7 @@ int add_block_group_free_space(struct btrfs_trans_handle *trans, goto out; } - ret = __add_block_group_free_space(trans, fs_info, block_group, path); + ret = __add_block_group_free_space(trans, block_group, path); out: btrfs_free_path(path); @@ -1317,10 +1308,9 @@ int add_block_group_free_space(struct btrfs_trans_handle *trans, } int remove_block_group_free_space(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group) { - struct btrfs_root *root = fs_info->free_space_root; + struct btrfs_root *root = trans->fs_info->free_space_root; struct btrfs_path *path; struct btrfs_key key, found_key; struct extent_buffer *leaf; @@ -1328,7 +1318,7 @@ int remove_block_group_free_space(struct btrfs_trans_handle *trans, int done = 0, nr; int ret; - if (!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) + if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE)) return 0; if (block_group->needs_free_space) { @@ -1439,7 +1429,6 @@ static int load_free_space_bitmaps(struct btrfs_caching_control *caching_ctl, extent_start = offset; } else if (prev_bit == 1 && bit == 0) { total_found += add_new_free_space(block_group, - fs_info, extent_start, offset); if (total_found > CACHING_CTL_WAKE_UP) { @@ -1453,8 +1442,8 @@ static int load_free_space_bitmaps(struct btrfs_caching_control *caching_ctl, } } if (prev_bit == 1) { - total_found += add_new_free_space(block_group, fs_info, - extent_start, end); + total_found += add_new_free_space(block_group, extent_start, + end); extent_count++; } @@ -1511,8 +1500,7 @@ static int load_free_space_extents(struct btrfs_caching_control *caching_ctl, caching_ctl->progress = key.objectid; - total_found += add_new_free_space(block_group, fs_info, - key.objectid, + total_found += add_new_free_space(block_group, key.objectid, key.objectid + key.offset); if (total_found > CACHING_CTL_WAKE_UP) { total_found = 0; diff --git a/fs/btrfs/free-space-tree.h b/fs/btrfs/free-space-tree.h index 874b4feecad2022868f0306062166858e0443bf4..3133651d7d706acf0214cae98408604cb888c96a 100644 --- a/fs/btrfs/free-space-tree.h +++ b/fs/btrfs/free-space-tree.h @@ -19,16 +19,12 @@ int btrfs_create_free_space_tree(struct btrfs_fs_info *fs_info); int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info); int load_free_space_tree(struct btrfs_caching_control *caching_ctl); int add_block_group_free_space(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group); int remove_block_group_free_space(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group); int add_to_free_space_tree(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, u64 start, u64 size); int remove_from_free_space_tree(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, u64 start, u64 size); #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS @@ -38,19 +34,15 @@ search_free_space_info(struct btrfs_trans_handle *trans, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, int cow); int __add_to_free_space_tree(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, u64 start, u64 size); int __remove_from_free_space_tree(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path, u64 start, u64 size); int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path); int convert_free_space_to_extents(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, struct btrfs_path *path); int free_space_test_bit(struct btrfs_block_group_cache *block_group, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 0b86cf10cf2ac79732ea548bbe970819ea758d4e..89b2082017830942e040063a2ca000d1a10f75df 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1018,8 +1018,10 @@ static noinline int cow_file_range(struct inode *inode, ram_size, /* ram_bytes */ BTRFS_COMPRESS_NONE, /* compress_type */ BTRFS_ORDERED_REGULAR /* type */); - if (IS_ERR(em)) + if (IS_ERR(em)) { + ret = PTR_ERR(em); goto out_reserve; + } free_extent_map(em); ret = btrfs_add_ordered_extent(inode, start, ins.objectid, @@ -1156,13 +1158,10 @@ static noinline void async_cow_submit(struct btrfs_work *work) nr_pages = (async_cow->end - async_cow->start + PAGE_SIZE) >> PAGE_SHIFT; - /* - * atomic_sub_return implies a barrier for waitqueue_active - */ + /* atomic_sub_return implies a barrier */ if (atomic_sub_return(nr_pages, &fs_info->async_delalloc_pages) < - 5 * SZ_1M && - waitqueue_active(&fs_info->async_submit_wait)) - wake_up(&fs_info->async_submit_wait); + 5 * SZ_1M) + cond_wake_up_nomb(&fs_info->async_submit_wait); if (async_cow->inode) submit_compressed_extents(async_cow->inode, async_cow); @@ -1373,6 +1372,13 @@ static noinline int run_delalloc_nocow(struct inode *inode, btrfs_file_extent_encryption(leaf, fi) || btrfs_file_extent_other_encoding(leaf, fi)) goto out_check; + /* + * Do the same check as in btrfs_cross_ref_exist but + * without the unnecessary search. + */ + if (btrfs_file_extent_generation(leaf, fi) <= + btrfs_root_last_snapshot(&root->root_item)) + goto out_check; if (extent_type == BTRFS_FILE_EXTENT_REG && !force) goto out_check; if (btrfs_extent_readonly(fs_info, disk_bytenr)) @@ -1754,6 +1760,7 @@ void __btrfs_del_delalloc_inode(struct btrfs_root *root, &inode->runtime_flags); root->nr_delalloc_inodes--; if (!root->nr_delalloc_inodes) { + ASSERT(list_empty(&root->delalloc_inodes)); spin_lock(&fs_info->delalloc_root_lock); BUG_ON(list_empty(&root->delalloc_root)); list_del_init(&root->delalloc_root); @@ -3158,6 +3165,9 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent) /* once for the tree */ btrfs_put_ordered_extent(ordered_extent); + /* Try to release some metadata so we don't get an OOM but don't wait */ + btrfs_btree_balance_dirty_nodelay(fs_info); + return ret; } @@ -3300,177 +3310,31 @@ void btrfs_run_delayed_iputs(struct btrfs_fs_info *fs_info) } /* - * This is called in transaction commit time. If there are no orphan - * files in the subvolume, it removes orphan item and frees block_rsv - * structure. - */ -void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans, - struct btrfs_root *root) -{ - struct btrfs_fs_info *fs_info = root->fs_info; - struct btrfs_block_rsv *block_rsv; - int ret; - - if (atomic_read(&root->orphan_inodes) || - root->orphan_cleanup_state != ORPHAN_CLEANUP_DONE) - return; - - spin_lock(&root->orphan_lock); - if (atomic_read(&root->orphan_inodes)) { - spin_unlock(&root->orphan_lock); - return; - } - - if (root->orphan_cleanup_state != ORPHAN_CLEANUP_DONE) { - spin_unlock(&root->orphan_lock); - return; - } - - block_rsv = root->orphan_block_rsv; - root->orphan_block_rsv = NULL; - spin_unlock(&root->orphan_lock); - - if (test_bit(BTRFS_ROOT_ORPHAN_ITEM_INSERTED, &root->state) && - btrfs_root_refs(&root->root_item) > 0) { - ret = btrfs_del_orphan_item(trans, fs_info->tree_root, - root->root_key.objectid); - if (ret) - btrfs_abort_transaction(trans, ret); - else - clear_bit(BTRFS_ROOT_ORPHAN_ITEM_INSERTED, - &root->state); - } - - if (block_rsv) { - WARN_ON(block_rsv->size > 0); - btrfs_free_block_rsv(fs_info, block_rsv); - } -} - -/* - * This creates an orphan entry for the given inode in case something goes - * wrong in the middle of an unlink/truncate. - * - * NOTE: caller of this function should reserve 5 units of metadata for - * this function. + * This creates an orphan entry for the given inode in case something goes wrong + * in the middle of an unlink. */ int btrfs_orphan_add(struct btrfs_trans_handle *trans, - struct btrfs_inode *inode) + struct btrfs_inode *inode) { - struct btrfs_fs_info *fs_info = btrfs_sb(inode->vfs_inode.i_sb); - struct btrfs_root *root = inode->root; - struct btrfs_block_rsv *block_rsv = NULL; - int reserve = 0; - bool insert = false; int ret; - if (!root->orphan_block_rsv) { - block_rsv = btrfs_alloc_block_rsv(fs_info, - BTRFS_BLOCK_RSV_TEMP); - if (!block_rsv) - return -ENOMEM; - } - - if (!test_and_set_bit(BTRFS_INODE_HAS_ORPHAN_ITEM, - &inode->runtime_flags)) - insert = true; - - if (!test_and_set_bit(BTRFS_INODE_ORPHAN_META_RESERVED, - &inode->runtime_flags)) - reserve = 1; - - spin_lock(&root->orphan_lock); - /* If someone has created ->orphan_block_rsv, be happy to use it. */ - if (!root->orphan_block_rsv) { - root->orphan_block_rsv = block_rsv; - } else if (block_rsv) { - btrfs_free_block_rsv(fs_info, block_rsv); - block_rsv = NULL; - } - - if (insert) - atomic_inc(&root->orphan_inodes); - spin_unlock(&root->orphan_lock); - - /* grab metadata reservation from transaction handle */ - if (reserve) { - ret = btrfs_orphan_reserve_metadata(trans, inode); - ASSERT(!ret); - if (ret) { - /* - * dec doesn't need spin_lock as ->orphan_block_rsv - * would be released only if ->orphan_inodes is - * zero. - */ - atomic_dec(&root->orphan_inodes); - clear_bit(BTRFS_INODE_ORPHAN_META_RESERVED, - &inode->runtime_flags); - if (insert) - clear_bit(BTRFS_INODE_HAS_ORPHAN_ITEM, - &inode->runtime_flags); - return ret; - } - } - - /* insert an orphan item to track this unlinked/truncated file */ - if (insert) { - ret = btrfs_insert_orphan_item(trans, root, btrfs_ino(inode)); - if (ret) { - if (reserve) { - clear_bit(BTRFS_INODE_ORPHAN_META_RESERVED, - &inode->runtime_flags); - btrfs_orphan_release_metadata(inode); - } - /* - * btrfs_orphan_commit_root may race with us and set - * ->orphan_block_rsv to zero, in order to avoid that, - * decrease ->orphan_inodes after everything is done. - */ - atomic_dec(&root->orphan_inodes); - if (ret != -EEXIST) { - clear_bit(BTRFS_INODE_HAS_ORPHAN_ITEM, - &inode->runtime_flags); - btrfs_abort_transaction(trans, ret); - return ret; - } - } - ret = 0; + ret = btrfs_insert_orphan_item(trans, inode->root, btrfs_ino(inode)); + if (ret && ret != -EEXIST) { + btrfs_abort_transaction(trans, ret); + return ret; } return 0; } /* - * We have done the truncate/delete so we can go ahead and remove the orphan - * item for this particular inode. + * We have done the delete so we can go ahead and remove the orphan item for + * this particular inode. */ static int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct btrfs_inode *inode) { - struct btrfs_root *root = inode->root; - int delete_item = 0; - int ret = 0; - - if (test_and_clear_bit(BTRFS_INODE_HAS_ORPHAN_ITEM, - &inode->runtime_flags)) - delete_item = 1; - - if (delete_item && trans) - ret = btrfs_del_orphan_item(trans, root, btrfs_ino(inode)); - - if (test_and_clear_bit(BTRFS_INODE_ORPHAN_META_RESERVED, - &inode->runtime_flags)) - btrfs_orphan_release_metadata(inode); - - /* - * btrfs_orphan_commit_root may race with us and set ->orphan_block_rsv - * to zero, in order to avoid that, decrease ->orphan_inodes after - * everything is done. - */ - if (delete_item) - atomic_dec(&root->orphan_inodes); - - return ret; + return btrfs_del_orphan_item(trans, inode->root, btrfs_ino(inode)); } /* @@ -3486,7 +3350,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) struct btrfs_trans_handle *trans; struct inode *inode; u64 last_objectid = 0; - int ret = 0, nr_unlink = 0, nr_truncate = 0; + int ret = 0, nr_unlink = 0; if (cmpxchg(&root->orphan_cleanup_state, 0, ORPHAN_CLEANUP_STARTED)) return 0; @@ -3586,12 +3450,31 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) key.offset = found_key.objectid - 1; continue; } + } + /* - * Inode is already gone but the orphan item is still there, - * kill the orphan item. + * If we have an inode with links, there are a couple of + * possibilities. Old kernels (before v3.12) used to create an + * orphan item for truncate indicating that there were possibly + * extent items past i_size that needed to be deleted. In v3.12, + * truncate was changed to update i_size in sync with the extent + * items, but the (useless) orphan item was still created. Since + * v4.18, we don't create the orphan item for truncate at all. + * + * So, this item could mean that we need to do a truncate, but + * only if this filesystem was last used on a pre-v3.12 kernel + * and was not cleanly unmounted. The odds of that are quite + * slim, and it's a pain to do the truncate now, so just delete + * the orphan item. + * + * It's also possible that this orphan item was supposed to be + * deleted but wasn't. The inode number may have been reused, + * but either way, we can delete the orphan item. */ - if (ret == -ENOENT) { + if (ret == -ENOENT || inode->i_nlink) { + if (!ret) + iput(inode); trans = btrfs_start_transaction(root, 1); if (IS_ERR(trans)) { ret = PTR_ERR(trans); @@ -3607,42 +3490,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) continue; } - /* - * add this inode to the orphan list so btrfs_orphan_del does - * the proper thing when we hit it - */ - set_bit(BTRFS_INODE_HAS_ORPHAN_ITEM, - &BTRFS_I(inode)->runtime_flags); - atomic_inc(&root->orphan_inodes); - - /* if we have links, this was a truncate, lets do that */ - if (inode->i_nlink) { - if (WARN_ON(!S_ISREG(inode->i_mode))) { - iput(inode); - continue; - } - nr_truncate++; - - /* 1 for the orphan item deletion. */ - trans = btrfs_start_transaction(root, 1); - if (IS_ERR(trans)) { - iput(inode); - ret = PTR_ERR(trans); - goto out; - } - ret = btrfs_orphan_add(trans, BTRFS_I(inode)); - btrfs_end_transaction(trans); - if (ret) { - iput(inode); - goto out; - } - - ret = btrfs_truncate(inode, false); - if (ret) - btrfs_orphan_del(NULL, BTRFS_I(inode)); - } else { - nr_unlink++; - } + nr_unlink++; /* this will do delete_inode and everything for us */ iput(inode); @@ -3654,12 +3502,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) root->orphan_cleanup_state = ORPHAN_CLEANUP_DONE; - if (root->orphan_block_rsv) - btrfs_block_rsv_release(fs_info, root->orphan_block_rsv, - (u64)-1); - - if (root->orphan_block_rsv || - test_bit(BTRFS_ROOT_ORPHAN_ITEM_INSERTED, &root->state)) { + if (test_bit(BTRFS_ROOT_ORPHAN_ITEM_INSERTED, &root->state)) { trans = btrfs_join_transaction(root); if (!IS_ERR(trans)) btrfs_end_transaction(trans); @@ -3667,8 +3510,6 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) if (nr_unlink) btrfs_debug(fs_info, "unlinked %d orphans", nr_unlink); - if (nr_truncate) - btrfs_debug(fs_info, "truncated %d orphans", nr_truncate); out: if (ret) @@ -3931,7 +3772,7 @@ static int btrfs_read_locked_inode(struct inode *inode) break; } - btrfs_update_iflags(inode); + btrfs_sync_inode_flags_to_i_flags(inode); return 0; make_bad: @@ -4245,7 +4086,7 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry) return ret; } -int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, +static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct inode *dir, u64 objectid, const char *name, int name_len) @@ -4326,6 +4167,262 @@ int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, return ret; } +/* + * Helper to check if the subvolume references other subvolumes or if it's + * default. + */ +static noinline int may_destroy_subvol(struct btrfs_root *root) +{ + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_path *path; + struct btrfs_dir_item *di; + struct btrfs_key key; + u64 dir_id; + int ret; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + /* Make sure this root isn't set as the default subvol */ + dir_id = btrfs_super_root_dir(fs_info->super_copy); + di = btrfs_lookup_dir_item(NULL, fs_info->tree_root, path, + dir_id, "default", 7, 0); + if (di && !IS_ERR(di)) { + btrfs_dir_item_key_to_cpu(path->nodes[0], di, &key); + if (key.objectid == root->root_key.objectid) { + ret = -EPERM; + btrfs_err(fs_info, + "deleting default subvolume %llu is not allowed", + key.objectid); + goto out; + } + btrfs_release_path(path); + } + + key.objectid = root->root_key.objectid; + key.type = BTRFS_ROOT_REF_KEY; + key.offset = (u64)-1; + + ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, path, 0, 0); + if (ret < 0) + goto out; + BUG_ON(ret == 0); + + ret = 0; + if (path->slots[0] > 0) { + path->slots[0]--; + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + if (key.objectid == root->root_key.objectid && + key.type == BTRFS_ROOT_REF_KEY) + ret = -ENOTEMPTY; + } +out: + btrfs_free_path(path); + return ret; +} + +/* Delete all dentries for inodes belonging to the root */ +static void btrfs_prune_dentries(struct btrfs_root *root) +{ + struct btrfs_fs_info *fs_info = root->fs_info; + struct rb_node *node; + struct rb_node *prev; + struct btrfs_inode *entry; + struct inode *inode; + u64 objectid = 0; + + if (!test_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state)) + WARN_ON(btrfs_root_refs(&root->root_item) != 0); + + spin_lock(&root->inode_lock); +again: + node = root->inode_tree.rb_node; + prev = NULL; + while (node) { + prev = node; + entry = rb_entry(node, struct btrfs_inode, rb_node); + + if (objectid < btrfs_ino(BTRFS_I(&entry->vfs_inode))) + node = node->rb_left; + else if (objectid > btrfs_ino(BTRFS_I(&entry->vfs_inode))) + node = node->rb_right; + else + break; + } + if (!node) { + while (prev) { + entry = rb_entry(prev, struct btrfs_inode, rb_node); + if (objectid <= btrfs_ino(BTRFS_I(&entry->vfs_inode))) { + node = prev; + break; + } + prev = rb_next(prev); + } + } + while (node) { + entry = rb_entry(node, struct btrfs_inode, rb_node); + objectid = btrfs_ino(BTRFS_I(&entry->vfs_inode)) + 1; + inode = igrab(&entry->vfs_inode); + if (inode) { + spin_unlock(&root->inode_lock); + if (atomic_read(&inode->i_count) > 1) + d_prune_aliases(inode); + /* + * btrfs_drop_inode will have it removed from the inode + * cache when its usage count hits zero. + */ + iput(inode); + cond_resched(); + spin_lock(&root->inode_lock); + goto again; + } + + if (cond_resched_lock(&root->inode_lock)) + goto again; + + node = rb_next(node); + } + spin_unlock(&root->inode_lock); +} + +int btrfs_delete_subvolume(struct inode *dir, struct dentry *dentry) +{ + struct btrfs_fs_info *fs_info = btrfs_sb(dentry->d_sb); + struct btrfs_root *root = BTRFS_I(dir)->root; + struct inode *inode = d_inode(dentry); + struct btrfs_root *dest = BTRFS_I(inode)->root; + struct btrfs_trans_handle *trans; + struct btrfs_block_rsv block_rsv; + u64 root_flags; + int ret; + int err; + + /* + * Don't allow to delete a subvolume with send in progress. This is + * inside the inode lock so the error handling that has to drop the bit + * again is not run concurrently. + */ + spin_lock(&dest->root_item_lock); + root_flags = btrfs_root_flags(&dest->root_item); + if (dest->send_in_progress == 0) { + btrfs_set_root_flags(&dest->root_item, + root_flags | BTRFS_ROOT_SUBVOL_DEAD); + spin_unlock(&dest->root_item_lock); + } else { + spin_unlock(&dest->root_item_lock); + btrfs_warn(fs_info, + "attempt to delete subvolume %llu during send", + dest->root_key.objectid); + return -EPERM; + } + + down_write(&fs_info->subvol_sem); + + err = may_destroy_subvol(dest); + if (err) + goto out_up_write; + + btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP); + /* + * One for dir inode, + * two for dir entries, + * two for root ref/backref. + */ + err = btrfs_subvolume_reserve_metadata(root, &block_rsv, 5, true); + if (err) + goto out_up_write; + + trans = btrfs_start_transaction(root, 0); + if (IS_ERR(trans)) { + err = PTR_ERR(trans); + goto out_release; + } + trans->block_rsv = &block_rsv; + trans->bytes_reserved = block_rsv.size; + + btrfs_record_snapshot_destroy(trans, BTRFS_I(dir)); + + ret = btrfs_unlink_subvol(trans, root, dir, + dest->root_key.objectid, + dentry->d_name.name, + dentry->d_name.len); + if (ret) { + err = ret; + btrfs_abort_transaction(trans, ret); + goto out_end_trans; + } + + btrfs_record_root_in_trans(trans, dest); + + memset(&dest->root_item.drop_progress, 0, + sizeof(dest->root_item.drop_progress)); + dest->root_item.drop_level = 0; + btrfs_set_root_refs(&dest->root_item, 0); + + if (!test_and_set_bit(BTRFS_ROOT_ORPHAN_ITEM_INSERTED, &dest->state)) { + ret = btrfs_insert_orphan_item(trans, + fs_info->tree_root, + dest->root_key.objectid); + if (ret) { + btrfs_abort_transaction(trans, ret); + err = ret; + goto out_end_trans; + } + } + + ret = btrfs_uuid_tree_remove(trans, dest->root_item.uuid, + BTRFS_UUID_KEY_SUBVOL, + dest->root_key.objectid); + if (ret && ret != -ENOENT) { + btrfs_abort_transaction(trans, ret); + err = ret; + goto out_end_trans; + } + if (!btrfs_is_empty_uuid(dest->root_item.received_uuid)) { + ret = btrfs_uuid_tree_remove(trans, + dest->root_item.received_uuid, + BTRFS_UUID_KEY_RECEIVED_SUBVOL, + dest->root_key.objectid); + if (ret && ret != -ENOENT) { + btrfs_abort_transaction(trans, ret); + err = ret; + goto out_end_trans; + } + } + +out_end_trans: + trans->block_rsv = NULL; + trans->bytes_reserved = 0; + ret = btrfs_end_transaction(trans); + if (ret && !err) + err = ret; + inode->i_flags |= S_DEAD; +out_release: + btrfs_subvolume_release_metadata(fs_info, &block_rsv); +out_up_write: + up_write(&fs_info->subvol_sem); + if (err) { + spin_lock(&dest->root_item_lock); + root_flags = btrfs_root_flags(&dest->root_item); + btrfs_set_root_flags(&dest->root_item, + root_flags & ~BTRFS_ROOT_SUBVOL_DEAD); + spin_unlock(&dest->root_item_lock); + } else { + d_invalidate(dentry); + btrfs_prune_dentries(dest); + ASSERT(dest->send_in_progress == 0); + + /* the last ref */ + if (dest->ino_cache_inode) { + iput(dest->ino_cache_inode); + dest->ino_cache_inode = NULL; + } + } + + return err; +} + static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) { struct inode *inode = d_inode(dentry); @@ -4337,7 +4434,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) if (inode->i_size > BTRFS_EMPTY_DIR_SIZE) return -ENOTEMPTY; if (btrfs_ino(BTRFS_I(inode)) == BTRFS_FIRST_FREE_OBJECTID) - return -EPERM; + return btrfs_delete_subvolume(dir, dentry); trans = __unlink_start_trans(dir); if (IS_ERR(trans)) @@ -4449,7 +4546,6 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, int pending_del_slot = 0; int extent_type = -1; int ret; - int err = 0; u64 ino = btrfs_ino(BTRFS_I(inode)); u64 bytes_deleted = 0; bool be_nice = false; @@ -4501,22 +4597,19 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, * up a huge file in a single leaf. Most of the time that * bytes_deleted is > 0, it will be huge by the time we get here */ - if (be_nice && bytes_deleted > SZ_32M) { - if (btrfs_should_end_transaction(trans)) { - err = -EAGAIN; - goto error; - } + if (be_nice && bytes_deleted > SZ_32M && + btrfs_should_end_transaction(trans)) { + ret = -EAGAIN; + goto out; } - path->leave_spinning = 1; ret = btrfs_search_slot(trans, root, &key, path, -1, 1); - if (ret < 0) { - err = ret; + if (ret < 0) goto out; - } if (ret > 0) { + ret = 0; /* there are no items in the tree for us to truncate, we're * done */ @@ -4627,7 +4720,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, * We have to bail so the last_size is set to * just before this extent. */ - err = NEED_TRUNCATE_BLOCK; + ret = NEED_TRUNCATE_BLOCK; break; } @@ -4666,7 +4759,10 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, extent_num_bytes, 0, btrfs_header_owner(leaf), ino, extent_offset); - BUG_ON(ret); + if (ret) { + btrfs_abort_transaction(trans, ret); + break; + } if (btrfs_should_throttle_delayed_refs(trans, fs_info)) btrfs_async_run_delayed_refs(fs_info, trans->delayed_ref_updates * 2, @@ -4694,7 +4790,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, pending_del_nr); if (ret) { btrfs_abort_transaction(trans, ret); - goto error; + break; } pending_del_nr = 0; } @@ -4705,8 +4801,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, trans->delayed_ref_updates = 0; ret = btrfs_run_delayed_refs(trans, updates * 2); - if (ret && !err) - err = ret; + if (ret) + break; } } /* @@ -4714,8 +4810,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, * and let the transaction restart */ if (should_end) { - err = -EAGAIN; - goto error; + ret = -EAGAIN; + break; } goto search_again; } else { @@ -4723,32 +4819,37 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, } } out: - if (pending_del_nr) { - ret = btrfs_del_items(trans, root, path, pending_del_slot, + if (ret >= 0 && pending_del_nr) { + int err; + + err = btrfs_del_items(trans, root, path, pending_del_slot, pending_del_nr); - if (ret) - btrfs_abort_transaction(trans, ret); + if (err) { + btrfs_abort_transaction(trans, err); + ret = err; + } } -error: if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) { ASSERT(last_size >= new_size); - if (!err && last_size > new_size) + if (!ret && last_size > new_size) last_size = new_size; btrfs_ordered_update_i_size(inode, last_size, NULL); } btrfs_free_path(path); - if (be_nice && bytes_deleted > SZ_32M) { + if (be_nice && bytes_deleted > SZ_32M && (ret >= 0 || ret == -EAGAIN)) { unsigned long updates = trans->delayed_ref_updates; + int err; + if (updates) { trans->delayed_ref_updates = 0; - ret = btrfs_run_delayed_refs(trans, updates * 2); - if (ret && !err) - err = ret; + err = btrfs_run_delayed_refs(trans, updates * 2); + if (err) + ret = err; } } - return err; + return ret; } /* @@ -5090,30 +5191,6 @@ static int btrfs_setsize(struct inode *inode, struct iattr *attr) set_bit(BTRFS_INODE_ORDERED_DATA_CLOSE, &BTRFS_I(inode)->runtime_flags); - /* - * 1 for the orphan item we're going to add - * 1 for the orphan item deletion. - */ - trans = btrfs_start_transaction(root, 2); - if (IS_ERR(trans)) - return PTR_ERR(trans); - - /* - * We need to do this in case we fail at _any_ point during the - * actual truncate. Once we do the truncate_setsize we could - * invalidate pages which forces any outstanding ordered io to - * be instantly completed which will give us extents that need - * to be truncated. If we fail to get an orphan inode down we - * could have left over extents that were never meant to live, - * so we need to guarantee from this point on that everything - * will be consistent. - */ - ret = btrfs_orphan_add(trans, BTRFS_I(inode)); - btrfs_end_transaction(trans); - if (ret) - return ret; - - /* we don't support swapfiles, so vmtruncate shouldn't fail */ truncate_setsize(inode, newsize); /* Disable nonlocked read DIO to avoid the end less truncate */ @@ -5125,29 +5202,16 @@ static int btrfs_setsize(struct inode *inode, struct iattr *attr) if (ret && inode->i_nlink) { int err; - /* To get a stable disk_i_size */ - err = btrfs_wait_ordered_range(inode, 0, (u64)-1); - if (err) { - btrfs_orphan_del(NULL, BTRFS_I(inode)); - return err; - } - /* - * failed to truncate, disk_i_size is only adjusted down - * as we remove extents, so it should represent the true - * size of the inode, so reset the in memory size and - * delete our orphan entry. + * Truncate failed, so fix up the in-memory size. We + * adjusted disk_i_size down as we removed extents, so + * wait for disk_i_size to be stable and then update the + * in-memory size to match. */ - trans = btrfs_join_transaction(root); - if (IS_ERR(trans)) { - btrfs_orphan_del(NULL, BTRFS_I(inode)); - return ret; - } - i_size_write(inode, BTRFS_I(inode)->disk_i_size); - err = btrfs_orphan_del(trans, BTRFS_I(inode)); + err = btrfs_wait_ordered_range(inode, 0, (u64)-1); if (err) - btrfs_abort_transaction(trans, err); - btrfs_end_transaction(trans); + return err; + i_size_write(inode, BTRFS_I(inode)->disk_i_size); } } @@ -5277,13 +5341,52 @@ static void evict_inode_truncate_pages(struct inode *inode) spin_unlock(&io_tree->lock); } +static struct btrfs_trans_handle *evict_refill_and_join(struct btrfs_root *root, + struct btrfs_block_rsv *rsv, + u64 min_size) +{ + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_block_rsv *global_rsv = &fs_info->global_block_rsv; + int failures = 0; + + for (;;) { + struct btrfs_trans_handle *trans; + int ret; + + ret = btrfs_block_rsv_refill(root, rsv, min_size, + BTRFS_RESERVE_FLUSH_LIMIT); + + if (ret && ++failures > 2) { + btrfs_warn(fs_info, + "could not allocate space for a delete; will truncate on mount"); + return ERR_PTR(-ENOSPC); + } + + trans = btrfs_join_transaction(root); + if (IS_ERR(trans) || !ret) + return trans; + + /* + * Try to steal from the global reserve if there is space for + * it. + */ + if (!btrfs_check_space_for_delayed_refs(trans, fs_info) && + !btrfs_block_rsv_migrate(global_rsv, rsv, min_size, 0)) + return trans; + + /* If not, commit and try again. */ + ret = btrfs_commit_transaction(trans); + if (ret) + return ERR_PTR(ret); + } +} + void btrfs_evict_inode(struct inode *inode) { struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); struct btrfs_trans_handle *trans; struct btrfs_root *root = BTRFS_I(inode)->root; - struct btrfs_block_rsv *rsv, *global_rsv; - int steal_from_global = 0; + struct btrfs_block_rsv *rsv; u64 min_size; int ret; @@ -5304,21 +5407,16 @@ void btrfs_evict_inode(struct inode *inode) btrfs_is_free_space_inode(BTRFS_I(inode)))) goto no_delete; - if (is_bad_inode(inode)) { - btrfs_orphan_del(NULL, BTRFS_I(inode)); + if (is_bad_inode(inode)) goto no_delete; - } /* do we really want it for ->i_nlink > 0 and zero btrfs_root_refs? */ if (!special_file(inode->i_mode)) btrfs_wait_ordered_range(inode, 0, (u64)-1); btrfs_free_io_failure_record(BTRFS_I(inode), 0, (u64)-1); - if (test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags)) { - BUG_ON(test_bit(BTRFS_INODE_HAS_ORPHAN_ITEM, - &BTRFS_I(inode)->runtime_flags)); + if (test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags)) goto no_delete; - } if (inode->i_nlink > 0) { BUG_ON(btrfs_root_refs(&root->root_item) != 0 && @@ -5327,130 +5425,63 @@ void btrfs_evict_inode(struct inode *inode) } ret = btrfs_commit_inode_delayed_inode(BTRFS_I(inode)); - if (ret) { - btrfs_orphan_del(NULL, BTRFS_I(inode)); + if (ret) goto no_delete; - } rsv = btrfs_alloc_block_rsv(fs_info, BTRFS_BLOCK_RSV_TEMP); - if (!rsv) { - btrfs_orphan_del(NULL, BTRFS_I(inode)); + if (!rsv) goto no_delete; - } rsv->size = min_size; rsv->failfast = 1; - global_rsv = &fs_info->global_block_rsv; btrfs_i_size_write(BTRFS_I(inode), 0); - /* - * This is a bit simpler than btrfs_truncate since we've already - * reserved our space for our orphan item in the unlink, so we just - * need to reserve some slack space in case we add bytes and update - * inode item when doing the truncate. - */ while (1) { - ret = btrfs_block_rsv_refill(root, rsv, min_size, - BTRFS_RESERVE_FLUSH_LIMIT); - - /* - * Try and steal from the global reserve since we will - * likely not use this space anyway, we want to try as - * hard as possible to get this to work. - */ - if (ret) - steal_from_global++; - else - steal_from_global = 0; - ret = 0; - - /* - * steal_from_global == 0: we reserved stuff, hooray! - * steal_from_global == 1: we didn't reserve stuff, boo! - * steal_from_global == 2: we've committed, still not a lot of - * room but maybe we'll have room in the global reserve this - * time. - * steal_from_global == 3: abandon all hope! - */ - if (steal_from_global > 2) { - btrfs_warn(fs_info, - "Could not get space for a delete, will truncate on mount %d", - ret); - btrfs_orphan_del(NULL, BTRFS_I(inode)); - btrfs_free_block_rsv(fs_info, rsv); - goto no_delete; - } - - trans = btrfs_join_transaction(root); - if (IS_ERR(trans)) { - btrfs_orphan_del(NULL, BTRFS_I(inode)); - btrfs_free_block_rsv(fs_info, rsv); - goto no_delete; - } - - /* - * We can't just steal from the global reserve, we need to make - * sure there is room to do it, if not we need to commit and try - * again. - */ - if (steal_from_global) { - if (!btrfs_check_space_for_delayed_refs(trans, fs_info)) - ret = btrfs_block_rsv_migrate(global_rsv, rsv, - min_size, 0); - else - ret = -ENOSPC; - } - - /* - * Couldn't steal from the global reserve, we have too much - * pending stuff built up, commit the transaction and try it - * again. - */ - if (ret) { - ret = btrfs_commit_transaction(trans); - if (ret) { - btrfs_orphan_del(NULL, BTRFS_I(inode)); - btrfs_free_block_rsv(fs_info, rsv); - goto no_delete; - } - continue; - } else { - steal_from_global = 0; - } + trans = evict_refill_and_join(root, rsv, min_size); + if (IS_ERR(trans)) + goto free_rsv; trans->block_rsv = rsv; ret = btrfs_truncate_inode_items(trans, root, inode, 0, 0); - if (ret != -ENOSPC && ret != -EAGAIN) - break; - trans->block_rsv = &fs_info->trans_block_rsv; btrfs_end_transaction(trans); - trans = NULL; btrfs_btree_balance_dirty(fs_info); + if (ret && ret != -ENOSPC && ret != -EAGAIN) + goto free_rsv; + else if (!ret) + break; } - btrfs_free_block_rsv(fs_info, rsv); - /* - * Errors here aren't a big deal, it just means we leave orphan items - * in the tree. They will be cleaned up on the next mount. + * Errors here aren't a big deal, it just means we leave orphan items in + * the tree. They will be cleaned up on the next mount. If the inode + * number gets reused, cleanup deletes the orphan item without doing + * anything, and unlink reuses the existing orphan item. + * + * If it turns out that we are dropping too many of these, we might want + * to add a mechanism for retrying these after a commit. */ - if (ret == 0) { - trans->block_rsv = root->orphan_block_rsv; + trans = evict_refill_and_join(root, rsv, min_size); + if (!IS_ERR(trans)) { + trans->block_rsv = rsv; btrfs_orphan_del(trans, BTRFS_I(inode)); - } else { - btrfs_orphan_del(NULL, BTRFS_I(inode)); + trans->block_rsv = &fs_info->trans_block_rsv; + btrfs_end_transaction(trans); } - trans->block_rsv = &fs_info->trans_block_rsv; if (!(root == fs_info->tree_root || root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID)) btrfs_return_ino(root, btrfs_ino(BTRFS_I(inode))); - btrfs_end_transaction(trans); - btrfs_btree_balance_dirty(fs_info); +free_rsv: + btrfs_free_block_rsv(fs_info, rsv); no_delete: + /* + * If we didn't successfully delete, the orphan item will still be in + * the tree and we'll retry on the next mount. Again, we might also want + * to retry these periodically in the future. + */ btrfs_remove_delayed_node(BTRFS_I(inode)); clear_inode(inode); } @@ -5612,84 +5643,21 @@ static void inode_tree_del(struct inode *inode) if (!RB_EMPTY_NODE(&BTRFS_I(inode)->rb_node)) { rb_erase(&BTRFS_I(inode)->rb_node, &root->inode_tree); RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node); - empty = RB_EMPTY_ROOT(&root->inode_tree); - } - spin_unlock(&root->inode_lock); - - if (empty && btrfs_root_refs(&root->root_item) == 0) { - synchronize_srcu(&fs_info->subvol_srcu); - spin_lock(&root->inode_lock); - empty = RB_EMPTY_ROOT(&root->inode_tree); - spin_unlock(&root->inode_lock); - if (empty) - btrfs_add_dead_root(root); - } -} - -void btrfs_invalidate_inodes(struct btrfs_root *root) -{ - struct btrfs_fs_info *fs_info = root->fs_info; - struct rb_node *node; - struct rb_node *prev; - struct btrfs_inode *entry; - struct inode *inode; - u64 objectid = 0; - - if (!test_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state)) - WARN_ON(btrfs_root_refs(&root->root_item) != 0); - - spin_lock(&root->inode_lock); -again: - node = root->inode_tree.rb_node; - prev = NULL; - while (node) { - prev = node; - entry = rb_entry(node, struct btrfs_inode, rb_node); - - if (objectid < btrfs_ino(BTRFS_I(&entry->vfs_inode))) - node = node->rb_left; - else if (objectid > btrfs_ino(BTRFS_I(&entry->vfs_inode))) - node = node->rb_right; - else - break; - } - if (!node) { - while (prev) { - entry = rb_entry(prev, struct btrfs_inode, rb_node); - if (objectid <= btrfs_ino(BTRFS_I(&entry->vfs_inode))) { - node = prev; - break; - } - prev = rb_next(prev); - } - } - while (node) { - entry = rb_entry(node, struct btrfs_inode, rb_node); - objectid = btrfs_ino(BTRFS_I(&entry->vfs_inode)) + 1; - inode = igrab(&entry->vfs_inode); - if (inode) { - spin_unlock(&root->inode_lock); - if (atomic_read(&inode->i_count) > 1) - d_prune_aliases(inode); - /* - * btrfs_drop_inode will have it removed from - * the inode cache when its usage count - * hits zero. - */ - iput(inode); - cond_resched(); - spin_lock(&root->inode_lock); - goto again; - } - - if (cond_resched_lock(&root->inode_lock)) - goto again; - - node = rb_next(node); + empty = RB_EMPTY_ROOT(&root->inode_tree); } spin_unlock(&root->inode_lock); + + if (empty && btrfs_root_refs(&root->root_item) == 0) { + synchronize_srcu(&fs_info->subvol_srcu); + spin_lock(&root->inode_lock); + empty = RB_EMPTY_ROOT(&root->inode_tree); + spin_unlock(&root->inode_lock); + if (empty) + btrfs_add_dead_root(root); + } } + static int btrfs_init_locked_inode(struct inode *inode, void *p) { struct btrfs_iget_args *args = p; @@ -5850,11 +5818,6 @@ static int btrfs_dentry_delete(const struct dentry *dentry) return 0; } -static void btrfs_dentry_release(struct dentry *dentry) -{ - kfree(dentry->d_fsdata); -} - static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { @@ -6270,7 +6233,7 @@ static void btrfs_inherit_iflags(struct inode *inode, struct inode *dir) BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM; } - btrfs_update_iflags(inode); + btrfs_sync_inode_flags_to_i_flags(inode); } static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans, @@ -6705,8 +6668,9 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir, * 2 items for inode and inode ref * 2 items for dir items * 1 item for parent inode + * 1 item for orphan item deletion if O_TMPFILE */ - trans = btrfs_start_transaction(root, 5); + trans = btrfs_start_transaction(root, inode->i_nlink ? 5 : 6); if (IS_ERR(trans)) { err = PTR_ERR(trans); trans = NULL; @@ -7083,7 +7047,7 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode, err = 0; write_lock(&em_tree->lock); - err = btrfs_add_extent_mapping(em_tree, &em, start, len); + err = btrfs_add_extent_mapping(fs_info, em_tree, &em, start, len); write_unlock(&em_tree->lock); out: @@ -7368,6 +7332,14 @@ noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len, btrfs_file_extent_other_encoding(leaf, fi)) goto out; + /* + * Do the same check as in btrfs_cross_ref_exist but without the + * unnecessary search. + */ + if (btrfs_file_extent_generation(leaf, fi) <= + btrfs_root_last_snapshot(&root->root_item)) + goto out; + backref_offset = btrfs_file_extent_offset(leaf, fi); if (orig_start) { @@ -7568,6 +7540,125 @@ static struct extent_map *create_io_em(struct inode *inode, u64 start, u64 len, return em; } + +static int btrfs_get_blocks_direct_read(struct extent_map *em, + struct buffer_head *bh_result, + struct inode *inode, + u64 start, u64 len) +{ + if (em->block_start == EXTENT_MAP_HOLE || + test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) + return -ENOENT; + + len = min(len, em->len - (start - em->start)); + + bh_result->b_blocknr = (em->block_start + (start - em->start)) >> + inode->i_blkbits; + bh_result->b_size = len; + bh_result->b_bdev = em->bdev; + set_buffer_mapped(bh_result); + + return 0; +} + +static int btrfs_get_blocks_direct_write(struct extent_map **map, + struct buffer_head *bh_result, + struct inode *inode, + struct btrfs_dio_data *dio_data, + u64 start, u64 len) +{ + struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); + struct extent_map *em = *map; + int ret = 0; + + /* + * We don't allocate a new extent in the following cases + * + * 1) The inode is marked as NODATACOW. In this case we'll just use the + * existing extent. + * 2) The extent is marked as PREALLOC. We're good to go here and can + * just use the extent. + * + */ + if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags) || + ((BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW) && + em->block_start != EXTENT_MAP_HOLE)) { + int type; + u64 block_start, orig_start, orig_block_len, ram_bytes; + + if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) + type = BTRFS_ORDERED_PREALLOC; + else + type = BTRFS_ORDERED_NOCOW; + len = min(len, em->len - (start - em->start)); + block_start = em->block_start + (start - em->start); + + if (can_nocow_extent(inode, start, &len, &orig_start, + &orig_block_len, &ram_bytes) == 1 && + btrfs_inc_nocow_writers(fs_info, block_start)) { + struct extent_map *em2; + + em2 = btrfs_create_dio_extent(inode, start, len, + orig_start, block_start, + len, orig_block_len, + ram_bytes, type); + btrfs_dec_nocow_writers(fs_info, block_start); + if (type == BTRFS_ORDERED_PREALLOC) { + free_extent_map(em); + *map = em = em2; + } + + if (em2 && IS_ERR(em2)) { + ret = PTR_ERR(em2); + goto out; + } + /* + * For inode marked NODATACOW or extent marked PREALLOC, + * use the existing or preallocated extent, so does not + * need to adjust btrfs_space_info's bytes_may_use. + */ + btrfs_free_reserved_data_space_noquota(inode, start, + len); + goto skip_cow; + } + } + + /* this will cow the extent */ + len = bh_result->b_size; + free_extent_map(em); + *map = em = btrfs_new_extent_direct(inode, start, len); + if (IS_ERR(em)) { + ret = PTR_ERR(em); + goto out; + } + + len = min(len, em->len - (start - em->start)); + +skip_cow: + bh_result->b_blocknr = (em->block_start + (start - em->start)) >> + inode->i_blkbits; + bh_result->b_size = len; + bh_result->b_bdev = em->bdev; + set_buffer_mapped(bh_result); + + if (!test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) + set_buffer_new(bh_result); + + /* + * Need to update the i_size under the extent lock so buffered + * readers will get the updated i_size when we unlock. + */ + if (!dio_data->overwrite && start + len > i_size_read(inode)) + i_size_write(inode, start + len); + + WARN_ON(dio_data->reserve < len); + dio_data->reserve -= len; + dio_data->unsubmitted_oe_range_end = start + len; + current->journal_info = dio_data; +out: + return ret; +} + static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { @@ -7636,116 +7727,36 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, goto unlock_err; } - /* Just a good old fashioned hole, return */ - if (!create && (em->block_start == EXTENT_MAP_HOLE || - test_bit(EXTENT_FLAG_PREALLOC, &em->flags))) { - free_extent_map(em); - goto unlock_err; - } - - /* - * We don't allocate a new extent in the following cases - * - * 1) The inode is marked as NODATACOW. In this case we'll just use the - * existing extent. - * 2) The extent is marked as PREALLOC. We're good to go here and can - * just use the extent. - * - */ - if (!create) { - len = min(len, em->len - (start - em->start)); - lockstart = start + len; - goto unlock; - } - - if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags) || - ((BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW) && - em->block_start != EXTENT_MAP_HOLE)) { - int type; - u64 block_start, orig_start, orig_block_len, ram_bytes; - - if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) - type = BTRFS_ORDERED_PREALLOC; - else - type = BTRFS_ORDERED_NOCOW; - len = min(len, em->len - (start - em->start)); - block_start = em->block_start + (start - em->start); - - if (can_nocow_extent(inode, start, &len, &orig_start, - &orig_block_len, &ram_bytes) == 1 && - btrfs_inc_nocow_writers(fs_info, block_start)) { - struct extent_map *em2; - - em2 = btrfs_create_dio_extent(inode, start, len, - orig_start, block_start, - len, orig_block_len, - ram_bytes, type); - btrfs_dec_nocow_writers(fs_info, block_start); - if (type == BTRFS_ORDERED_PREALLOC) { - free_extent_map(em); - em = em2; - } - if (em2 && IS_ERR(em2)) { - ret = PTR_ERR(em2); - goto unlock_err; - } - /* - * For inode marked NODATACOW or extent marked PREALLOC, - * use the existing or preallocated extent, so does not - * need to adjust btrfs_space_info's bytes_may_use. - */ - btrfs_free_reserved_data_space_noquota(inode, - start, len); - goto unlock; - } - } - - /* - * this will cow the extent, reset the len in case we changed - * it above - */ - len = bh_result->b_size; - free_extent_map(em); - em = btrfs_new_extent_direct(inode, start, len); - if (IS_ERR(em)) { - ret = PTR_ERR(em); - goto unlock_err; - } - len = min(len, em->len - (start - em->start)); -unlock: - bh_result->b_blocknr = (em->block_start + (start - em->start)) >> - inode->i_blkbits; - bh_result->b_size = len; - bh_result->b_bdev = em->bdev; - set_buffer_mapped(bh_result); if (create) { - if (!test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) - set_buffer_new(bh_result); + ret = btrfs_get_blocks_direct_write(&em, bh_result, inode, + dio_data, start, len); + if (ret < 0) + goto unlock_err; + /* clear and unlock the entire range */ + clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend, + unlock_bits, 1, 0, &cached_state); + } else { + ret = btrfs_get_blocks_direct_read(em, bh_result, inode, + start, len); + /* Can be negative only if we read from a hole */ + if (ret < 0) { + ret = 0; + free_extent_map(em); + goto unlock_err; + } /* - * Need to update the i_size under the extent lock so buffered - * readers will get the updated i_size when we unlock. + * We need to unlock only the end area that we aren't using. + * The rest is going to be unlocked by the endio routine. */ - if (!dio_data->overwrite && start + len > i_size_read(inode)) - i_size_write(inode, start + len); - - WARN_ON(dio_data->reserve < len); - dio_data->reserve -= len; - dio_data->unsubmitted_oe_range_end = start + len; - current->journal_info = dio_data; - } - - /* - * In the case of write we need to clear and unlock the entire range, - * in the case of read we need to unlock only the end area that we - * aren't using if there is any left over space. - */ - if (lockstart < lockend) { - clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, - lockend, unlock_bits, 1, 0, - &cached_state); - } else { - free_extent_state(cached_state); + lockstart = start + bh_result->b_size; + if (lockstart < lockend) { + clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, + lockend, unlock_bits, 1, 0, + &cached_state); + } else { + free_extent_state(cached_state); + } } free_extent_map(em); @@ -8131,7 +8142,6 @@ static void __endio_write_update_ordered(struct inode *inode, u64 ordered_offset = offset; u64 ordered_bytes = bytes; u64 last_offset; - int ret; if (btrfs_is_free_space_inode(BTRFS_I(inode))) { wq = fs_info->endio_freespace_worker; @@ -8141,32 +8151,31 @@ static void __endio_write_update_ordered(struct inode *inode, func = btrfs_endio_write_helper; } -again: - last_offset = ordered_offset; - ret = btrfs_dec_test_first_ordered_pending(inode, &ordered, - &ordered_offset, - ordered_bytes, - uptodate); - if (!ret) - goto out_test; - - btrfs_init_work(&ordered->work, func, finish_ordered_fn, NULL, NULL); - btrfs_queue_work(wq, &ordered->work); -out_test: - /* - * If btrfs_dec_test_ordered_pending does not find any ordered extent - * in the range, we can exit. - */ - if (ordered_offset == last_offset) - return; - /* - * our bio might span multiple ordered extents. If we haven't - * completed the accounting for the whole dio, go back and try again - */ - if (ordered_offset < offset + bytes) { - ordered_bytes = offset + bytes - ordered_offset; - ordered = NULL; - goto again; + while (ordered_offset < offset + bytes) { + last_offset = ordered_offset; + if (btrfs_dec_test_first_ordered_pending(inode, &ordered, + &ordered_offset, + ordered_bytes, + uptodate)) { + btrfs_init_work(&ordered->work, func, + finish_ordered_fn, + NULL, NULL); + btrfs_queue_work(wq, &ordered->work); + } + /* + * If btrfs_dec_test_ordered_pending does not find any ordered + * extent in the range, we can exit. + */ + if (ordered_offset == last_offset) + return; + /* + * Our bio might span multiple ordered extents. In this case + * we keep goin until we have accounted the whole dio. + */ + if (ordered_offset < offset + bytes) { + ordered_bytes = offset + bytes - ordered_offset; + ordered = NULL; + } } } @@ -8705,29 +8714,19 @@ static int btrfs_writepage(struct page *page, struct writeback_control *wbc) static int btrfs_writepages(struct address_space *mapping, struct writeback_control *wbc) { - struct extent_io_tree *tree; - - tree = &BTRFS_I(mapping->host)->io_tree; - return extent_writepages(tree, mapping, wbc); + return extent_writepages(mapping, wbc); } static int btrfs_readpages(struct file *file, struct address_space *mapping, struct list_head *pages, unsigned nr_pages) { - struct extent_io_tree *tree; - tree = &BTRFS_I(mapping->host)->io_tree; - return extent_readpages(tree, mapping, pages, nr_pages); + return extent_readpages(mapping, pages, nr_pages); } + static int __btrfs_releasepage(struct page *page, gfp_t gfp_flags) { - struct extent_io_tree *tree; - struct extent_map_tree *map; - int ret; - - tree = &BTRFS_I(page->mapping->host)->io_tree; - map = &BTRFS_I(page->mapping->host)->extent_tree; - ret = try_release_extent_mapping(map, tree, page, gfp_flags); + int ret = try_release_extent_mapping(page, gfp_flags); if (ret == 1) { ClearPagePrivate(page); set_page_private(page, 0); @@ -8868,8 +8867,8 @@ static void btrfs_invalidatepage(struct page *page, unsigned int offset, * * We are not allowed to take the i_mutex here so we have to play games to * protect against truncate races as the page could now be beyond EOF. Because - * vmtruncate() writes the inode size before removing pages, once we have the - * page lock we can determine safely if the page is beyond EOF. If it is not + * truncate_setsize() writes the inode size before removing pages, once we have + * the page lock we can determine safely if the page is beyond EOF. If it is not * beyond EOF, then the page is guaranteed safe against truncation until we * unlock the page. */ @@ -9031,8 +9030,7 @@ static int btrfs_truncate(struct inode *inode, bool skip_writeback) struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_block_rsv *rsv; - int ret = 0; - int err = 0; + int ret; struct btrfs_trans_handle *trans; u64 mask = fs_info->sectorsize - 1; u64 min_size = btrfs_calc_trunc_metadata_size(fs_info, 1); @@ -9045,39 +9043,31 @@ static int btrfs_truncate(struct inode *inode, bool skip_writeback) } /* - * Yes ladies and gentlemen, this is indeed ugly. The fact is we have - * 3 things going on here + * Yes ladies and gentlemen, this is indeed ugly. We have a couple of + * things going on here: * - * 1) We need to reserve space for our orphan item and the space to - * delete our orphan item. Lord knows we don't want to have a dangling - * orphan item because we didn't reserve space to remove it. + * 1) We need to reserve space to update our inode. * - * 2) We need to reserve space to update our inode. - * - * 3) We need to have something to cache all the space that is going to + * 2) We need to have something to cache all the space that is going to * be free'd up by the truncate operation, but also have some slack * space reserved in case it uses space during the truncate (thank you * very much snapshotting). * - * And we need these to all be separate. The fact is we can use a lot of + * And we need these to be separate. The fact is we can use a lot of * space doing the truncate, and we have no earthly idea how much space * we will use, so we need the truncate reservation to be separate so it - * doesn't end up using space reserved for updating the inode or - * removing the orphan item. We also need to be able to stop the - * transaction and start a new one, which means we need to be able to - * update the inode several times, and we have no idea of knowing how - * many times that will be, so we can't just reserve 1 item for the - * entirety of the operation, so that has to be done separately as well. - * Then there is the orphan item, which does indeed need to be held on - * to for the whole operation, and we need nobody to touch this reserved - * space except the orphan code. + * doesn't end up using space reserved for updating the inode. We also + * need to be able to stop the transaction and start a new one, which + * means we need to be able to update the inode several times, and we + * have no idea of knowing how many times that will be, so we can't just + * reserve 1 item for the entirety of the operation, so that has to be + * done separately as well. * * So that leaves us with * - * 1) root->orphan_block_rsv - for the orphan deletion. - * 2) rsv - for the truncate reservation, which we will steal from the + * 1) rsv - for the truncate reservation, which we will steal from the * transaction reservation. - * 3) fs_info->trans_block_rsv - this will have 1 items worth left for + * 2) fs_info->trans_block_rsv - this will have 1 items worth left for * updating the inode. */ rsv = btrfs_alloc_block_rsv(fs_info, BTRFS_BLOCK_RSV_TEMP); @@ -9092,7 +9082,7 @@ static int btrfs_truncate(struct inode *inode, bool skip_writeback) */ trans = btrfs_start_transaction(root, 2); if (IS_ERR(trans)) { - err = PTR_ERR(trans); + ret = PTR_ERR(trans); goto out; } @@ -9116,24 +9106,19 @@ static int btrfs_truncate(struct inode *inode, bool skip_writeback) inode->i_size, BTRFS_EXTENT_DATA_KEY); trans->block_rsv = &fs_info->trans_block_rsv; - if (ret != -ENOSPC && ret != -EAGAIN) { - if (ret < 0) - err = ret; + if (ret != -ENOSPC && ret != -EAGAIN) break; - } ret = btrfs_update_inode(trans, root, inode); - if (ret) { - err = ret; + if (ret) break; - } btrfs_end_transaction(trans); btrfs_btree_balance_dirty(fs_info); trans = btrfs_start_transaction(root, 2); if (IS_ERR(trans)) { - ret = err = PTR_ERR(trans); + ret = PTR_ERR(trans); trans = NULL; break; } @@ -9166,29 +9151,23 @@ static int btrfs_truncate(struct inode *inode, bool skip_writeback) btrfs_ordered_update_i_size(inode, inode->i_size, NULL); } - if (ret == 0 && inode->i_nlink > 0) { - trans->block_rsv = root->orphan_block_rsv; - ret = btrfs_orphan_del(trans, BTRFS_I(inode)); - if (ret) - err = ret; - } - if (trans) { + int ret2; + trans->block_rsv = &fs_info->trans_block_rsv; - ret = btrfs_update_inode(trans, root, inode); - if (ret && !err) - err = ret; + ret2 = btrfs_update_inode(trans, root, inode); + if (ret2 && !ret) + ret = ret2; - ret = btrfs_end_transaction(trans); + ret2 = btrfs_end_transaction(trans); + if (ret2 && !ret) + ret = ret2; btrfs_btree_balance_dirty(fs_info); } out: btrfs_free_block_rsv(fs_info, rsv); - if (ret && !err) - err = ret; - - return err; + return ret; } /* @@ -9324,13 +9303,6 @@ void btrfs_destroy_inode(struct inode *inode) if (!root) goto free; - if (test_bit(BTRFS_INODE_HAS_ORPHAN_ITEM, - &BTRFS_I(inode)->runtime_flags)) { - btrfs_info(fs_info, "inode %llu still on the orphan list", - btrfs_ino(BTRFS_I(inode))); - atomic_dec(&root->orphan_inodes); - } - while (1) { ordered = btrfs_lookup_first_ordered_extent(inode, (u64)-1); if (!ordered) @@ -9964,6 +9936,13 @@ static int btrfs_rename2(struct inode *old_dir, struct dentry *old_dentry, return btrfs_rename(old_dir, old_dentry, new_dir, new_dentry, flags); } +struct btrfs_delalloc_work { + struct inode *inode; + struct completion completion; + struct list_head list; + struct btrfs_work work; +}; + static void btrfs_run_delalloc_work(struct btrfs_work *work) { struct btrfs_delalloc_work *delalloc_work; @@ -9977,15 +9956,11 @@ static void btrfs_run_delalloc_work(struct btrfs_work *work) &BTRFS_I(inode)->runtime_flags)) filemap_flush(inode->i_mapping); - if (delalloc_work->delay_iput) - btrfs_add_delayed_iput(inode); - else - iput(inode); + iput(inode); complete(&delalloc_work->completion); } -struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode, - int delay_iput) +static struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode) { struct btrfs_delalloc_work *work; @@ -9996,7 +9971,6 @@ struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode, init_completion(&work->completion); INIT_LIST_HEAD(&work->list); work->inode = inode; - work->delay_iput = delay_iput; WARN_ON_ONCE(!inode); btrfs_init_work(&work->work, btrfs_flush_delalloc_helper, btrfs_run_delalloc_work, NULL, NULL); @@ -10004,18 +9978,11 @@ struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode, return work; } -void btrfs_wait_and_free_delalloc_work(struct btrfs_delalloc_work *work) -{ - wait_for_completion(&work->completion); - kfree(work); -} - /* * some fairly slow code that needs optimization. This walks the list * of all the inodes with pending delalloc and forces them to disk. */ -static int __start_delalloc_inodes(struct btrfs_root *root, int delay_iput, - int nr) +static int start_delalloc_inodes(struct btrfs_root *root, int nr) { struct btrfs_inode *binode; struct inode *inode; @@ -10043,12 +10010,9 @@ static int __start_delalloc_inodes(struct btrfs_root *root, int delay_iput, } spin_unlock(&root->delalloc_lock); - work = btrfs_alloc_delalloc_work(inode, delay_iput); + work = btrfs_alloc_delalloc_work(inode); if (!work) { - if (delay_iput) - btrfs_add_delayed_iput(inode); - else - iput(inode); + iput(inode); ret = -ENOMEM; goto out; } @@ -10066,10 +10030,11 @@ static int __start_delalloc_inodes(struct btrfs_root *root, int delay_iput, out: list_for_each_entry_safe(work, next, &works, list) { list_del_init(&work->list); - btrfs_wait_and_free_delalloc_work(work); + wait_for_completion(&work->completion); + kfree(work); } - if (!list_empty_careful(&splice)) { + if (!list_empty(&splice)) { spin_lock(&root->delalloc_lock); list_splice_tail(&splice, &root->delalloc_inodes); spin_unlock(&root->delalloc_lock); @@ -10078,7 +10043,7 @@ static int __start_delalloc_inodes(struct btrfs_root *root, int delay_iput, return ret; } -int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput) +int btrfs_start_delalloc_inodes(struct btrfs_root *root) { struct btrfs_fs_info *fs_info = root->fs_info; int ret; @@ -10086,14 +10051,13 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput) if (test_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state)) return -EROFS; - ret = __start_delalloc_inodes(root, delay_iput, -1); + ret = start_delalloc_inodes(root, -1); if (ret > 0) ret = 0; return ret; } -int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput, - int nr) +int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int nr) { struct btrfs_root *root; struct list_head splice; @@ -10116,7 +10080,7 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput, &fs_info->delalloc_roots); spin_unlock(&fs_info->delalloc_root_lock); - ret = __start_delalloc_inodes(root, delay_iput, nr); + ret = start_delalloc_inodes(root, nr); btrfs_put_fs_root(root); if (ret < 0) goto out; @@ -10131,7 +10095,7 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput, ret = 0; out: - if (!list_empty_careful(&splice)) { + if (!list_empty(&splice)) { spin_lock(&fs_info->delalloc_root_lock); list_splice_tail(&splice, &fs_info->delalloc_roots); spin_unlock(&fs_info->delalloc_root_lock); @@ -10669,5 +10633,4 @@ static const struct inode_operations btrfs_symlink_inode_operations = { const struct dentry_operations btrfs_dentry_operations = { .d_delete = btrfs_dentry_delete, - .d_release = btrfs_dentry_release, }; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 632e26d6f7ce0cd26284b0e39c140c7f09a1f3d2..d29992f7dc6356b2de853f5dde76cebedc3e30d0 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -93,20 +93,22 @@ static int btrfs_clone(struct inode *src, struct inode *inode, int no_time_update); /* Mask out flags that are inappropriate for the given type of inode. */ -static unsigned int btrfs_mask_flags(umode_t mode, unsigned int flags) +static unsigned int btrfs_mask_fsflags_for_type(struct inode *inode, + unsigned int flags) { - if (S_ISDIR(mode)) + if (S_ISDIR(inode->i_mode)) return flags; - else if (S_ISREG(mode)) + else if (S_ISREG(inode->i_mode)) return flags & ~FS_DIRSYNC_FL; else return flags & (FS_NODUMP_FL | FS_NOATIME_FL); } /* - * Export inode flags to the format expected by the FS_IOC_GETFLAGS ioctl. + * Export internal inode flags to the format expected by the FS_IOC_GETFLAGS + * ioctl. */ -static unsigned int btrfs_flags_to_ioctl(unsigned int flags) +static unsigned int btrfs_inode_flags_to_fsflags(unsigned int flags) { unsigned int iflags = 0; @@ -136,20 +138,20 @@ static unsigned int btrfs_flags_to_ioctl(unsigned int flags) /* * Update inode->i_flags based on the btrfs internal flags. */ -void btrfs_update_iflags(struct inode *inode) +void btrfs_sync_inode_flags_to_i_flags(struct inode *inode) { - struct btrfs_inode *ip = BTRFS_I(inode); + struct btrfs_inode *binode = BTRFS_I(inode); unsigned int new_fl = 0; - if (ip->flags & BTRFS_INODE_SYNC) + if (binode->flags & BTRFS_INODE_SYNC) new_fl |= S_SYNC; - if (ip->flags & BTRFS_INODE_IMMUTABLE) + if (binode->flags & BTRFS_INODE_IMMUTABLE) new_fl |= S_IMMUTABLE; - if (ip->flags & BTRFS_INODE_APPEND) + if (binode->flags & BTRFS_INODE_APPEND) new_fl |= S_APPEND; - if (ip->flags & BTRFS_INODE_NOATIME) + if (binode->flags & BTRFS_INODE_NOATIME) new_fl |= S_NOATIME; - if (ip->flags & BTRFS_INODE_DIRSYNC) + if (binode->flags & BTRFS_INODE_DIRSYNC) new_fl |= S_DIRSYNC; set_mask_bits(&inode->i_flags, @@ -159,15 +161,16 @@ void btrfs_update_iflags(struct inode *inode) static int btrfs_ioctl_getflags(struct file *file, void __user *arg) { - struct btrfs_inode *ip = BTRFS_I(file_inode(file)); - unsigned int flags = btrfs_flags_to_ioctl(ip->flags); + struct btrfs_inode *binode = BTRFS_I(file_inode(file)); + unsigned int flags = btrfs_inode_flags_to_fsflags(binode->flags); if (copy_to_user(arg, &flags, sizeof(flags))) return -EFAULT; return 0; } -static int check_flags(unsigned int flags) +/* Check if @flags are a supported and valid set of FS_*_FL flags */ +static int check_fsflags(unsigned int flags) { if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \ FS_NOATIME_FL | FS_NODUMP_FL | \ @@ -186,13 +189,13 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) { struct inode *inode = file_inode(file); struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); - struct btrfs_inode *ip = BTRFS_I(inode); - struct btrfs_root *root = ip->root; + struct btrfs_inode *binode = BTRFS_I(inode); + struct btrfs_root *root = binode->root; struct btrfs_trans_handle *trans; - unsigned int flags, oldflags; + unsigned int fsflags, old_fsflags; int ret; - u64 ip_oldflags; - unsigned int i_oldflags; + u64 old_flags; + unsigned int old_i_flags; umode_t mode; if (!inode_owner_or_capable(inode)) @@ -201,10 +204,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) if (btrfs_root_readonly(root)) return -EROFS; - if (copy_from_user(&flags, arg, sizeof(flags))) + if (copy_from_user(&fsflags, arg, sizeof(fsflags))) return -EFAULT; - ret = check_flags(flags); + ret = check_fsflags(fsflags); if (ret) return ret; @@ -214,44 +217,44 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) inode_lock(inode); - ip_oldflags = ip->flags; - i_oldflags = inode->i_flags; + old_flags = binode->flags; + old_i_flags = inode->i_flags; mode = inode->i_mode; - flags = btrfs_mask_flags(inode->i_mode, flags); - oldflags = btrfs_flags_to_ioctl(ip->flags); - if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { + fsflags = btrfs_mask_fsflags_for_type(inode, fsflags); + old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags); + if ((fsflags ^ old_fsflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) { ret = -EPERM; goto out_unlock; } } - if (flags & FS_SYNC_FL) - ip->flags |= BTRFS_INODE_SYNC; + if (fsflags & FS_SYNC_FL) + binode->flags |= BTRFS_INODE_SYNC; else - ip->flags &= ~BTRFS_INODE_SYNC; - if (flags & FS_IMMUTABLE_FL) - ip->flags |= BTRFS_INODE_IMMUTABLE; + binode->flags &= ~BTRFS_INODE_SYNC; + if (fsflags & FS_IMMUTABLE_FL) + binode->flags |= BTRFS_INODE_IMMUTABLE; else - ip->flags &= ~BTRFS_INODE_IMMUTABLE; - if (flags & FS_APPEND_FL) - ip->flags |= BTRFS_INODE_APPEND; + binode->flags &= ~BTRFS_INODE_IMMUTABLE; + if (fsflags & FS_APPEND_FL) + binode->flags |= BTRFS_INODE_APPEND; else - ip->flags &= ~BTRFS_INODE_APPEND; - if (flags & FS_NODUMP_FL) - ip->flags |= BTRFS_INODE_NODUMP; + binode->flags &= ~BTRFS_INODE_APPEND; + if (fsflags & FS_NODUMP_FL) + binode->flags |= BTRFS_INODE_NODUMP; else - ip->flags &= ~BTRFS_INODE_NODUMP; - if (flags & FS_NOATIME_FL) - ip->flags |= BTRFS_INODE_NOATIME; + binode->flags &= ~BTRFS_INODE_NODUMP; + if (fsflags & FS_NOATIME_FL) + binode->flags |= BTRFS_INODE_NOATIME; else - ip->flags &= ~BTRFS_INODE_NOATIME; - if (flags & FS_DIRSYNC_FL) - ip->flags |= BTRFS_INODE_DIRSYNC; + binode->flags &= ~BTRFS_INODE_NOATIME; + if (fsflags & FS_DIRSYNC_FL) + binode->flags |= BTRFS_INODE_DIRSYNC; else - ip->flags &= ~BTRFS_INODE_DIRSYNC; - if (flags & FS_NOCOW_FL) { + binode->flags &= ~BTRFS_INODE_DIRSYNC; + if (fsflags & FS_NOCOW_FL) { if (S_ISREG(mode)) { /* * It's safe to turn csums off here, no extents exist. @@ -259,10 +262,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) * status of the file and will not set it. */ if (inode->i_size == 0) - ip->flags |= BTRFS_INODE_NODATACOW - | BTRFS_INODE_NODATASUM; + binode->flags |= BTRFS_INODE_NODATACOW + | BTRFS_INODE_NODATASUM; } else { - ip->flags |= BTRFS_INODE_NODATACOW; + binode->flags |= BTRFS_INODE_NODATACOW; } } else { /* @@ -270,10 +273,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) */ if (S_ISREG(mode)) { if (inode->i_size == 0) - ip->flags &= ~(BTRFS_INODE_NODATACOW + binode->flags &= ~(BTRFS_INODE_NODATACOW | BTRFS_INODE_NODATASUM); } else { - ip->flags &= ~BTRFS_INODE_NODATACOW; + binode->flags &= ~BTRFS_INODE_NODATACOW; } } @@ -282,18 +285,18 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) * flag may be changed automatically if compression code won't make * things smaller. */ - if (flags & FS_NOCOMP_FL) { - ip->flags &= ~BTRFS_INODE_COMPRESS; - ip->flags |= BTRFS_INODE_NOCOMPRESS; + if (fsflags & FS_NOCOMP_FL) { + binode->flags &= ~BTRFS_INODE_COMPRESS; + binode->flags |= BTRFS_INODE_NOCOMPRESS; ret = btrfs_set_prop(inode, "btrfs.compression", NULL, 0, 0); if (ret && ret != -ENODATA) goto out_drop; - } else if (flags & FS_COMPR_FL) { + } else if (fsflags & FS_COMPR_FL) { const char *comp; - ip->flags |= BTRFS_INODE_COMPRESS; - ip->flags &= ~BTRFS_INODE_NOCOMPRESS; + binode->flags |= BTRFS_INODE_COMPRESS; + binode->flags &= ~BTRFS_INODE_NOCOMPRESS; comp = btrfs_compress_type2str(fs_info->compress_type); if (!comp || comp[0] == 0) @@ -308,7 +311,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) ret = btrfs_set_prop(inode, "btrfs.compression", NULL, 0, 0); if (ret && ret != -ENODATA) goto out_drop; - ip->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS); + binode->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS); } trans = btrfs_start_transaction(root, 1); @@ -317,7 +320,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) goto out_drop; } - btrfs_update_iflags(inode); + btrfs_sync_inode_flags_to_i_flags(inode); inode_inc_iversion(inode); inode->i_ctime = current_time(inode); ret = btrfs_update_inode(trans, root, inode); @@ -325,8 +328,8 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) btrfs_end_transaction(trans); out_drop: if (ret) { - ip->flags = ip_oldflags; - inode->i_flags = i_oldflags; + binode->flags = old_flags; + inode->i_flags = old_i_flags; } out_unlock: @@ -335,6 +338,148 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) return ret; } +/* + * Translate btrfs internal inode flags to xflags as expected by the + * FS_IOC_FSGETXATT ioctl. Filter only the supported ones, unknown flags are + * silently dropped. + */ +static unsigned int btrfs_inode_flags_to_xflags(unsigned int flags) +{ + unsigned int xflags = 0; + + if (flags & BTRFS_INODE_APPEND) + xflags |= FS_XFLAG_APPEND; + if (flags & BTRFS_INODE_IMMUTABLE) + xflags |= FS_XFLAG_IMMUTABLE; + if (flags & BTRFS_INODE_NOATIME) + xflags |= FS_XFLAG_NOATIME; + if (flags & BTRFS_INODE_NODUMP) + xflags |= FS_XFLAG_NODUMP; + if (flags & BTRFS_INODE_SYNC) + xflags |= FS_XFLAG_SYNC; + + return xflags; +} + +/* Check if @flags are a supported and valid set of FS_XFLAGS_* flags */ +static int check_xflags(unsigned int flags) +{ + if (flags & ~(FS_XFLAG_APPEND | FS_XFLAG_IMMUTABLE | FS_XFLAG_NOATIME | + FS_XFLAG_NODUMP | FS_XFLAG_SYNC)) + return -EOPNOTSUPP; + return 0; +} + +/* + * Set the xflags from the internal inode flags. The remaining items of fsxattr + * are zeroed. + */ +static int btrfs_ioctl_fsgetxattr(struct file *file, void __user *arg) +{ + struct btrfs_inode *binode = BTRFS_I(file_inode(file)); + struct fsxattr fa; + + memset(&fa, 0, sizeof(fa)); + fa.fsx_xflags = btrfs_inode_flags_to_xflags(binode->flags); + + if (copy_to_user(arg, &fa, sizeof(fa))) + return -EFAULT; + + return 0; +} + +static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg) +{ + struct inode *inode = file_inode(file); + struct btrfs_inode *binode = BTRFS_I(inode); + struct btrfs_root *root = binode->root; + struct btrfs_trans_handle *trans; + struct fsxattr fa; + unsigned old_flags; + unsigned old_i_flags; + int ret = 0; + + if (!inode_owner_or_capable(inode)) + return -EPERM; + + if (btrfs_root_readonly(root)) + return -EROFS; + + memset(&fa, 0, sizeof(fa)); + if (copy_from_user(&fa, arg, sizeof(fa))) + return -EFAULT; + + ret = check_xflags(fa.fsx_xflags); + if (ret) + return ret; + + if (fa.fsx_extsize != 0 || fa.fsx_projid != 0 || fa.fsx_cowextsize != 0) + return -EOPNOTSUPP; + + ret = mnt_want_write_file(file); + if (ret) + return ret; + + inode_lock(inode); + + old_flags = binode->flags; + old_i_flags = inode->i_flags; + + /* We need the capabilities to change append-only or immutable inode */ + if (((old_flags & (BTRFS_INODE_APPEND | BTRFS_INODE_IMMUTABLE)) || + (fa.fsx_xflags & (FS_XFLAG_APPEND | FS_XFLAG_IMMUTABLE))) && + !capable(CAP_LINUX_IMMUTABLE)) { + ret = -EPERM; + goto out_unlock; + } + + if (fa.fsx_xflags & FS_XFLAG_SYNC) + binode->flags |= BTRFS_INODE_SYNC; + else + binode->flags &= ~BTRFS_INODE_SYNC; + if (fa.fsx_xflags & FS_XFLAG_IMMUTABLE) + binode->flags |= BTRFS_INODE_IMMUTABLE; + else + binode->flags &= ~BTRFS_INODE_IMMUTABLE; + if (fa.fsx_xflags & FS_XFLAG_APPEND) + binode->flags |= BTRFS_INODE_APPEND; + else + binode->flags &= ~BTRFS_INODE_APPEND; + if (fa.fsx_xflags & FS_XFLAG_NODUMP) + binode->flags |= BTRFS_INODE_NODUMP; + else + binode->flags &= ~BTRFS_INODE_NODUMP; + if (fa.fsx_xflags & FS_XFLAG_NOATIME) + binode->flags |= BTRFS_INODE_NOATIME; + else + binode->flags &= ~BTRFS_INODE_NOATIME; + + /* 1 item for the inode */ + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out_unlock; + } + + btrfs_sync_inode_flags_to_i_flags(inode); + inode_inc_iversion(inode); + inode->i_ctime = current_time(inode); + ret = btrfs_update_inode(trans, root, inode); + + btrfs_end_transaction(trans); + +out_unlock: + if (ret) { + binode->flags = old_flags; + inode->i_flags = old_i_flags; + } + + inode_unlock(inode); + mnt_drop_write_file(file); + + return ret; +} + static int btrfs_ioctl_getversion(struct file *file, int __user *arg) { struct inode *inode = file_inode(file); @@ -424,7 +569,6 @@ static noinline int create_subvol(struct inode *dir, u64 objectid; u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID; u64 index = 0; - u64 qgroup_reserved; uuid_le new_uuid; root_item = kzalloc(sizeof(*root_item), GFP_KERNEL); @@ -449,8 +593,7 @@ static noinline int create_subvol(struct inode *dir, * The same as the snapshot creation, please see the comment * of create_snapshot(). */ - ret = btrfs_subvolume_reserve_metadata(root, &block_rsv, - 8, &qgroup_reserved, false); + ret = btrfs_subvolume_reserve_metadata(root, &block_rsv, 8, false); if (ret) goto fail_free; @@ -573,7 +716,7 @@ static noinline int create_subvol(struct inode *dir, btrfs_ino(BTRFS_I(dir)), index, name, namelen); BUG_ON(ret); - ret = btrfs_uuid_tree_add(trans, fs_info, root_item->uuid, + ret = btrfs_uuid_tree_add(trans, root_item->uuid, BTRFS_UUID_KEY_SUBVOL, objectid); if (ret) btrfs_abort_transaction(trans, ret); @@ -640,7 +783,7 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, wait_event(root->subv_writers->wait, percpu_counter_sum(&root->subv_writers->counter) == 0); - ret = btrfs_start_delalloc_inodes(root, 0); + ret = btrfs_start_delalloc_inodes(root); if (ret) goto dec_and_free; @@ -658,7 +801,6 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, */ ret = btrfs_subvolume_reserve_metadata(BTRFS_I(dir)->root, &pending_snapshot->block_rsv, 8, - &pending_snapshot->qgroup_reserved, false); if (ret) goto dec_and_free; @@ -1457,7 +1599,6 @@ static noinline int btrfs_ioctl_resize(struct file *file, return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; } - mutex_lock(&fs_info->volume_mutex); vol_args = memdup_user(arg, sizeof(*vol_args)); if (IS_ERR(vol_args)) { ret = PTR_ERR(vol_args); @@ -1565,7 +1706,6 @@ static noinline int btrfs_ioctl_resize(struct file *file, out_free: kfree(vol_args); out: - mutex_unlock(&fs_info->volume_mutex); clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); mnt_drop_write_file(file); return ret; @@ -1832,60 +1972,6 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file, return ret; } -/* - * helper to check if the subvolume references other subvolumes - */ -static noinline int may_destroy_subvol(struct btrfs_root *root) -{ - struct btrfs_fs_info *fs_info = root->fs_info; - struct btrfs_path *path; - struct btrfs_dir_item *di; - struct btrfs_key key; - u64 dir_id; - int ret; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - /* Make sure this root isn't set as the default subvol */ - dir_id = btrfs_super_root_dir(fs_info->super_copy); - di = btrfs_lookup_dir_item(NULL, fs_info->tree_root, path, - dir_id, "default", 7, 0); - if (di && !IS_ERR(di)) { - btrfs_dir_item_key_to_cpu(path->nodes[0], di, &key); - if (key.objectid == root->root_key.objectid) { - ret = -EPERM; - btrfs_err(fs_info, - "deleting default subvolume %llu is not allowed", - key.objectid); - goto out; - } - btrfs_release_path(path); - } - - key.objectid = root->root_key.objectid; - key.type = BTRFS_ROOT_REF_KEY; - key.offset = (u64)-1; - - ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, path, 0, 0); - if (ret < 0) - goto out; - BUG_ON(ret == 0); - - ret = 0; - if (path->slots[0] > 0) { - path->slots[0]--; - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); - if (key.objectid == root->root_key.objectid && - key.type == BTRFS_ROOT_REF_KEY) - ret = -ENOTEMPTY; - } -out: - btrfs_free_path(path); - return ret; -} - static noinline int key_in_sk(struct btrfs_key *key, struct btrfs_ioctl_search_key *sk) { @@ -2066,7 +2152,7 @@ static noinline int search_ioctl(struct inode *inode, root = btrfs_read_fs_root_no_name(info, &key); if (IS_ERR(root)) { btrfs_free_path(path); - return -ENOENT; + return PTR_ERR(root); } } @@ -2200,8 +2286,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, key.offset = (u64)-1; root = btrfs_read_fs_root_no_name(info, &key); if (IS_ERR(root)) { - btrfs_err(info, "could not find root %llu", tree_id); - ret = -ENOENT; + ret = PTR_ERR(root); goto out; } @@ -2237,64 +2322,482 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, goto out; } - *(ptr + len) = '/'; - read_extent_buffer(l, ptr, (unsigned long)(iref + 1), len); - - if (key.offset == BTRFS_FIRST_FREE_OBJECTID) - break; + *(ptr + len) = '/'; + read_extent_buffer(l, ptr, (unsigned long)(iref + 1), len); + + if (key.offset == BTRFS_FIRST_FREE_OBJECTID) + break; + + btrfs_release_path(path); + key.objectid = key.offset; + key.offset = (u64)-1; + dirid = key.objectid; + } + memmove(name, ptr, total_len); + name[total_len] = '\0'; + ret = 0; +out: + btrfs_free_path(path); + return ret; +} + +static int btrfs_search_path_in_tree_user(struct inode *inode, + struct btrfs_ioctl_ino_lookup_user_args *args) +{ + struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info; + struct super_block *sb = inode->i_sb; + struct btrfs_key upper_limit = BTRFS_I(inode)->location; + u64 treeid = BTRFS_I(inode)->root->root_key.objectid; + u64 dirid = args->dirid; + unsigned long item_off; + unsigned long item_len; + struct btrfs_inode_ref *iref; + struct btrfs_root_ref *rref; + struct btrfs_root *root; + struct btrfs_path *path; + struct btrfs_key key, key2; + struct extent_buffer *leaf; + struct inode *temp_inode; + char *ptr; + int slot; + int len; + int total_len = 0; + int ret; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + /* + * If the bottom subvolume does not exist directly under upper_limit, + * construct the path in from the bottom up. + */ + if (dirid != upper_limit.objectid) { + ptr = &args->path[BTRFS_INO_LOOKUP_USER_PATH_MAX - 1]; + + key.objectid = treeid; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + root = btrfs_read_fs_root_no_name(fs_info, &key); + if (IS_ERR(root)) { + ret = PTR_ERR(root); + goto out; + } + + key.objectid = dirid; + key.type = BTRFS_INODE_REF_KEY; + key.offset = (u64)-1; + while (1) { + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) { + goto out; + } else if (ret > 0) { + ret = btrfs_previous_item(root, path, dirid, + BTRFS_INODE_REF_KEY); + if (ret < 0) { + goto out; + } else if (ret > 0) { + ret = -ENOENT; + goto out; + } + } + + leaf = path->nodes[0]; + slot = path->slots[0]; + btrfs_item_key_to_cpu(leaf, &key, slot); + + iref = btrfs_item_ptr(leaf, slot, struct btrfs_inode_ref); + len = btrfs_inode_ref_name_len(leaf, iref); + ptr -= len + 1; + total_len += len + 1; + if (ptr < args->path) { + ret = -ENAMETOOLONG; + goto out; + } + + *(ptr + len) = '/'; + read_extent_buffer(leaf, ptr, + (unsigned long)(iref + 1), len); + + /* Check the read+exec permission of this directory */ + ret = btrfs_previous_item(root, path, dirid, + BTRFS_INODE_ITEM_KEY); + if (ret < 0) { + goto out; + } else if (ret > 0) { + ret = -ENOENT; + goto out; + } + + leaf = path->nodes[0]; + slot = path->slots[0]; + btrfs_item_key_to_cpu(leaf, &key2, slot); + if (key2.objectid != dirid) { + ret = -ENOENT; + goto out; + } + + temp_inode = btrfs_iget(sb, &key2, root, NULL); + ret = inode_permission(temp_inode, MAY_READ | MAY_EXEC); + iput(temp_inode); + if (ret) { + ret = -EACCES; + goto out; + } + + if (key.offset == upper_limit.objectid) + break; + if (key.objectid == BTRFS_FIRST_FREE_OBJECTID) { + ret = -EACCES; + goto out; + } + + btrfs_release_path(path); + key.objectid = key.offset; + key.offset = (u64)-1; + dirid = key.objectid; + } + + memmove(args->path, ptr, total_len); + args->path[total_len] = '\0'; + btrfs_release_path(path); + } + + /* Get the bottom subvolume's name from ROOT_REF */ + root = fs_info->tree_root; + key.objectid = treeid; + key.type = BTRFS_ROOT_REF_KEY; + key.offset = args->treeid; + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) { + goto out; + } else if (ret > 0) { + ret = -ENOENT; + goto out; + } + + leaf = path->nodes[0]; + slot = path->slots[0]; + btrfs_item_key_to_cpu(leaf, &key, slot); + + item_off = btrfs_item_ptr_offset(leaf, slot); + item_len = btrfs_item_size_nr(leaf, slot); + /* Check if dirid in ROOT_REF corresponds to passed dirid */ + rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref); + if (args->dirid != btrfs_root_ref_dirid(leaf, rref)) { + ret = -EINVAL; + goto out; + } + + /* Copy subvolume's name */ + item_off += sizeof(struct btrfs_root_ref); + item_len -= sizeof(struct btrfs_root_ref); + read_extent_buffer(leaf, args->name, item_off, item_len); + args->name[item_len] = 0; + +out: + btrfs_free_path(path); + return ret; +} + +static noinline int btrfs_ioctl_ino_lookup(struct file *file, + void __user *argp) +{ + struct btrfs_ioctl_ino_lookup_args *args; + struct inode *inode; + int ret = 0; + + args = memdup_user(argp, sizeof(*args)); + if (IS_ERR(args)) + return PTR_ERR(args); + + inode = file_inode(file); + + /* + * Unprivileged query to obtain the containing subvolume root id. The + * path is reset so it's consistent with btrfs_search_path_in_tree. + */ + if (args->treeid == 0) + args->treeid = BTRFS_I(inode)->root->root_key.objectid; + + if (args->objectid == BTRFS_FIRST_FREE_OBJECTID) { + args->name[0] = 0; + goto out; + } + + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto out; + } + + ret = btrfs_search_path_in_tree(BTRFS_I(inode)->root->fs_info, + args->treeid, args->objectid, + args->name); + +out: + if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) + ret = -EFAULT; + + kfree(args); + return ret; +} + +/* + * Version of ino_lookup ioctl (unprivileged) + * + * The main differences from ino_lookup ioctl are: + * + * 1. Read + Exec permission will be checked using inode_permission() during + * path construction. -EACCES will be returned in case of failure. + * 2. Path construction will be stopped at the inode number which corresponds + * to the fd with which this ioctl is called. If constructed path does not + * exist under fd's inode, -EACCES will be returned. + * 3. The name of bottom subvolume is also searched and filled. + */ +static int btrfs_ioctl_ino_lookup_user(struct file *file, void __user *argp) +{ + struct btrfs_ioctl_ino_lookup_user_args *args; + struct inode *inode; + int ret; + + args = memdup_user(argp, sizeof(*args)); + if (IS_ERR(args)) + return PTR_ERR(args); + + inode = file_inode(file); + + if (args->dirid == BTRFS_FIRST_FREE_OBJECTID && + BTRFS_I(inode)->location.objectid != BTRFS_FIRST_FREE_OBJECTID) { + /* + * The subvolume does not exist under fd with which this is + * called + */ + kfree(args); + return -EACCES; + } + + ret = btrfs_search_path_in_tree_user(inode, args); + + if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) + ret = -EFAULT; + + kfree(args); + return ret; +} + +/* Get the subvolume information in BTRFS_ROOT_ITEM and BTRFS_ROOT_BACKREF */ +static int btrfs_ioctl_get_subvol_info(struct file *file, void __user *argp) +{ + struct btrfs_ioctl_get_subvol_info_args *subvol_info; + struct btrfs_fs_info *fs_info; + struct btrfs_root *root; + struct btrfs_path *path; + struct btrfs_key key; + struct btrfs_root_item *root_item; + struct btrfs_root_ref *rref; + struct extent_buffer *leaf; + unsigned long item_off; + unsigned long item_len; + struct inode *inode; + int slot; + int ret = 0; + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + subvol_info = kzalloc(sizeof(*subvol_info), GFP_KERNEL); + if (!subvol_info) { + btrfs_free_path(path); + return -ENOMEM; + } + + inode = file_inode(file); + fs_info = BTRFS_I(inode)->root->fs_info; + + /* Get root_item of inode's subvolume */ + key.objectid = BTRFS_I(inode)->root->root_key.objectid; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + root = btrfs_read_fs_root_no_name(fs_info, &key); + if (IS_ERR(root)) { + ret = PTR_ERR(root); + goto out; + } + root_item = &root->root_item; + + subvol_info->treeid = key.objectid; + + subvol_info->generation = btrfs_root_generation(root_item); + subvol_info->flags = btrfs_root_flags(root_item); + + memcpy(subvol_info->uuid, root_item->uuid, BTRFS_UUID_SIZE); + memcpy(subvol_info->parent_uuid, root_item->parent_uuid, + BTRFS_UUID_SIZE); + memcpy(subvol_info->received_uuid, root_item->received_uuid, + BTRFS_UUID_SIZE); + + subvol_info->ctransid = btrfs_root_ctransid(root_item); + subvol_info->ctime.sec = btrfs_stack_timespec_sec(&root_item->ctime); + subvol_info->ctime.nsec = btrfs_stack_timespec_nsec(&root_item->ctime); + + subvol_info->otransid = btrfs_root_otransid(root_item); + subvol_info->otime.sec = btrfs_stack_timespec_sec(&root_item->otime); + subvol_info->otime.nsec = btrfs_stack_timespec_nsec(&root_item->otime); + + subvol_info->stransid = btrfs_root_stransid(root_item); + subvol_info->stime.sec = btrfs_stack_timespec_sec(&root_item->stime); + subvol_info->stime.nsec = btrfs_stack_timespec_nsec(&root_item->stime); + + subvol_info->rtransid = btrfs_root_rtransid(root_item); + subvol_info->rtime.sec = btrfs_stack_timespec_sec(&root_item->rtime); + subvol_info->rtime.nsec = btrfs_stack_timespec_nsec(&root_item->rtime); + + if (key.objectid != BTRFS_FS_TREE_OBJECTID) { + /* Search root tree for ROOT_BACKREF of this subvolume */ + root = fs_info->tree_root; + + key.type = BTRFS_ROOT_BACKREF_KEY; + key.offset = 0; + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) { + goto out; + } else if (path->slots[0] >= + btrfs_header_nritems(path->nodes[0])) { + ret = btrfs_next_leaf(root, path); + if (ret < 0) { + goto out; + } else if (ret > 0) { + ret = -EUCLEAN; + goto out; + } + } + + leaf = path->nodes[0]; + slot = path->slots[0]; + btrfs_item_key_to_cpu(leaf, &key, slot); + if (key.objectid == subvol_info->treeid && + key.type == BTRFS_ROOT_BACKREF_KEY) { + subvol_info->parent_id = key.offset; + + rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref); + subvol_info->dirid = btrfs_root_ref_dirid(leaf, rref); + + item_off = btrfs_item_ptr_offset(leaf, slot) + + sizeof(struct btrfs_root_ref); + item_len = btrfs_item_size_nr(leaf, slot) + - sizeof(struct btrfs_root_ref); + read_extent_buffer(leaf, subvol_info->name, + item_off, item_len); + } else { + ret = -ENOENT; + goto out; + } + } + + if (copy_to_user(argp, subvol_info, sizeof(*subvol_info))) + ret = -EFAULT; - btrfs_release_path(path); - key.objectid = key.offset; - key.offset = (u64)-1; - dirid = key.objectid; - } - memmove(name, ptr, total_len); - name[total_len] = '\0'; - ret = 0; out: btrfs_free_path(path); + kzfree(subvol_info); return ret; } -static noinline int btrfs_ioctl_ino_lookup(struct file *file, - void __user *argp) +/* + * Return ROOT_REF information of the subvolume containing this inode + * except the subvolume name. + */ +static int btrfs_ioctl_get_subvol_rootref(struct file *file, void __user *argp) { - struct btrfs_ioctl_ino_lookup_args *args; - struct inode *inode; - int ret = 0; + struct btrfs_ioctl_get_subvol_rootref_args *rootrefs; + struct btrfs_root_ref *rref; + struct btrfs_root *root; + struct btrfs_path *path; + struct btrfs_key key; + struct extent_buffer *leaf; + struct inode *inode; + u64 objectid; + int slot; + int ret; + u8 found; - args = memdup_user(argp, sizeof(*args)); - if (IS_ERR(args)) - return PTR_ERR(args); + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + rootrefs = memdup_user(argp, sizeof(*rootrefs)); + if (IS_ERR(rootrefs)) { + btrfs_free_path(path); + return PTR_ERR(rootrefs); + } inode = file_inode(file); + root = BTRFS_I(inode)->root->fs_info->tree_root; + objectid = BTRFS_I(inode)->root->root_key.objectid; - /* - * Unprivileged query to obtain the containing subvolume root id. The - * path is reset so it's consistent with btrfs_search_path_in_tree. - */ - if (args->treeid == 0) - args->treeid = BTRFS_I(inode)->root->root_key.objectid; + key.objectid = objectid; + key.type = BTRFS_ROOT_REF_KEY; + key.offset = rootrefs->min_treeid; + found = 0; - if (args->objectid == BTRFS_FIRST_FREE_OBJECTID) { - args->name[0] = 0; + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); + if (ret < 0) { goto out; + } else if (path->slots[0] >= + btrfs_header_nritems(path->nodes[0])) { + ret = btrfs_next_leaf(root, path); + if (ret < 0) { + goto out; + } else if (ret > 0) { + ret = -EUCLEAN; + goto out; + } } + while (1) { + leaf = path->nodes[0]; + slot = path->slots[0]; - if (!capable(CAP_SYS_ADMIN)) { - ret = -EPERM; - goto out; - } + btrfs_item_key_to_cpu(leaf, &key, slot); + if (key.objectid != objectid || key.type != BTRFS_ROOT_REF_KEY) { + ret = 0; + goto out; + } - ret = btrfs_search_path_in_tree(BTRFS_I(inode)->root->fs_info, - args->treeid, args->objectid, - args->name); + if (found == BTRFS_MAX_ROOTREF_BUFFER_NUM) { + ret = -EOVERFLOW; + goto out; + } + + rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref); + rootrefs->rootref[found].treeid = key.offset; + rootrefs->rootref[found].dirid = + btrfs_root_ref_dirid(leaf, rref); + found++; + + ret = btrfs_next_item(root, path); + if (ret < 0) { + goto out; + } else if (ret > 0) { + ret = -EUCLEAN; + goto out; + } + } out: - if (ret == 0 && copy_to_user(argp, args, sizeof(*args))) - ret = -EFAULT; + if (!ret || ret == -EOVERFLOW) { + rootrefs->num_items = found; + /* update min_treeid for next search */ + if (found) + rootrefs->min_treeid = + rootrefs->rootref[found - 1].treeid + 1; + if (copy_to_user(argp, rootrefs, sizeof(*rootrefs))) + ret = -EFAULT; + } + + kfree(rootrefs); + btrfs_free_path(path); - kfree(args); return ret; } @@ -2309,12 +2812,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, struct btrfs_root *root = BTRFS_I(dir)->root; struct btrfs_root *dest = NULL; struct btrfs_ioctl_vol_args *vol_args; - struct btrfs_trans_handle *trans; - struct btrfs_block_rsv block_rsv; - u64 root_flags; - u64 qgroup_reserved; int namelen; - int ret; int err = 0; if (!S_ISDIR(dir->i_mode)) @@ -2398,133 +2896,11 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, } inode_lock(inode); - - /* - * Don't allow to delete a subvolume with send in progress. This is - * inside the i_mutex so the error handling that has to drop the bit - * again is not run concurrently. - */ - spin_lock(&dest->root_item_lock); - root_flags = btrfs_root_flags(&dest->root_item); - if (dest->send_in_progress == 0) { - btrfs_set_root_flags(&dest->root_item, - root_flags | BTRFS_ROOT_SUBVOL_DEAD); - spin_unlock(&dest->root_item_lock); - } else { - spin_unlock(&dest->root_item_lock); - btrfs_warn(fs_info, - "Attempt to delete subvolume %llu during send", - dest->root_key.objectid); - err = -EPERM; - goto out_unlock_inode; - } - - down_write(&fs_info->subvol_sem); - - err = may_destroy_subvol(dest); - if (err) - goto out_up_write; - - btrfs_init_block_rsv(&block_rsv, BTRFS_BLOCK_RSV_TEMP); - /* - * One for dir inode, two for dir entries, two for root - * ref/backref. - */ - err = btrfs_subvolume_reserve_metadata(root, &block_rsv, - 5, &qgroup_reserved, true); - if (err) - goto out_up_write; - - trans = btrfs_start_transaction(root, 0); - if (IS_ERR(trans)) { - err = PTR_ERR(trans); - goto out_release; - } - trans->block_rsv = &block_rsv; - trans->bytes_reserved = block_rsv.size; - - btrfs_record_snapshot_destroy(trans, BTRFS_I(dir)); - - ret = btrfs_unlink_subvol(trans, root, dir, - dest->root_key.objectid, - dentry->d_name.name, - dentry->d_name.len); - if (ret) { - err = ret; - btrfs_abort_transaction(trans, ret); - goto out_end_trans; - } - - btrfs_record_root_in_trans(trans, dest); - - memset(&dest->root_item.drop_progress, 0, - sizeof(dest->root_item.drop_progress)); - dest->root_item.drop_level = 0; - btrfs_set_root_refs(&dest->root_item, 0); - - if (!test_and_set_bit(BTRFS_ROOT_ORPHAN_ITEM_INSERTED, &dest->state)) { - ret = btrfs_insert_orphan_item(trans, - fs_info->tree_root, - dest->root_key.objectid); - if (ret) { - btrfs_abort_transaction(trans, ret); - err = ret; - goto out_end_trans; - } - } - - ret = btrfs_uuid_tree_rem(trans, fs_info, dest->root_item.uuid, - BTRFS_UUID_KEY_SUBVOL, - dest->root_key.objectid); - if (ret && ret != -ENOENT) { - btrfs_abort_transaction(trans, ret); - err = ret; - goto out_end_trans; - } - if (!btrfs_is_empty_uuid(dest->root_item.received_uuid)) { - ret = btrfs_uuid_tree_rem(trans, fs_info, - dest->root_item.received_uuid, - BTRFS_UUID_KEY_RECEIVED_SUBVOL, - dest->root_key.objectid); - if (ret && ret != -ENOENT) { - btrfs_abort_transaction(trans, ret); - err = ret; - goto out_end_trans; - } - } - -out_end_trans: - trans->block_rsv = NULL; - trans->bytes_reserved = 0; - ret = btrfs_end_transaction(trans); - if (ret && !err) - err = ret; - inode->i_flags |= S_DEAD; -out_release: - btrfs_subvolume_release_metadata(fs_info, &block_rsv); -out_up_write: - up_write(&fs_info->subvol_sem); - if (err) { - spin_lock(&dest->root_item_lock); - root_flags = btrfs_root_flags(&dest->root_item); - btrfs_set_root_flags(&dest->root_item, - root_flags & ~BTRFS_ROOT_SUBVOL_DEAD); - spin_unlock(&dest->root_item_lock); - } -out_unlock_inode: + err = btrfs_delete_subvolume(dir, dentry); inode_unlock(inode); - if (!err) { - d_invalidate(dentry); - btrfs_invalidate_inodes(dest); + if (!err) d_delete(dentry); - ASSERT(dest->send_in_progress == 0); - /* the last ref */ - if (dest->ino_cache_inode) { - iput(dest->ino_cache_inode); - dest->ino_cache_inode = NULL; - } - } out_dput: dput(dentry); out_unlock_dir: @@ -2613,7 +2989,6 @@ static long btrfs_ioctl_add_dev(struct btrfs_fs_info *fs_info, void __user *arg) if (test_and_set_bit(BTRFS_FS_EXCL_OP, &fs_info->flags)) return BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; - mutex_lock(&fs_info->volume_mutex); vol_args = memdup_user(arg, sizeof(*vol_args)); if (IS_ERR(vol_args)) { ret = PTR_ERR(vol_args); @@ -2628,7 +3003,6 @@ static long btrfs_ioctl_add_dev(struct btrfs_fs_info *fs_info, void __user *arg) kfree(vol_args); out: - mutex_unlock(&fs_info->volume_mutex); clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); return ret; } @@ -2654,8 +3028,10 @@ static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg) } /* Check for compatibility reject unknown flags */ - if (vol_args->flags & ~BTRFS_VOL_ARG_V2_FLAGS_SUPPORTED) - return -EOPNOTSUPP; + if (vol_args->flags & ~BTRFS_VOL_ARG_V2_FLAGS_SUPPORTED) { + ret = -EOPNOTSUPP; + goto out; + } if (test_and_set_bit(BTRFS_FS_EXCL_OP, &fs_info->flags)) { ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; @@ -2954,8 +3330,6 @@ static void btrfs_cmp_data_free(struct cmp_pages *cmp) put_page(pg); } } - kfree(cmp->src_pages); - kfree(cmp->dst_pages); } static int btrfs_cmp_data_prepare(struct inode *src, u64 loff, @@ -2964,40 +3338,14 @@ static int btrfs_cmp_data_prepare(struct inode *src, u64 loff, { int ret; int num_pages = PAGE_ALIGN(len) >> PAGE_SHIFT; - struct page **src_pgarr, **dst_pgarr; - /* - * We must gather up all the pages before we initiate our - * extent locking. We use an array for the page pointers. Size - * of the array is bounded by len, which is in turn bounded by - * BTRFS_MAX_DEDUPE_LEN. - */ - src_pgarr = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); - dst_pgarr = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); - if (!src_pgarr || !dst_pgarr) { - kfree(src_pgarr); - kfree(dst_pgarr); - return -ENOMEM; - } cmp->num_pages = num_pages; - cmp->src_pages = src_pgarr; - cmp->dst_pages = dst_pgarr; - - /* - * If deduping ranges in the same inode, locking rules make it mandatory - * to always lock pages in ascending order to avoid deadlocks with - * concurrent tasks (such as starting writeback/delalloc). - */ - if (src == dst && dst_loff < loff) { - swap(src_pgarr, dst_pgarr); - swap(loff, dst_loff); - } - ret = gather_extent_pages(src, src_pgarr, cmp->num_pages, loff); + ret = gather_extent_pages(src, cmp->src_pages, num_pages, loff); if (ret) goto out; - ret = gather_extent_pages(dst, dst_pgarr, cmp->num_pages, dst_loff); + ret = gather_extent_pages(dst, cmp->dst_pages, num_pages, dst_loff); out: if (ret) @@ -3067,31 +3415,23 @@ static int extent_same_check_offsets(struct inode *inode, u64 off, u64 *plen, return 0; } -static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, - struct inode *dst, u64 dst_loff) +static int btrfs_extent_same_range(struct inode *src, u64 loff, u64 olen, + struct inode *dst, u64 dst_loff, + struct cmp_pages *cmp) { int ret; u64 len = olen; - struct cmp_pages cmp; bool same_inode = (src == dst); u64 same_lock_start = 0; u64 same_lock_len = 0; - if (len == 0) - return 0; - - if (same_inode) - inode_lock(src); - else - btrfs_double_inode_lock(src, dst); - ret = extent_same_check_offsets(src, loff, &len, olen); if (ret) - goto out_unlock; + return ret; ret = extent_same_check_offsets(dst, dst_loff, &len, olen); if (ret) - goto out_unlock; + return ret; if (same_inode) { /* @@ -3108,32 +3448,21 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, * allow an unaligned length so long as it ends at * i_size. */ - if (len != olen) { - ret = -EINVAL; - goto out_unlock; - } + if (len != olen) + return -EINVAL; /* Check for overlapping ranges */ - if (dst_loff + len > loff && dst_loff < loff + len) { - ret = -EINVAL; - goto out_unlock; - } + if (dst_loff + len > loff && dst_loff < loff + len) + return -EINVAL; same_lock_start = min_t(u64, loff, dst_loff); same_lock_len = max_t(u64, loff, dst_loff) + len - same_lock_start; } - /* don't make the dst file partly checksummed */ - if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) != - (BTRFS_I(dst)->flags & BTRFS_INODE_NODATASUM)) { - ret = -EINVAL; - goto out_unlock; - } - again: - ret = btrfs_cmp_data_prepare(src, loff, dst, dst_loff, olen, &cmp); + ret = btrfs_cmp_data_prepare(src, loff, dst, dst_loff, olen, cmp); if (ret) - goto out_unlock; + return ret; if (same_inode) ret = lock_extent_range(src, same_lock_start, same_lock_len, @@ -3154,7 +3483,7 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, * Ranges in the io trees already unlocked. Now unlock all * pages before waiting for all IO to complete. */ - btrfs_cmp_data_free(&cmp); + btrfs_cmp_data_free(cmp); if (same_inode) { btrfs_wait_ordered_range(src, same_lock_start, same_lock_len); @@ -3167,12 +3496,12 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, ASSERT(ret == 0); if (WARN_ON(ret)) { /* ranges in the io trees already unlocked */ - btrfs_cmp_data_free(&cmp); + btrfs_cmp_data_free(cmp); return ret; } /* pass original length for comparison so we stay within i_size */ - ret = btrfs_cmp_data(olen, &cmp); + ret = btrfs_cmp_data(olen, cmp); if (ret == 0) ret = btrfs_clone(src, dst, loff, olen, len, dst_loff, 1); @@ -3182,18 +3511,91 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, else btrfs_double_extent_unlock(src, loff, dst, dst_loff, len); - btrfs_cmp_data_free(&cmp); + btrfs_cmp_data_free(cmp); + + return ret; +} + +#define BTRFS_MAX_DEDUPE_LEN SZ_16M + +static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, + struct inode *dst, u64 dst_loff) +{ + int ret; + struct cmp_pages cmp; + int num_pages = PAGE_ALIGN(BTRFS_MAX_DEDUPE_LEN) >> PAGE_SHIFT; + bool same_inode = (src == dst); + u64 i, tail_len, chunk_count; + + if (olen == 0) + return 0; + + if (same_inode) + inode_lock(src); + else + btrfs_double_inode_lock(src, dst); + + /* don't make the dst file partly checksummed */ + if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) != + (BTRFS_I(dst)->flags & BTRFS_INODE_NODATASUM)) { + ret = -EINVAL; + goto out_unlock; + } + + tail_len = olen % BTRFS_MAX_DEDUPE_LEN; + chunk_count = div_u64(olen, BTRFS_MAX_DEDUPE_LEN); + if (chunk_count == 0) + num_pages = PAGE_ALIGN(tail_len) >> PAGE_SHIFT; + + /* + * If deduping ranges in the same inode, locking rules make it + * mandatory to always lock pages in ascending order to avoid deadlocks + * with concurrent tasks (such as starting writeback/delalloc). + */ + if (same_inode && dst_loff < loff) + swap(loff, dst_loff); + + /* + * We must gather up all the pages before we initiate our extent + * locking. We use an array for the page pointers. Size of the array is + * bounded by len, which is in turn bounded by BTRFS_MAX_DEDUPE_LEN. + */ + cmp.src_pages = kvmalloc_array(num_pages, sizeof(struct page *), + GFP_KERNEL | __GFP_ZERO); + cmp.dst_pages = kvmalloc_array(num_pages, sizeof(struct page *), + GFP_KERNEL | __GFP_ZERO); + if (!cmp.src_pages || !cmp.dst_pages) { + ret = -ENOMEM; + goto out_free; + } + + for (i = 0; i < chunk_count; i++) { + ret = btrfs_extent_same_range(src, loff, BTRFS_MAX_DEDUPE_LEN, + dst, dst_loff, &cmp); + if (ret) + goto out_unlock; + + loff += BTRFS_MAX_DEDUPE_LEN; + dst_loff += BTRFS_MAX_DEDUPE_LEN; + } + + if (tail_len > 0) + ret = btrfs_extent_same_range(src, loff, tail_len, dst, + dst_loff, &cmp); + out_unlock: if (same_inode) inode_unlock(src); else btrfs_double_inode_unlock(src, dst); +out_free: + kvfree(cmp.src_pages); + kvfree(cmp.dst_pages); + return ret; } -#define BTRFS_MAX_DEDUPE_LEN SZ_16M - ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen, struct file *dst_file, u64 dst_loff) { @@ -3202,9 +3604,6 @@ ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen, u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize; ssize_t res; - if (olen > BTRFS_MAX_DEDUPE_LEN) - olen = BTRFS_MAX_DEDUPE_LEN; - if (WARN_ON_ONCE(bs < PAGE_SIZE)) { /* * Btrfs does not support blocksize < page_size. As a @@ -3826,11 +4225,6 @@ static noinline int btrfs_clone_files(struct file *file, struct file *file_src, src->i_sb != inode->i_sb) return -EXDEV; - /* don't make the dst file partly checksummed */ - if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) != - (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) - return -EINVAL; - if (S_ISDIR(src->i_mode) || S_ISDIR(inode->i_mode)) return -EISDIR; @@ -3840,6 +4234,13 @@ static noinline int btrfs_clone_files(struct file *file, struct file *file_src, inode_lock(src); } + /* don't make the dst file partly checksummed */ + if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) != + (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) { + ret = -EINVAL; + goto out_unlock; + } + /* determine range to clone */ ret = -EINVAL; if (off + len > src->i_size || off + len < off) @@ -4007,8 +4408,8 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp) return ret; } -void btrfs_get_block_group_info(struct list_head *groups_list, - struct btrfs_ioctl_space_info *space) +static void get_block_group_info(struct list_head *groups_list, + struct btrfs_ioctl_space_info *space) { struct btrfs_block_group_cache *block_group; @@ -4124,8 +4525,8 @@ static long btrfs_ioctl_space_info(struct btrfs_fs_info *fs_info, down_read(&info->groups_sem); for (c = 0; c < BTRFS_NR_RAID_TYPES; c++) { if (!list_empty(&info->block_groups[c])) { - btrfs_get_block_group_info( - &info->block_groups[c], &space); + get_block_group_info(&info->block_groups[c], + &space); memcpy(dest, &space, sizeof(space)); dest++; space_args.total_spaces++; @@ -4490,14 +4891,14 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_fs_info *fs_info, return ret; } -void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock, +void btrfs_update_ioctl_balance_args(struct btrfs_fs_info *fs_info, struct btrfs_ioctl_balance_args *bargs) { struct btrfs_balance_control *bctl = fs_info->balance_ctl; bargs->flags = bctl->flags; - if (atomic_read(&fs_info->balance_running)) + if (test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)) bargs->state |= BTRFS_BALANCE_STATE_RUNNING; if (atomic_read(&fs_info->balance_pause_req)) bargs->state |= BTRFS_BALANCE_STATE_PAUSE_REQ; @@ -4508,13 +4909,9 @@ void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock, memcpy(&bargs->meta, &bctl->meta, sizeof(bargs->meta)); memcpy(&bargs->sys, &bctl->sys, sizeof(bargs->sys)); - if (lock) { - spin_lock(&fs_info->balance_lock); - memcpy(&bargs->stat, &bctl->stat, sizeof(bargs->stat)); - spin_unlock(&fs_info->balance_lock); - } else { - memcpy(&bargs->stat, &bctl->stat, sizeof(bargs->stat)); - } + spin_lock(&fs_info->balance_lock); + memcpy(&bargs->stat, &bctl->stat, sizeof(bargs->stat)); + spin_unlock(&fs_info->balance_lock); } static long btrfs_ioctl_balance(struct file *file, void __user *arg) @@ -4535,7 +4932,6 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg) again: if (!test_and_set_bit(BTRFS_FS_EXCL_OP, &fs_info->flags)) { - mutex_lock(&fs_info->volume_mutex); mutex_lock(&fs_info->balance_mutex); need_unlock = true; goto locked; @@ -4550,21 +4946,22 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg) mutex_lock(&fs_info->balance_mutex); if (fs_info->balance_ctl) { /* this is either (2) or (3) */ - if (!atomic_read(&fs_info->balance_running)) { + if (!test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)) { mutex_unlock(&fs_info->balance_mutex); - if (!mutex_trylock(&fs_info->volume_mutex)) - goto again; + /* + * Lock released to allow other waiters to continue, + * we'll reexamine the status again. + */ mutex_lock(&fs_info->balance_mutex); if (fs_info->balance_ctl && - !atomic_read(&fs_info->balance_running)) { + !test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)) { /* this is (3) */ need_unlock = false; goto locked; } mutex_unlock(&fs_info->balance_mutex); - mutex_unlock(&fs_info->volume_mutex); goto again; } else { /* this is (2) */ @@ -4617,7 +5014,6 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg) goto out_bargs; } - bctl->fs_info = fs_info; if (arg) { memcpy(&bctl->data, &bargs->data, sizeof(bctl->data)); memcpy(&bctl->meta, &bargs->meta, sizeof(bctl->meta)); @@ -4636,14 +5032,14 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg) do_balance: /* - * Ownership of bctl and filesystem flag BTRFS_FS_EXCL_OP - * goes to to btrfs_balance. bctl is freed in __cancel_balance, - * or, if restriper was paused all the way until unmount, in - * free_fs_info. The flag is cleared in __cancel_balance. + * Ownership of bctl and filesystem flag BTRFS_FS_EXCL_OP goes to + * btrfs_balance. bctl is freed in reset_balance_state, or, if + * restriper was paused all the way until unmount, in free_fs_info. + * The flag should be cleared after reset_balance_state. */ need_unlock = false; - ret = btrfs_balance(bctl, bargs); + ret = btrfs_balance(fs_info, bctl, bargs); bctl = NULL; if (arg) { @@ -4657,7 +5053,6 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg) kfree(bargs); out_unlock: mutex_unlock(&fs_info->balance_mutex); - mutex_unlock(&fs_info->volume_mutex); if (need_unlock) clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); out: @@ -4701,7 +5096,7 @@ static long btrfs_ioctl_balance_progress(struct btrfs_fs_info *fs_info, goto out; } - update_ioctl_balance_args(fs_info, 1, bargs); + btrfs_update_ioctl_balance_args(fs_info, bargs); if (copy_to_user(arg, bargs, sizeof(*bargs))) ret = -EFAULT; @@ -5038,8 +5433,7 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file, BTRFS_UUID_SIZE); if (received_uuid_changed && !btrfs_is_empty_uuid(root_item->received_uuid)) { - ret = btrfs_uuid_tree_rem(trans, fs_info, - root_item->received_uuid, + ret = btrfs_uuid_tree_remove(trans, root_item->received_uuid, BTRFS_UUID_KEY_RECEIVED_SUBVOL, root->root_key.objectid); if (ret && ret != -ENOENT) { @@ -5063,7 +5457,7 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file, goto out; } if (received_uuid_changed && !btrfs_is_empty_uuid(sa->uuid)) { - ret = btrfs_uuid_tree_add(trans, fs_info, sa->uuid, + ret = btrfs_uuid_tree_add(trans, sa->uuid, BTRFS_UUID_KEY_RECEIVED_SUBVOL, root->root_key.objectid); if (ret < 0 && ret != -EEXIST) { @@ -5497,7 +5891,7 @@ long btrfs_ioctl(struct file *file, unsigned int case BTRFS_IOC_SYNC: { int ret; - ret = btrfs_start_delalloc_roots(fs_info, 0, -1); + ret = btrfs_start_delalloc_roots(fs_info, -1); if (ret) return ret; ret = btrfs_sync_fs(inode->i_sb, 1); @@ -5565,6 +5959,16 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_get_features(file, argp); case BTRFS_IOC_SET_FEATURES: return btrfs_ioctl_set_features(file, argp); + case FS_IOC_FSGETXATTR: + return btrfs_ioctl_fsgetxattr(file, argp); + case FS_IOC_FSSETXATTR: + return btrfs_ioctl_fssetxattr(file, argp); + case BTRFS_IOC_GET_SUBVOL_INFO: + return btrfs_ioctl_get_subvol_info(file, argp); + case BTRFS_IOC_GET_SUBVOL_ROOTREF: + return btrfs_ioctl_get_subvol_rootref(file, argp); + case BTRFS_IOC_INO_LOOKUP_USER: + return btrfs_ioctl_ino_lookup_user(file, argp); } return -ENOTTY; diff --git a/fs/btrfs/locking.c b/fs/btrfs/locking.c index e4faefac9d16e91939ac8d8504adf646decde4e6..1da768e5ef75bda36142c084d6ba7c2b5c4bc809 100644 --- a/fs/btrfs/locking.c +++ b/fs/btrfs/locking.c @@ -66,22 +66,16 @@ void btrfs_clear_lock_blocking_rw(struct extent_buffer *eb, int rw) write_lock(&eb->lock); WARN_ON(atomic_read(&eb->spinning_writers)); atomic_inc(&eb->spinning_writers); - /* - * atomic_dec_and_test implies a barrier for waitqueue_active - */ - if (atomic_dec_and_test(&eb->blocking_writers) && - waitqueue_active(&eb->write_lock_wq)) - wake_up(&eb->write_lock_wq); + /* atomic_dec_and_test implies a barrier */ + if (atomic_dec_and_test(&eb->blocking_writers)) + cond_wake_up_nomb(&eb->write_lock_wq); } else if (rw == BTRFS_READ_LOCK_BLOCKING) { BUG_ON(atomic_read(&eb->blocking_readers) == 0); read_lock(&eb->lock); atomic_inc(&eb->spinning_readers); - /* - * atomic_dec_and_test implies a barrier for waitqueue_active - */ - if (atomic_dec_and_test(&eb->blocking_readers) && - waitqueue_active(&eb->read_lock_wq)) - wake_up(&eb->read_lock_wq); + /* atomic_dec_and_test implies a barrier */ + if (atomic_dec_and_test(&eb->blocking_readers)) + cond_wake_up_nomb(&eb->read_lock_wq); } } @@ -221,12 +215,9 @@ void btrfs_tree_read_unlock_blocking(struct extent_buffer *eb) } btrfs_assert_tree_read_locked(eb); WARN_ON(atomic_read(&eb->blocking_readers) == 0); - /* - * atomic_dec_and_test implies a barrier for waitqueue_active - */ - if (atomic_dec_and_test(&eb->blocking_readers) && - waitqueue_active(&eb->read_lock_wq)) - wake_up(&eb->read_lock_wq); + /* atomic_dec_and_test implies a barrier */ + if (atomic_dec_and_test(&eb->blocking_readers)) + cond_wake_up_nomb(&eb->read_lock_wq); atomic_dec(&eb->read_locks); } @@ -275,12 +266,9 @@ void btrfs_tree_unlock(struct extent_buffer *eb) if (blockers) { WARN_ON(atomic_read(&eb->spinning_writers)); atomic_dec(&eb->blocking_writers); - /* - * Make sure counter is updated before we wake up waiters. - */ + /* Use the lighter barrier after atomic */ smp_mb__after_atomic(); - if (waitqueue_active(&eb->write_lock_wq)) - wake_up(&eb->write_lock_wq); + cond_wake_up_nomb(&eb->write_lock_wq); } else { WARN_ON(atomic_read(&eb->spinning_writers) != 1); atomic_dec(&eb->spinning_writers); diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 0667ea07f766fc0bf63a8a219d779d99601e6a31..b6a4cc178beefd3cee6ce5573f0954fa084b71f0 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -17,6 +17,43 @@ #define LZO_LEN 4 +/* + * Btrfs LZO compression format + * + * Regular and inlined LZO compressed data extents consist of: + * + * 1. Header + * Fixed size. LZO_LEN (4) bytes long, LE32. + * Records the total size (including the header) of compressed data. + * + * 2. Segment(s) + * Variable size. Each segment includes one segment header, followd by data + * payload. + * One regular LZO compressed extent can have one or more segments. + * For inlined LZO compressed extent, only one segment is allowed. + * One segment represents at most one page of uncompressed data. + * + * 2.1 Segment header + * Fixed size. LZO_LEN (4) bytes long, LE32. + * Records the total size of the segment (not including the header). + * Segment header never crosses page boundary, thus it's possible to + * have at most 3 padding zeros at the end of the page. + * + * 2.2 Data Payload + * Variable size. Size up limit should be lzo1x_worst_compress(PAGE_SIZE) + * which is 4419 for a 4KiB page. + * + * Example: + * Page 1: + * 0 0x2 0x4 0x6 0x8 0xa 0xc 0xe 0x10 + * 0x0000 | Header | SegHdr 01 | Data payload 01 ... | + * ... + * 0x0ff0 | SegHdr N | Data payload N ... |00| + * ^^ padding zeros + * Page 2: + * 0x1000 | SegHdr N+1| Data payload N+1 ... | + */ + struct workspace { void *mem; void *buf; /* where decompressed data goes */ @@ -258,6 +295,7 @@ static int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) unsigned long working_bytes; size_t in_len; size_t out_len; + const size_t max_segment_len = lzo1x_worst_compress(PAGE_SIZE); unsigned long in_offset; unsigned long in_page_bytes_left; unsigned long tot_in; @@ -271,10 +309,22 @@ static int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) data_in = kmap(pages_in[0]); tot_len = read_compress_length(data_in); + /* + * Compressed data header check. + * + * The real compressed size can't exceed the maximum extent length, and + * all pages should be used (whole unused page with just the segment + * header is not possible). If this happens it means the compressed + * extent is corrupted. + */ + if (tot_len > min_t(size_t, BTRFS_MAX_COMPRESSED, srclen) || + tot_len < srclen - PAGE_SIZE) { + ret = -EUCLEAN; + goto done; + } tot_in = LZO_LEN; in_offset = LZO_LEN; - tot_len = min_t(size_t, srclen, tot_len); in_page_bytes_left = PAGE_SIZE - LZO_LEN; tot_out = 0; @@ -285,6 +335,17 @@ static int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) in_offset += LZO_LEN; tot_in += LZO_LEN; + /* + * Segment header check. + * + * The segment length must not exceed the maximum LZO + * compression size, nor the total compressed size. + */ + if (in_len > max_segment_len || tot_in + in_len > tot_len) { + ret = -EUCLEAN; + goto done; + } + tot_in += in_len; working_bytes = in_len; may_late_unmap = need_unmap = false; @@ -335,7 +396,7 @@ static int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) } } - out_len = lzo1x_worst_compress(PAGE_SIZE); + out_len = max_segment_len; ret = lzo1x_decompress_safe(buf, in_len, workspace->buf, &out_len); if (need_unmap) @@ -369,15 +430,24 @@ static int lzo_decompress(struct list_head *ws, unsigned char *data_in, struct workspace *workspace = list_entry(ws, struct workspace, list); size_t in_len; size_t out_len; + size_t max_segment_len = lzo1x_worst_compress(PAGE_SIZE); int ret = 0; char *kaddr; unsigned long bytes; - BUG_ON(srclen < LZO_LEN); + if (srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2) + return -EUCLEAN; + in_len = read_compress_length(data_in); + if (in_len != srclen) + return -EUCLEAN; data_in += LZO_LEN; in_len = read_compress_length(data_in); + if (in_len != srclen - LZO_LEN * 2) { + ret = -EUCLEAN; + goto out; + } data_in += LZO_LEN; out_len = PAGE_SIZE; diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 6db8bb2f2c28ea07a87ebdcbb8e983af7623baff..2e1a1694a33d1a7a51a0149015f48527a1044e38 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -343,11 +343,8 @@ int btrfs_dec_test_first_ordered_pending(struct inode *inode, if (entry->bytes_left == 0) { ret = test_and_set_bit(BTRFS_ORDERED_IO_DONE, &entry->flags); - /* - * Implicit memory barrier after test_and_set_bit - */ - if (waitqueue_active(&entry->wait)) - wake_up(&entry->wait); + /* test_and_set_bit implies a barrier */ + cond_wake_up_nomb(&entry->wait); } else { ret = 1; } @@ -410,11 +407,8 @@ int btrfs_dec_test_ordered_pending(struct inode *inode, if (entry->bytes_left == 0) { ret = test_and_set_bit(BTRFS_ORDERED_IO_DONE, &entry->flags); - /* - * Implicit memory barrier after test_and_set_bit - */ - if (waitqueue_active(&entry->wait)) - wake_up(&entry->wait); + /* test_and_set_bit implies a barrier */ + cond_wake_up_nomb(&entry->wait); } else { ret = 1; } diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c index 21a831d3d087418bc2e4db884f4fd6a25e9df4e5..a4e11cf04671e6777802922d460dec16007b8380 100644 --- a/fs/btrfs/print-tree.c +++ b/fs/btrfs/print-tree.c @@ -166,6 +166,25 @@ static void print_uuid_item(struct extent_buffer *l, unsigned long offset, } } +/* + * Helper to output refs and locking status of extent buffer. Useful to debug + * race condition related problems. + */ +static void print_eb_refs_lock(struct extent_buffer *eb) +{ +#ifdef CONFIG_BTRFS_DEBUG + btrfs_info(eb->fs_info, +"refs %u lock (w:%d r:%d bw:%d br:%d sw:%d sr:%d) lock_owner %u current %u", + atomic_read(&eb->refs), atomic_read(&eb->write_locks), + atomic_read(&eb->read_locks), + atomic_read(&eb->blocking_writers), + atomic_read(&eb->blocking_readers), + atomic_read(&eb->spinning_writers), + atomic_read(&eb->spinning_readers), + eb->lock_owner, current->pid); +#endif +} + void btrfs_print_leaf(struct extent_buffer *l) { struct btrfs_fs_info *fs_info; @@ -193,6 +212,7 @@ void btrfs_print_leaf(struct extent_buffer *l) "leaf %llu gen %llu total ptrs %d free space %d owner %llu", btrfs_header_bytenr(l), btrfs_header_generation(l), nr, btrfs_leaf_free_space(fs_info, l), btrfs_header_owner(l)); + print_eb_refs_lock(l); for (i = 0 ; i < nr ; i++) { item = btrfs_item_nr(i); btrfs_item_key_to_cpu(l, &key, i); @@ -347,6 +367,7 @@ void btrfs_print_tree(struct extent_buffer *c, bool follow) btrfs_header_bytenr(c), level, btrfs_header_generation(c), nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(fs_info) - nr, btrfs_header_owner(c)); + print_eb_refs_lock(c); for (i = 0; i < nr; i++) { btrfs_node_key_to_cpu(c, &key, i); pr_info("\tkey %d (%llu %u %llu) block %llu gen %llu\n", diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 9fb758d5077a8441e994df53652fe32f771c2a49..1874a6d2e6f5422c809759d0ca29e9bb973826bb 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -1882,8 +1882,8 @@ static int qgroup_update_counters(struct btrfs_fs_info *fs_info, cur_old_count = btrfs_qgroup_get_old_refcnt(qg, seq); cur_new_count = btrfs_qgroup_get_new_refcnt(qg, seq); - trace_qgroup_update_counters(fs_info, qg->qgroupid, - cur_old_count, cur_new_count); + trace_qgroup_update_counters(fs_info, qg, cur_old_count, + cur_new_count); /* Rfer update part */ if (cur_old_count == 0 && cur_new_count > 0) { @@ -2014,8 +2014,8 @@ btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, BUG_ON(!fs_info->quota_root); - trace_btrfs_qgroup_account_extent(fs_info, bytenr, num_bytes, - nr_old_roots, nr_new_roots); + trace_btrfs_qgroup_account_extent(fs_info, trans->transid, bytenr, + num_bytes, nr_old_roots, nr_new_roots); qgroups = ulist_alloc(GFP_NOFS); if (!qgroups) { @@ -2579,6 +2579,21 @@ void btrfs_qgroup_free_refroot(struct btrfs_fs_info *fs_info, spin_unlock(&fs_info->qgroup_lock); } +/* + * Check if the leaf is the last leaf. Which means all node pointers + * are at their last position. + */ +static bool is_last_leaf(struct btrfs_path *path) +{ + int i; + + for (i = 1; i < BTRFS_MAX_LEVEL && path->nodes[i]; i++) { + if (path->slots[i] != btrfs_header_nritems(path->nodes[i]) - 1) + return false; + } + return true; +} + /* * returns < 0 on error, 0 when more leafs are to be scanned. * returns 1 when done. @@ -2590,8 +2605,8 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path, struct btrfs_key found; struct extent_buffer *scratch_leaf = NULL; struct ulist *roots = NULL; - struct seq_list tree_mod_seq_elem = SEQ_LIST_INIT(tree_mod_seq_elem); u64 num_bytes; + bool done; int slot; int ret; @@ -2620,12 +2635,12 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path, mutex_unlock(&fs_info->qgroup_rescan_lock); return ret; } + done = is_last_leaf(path); btrfs_item_key_to_cpu(path->nodes[0], &found, btrfs_header_nritems(path->nodes[0]) - 1); fs_info->qgroup_rescan_progress.objectid = found.objectid + 1; - btrfs_get_tree_mod_seq(fs_info, &tree_mod_seq_elem); scratch_leaf = btrfs_clone_extent_buffer(path->nodes[0]); if (!scratch_leaf) { ret = -ENOMEM; @@ -2664,8 +2679,9 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path, btrfs_tree_read_unlock_blocking(scratch_leaf); free_extent_buffer(scratch_leaf); } - btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem); + if (done && !ret) + ret = 1; return ret; } @@ -2681,6 +2697,12 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) path = btrfs_alloc_path(); if (!path) goto out; + /* + * Rescan should only search for commit root, and any later difference + * should be recorded by qgroup + */ + path->search_commit_root = 1; + path->skip_locking = 1; err = 0; while (!err && !btrfs_fs_closing(fs_info)) { @@ -2760,26 +2782,36 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid, { int ret = 0; - if (!init_flags && - (!(fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) || - !(fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_ON))) { - ret = -EINVAL; - goto err; + if (!init_flags) { + /* we're resuming qgroup rescan at mount time */ + if (!(fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN)) + btrfs_warn(fs_info, + "qgroup rescan init failed, qgroup is not enabled"); + else if (!(fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_ON)) + btrfs_warn(fs_info, + "qgroup rescan init failed, qgroup rescan is not queued"); + return -EINVAL; } mutex_lock(&fs_info->qgroup_rescan_lock); spin_lock(&fs_info->qgroup_lock); if (init_flags) { - if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) + if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) { + btrfs_warn(fs_info, + "qgroup rescan is already in progress"); ret = -EINPROGRESS; - else if (!(fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_ON)) + } else if (!(fs_info->qgroup_flags & + BTRFS_QGROUP_STATUS_FLAG_ON)) { + btrfs_warn(fs_info, + "qgroup rescan init failed, qgroup is not enabled"); ret = -EINVAL; + } if (ret) { spin_unlock(&fs_info->qgroup_lock); mutex_unlock(&fs_info->qgroup_rescan_lock); - goto err; + return ret; } fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_RESCAN; } @@ -2798,13 +2830,6 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid, btrfs_init_work(&fs_info->qgroup_rescan_work, btrfs_qgroup_rescan_helper, btrfs_qgroup_rescan_worker, NULL, NULL); - - if (ret) { -err: - btrfs_info(fs_info, "qgroup_rescan_init failed with %d", ret); - return ret; - } - return 0; } diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index 9abd950e7f7882f0052770ed6d49fe71b5f5dafc..5e4ad134b9ad12881edb33b7962e13155edc9d45 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -163,6 +163,12 @@ struct btrfs_raid_bio { * bitmap to record which horizontal stripe has data */ unsigned long *dbitmap; + + /* allocated with real_stripes-many pointers for finish_*() calls */ + void **finish_pointers; + + /* allocated with stripe_npages-many bits for finish_*() calls */ + unsigned long *finish_pbitmap; }; static int __raid56_parity_recover(struct btrfs_raid_bio *rbio); @@ -981,9 +987,14 @@ static struct btrfs_raid_bio *alloc_rbio(struct btrfs_fs_info *fs_info, int stripe_npages = DIV_ROUND_UP(stripe_len, PAGE_SIZE); void *p; - rbio = kzalloc(sizeof(*rbio) + num_pages * sizeof(struct page *) * 2 + - DIV_ROUND_UP(stripe_npages, BITS_PER_LONG) * - sizeof(long), GFP_NOFS); + rbio = kzalloc(sizeof(*rbio) + + sizeof(*rbio->stripe_pages) * num_pages + + sizeof(*rbio->bio_pages) * num_pages + + sizeof(*rbio->finish_pointers) * real_stripes + + sizeof(*rbio->dbitmap) * BITS_TO_LONGS(stripe_npages) + + sizeof(*rbio->finish_pbitmap) * + BITS_TO_LONGS(stripe_npages), + GFP_NOFS); if (!rbio) return ERR_PTR(-ENOMEM); @@ -1005,13 +1016,20 @@ static struct btrfs_raid_bio *alloc_rbio(struct btrfs_fs_info *fs_info, atomic_set(&rbio->stripes_pending, 0); /* - * the stripe_pages and bio_pages array point to the extra + * the stripe_pages, bio_pages, etc arrays point to the extra * memory we allocated past the end of the rbio */ p = rbio + 1; - rbio->stripe_pages = p; - rbio->bio_pages = p + sizeof(struct page *) * num_pages; - rbio->dbitmap = p + sizeof(struct page *) * num_pages * 2; +#define CONSUME_ALLOC(ptr, count) do { \ + ptr = p; \ + p = (unsigned char *)p + sizeof(*(ptr)) * (count); \ + } while (0) + CONSUME_ALLOC(rbio->stripe_pages, num_pages); + CONSUME_ALLOC(rbio->bio_pages, num_pages); + CONSUME_ALLOC(rbio->finish_pointers, real_stripes); + CONSUME_ALLOC(rbio->dbitmap, BITS_TO_LONGS(stripe_npages)); + CONSUME_ALLOC(rbio->finish_pbitmap, BITS_TO_LONGS(stripe_npages)); +#undef CONSUME_ALLOC if (bbio->map_type & BTRFS_BLOCK_GROUP_RAID5) nr_data = real_stripes - 1; @@ -1180,7 +1198,7 @@ static void index_rbio_pages(struct btrfs_raid_bio *rbio) static noinline void finish_rmw(struct btrfs_raid_bio *rbio) { struct btrfs_bio *bbio = rbio->bbio; - void *pointers[rbio->real_stripes]; + void **pointers = rbio->finish_pointers; int nr_data = rbio->nr_data; int stripe; int pagenr; @@ -2350,8 +2368,8 @@ static noinline void finish_parity_scrub(struct btrfs_raid_bio *rbio, int need_check) { struct btrfs_bio *bbio = rbio->bbio; - void *pointers[rbio->real_stripes]; - DECLARE_BITMAP(pbitmap, rbio->stripe_npages); + void **pointers = rbio->finish_pointers; + unsigned long *pbitmap = rbio->finish_pbitmap; int nr_data = rbio->nr_data; int stripe; int pagenr; diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index b041b945a7ae81d8d49272871f8c0009d3ea4ae2..879b76fa881a5a6ded712dac891bd34d60ffdc18 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -4299,7 +4299,7 @@ struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info, return inode; } -static struct reloc_control *alloc_reloc_control(struct btrfs_fs_info *fs_info) +static struct reloc_control *alloc_reloc_control(void) { struct reloc_control *rc; @@ -4344,7 +4344,7 @@ static void describe_relocation(struct btrfs_fs_info *fs_info, DESCRIBE_FLAG(RAID5, "raid5"); DESCRIBE_FLAG(RAID6, "raid6"); if (flags) - snprintf(buf, buf - bp + sizeof(buf), "|0x%llx", flags); + snprintf(bp, buf - bp + sizeof(buf), "|0x%llx", flags); #undef DESCRIBE_FLAG } @@ -4366,7 +4366,7 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start) int rw = 0; int err = 0; - rc = alloc_reloc_control(fs_info); + rc = alloc_reloc_control(); if (!rc) return -ENOMEM; @@ -4562,7 +4562,7 @@ int btrfs_recover_relocation(struct btrfs_root *root) if (list_empty(&reloc_roots)) goto out; - rc = alloc_reloc_control(fs_info); + rc = alloc_reloc_control(); if (!rc) { err = -ENOMEM; goto out; diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 52b39a0924e9f8ebcbc118b5a7f869b7a2b6f042..a5900586201056f0c7429a35ad1b2255c917f40a 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -3984,6 +3984,7 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx, spin_lock(&fs_info->unused_bgs_lock); if (list_empty(&cache->bg_list)) { btrfs_get_block_group(cache); + trace_btrfs_add_unused_block_group(cache); list_add_tail(&cache->bg_list, &fs_info->unused_bgs); } diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index c0074d2d7d6d2788d1909f5a945addaa7fc065c8..c47f62b1922678752aa721cde6cfce8f23f20aac 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -235,6 +235,7 @@ struct orphan_dir_info { struct rb_node node; u64 ino; u64 gen; + u64 last_dir_index_offset; }; struct name_cache_entry { @@ -2844,12 +2845,6 @@ add_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino) struct rb_node *parent = NULL; struct orphan_dir_info *entry, *odi; - odi = kmalloc(sizeof(*odi), GFP_KERNEL); - if (!odi) - return ERR_PTR(-ENOMEM); - odi->ino = dir_ino; - odi->gen = 0; - while (*p) { parent = *p; entry = rb_entry(parent, struct orphan_dir_info, node); @@ -2858,11 +2853,17 @@ add_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino) } else if (dir_ino > entry->ino) { p = &(*p)->rb_right; } else { - kfree(odi); return entry; } } + odi = kmalloc(sizeof(*odi), GFP_KERNEL); + if (!odi) + return ERR_PTR(-ENOMEM); + odi->ino = dir_ino; + odi->gen = 0; + odi->last_dir_index_offset = 0; + rb_link_node(&odi->node, parent, p); rb_insert_color(&odi->node, &sctx->orphan_dirs); return odi; @@ -2917,6 +2918,7 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen, struct btrfs_key found_key; struct btrfs_key loc; struct btrfs_dir_item *di; + struct orphan_dir_info *odi = NULL; /* * Don't try to rmdir the top/root subvolume dir. @@ -2931,6 +2933,11 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen, key.objectid = dir; key.type = BTRFS_DIR_INDEX_KEY; key.offset = 0; + + odi = get_orphan_dir_info(sctx, dir); + if (odi) + key.offset = odi->last_dir_index_offset; + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); if (ret < 0) goto out; @@ -2958,30 +2965,33 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen, dm = get_waiting_dir_move(sctx, loc.objectid); if (dm) { - struct orphan_dir_info *odi; - odi = add_orphan_dir_info(sctx, dir); if (IS_ERR(odi)) { ret = PTR_ERR(odi); goto out; } odi->gen = dir_gen; + odi->last_dir_index_offset = found_key.offset; dm->rmdir_ino = dir; ret = 0; goto out; } if (loc.objectid > send_progress) { - struct orphan_dir_info *odi; - - odi = get_orphan_dir_info(sctx, dir); - free_orphan_dir_info(sctx, odi); + odi = add_orphan_dir_info(sctx, dir); + if (IS_ERR(odi)) { + ret = PTR_ERR(odi); + goto out; + } + odi->gen = dir_gen; + odi->last_dir_index_offset = found_key.offset; ret = 0; goto out; } path->slots[0]++; } + free_orphan_dir_info(sctx, odi); ret = 1; @@ -3259,13 +3269,16 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) if (rmdir_ino) { struct orphan_dir_info *odi; + u64 gen; odi = get_orphan_dir_info(sctx, rmdir_ino); if (!odi) { /* already deleted */ goto finish; } - ret = can_rmdir(sctx, rmdir_ino, odi->gen, sctx->cur_ino); + gen = odi->gen; + + ret = can_rmdir(sctx, rmdir_ino, gen, sctx->cur_ino); if (ret < 0) goto out; if (!ret) @@ -3276,13 +3289,12 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) ret = -ENOMEM; goto out; } - ret = get_cur_path(sctx, rmdir_ino, odi->gen, name); + ret = get_cur_path(sctx, rmdir_ino, gen, name); if (ret < 0) goto out; ret = send_rmdir(sctx, name); if (ret < 0) goto out; - free_orphan_dir_info(sctx, odi); } finish: @@ -6454,7 +6466,7 @@ static void btrfs_root_dec_send_in_progress(struct btrfs_root* root) */ if (root->send_in_progress < 0) btrfs_err(root->fs_info, - "send_in_progres unbalanced %d root %llu", + "send_in_progress unbalanced %d root %llu", root->send_in_progress, root->root_key.objectid); spin_unlock(&root->root_item_lock); } diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 0628092b0b1bde633f9e2c0f6c339feda75013ec..81107ad49f3a9c75eccf2c95f9dad8d7d0a0a6f6 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -323,6 +323,7 @@ enum { Opt_ssd, Opt_nossd, Opt_ssd_spread, Opt_nossd_spread, Opt_subvol, + Opt_subvol_empty, Opt_subvolid, Opt_thread_pool, Opt_treelog, Opt_notreelog, @@ -388,6 +389,7 @@ static const match_table_t tokens = { {Opt_ssd_spread, "ssd_spread"}, {Opt_nossd_spread, "nossd_spread"}, {Opt_subvol, "subvol=%s"}, + {Opt_subvol_empty, "subvol="}, {Opt_subvolid, "subvolid=%s"}, {Opt_thread_pool, "thread_pool=%u"}, {Opt_treelog, "treelog"}, @@ -461,6 +463,7 @@ int btrfs_parse_options(struct btrfs_fs_info *info, char *options, btrfs_set_opt(info->mount_opt, DEGRADED); break; case Opt_subvol: + case Opt_subvol_empty: case Opt_subvolid: case Opt_subvolrootid: case Opt_device: @@ -1782,10 +1785,8 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data) } ret = btrfs_parse_options(fs_info, data, *flags); - if (ret) { - ret = -EINVAL; + if (ret) goto restore; - } btrfs_remount_begin(fs_info, old_opts, *flags); btrfs_resize_thread_pool(fs_info, diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 4848a4318fb5429ada1e83114c48e99822f121df..4a4e960c7c665f56a9f418b2806d5a2904e0eb42 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -210,12 +210,42 @@ static struct attribute *btrfs_supported_feature_attrs[] = { NULL }; +/* + * Features which depend on feature bits and may differ between each fs. + * + * /sys/fs/btrfs/features lists all available features of this kernel while + * /sys/fs/btrfs/UUID/features shows features of the fs which are enabled or + * can be changed online. + */ static const struct attribute_group btrfs_feature_attr_group = { .name = "features", .is_visible = btrfs_feature_visible, .attrs = btrfs_supported_feature_attrs, }; +static ssize_t rmdir_subvol_show(struct kobject *kobj, + struct kobj_attribute *ka, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0\n"); +} +BTRFS_ATTR(static_feature, rmdir_subvol, rmdir_subvol_show); + +static struct attribute *btrfs_supported_static_feature_attrs[] = { + BTRFS_ATTR_PTR(static_feature, rmdir_subvol), + NULL +}; + +/* + * Features which only depend on kernel version. + * + * These are listed in /sys/fs/btrfs/features along with + * btrfs_feature_attr_group + */ +static const struct attribute_group btrfs_static_feature_attr_group = { + .name = "features", + .attrs = btrfs_supported_static_feature_attrs, +}; + static ssize_t btrfs_show_u64(u64 *value_ptr, spinlock_t *lock, char *buf) { u64 val; @@ -514,10 +544,11 @@ static inline struct btrfs_fs_info *to_fs_info(struct kobject *kobj) } #define NUM_FEATURE_BITS 64 -static char btrfs_unknown_feature_names[3][NUM_FEATURE_BITS][13]; -static struct btrfs_feature_attr btrfs_feature_attrs[3][NUM_FEATURE_BITS]; +#define BTRFS_FEATURE_NAME_MAX 13 +static char btrfs_unknown_feature_names[FEAT_MAX][NUM_FEATURE_BITS][BTRFS_FEATURE_NAME_MAX]; +static struct btrfs_feature_attr btrfs_feature_attrs[FEAT_MAX][NUM_FEATURE_BITS]; -static const u64 supported_feature_masks[3] = { +static const u64 supported_feature_masks[FEAT_MAX] = { [FEAT_COMPAT] = BTRFS_FEATURE_COMPAT_SUPP, [FEAT_COMPAT_RO] = BTRFS_FEATURE_COMPAT_RO_SUPP, [FEAT_INCOMPAT] = BTRFS_FEATURE_INCOMPAT_SUPP, @@ -589,7 +620,7 @@ void btrfs_sysfs_remove_fsid(struct btrfs_fs_devices *fs_devs) return; } - list_for_each_entry(fs_devs, fs_uuids, list) { + list_for_each_entry(fs_devs, fs_uuids, fs_list) { __btrfs_sysfs_remove_fsid(fs_devs); } } @@ -609,7 +640,7 @@ void btrfs_sysfs_remove_mounted(struct btrfs_fs_info *fs_info) btrfs_sysfs_rm_device_link(fs_info->fs_devices, NULL); } -const char * const btrfs_feature_set_names[3] = { +const char * const btrfs_feature_set_names[FEAT_MAX] = { [FEAT_COMPAT] = "compat", [FEAT_COMPAT_RO] = "compat_ro", [FEAT_INCOMPAT] = "incompat", @@ -673,7 +704,7 @@ static void init_feature_attrs(void) if (fa->kobj_attr.attr.name) continue; - snprintf(name, 13, "%s:%u", + snprintf(name, BTRFS_FEATURE_NAME_MAX, "%s:%u", btrfs_feature_set_names[set], i); fa->kobj_attr.attr.name = name; @@ -900,8 +931,15 @@ int __init btrfs_init_sysfs(void) ret = sysfs_create_group(&btrfs_kset->kobj, &btrfs_feature_attr_group); if (ret) goto out2; + ret = sysfs_merge_group(&btrfs_kset->kobj, + &btrfs_static_feature_attr_group); + if (ret) + goto out_remove_group; return 0; + +out_remove_group: + sysfs_remove_group(&btrfs_kset->kobj, &btrfs_feature_attr_group); out2: debugfs_remove_recursive(btrfs_debugfs_root_dentry); out1: @@ -912,6 +950,8 @@ int __init btrfs_init_sysfs(void) void __cold btrfs_exit_sysfs(void) { + sysfs_unmerge_group(&btrfs_kset->kobj, + &btrfs_static_feature_attr_group); sysfs_remove_group(&btrfs_kset->kobj, &btrfs_feature_attr_group); kset_unregister(btrfs_kset); debugfs_remove_recursive(btrfs_debugfs_root_dentry); diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h index b567560d9aa984d70b645d9e4aadd8d5198e0e13..c6ee600aff899945e2e547e59826892d926bb8f8 100644 --- a/fs/btrfs/sysfs.h +++ b/fs/btrfs/sysfs.h @@ -9,7 +9,7 @@ extern u64 btrfs_debugfs_test; enum btrfs_feature_set { - FEAT_COMPAT, + FEAT_COMPAT = 0, FEAT_COMPAT_RO, FEAT_INCOMPAT, FEAT_MAX @@ -77,7 +77,7 @@ attr_to_btrfs_feature_attr(struct attribute *attr) } char *btrfs_printable_features(enum btrfs_feature_set set, u64 flags); -extern const char * const btrfs_feature_set_names[3]; +extern const char * const btrfs_feature_set_names[FEAT_MAX]; extern struct kobj_type space_info_ktype; extern struct kobj_type btrfs_raid_ktype; int btrfs_sysfs_add_device_link(struct btrfs_fs_devices *fs_devices, diff --git a/fs/btrfs/tests/btrfs-tests.c b/fs/btrfs/tests/btrfs-tests.c index 30ed438da2a9f71ba735bcd31f852cfdedc2f4db..db72b3b6209e000e8d4a1a94507ed9185715dd28 100644 --- a/fs/btrfs/tests/btrfs-tests.c +++ b/fs/btrfs/tests/btrfs-tests.c @@ -219,11 +219,13 @@ void btrfs_free_dummy_block_group(struct btrfs_block_group_cache *cache) kfree(cache); } -void btrfs_init_dummy_trans(struct btrfs_trans_handle *trans) +void btrfs_init_dummy_trans(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info) { memset(trans, 0, sizeof(*trans)); trans->transid = 1; trans->type = __TRANS_DUMMY; + trans->fs_info = fs_info; } int btrfs_run_sanity_tests(void) diff --git a/fs/btrfs/tests/btrfs-tests.h b/fs/btrfs/tests/btrfs-tests.h index a5a0b9500d3e344307c12e88887c8e4f6ac8328f..70ff9f9d86a1b2e35a050fb877d473f74751b282 100644 --- a/fs/btrfs/tests/btrfs-tests.h +++ b/fs/btrfs/tests/btrfs-tests.h @@ -9,7 +9,8 @@ #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS int btrfs_run_sanity_tests(void); -#define test_msg(fmt, ...) pr_info("BTRFS: selftest: " fmt, ##__VA_ARGS__) +#define test_msg(fmt, ...) pr_info("BTRFS: selftest: " fmt "\n", ##__VA_ARGS__) +#define test_err(fmt, ...) pr_err("BTRFS: selftest: " fmt "\n", ##__VA_ARGS__) struct btrfs_root; struct btrfs_trans_handle; @@ -28,7 +29,8 @@ void btrfs_free_dummy_root(struct btrfs_root *root); struct btrfs_block_group_cache * btrfs_alloc_dummy_block_group(struct btrfs_fs_info *fs_info, unsigned long length); void btrfs_free_dummy_block_group(struct btrfs_block_group_cache *cache); -void btrfs_init_dummy_trans(struct btrfs_trans_handle *trans); +void btrfs_init_dummy_trans(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info); #else static inline int btrfs_run_sanity_tests(void) { diff --git a/fs/btrfs/tests/extent-buffer-tests.c b/fs/btrfs/tests/extent-buffer-tests.c index 31e8a9ec228c5cbec7ff98128e436ee5f2eb43f1..7d72eab6d32c2687967d3b0d992749b80c9e65d7 100644 --- a/fs/btrfs/tests/extent-buffer-tests.c +++ b/fs/btrfs/tests/extent-buffer-tests.c @@ -26,31 +26,31 @@ static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) u32 value_len = strlen(value); int ret = 0; - test_msg("Running btrfs_split_item tests\n"); + test_msg("running btrfs_split_item tests"); fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize); if (!fs_info) { - test_msg("Could not allocate fs_info\n"); + test_err("could not allocate fs_info"); return -ENOMEM; } root = btrfs_alloc_dummy_root(fs_info); if (IS_ERR(root)) { - test_msg("Could not allocate root\n"); + test_err("could not allocate root"); ret = PTR_ERR(root); goto out; } path = btrfs_alloc_path(); if (!path) { - test_msg("Could not allocate path\n"); + test_err("could not allocate path"); ret = -ENOMEM; goto out; } path->nodes[0] = eb = alloc_dummy_extent_buffer(fs_info, nodesize); if (!eb) { - test_msg("Could not allocate dummy buffer\n"); + test_err("could not allocate dummy buffer"); ret = -ENOMEM; goto out; } @@ -75,7 +75,7 @@ static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) */ ret = btrfs_split_item(NULL, root, path, &key, 17); if (ret) { - test_msg("Split item failed %d\n", ret); + test_err("split item failed %d", ret); goto out; } @@ -86,14 +86,14 @@ static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) 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"); + test_err("invalid key at slot 0"); 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"); + test_err("invalid len in the first split"); ret = -EINVAL; goto out; } @@ -101,8 +101,8 @@ static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) 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", + test_err( +"data in the buffer doesn't match what it should in the first split have='%.*s' want '%s'", (int)strlen(split1), buf, split1); ret = -EINVAL; goto out; @@ -111,14 +111,14 @@ static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) 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"); + test_err("invalid key at slot 1"); 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"); + test_err("invalid len in the second split"); ret = -EINVAL; goto out; } @@ -126,8 +126,8 @@ static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) 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"); + test_err( + "data in the buffer doesn't match what it should in the second split"); ret = -EINVAL; goto out; } @@ -136,21 +136,21 @@ static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) /* 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); + test_err("second split item failed %d", 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"); + test_err("invalid key at slot 0"); 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"); + test_err("invalid len in the first split"); ret = -EINVAL; goto out; } @@ -158,8 +158,8 @@ static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) 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"); + test_err( + "data in the buffer doesn't match what it should in the third split"); ret = -EINVAL; goto out; } @@ -167,14 +167,14 @@ static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) 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"); + test_err("invalid key at slot 1"); 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"); + test_err("invalid len in the second split"); ret = -EINVAL; goto out; } @@ -182,8 +182,8 @@ static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) 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"); + test_err( + "data in the buffer doesn't match what it should in the fourth split"); ret = -EINVAL; goto out; } @@ -191,14 +191,14 @@ static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) 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"); + test_err("invalid key at slot 2"); 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"); + test_err("invalid len in the second split"); ret = -EINVAL; goto out; } @@ -206,8 +206,8 @@ static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) 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"); + test_err( + "data in the buffer doesn't match what it should in the last chunk"); ret = -EINVAL; goto out; } @@ -220,6 +220,6 @@ static int test_btrfs_split_item(u32 sectorsize, u32 nodesize) int btrfs_test_extent_buffer_operations(u32 sectorsize, u32 nodesize) { - test_msg("Running extent buffer operation tests\n"); + test_msg("running extent buffer operation tests"); return test_btrfs_split_item(sectorsize, nodesize); } diff --git a/fs/btrfs/tests/extent-io-tests.c b/fs/btrfs/tests/extent-io-tests.c index 76aa5a678a96a2471304b5fa7edd417a7f6e7103..d9269a531a4d4e0f55d247afc861d5ec32bd5034 100644 --- a/fs/btrfs/tests/extent-io-tests.c +++ b/fs/btrfs/tests/extent-io-tests.c @@ -46,7 +46,9 @@ static noinline int process_page_range(struct inode *inode, u64 start, u64 end, 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); + printk(KERN_ERR + "stuck in a loop, start %llu, end %llu, nr_pages %lu, ret %d\n", + start, end, nr_pages, ret); break; } } @@ -66,11 +68,11 @@ static int test_find_delalloc(u32 sectorsize) u64 found; int ret = -EINVAL; - test_msg("Running find delalloc tests\n"); + test_msg("running find delalloc tests"); inode = btrfs_new_test_inode(); if (!inode) { - test_msg("Failed to allocate test inode\n"); + test_err("failed to allocate test inode"); return -ENOMEM; } @@ -84,7 +86,7 @@ static int test_find_delalloc(u32 sectorsize) for (index = 0; index < (total_dirty >> PAGE_SHIFT); index++) { page = find_or_create_page(inode->i_mapping, index, GFP_KERNEL); if (!page) { - test_msg("Failed to allocate test page\n"); + test_err("failed to allocate test page"); ret = -ENOMEM; goto out; } @@ -107,11 +109,11 @@ static int test_find_delalloc(u32 sectorsize) 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"); + test_err("should have found at least one delalloc"); goto out_bits; } if (start != 0 || end != (sectorsize - 1)) { - test_msg("Expected start 0 end %u, got start %llu end %llu\n", + test_err("expected start 0 end %u, got start %llu end %llu", sectorsize - 1, start, end); goto out_bits; } @@ -129,7 +131,7 @@ static int test_find_delalloc(u32 sectorsize) locked_page = find_lock_page(inode->i_mapping, test_start >> PAGE_SHIFT); if (!locked_page) { - test_msg("Couldn't find the locked page\n"); + test_err("couldn't find the locked page"); goto out_bits; } set_extent_delalloc(&tmp, sectorsize, max_bytes - 1, 0, NULL); @@ -138,17 +140,17 @@ static int test_find_delalloc(u32 sectorsize) 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"); + test_err("couldn't find delalloc in our range"); 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); + test_err("expected start %llu end %llu, got start %llu, end %llu", + 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"); + test_err("there were unlocked pages in the range"); goto out_bits; } unlock_extent(&tmp, start, end); @@ -164,7 +166,7 @@ static int test_find_delalloc(u32 sectorsize) locked_page = find_lock_page(inode->i_mapping, test_start >> PAGE_SHIFT); if (!locked_page) { - test_msg("Couldn't find the locked page\n"); + test_err("couldn't find the locked page"); goto out_bits; } start = test_start; @@ -172,11 +174,11 @@ static int test_find_delalloc(u32 sectorsize) 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"); + test_err("found range when we shouldn't have"); goto out_bits; } if (end != (u64)-1) { - test_msg("Did not return the proper end offset\n"); + test_err("did not return the proper end offset"); goto out_bits; } @@ -193,17 +195,17 @@ static int test_find_delalloc(u32 sectorsize) found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, &end, max_bytes); if (!found) { - test_msg("Didn't find our range\n"); + test_err("didn't find our range"); 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_err("expected start %llu end %llu, got start %llu end %llu", 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"); + test_err("pages in range were not all locked"); goto out_bits; } unlock_extent(&tmp, start, end); @@ -215,7 +217,7 @@ static int test_find_delalloc(u32 sectorsize) page = find_get_page(inode->i_mapping, (max_bytes + SZ_1M) >> PAGE_SHIFT); if (!page) { - test_msg("Couldn't find our page\n"); + test_err("couldn't find our page"); goto out_bits; } ClearPageDirty(page); @@ -234,18 +236,17 @@ static int test_find_delalloc(u32 sectorsize) found = find_lock_delalloc_range(inode, &tmp, locked_page, &start, &end, max_bytes); if (!found) { - test_msg("Didn't find our range\n"); + test_err("didn't find our range"); goto out_bits; } if (start != test_start && end != test_start + PAGE_SIZE - 1) { - test_msg("Expected start %Lu end %Lu, got start %Lu end %Lu\n", - test_start, test_start + PAGE_SIZE - 1, start, - end); + test_err("expected start %llu end %llu, got start %llu end %llu", + test_start, test_start + PAGE_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"); + test_err("pages in range were not all locked"); goto out_bits; } ret = 0; @@ -271,14 +272,14 @@ static int check_eb_bitmap(unsigned long *bitmap, struct extent_buffer *eb, bit = !!test_bit(i, bitmap); bit1 = !!extent_buffer_test_bit(eb, 0, i); if (bit1 != bit) { - test_msg("Bits do not match\n"); + test_err("bits do not match"); return -EINVAL; } bit1 = !!extent_buffer_test_bit(eb, i / BITS_PER_BYTE, i % BITS_PER_BYTE); if (bit1 != bit) { - test_msg("Offset bits do not match\n"); + test_err("offset bits do not match"); return -EINVAL; } } @@ -295,7 +296,7 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb, memset(bitmap, 0, len); memzero_extent_buffer(eb, 0, len); if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) { - test_msg("Bitmap was not zeroed\n"); + test_err("bitmap was not zeroed"); return -EINVAL; } @@ -303,7 +304,7 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb, extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE); ret = check_eb_bitmap(bitmap, eb, len); if (ret) { - test_msg("Setting all bits failed\n"); + test_err("setting all bits failed"); return ret; } @@ -311,7 +312,7 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb, extent_buffer_bitmap_clear(eb, 0, 0, len * BITS_PER_BYTE); ret = check_eb_bitmap(bitmap, eb, len); if (ret) { - test_msg("Clearing all bits failed\n"); + test_err("clearing all bits failed"); return ret; } @@ -324,7 +325,7 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb, sizeof(long) * BITS_PER_BYTE); ret = check_eb_bitmap(bitmap, eb, len); if (ret) { - test_msg("Setting straddling pages failed\n"); + test_err("setting straddling pages failed"); return ret; } @@ -337,7 +338,7 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb, sizeof(long) * BITS_PER_BYTE); ret = check_eb_bitmap(bitmap, eb, len); if (ret) { - test_msg("Clearing straddling pages failed\n"); + test_err("clearing straddling pages failed"); return ret; } } @@ -361,7 +362,7 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb, ret = check_eb_bitmap(bitmap, eb, len); if (ret) { - test_msg("Random bit pattern failed\n"); + test_err("random bit pattern failed"); return ret; } @@ -376,7 +377,7 @@ static int test_eb_bitmaps(u32 sectorsize, u32 nodesize) struct extent_buffer *eb; int ret; - test_msg("Running extent buffer bitmap tests\n"); + test_msg("running extent buffer bitmap tests"); /* * In ppc64, sectorsize can be 64K, thus 4 * 64K will be larger than @@ -389,13 +390,13 @@ static int test_eb_bitmaps(u32 sectorsize, u32 nodesize) bitmap = kmalloc(len, GFP_KERNEL); if (!bitmap) { - test_msg("Couldn't allocate test bitmap\n"); + test_err("couldn't allocate test bitmap"); return -ENOMEM; } eb = __alloc_dummy_extent_buffer(fs_info, 0, len); if (!eb) { - test_msg("Couldn't allocate test extent buffer\n"); + test_err("couldn't allocate test extent buffer"); kfree(bitmap); return -ENOMEM; } @@ -408,7 +409,7 @@ static int test_eb_bitmaps(u32 sectorsize, u32 nodesize) free_extent_buffer(eb); eb = __alloc_dummy_extent_buffer(NULL, nodesize / 2, len); if (!eb) { - test_msg("Couldn't allocate test extent buffer\n"); + test_err("couldn't allocate test extent buffer"); kfree(bitmap); return -ENOMEM; } @@ -424,7 +425,7 @@ int btrfs_test_extent_io(u32 sectorsize, u32 nodesize) { int ret; - test_msg("Running extent I/O tests\n"); + test_msg("running extent I/O tests"); ret = test_find_delalloc(sectorsize); if (ret) @@ -432,6 +433,6 @@ int btrfs_test_extent_io(u32 sectorsize, u32 nodesize) ret = test_eb_bitmaps(sectorsize, nodesize); out: - test_msg("Extent I/O tests finished\n"); + test_msg("extent I/O tests finished"); return ret; } diff --git a/fs/btrfs/tests/extent-map-tests.c b/fs/btrfs/tests/extent-map-tests.c index 79e0a5f4d9c9a591e02f0a9854e9b09721170ffc..385a5316e4bf7c74dbdd1667aea54d7154e8be98 100644 --- a/fs/btrfs/tests/extent-map-tests.c +++ b/fs/btrfs/tests/extent-map-tests.c @@ -19,8 +19,8 @@ static void free_extent_map_tree(struct extent_map_tree *em_tree) #ifdef CONFIG_BTRFS_DEBUG if (refcount_read(&em->refs) != 1) { - test_msg( -"em leak: em (start 0x%llx len 0x%llx block_start 0x%llx block_len 0x%llx) refs %d\n", + test_err( +"em leak: em (start 0x%llx len 0x%llx block_start 0x%llx block_len 0x%llx) refs %d", em->start, em->len, em->block_start, em->block_len, refcount_read(&em->refs)); @@ -47,7 +47,8 @@ static void free_extent_map_tree(struct extent_map_tree *em_tree) * ->add_extent_mapping(0, 16K) * -> #handle -EEXIST */ -static void test_case_1(struct extent_map_tree *em_tree) +static void test_case_1(struct btrfs_fs_info *fs_info, + struct extent_map_tree *em_tree) { struct extent_map *em; u64 start = 0; @@ -90,14 +91,14 @@ static void test_case_1(struct extent_map_tree *em_tree) em->len = len; em->block_start = start; em->block_len = len; - ret = btrfs_add_extent_mapping(em_tree, &em, em->start, em->len); + ret = btrfs_add_extent_mapping(fs_info, em_tree, &em, em->start, em->len); if (ret) - test_msg("case1 [%llu %llu]: ret %d\n", start, start + len, ret); + test_err("case1 [%llu %llu]: ret %d", start, start + len, ret); if (em && (em->start != 0 || extent_map_end(em) != SZ_16K || em->block_start != 0 || em->block_len != SZ_16K)) - test_msg( -"case1 [%llu %llu]: ret %d return a wrong em (start %llu len %llu block_start %llu block_len %llu\n", + test_err( +"case1 [%llu %llu]: ret %d return a wrong em (start %llu len %llu block_start %llu block_len %llu", start, start + len, ret, em->start, em->len, em->block_start, em->block_len); free_extent_map(em); @@ -112,7 +113,8 @@ static void test_case_1(struct extent_map_tree *em_tree) * Reading the inline ending up with EEXIST, ie. read an inline * extent and discard page cache and read it again. */ -static void test_case_2(struct extent_map_tree *em_tree) +static void test_case_2(struct btrfs_fs_info *fs_info, + struct extent_map_tree *em_tree) { struct extent_map *em; int ret; @@ -153,14 +155,14 @@ static void test_case_2(struct extent_map_tree *em_tree) em->len = SZ_1K; em->block_start = EXTENT_MAP_INLINE; em->block_len = (u64)-1; - ret = btrfs_add_extent_mapping(em_tree, &em, em->start, em->len); + ret = btrfs_add_extent_mapping(fs_info, em_tree, &em, em->start, em->len); if (ret) - test_msg("case2 [0 1K]: ret %d\n", ret); + test_err("case2 [0 1K]: ret %d", ret); if (em && (em->start != 0 || extent_map_end(em) != SZ_1K || em->block_start != EXTENT_MAP_INLINE || em->block_len != (u64)-1)) - test_msg( -"case2 [0 1K]: ret %d return a wrong em (start %llu len %llu block_start %llu block_len %llu\n", + test_err( +"case2 [0 1K]: ret %d return a wrong em (start %llu len %llu block_start %llu block_len %llu", ret, em->start, em->len, em->block_start, em->block_len); free_extent_map(em); @@ -169,7 +171,8 @@ static void test_case_2(struct extent_map_tree *em_tree) free_extent_map_tree(em_tree); } -static void __test_case_3(struct extent_map_tree *em_tree, u64 start) +static void __test_case_3(struct btrfs_fs_info *fs_info, + struct extent_map_tree *em_tree, u64 start) { struct extent_map *em; u64 len = SZ_4K; @@ -198,9 +201,9 @@ static void __test_case_3(struct extent_map_tree *em_tree, u64 start) em->len = SZ_16K; em->block_start = 0; em->block_len = SZ_16K; - ret = btrfs_add_extent_mapping(em_tree, &em, start, len); + ret = btrfs_add_extent_mapping(fs_info, em_tree, &em, start, len); if (ret) - test_msg("case3 [0x%llx 0x%llx): ret %d\n", + test_err("case3 [0x%llx 0x%llx): ret %d", start, start + len, ret); /* * Since bytes within em are contiguous, em->block_start is identical to @@ -209,8 +212,8 @@ static void __test_case_3(struct extent_map_tree *em_tree, u64 start) if (em && (start < em->start || start + len > extent_map_end(em) || em->start != em->block_start || em->len != em->block_len)) - test_msg( -"case3 [0x%llx 0x%llx): ret %d em (start 0x%llx len 0x%llx block_start 0x%llx block_len 0x%llx)\n", + test_err( +"case3 [0x%llx 0x%llx): ret %d em (start 0x%llx len 0x%llx block_start 0x%llx block_len 0x%llx)", start, start + len, ret, em->start, em->len, em->block_start, em->block_len); free_extent_map(em); @@ -235,14 +238,16 @@ static void __test_case_3(struct extent_map_tree *em_tree, u64 start) * -> add_extent_mapping() * -> add_extent_mapping() */ -static void test_case_3(struct extent_map_tree *em_tree) +static void test_case_3(struct btrfs_fs_info *fs_info, + struct extent_map_tree *em_tree) { - __test_case_3(em_tree, 0); - __test_case_3(em_tree, SZ_8K); - __test_case_3(em_tree, (12 * 1024ULL)); + __test_case_3(fs_info, em_tree, 0); + __test_case_3(fs_info, em_tree, SZ_8K); + __test_case_3(fs_info, em_tree, (12 * 1024ULL)); } -static void __test_case_4(struct extent_map_tree *em_tree, u64 start) +static void __test_case_4(struct btrfs_fs_info *fs_info, + struct extent_map_tree *em_tree, u64 start) { struct extent_map *em; u64 len = SZ_4K; @@ -283,14 +288,14 @@ static void __test_case_4(struct extent_map_tree *em_tree, u64 start) em->len = SZ_32K; em->block_start = 0; em->block_len = SZ_32K; - ret = btrfs_add_extent_mapping(em_tree, &em, start, len); + ret = btrfs_add_extent_mapping(fs_info, em_tree, &em, start, len); if (ret) - test_msg("case4 [0x%llx 0x%llx): ret %d\n", + test_err("case4 [0x%llx 0x%llx): ret %d", start, len, ret); if (em && (start < em->start || start + len > extent_map_end(em))) - test_msg( -"case4 [0x%llx 0x%llx): ret %d, added wrong em (start 0x%llx len 0x%llx block_start 0x%llx block_len 0x%llx)\n", + test_err( +"case4 [0x%llx 0x%llx): ret %d, added wrong em (start 0x%llx len 0x%llx block_start 0x%llx block_len 0x%llx)", start, len, ret, em->start, em->len, em->block_start, em->block_len); free_extent_map(em); @@ -324,30 +329,45 @@ static void __test_case_4(struct extent_map_tree *em_tree, u64 start) * # handle -EEXIST when adding * # [0, 32K) */ -static void test_case_4(struct extent_map_tree *em_tree) +static void test_case_4(struct btrfs_fs_info *fs_info, + struct extent_map_tree *em_tree) { - __test_case_4(em_tree, 0); - __test_case_4(em_tree, SZ_4K); + __test_case_4(fs_info, em_tree, 0); + __test_case_4(fs_info, em_tree, SZ_4K); } int btrfs_test_extent_map(void) { + struct btrfs_fs_info *fs_info = NULL; struct extent_map_tree *em_tree; - test_msg("Running extent_map tests\n"); + test_msg("running extent_map tests"); + + /* + * Note: the fs_info is not set up completely, we only need + * fs_info::fsid for the tracepoint. + */ + fs_info = btrfs_alloc_dummy_fs_info(PAGE_SIZE, PAGE_SIZE); + if (!fs_info) { + test_msg("Couldn't allocate dummy fs info"); + return -ENOMEM; + } em_tree = kzalloc(sizeof(*em_tree), GFP_KERNEL); if (!em_tree) /* Skip the test on error. */ - return 0; + goto out; extent_map_tree_init(em_tree); - test_case_1(em_tree); - test_case_2(em_tree); - test_case_3(em_tree); - test_case_4(em_tree); + test_case_1(fs_info, em_tree); + test_case_2(fs_info, em_tree); + test_case_3(fs_info, em_tree); + test_case_4(fs_info, em_tree); kfree(em_tree); +out: + btrfs_free_dummy_fs_info(fs_info); + return 0; } diff --git a/fs/btrfs/tests/free-space-tests.c b/fs/btrfs/tests/free-space-tests.c index d3c9f8a59ba53f92f15a8699c745d6b93501220d..5c2f77e9439bee97c384c6c7ab0981656c327dea 100644 --- a/fs/btrfs/tests/free-space-tests.c +++ b/fs/btrfs/tests/free-space-tests.c @@ -20,63 +20,63 @@ static int test_extents(struct btrfs_block_group_cache *cache) { int ret = 0; - test_msg("Running extent only tests\n"); + test_msg("running extent only tests"); /* First just make sure we can remove an entire entry */ ret = btrfs_add_free_space(cache, 0, SZ_4M); if (ret) { - test_msg("Error adding initial extents %d\n", ret); + test_err("error adding initial extents %d", ret); return ret; } ret = btrfs_remove_free_space(cache, 0, SZ_4M); if (ret) { - test_msg("Error removing extent %d\n", ret); + test_err("error removing extent %d", ret); return ret; } if (test_check_exists(cache, 0, SZ_4M)) { - test_msg("Full remove left some lingering space\n"); + test_err("full remove left some lingering space"); return -1; } /* Ok edge and middle cases now */ ret = btrfs_add_free_space(cache, 0, SZ_4M); if (ret) { - test_msg("Error adding half extent %d\n", ret); + test_err("error adding half extent %d", ret); return ret; } ret = btrfs_remove_free_space(cache, 3 * SZ_1M, SZ_1M); if (ret) { - test_msg("Error removing tail end %d\n", ret); + test_err("error removing tail end %d", ret); return ret; } ret = btrfs_remove_free_space(cache, 0, SZ_1M); if (ret) { - test_msg("Error removing front end %d\n", ret); + test_err("error removing front end %d", ret); return ret; } ret = btrfs_remove_free_space(cache, SZ_2M, 4096); if (ret) { - test_msg("Error removing middle piece %d\n", ret); + test_err("error removing middle piece %d", ret); return ret; } if (test_check_exists(cache, 0, SZ_1M)) { - test_msg("Still have space at the front\n"); + test_err("still have space at the front"); return -1; } if (test_check_exists(cache, SZ_2M, 4096)) { - test_msg("Still have space in the middle\n"); + test_err("still have space in the middle"); return -1; } if (test_check_exists(cache, 3 * SZ_1M, SZ_1M)) { - test_msg("Still have space at the end\n"); + test_err("still have space at the end"); return -1; } @@ -92,34 +92,34 @@ static int test_bitmaps(struct btrfs_block_group_cache *cache, u64 next_bitmap_offset; int ret; - test_msg("Running bitmap only tests\n"); + test_msg("running bitmap only tests"); ret = test_add_free_space_entry(cache, 0, SZ_4M, 1); if (ret) { - test_msg("Couldn't create a bitmap entry %d\n", ret); + test_err("couldn't create a bitmap entry %d", ret); return ret; } ret = btrfs_remove_free_space(cache, 0, SZ_4M); if (ret) { - test_msg("Error removing bitmap full range %d\n", ret); + test_err("error removing bitmap full range %d", ret); return ret; } if (test_check_exists(cache, 0, SZ_4M)) { - test_msg("Left some space in bitmap\n"); + test_err("left some space in bitmap"); return -1; } ret = test_add_free_space_entry(cache, 0, SZ_4M, 1); if (ret) { - test_msg("Couldn't add to our bitmap entry %d\n", ret); + test_err("couldn't add to our bitmap entry %d", ret); return ret; } ret = btrfs_remove_free_space(cache, SZ_1M, SZ_2M); if (ret) { - test_msg("Couldn't remove middle chunk %d\n", ret); + test_err("couldn't remove middle chunk %d", ret); return ret; } @@ -133,19 +133,19 @@ static int test_bitmaps(struct btrfs_block_group_cache *cache, ret = test_add_free_space_entry(cache, next_bitmap_offset - SZ_2M, SZ_4M, 1); if (ret) { - test_msg("Couldn't add space that straddles two bitmaps %d\n", + test_err("couldn't add space that straddles two bitmaps %d", ret); return ret; } ret = btrfs_remove_free_space(cache, next_bitmap_offset - SZ_1M, SZ_2M); if (ret) { - test_msg("Couldn't remove overlapping space %d\n", ret); + test_err("couldn't remove overlapping space %d", ret); return ret; } if (test_check_exists(cache, next_bitmap_offset - SZ_1M, SZ_2M)) { - test_msg("Left some space when removing overlapping\n"); + test_err("left some space when removing overlapping"); return -1; } @@ -161,7 +161,7 @@ static int test_bitmaps_and_extents(struct btrfs_block_group_cache *cache, u64 bitmap_offset = (u64)(BITS_PER_BITMAP * sectorsize); int ret; - test_msg("Running bitmap and extent tests\n"); + test_msg("running bitmap and extent tests"); /* * First let's do something simple, an extent at the same offset as the @@ -170,42 +170,42 @@ static int test_bitmaps_and_extents(struct btrfs_block_group_cache *cache, */ ret = test_add_free_space_entry(cache, SZ_4M, SZ_1M, 1); if (ret) { - test_msg("Couldn't create bitmap entry %d\n", ret); + test_err("couldn't create bitmap entry %d", ret); return ret; } ret = test_add_free_space_entry(cache, 0, SZ_1M, 0); if (ret) { - test_msg("Couldn't add extent entry %d\n", ret); + test_err("couldn't add extent entry %d", ret); return ret; } ret = btrfs_remove_free_space(cache, 0, SZ_1M); if (ret) { - test_msg("Couldn't remove extent entry %d\n", ret); + test_err("couldn't remove extent entry %d", ret); return ret; } if (test_check_exists(cache, 0, SZ_1M)) { - test_msg("Left remnants after our remove\n"); + test_err("left remnants after our remove"); return -1; } /* Now to add back the extent entry and remove from the bitmap */ ret = test_add_free_space_entry(cache, 0, SZ_1M, 0); if (ret) { - test_msg("Couldn't re-add extent entry %d\n", ret); + test_err("couldn't re-add extent entry %d", ret); return ret; } ret = btrfs_remove_free_space(cache, SZ_4M, SZ_1M); if (ret) { - test_msg("Couldn't remove from bitmap %d\n", ret); + test_err("couldn't remove from bitmap %d", ret); return ret; } if (test_check_exists(cache, SZ_4M, SZ_1M)) { - test_msg("Left remnants in the bitmap\n"); + test_err("left remnants in the bitmap"); return -1; } @@ -215,18 +215,18 @@ static int test_bitmaps_and_extents(struct btrfs_block_group_cache *cache, */ ret = test_add_free_space_entry(cache, SZ_1M, SZ_4M, 1); if (ret) { - test_msg("Couldn't add to a bitmap %d\n", ret); + test_err("couldn't add to a bitmap %d", ret); return ret; } ret = btrfs_remove_free_space(cache, SZ_512K, 3 * SZ_1M); if (ret) { - test_msg("Couldn't remove overlapping space %d\n", ret); + test_err("couldn't remove overlapping space %d", ret); return ret; } if (test_check_exists(cache, SZ_512K, 3 * SZ_1M)) { - test_msg("Left over pieces after removing overlapping\n"); + test_err("left over pieces after removing overlapping"); return -1; } @@ -235,24 +235,24 @@ static int test_bitmaps_and_extents(struct btrfs_block_group_cache *cache, /* Now with the extent entry offset into the bitmap */ ret = test_add_free_space_entry(cache, SZ_4M, SZ_4M, 1); if (ret) { - test_msg("Couldn't add space to the bitmap %d\n", ret); + test_err("couldn't add space to the bitmap %d", ret); return ret; } ret = test_add_free_space_entry(cache, SZ_2M, SZ_2M, 0); if (ret) { - test_msg("Couldn't add extent to the cache %d\n", ret); + test_err("couldn't add extent to the cache %d", ret); return ret; } ret = btrfs_remove_free_space(cache, 3 * SZ_1M, SZ_4M); if (ret) { - test_msg("Problem removing overlapping space %d\n", ret); + test_err("problem removing overlapping space %d", ret); return ret; } if (test_check_exists(cache, 3 * SZ_1M, SZ_4M)) { - test_msg("Left something behind when removing space"); + test_err("left something behind when removing space"); return -1; } @@ -269,25 +269,25 @@ static int test_bitmaps_and_extents(struct btrfs_block_group_cache *cache, __btrfs_remove_free_space_cache(cache->free_space_ctl); ret = test_add_free_space_entry(cache, bitmap_offset + SZ_4M, SZ_4M, 1); if (ret) { - test_msg("Couldn't add bitmap %d\n", ret); + test_err("couldn't add bitmap %d", ret); return ret; } ret = test_add_free_space_entry(cache, bitmap_offset - SZ_1M, 5 * SZ_1M, 0); if (ret) { - test_msg("Couldn't add extent entry %d\n", ret); + test_err("couldn't add extent entry %d", ret); return ret; } ret = btrfs_remove_free_space(cache, bitmap_offset + SZ_1M, 5 * SZ_1M); if (ret) { - test_msg("Failed to free our space %d\n", ret); + test_err("failed to free our space %d", ret); return ret; } if (test_check_exists(cache, bitmap_offset + SZ_1M, 5 * SZ_1M)) { - test_msg("Left stuff over\n"); + test_err("left stuff over"); return -1; } @@ -301,19 +301,19 @@ static int test_bitmaps_and_extents(struct btrfs_block_group_cache *cache, */ ret = test_add_free_space_entry(cache, SZ_1M, SZ_2M, 1); if (ret) { - test_msg("Couldn't add bitmap entry %d\n", ret); + test_err("couldn't add bitmap entry %d", ret); return ret; } ret = test_add_free_space_entry(cache, 3 * SZ_1M, SZ_1M, 0); if (ret) { - test_msg("Couldn't add extent entry %d\n", ret); + test_err("couldn't add extent entry %d", ret); return ret; } ret = btrfs_remove_free_space(cache, SZ_1M, 3 * SZ_1M); if (ret) { - test_msg("Error removing bitmap and extent overlapping %d\n", ret); + test_err("error removing bitmap and extent overlapping %d", ret); return ret; } @@ -335,12 +335,14 @@ check_num_extents_and_bitmaps(const struct btrfs_block_group_cache *cache, const int num_bitmaps) { if (cache->free_space_ctl->free_extents != num_extents) { - test_msg("Incorrect # of extent entries in the cache: %d, expected %d\n", + test_err( + "incorrect # of extent entries in the cache: %d, expected %d", cache->free_space_ctl->free_extents, num_extents); return -EINVAL; } if (cache->free_space_ctl->total_bitmaps != num_bitmaps) { - test_msg("Incorrect # of extent entries in the cache: %d, expected %d\n", + test_err( + "incorrect # of extent entries in the cache: %d, expected %d", cache->free_space_ctl->total_bitmaps, num_bitmaps); return -EINVAL; } @@ -358,7 +360,7 @@ static int check_cache_empty(struct btrfs_block_group_cache *cache) * allocate. */ if (cache->free_space_ctl->free_space != 0) { - test_msg("Cache free space is not 0\n"); + test_err("cache free space is not 0"); return -EINVAL; } @@ -366,7 +368,7 @@ static int check_cache_empty(struct btrfs_block_group_cache *cache) offset = btrfs_find_space_for_alloc(cache, 0, 4096, 0, &max_extent_size); if (offset != 0) { - test_msg("Space allocation did not fail, returned offset: %llu", + test_err("space allocation did not fail, returned offset: %llu", offset); return -EINVAL; } @@ -402,7 +404,7 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, }; const struct btrfs_free_space_op *orig_free_space_ops; - test_msg("Running space stealing from bitmap to extent\n"); + test_msg("running space stealing from bitmap to extent"); /* * For this test, we want to ensure we end up with an extent entry @@ -430,7 +432,7 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, */ ret = test_add_free_space_entry(cache, SZ_128M - SZ_256K, SZ_128K, 0); if (ret) { - test_msg("Couldn't add extent entry %d\n", ret); + test_err("couldn't add extent entry %d", ret); return ret; } @@ -438,7 +440,7 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, ret = test_add_free_space_entry(cache, SZ_128M + SZ_512K, SZ_128M - SZ_512K, 1); if (ret) { - test_msg("Couldn't add bitmap entry %d\n", ret); + test_err("couldn't add bitmap entry %d", ret); return ret; } @@ -457,17 +459,17 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, SZ_128M + 768 * SZ_1K, SZ_128M - 768 * SZ_1K); if (ret) { - test_msg("Failed to free part of bitmap space %d\n", ret); + test_err("failed to free part of bitmap space %d", ret); return ret; } /* Confirm that only those 2 ranges are marked as free. */ if (!test_check_exists(cache, SZ_128M - SZ_256K, SZ_128K)) { - test_msg("Free space range missing\n"); + test_err("free space range missing"); return -ENOENT; } if (!test_check_exists(cache, SZ_128M + SZ_512K, SZ_256K)) { - test_msg("Free space range missing\n"); + test_err("free space range missing"); return -ENOENT; } @@ -477,7 +479,7 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, */ if (test_check_exists(cache, SZ_128M + 768 * SZ_1K, SZ_128M - 768 * SZ_1K)) { - test_msg("Bitmap region not removed from space cache\n"); + test_err("bitmap region not removed from space cache"); return -EINVAL; } @@ -486,7 +488,7 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, * covered by the bitmap, isn't marked as free. */ if (test_check_exists(cache, SZ_128M + SZ_256K, SZ_256K)) { - test_msg("Invalid bitmap region marked as free\n"); + test_err("invalid bitmap region marked as free"); return -EINVAL; } @@ -495,7 +497,7 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, * by the bitmap too, isn't marked as free either. */ if (test_check_exists(cache, SZ_128M, SZ_256K)) { - test_msg("Invalid bitmap region marked as free\n"); + test_err("invalid bitmap region marked as free"); return -EINVAL; } @@ -506,12 +508,12 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, */ ret = btrfs_add_free_space(cache, SZ_128M, SZ_512K); if (ret) { - test_msg("Error adding free space: %d\n", ret); + test_err("error adding free space: %d", ret); return ret; } /* Confirm the region is marked as free. */ if (!test_check_exists(cache, SZ_128M, SZ_512K)) { - test_msg("Bitmap region not marked as free\n"); + test_err("bitmap region not marked as free"); return -ENOENT; } @@ -531,7 +533,7 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, */ ret = btrfs_add_free_space(cache, SZ_128M + SZ_16M, sectorsize); if (ret) { - test_msg("Error adding free space: %d\n", ret); + test_err("error adding free space: %d", ret); return ret; } @@ -550,12 +552,12 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, */ ret = btrfs_add_free_space(cache, SZ_128M - SZ_128K, SZ_128K); if (ret) { - test_msg("Error adding free space: %d\n", ret); + test_err("error adding free space: %d", ret); return ret; } /* Confirm the region is marked as free. */ if (!test_check_exists(cache, SZ_128M - SZ_128K, SZ_128K)) { - test_msg("Extent region not marked as free\n"); + test_err("extent region not marked as free"); return -ENOENT; } @@ -583,12 +585,12 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, * allocate the whole free space at once. */ if (!test_check_exists(cache, SZ_128M - SZ_256K, SZ_1M)) { - test_msg("Expected region not marked as free\n"); + test_err("expected region not marked as free"); return -ENOENT; } if (cache->free_space_ctl->free_space != (SZ_1M + sectorsize)) { - test_msg("Cache free space is not 1Mb + %u\n", sectorsize); + test_err("cache free space is not 1Mb + %u", sectorsize); return -EINVAL; } @@ -596,7 +598,8 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, 0, SZ_1M, 0, &max_extent_size); if (offset != (SZ_128M - SZ_256K)) { - test_msg("Failed to allocate 1Mb from space cache, returned offset is: %llu\n", + test_err( + "failed to allocate 1Mb from space cache, returned offset is: %llu", offset); return -EINVAL; } @@ -610,7 +613,7 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, return ret; if (cache->free_space_ctl->free_space != sectorsize) { - test_msg("Cache free space is not %u\n", sectorsize); + test_err("cache free space is not %u", sectorsize); return -EINVAL; } @@ -618,7 +621,7 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, 0, sectorsize, 0, &max_extent_size); if (offset != (SZ_128M + SZ_16M)) { - test_msg("Failed to allocate %u, returned offset : %llu\n", + test_err("failed to allocate %u, returned offset : %llu", sectorsize, offset); return -EINVAL; } @@ -640,14 +643,14 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, */ ret = test_add_free_space_entry(cache, SZ_128M + SZ_128K, SZ_128K, 0); if (ret) { - test_msg("Couldn't add extent entry %d\n", ret); + test_err("couldn't add extent entry %d", ret); return ret; } /* Bitmap entry covering free space range [0, 128Mb - 512Kb[ */ ret = test_add_free_space_entry(cache, 0, SZ_128M - SZ_512K, 1); if (ret) { - test_msg("Couldn't add bitmap entry %d\n", ret); + test_err("couldn't add bitmap entry %d", ret); return ret; } @@ -664,17 +667,17 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, */ ret = btrfs_remove_free_space(cache, 0, SZ_128M - 768 * SZ_1K); if (ret) { - test_msg("Failed to free part of bitmap space %d\n", ret); + test_err("failed to free part of bitmap space %d", ret); return ret; } /* Confirm that only those 2 ranges are marked as free. */ if (!test_check_exists(cache, SZ_128M + SZ_128K, SZ_128K)) { - test_msg("Free space range missing\n"); + test_err("free space range missing"); return -ENOENT; } if (!test_check_exists(cache, SZ_128M - 768 * SZ_1K, SZ_256K)) { - test_msg("Free space range missing\n"); + test_err("free space range missing"); return -ENOENT; } @@ -683,7 +686,7 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, * as free anymore. */ if (test_check_exists(cache, 0, SZ_128M - 768 * SZ_1K)) { - test_msg("Bitmap region not removed from space cache\n"); + test_err("bitmap region not removed from space cache"); return -EINVAL; } @@ -692,7 +695,7 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, * covered by the bitmap, isn't marked as free. */ if (test_check_exists(cache, SZ_128M - SZ_512K, SZ_512K)) { - test_msg("Invalid bitmap region marked as free\n"); + test_err("invalid bitmap region marked as free"); return -EINVAL; } @@ -703,12 +706,12 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, */ ret = btrfs_add_free_space(cache, SZ_128M - SZ_512K, SZ_512K); if (ret) { - test_msg("Error adding free space: %d\n", ret); + test_err("error adding free space: %d", ret); return ret; } /* Confirm the region is marked as free. */ if (!test_check_exists(cache, SZ_128M - SZ_512K, SZ_512K)) { - test_msg("Bitmap region not marked as free\n"); + test_err("bitmap region not marked as free"); return -ENOENT; } @@ -728,7 +731,7 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, */ ret = btrfs_add_free_space(cache, SZ_32M, 2 * sectorsize); if (ret) { - test_msg("Error adding free space: %d\n", ret); + test_err("error adding free space: %d", ret); return ret; } @@ -739,12 +742,12 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, */ ret = btrfs_add_free_space(cache, SZ_128M, SZ_128K); if (ret) { - test_msg("Error adding free space: %d\n", ret); + test_err("error adding free space: %d", ret); return ret; } /* Confirm the region is marked as free. */ if (!test_check_exists(cache, SZ_128M, SZ_128K)) { - test_msg("Extent region not marked as free\n"); + test_err("extent region not marked as free"); return -ENOENT; } @@ -772,19 +775,20 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, * allocate the whole free space at once. */ if (!test_check_exists(cache, SZ_128M - 768 * SZ_1K, SZ_1M)) { - test_msg("Expected region not marked as free\n"); + test_err("expected region not marked as free"); return -ENOENT; } if (cache->free_space_ctl->free_space != (SZ_1M + 2 * sectorsize)) { - test_msg("Cache free space is not 1Mb + %u\n", 2 * sectorsize); + test_err("cache free space is not 1Mb + %u", 2 * sectorsize); return -EINVAL; } offset = btrfs_find_space_for_alloc(cache, 0, SZ_1M, 0, &max_extent_size); if (offset != (SZ_128M - 768 * SZ_1K)) { - test_msg("Failed to allocate 1Mb from space cache, returned offset is: %llu\n", + test_err( + "failed to allocate 1Mb from space cache, returned offset is: %llu", offset); return -EINVAL; } @@ -798,7 +802,7 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, return ret; if (cache->free_space_ctl->free_space != 2 * sectorsize) { - test_msg("Cache free space is not %u\n", 2 * sectorsize); + test_err("cache free space is not %u", 2 * sectorsize); return -EINVAL; } @@ -806,9 +810,8 @@ test_steal_space_from_bitmap_to_extent(struct btrfs_block_group_cache *cache, 0, 2 * sectorsize, 0, &max_extent_size); if (offset != SZ_32M) { - test_msg("Failed to allocate %u, offset: %llu\n", - 2 * sectorsize, - offset); + test_err("failed to allocate %u, offset: %llu", + 2 * sectorsize, offset); return -EINVAL; } @@ -829,7 +832,7 @@ int btrfs_test_free_space_cache(u32 sectorsize, u32 nodesize) struct btrfs_root *root = NULL; int ret = -ENOMEM; - test_msg("Running btrfs free space cache tests\n"); + test_msg("running btrfs free space cache tests"); fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize); if (!fs_info) return -ENOMEM; @@ -843,7 +846,7 @@ int btrfs_test_free_space_cache(u32 sectorsize, u32 nodesize) cache = btrfs_alloc_dummy_block_group(fs_info, BITS_PER_BITMAP * sectorsize + PAGE_SIZE); if (!cache) { - test_msg("Couldn't run the tests\n"); + test_err("couldn't run the tests"); btrfs_free_dummy_fs_info(fs_info); return 0; } @@ -871,6 +874,6 @@ int btrfs_test_free_space_cache(u32 sectorsize, u32 nodesize) btrfs_free_dummy_block_group(cache); btrfs_free_dummy_root(root); btrfs_free_dummy_fs_info(fs_info); - test_msg("Free space cache tests finished\n"); + test_msg("free space cache tests finished"); return ret; } diff --git a/fs/btrfs/tests/free-space-tree-tests.c b/fs/btrfs/tests/free-space-tree-tests.c index e1f9666c4974074ef410e19f6089751e27a7bac3..89346da890cf7188db3720d9b1c42cb76adfcdbe 100644 --- a/fs/btrfs/tests/free-space-tree-tests.c +++ b/fs/btrfs/tests/free-space-tree-tests.c @@ -32,7 +32,7 @@ static int __check_free_space_extents(struct btrfs_trans_handle *trans, info = search_free_space_info(trans, fs_info, cache, path, 0); if (IS_ERR(info)) { - test_msg("Could not find free space info\n"); + test_err("could not find free space info"); ret = PTR_ERR(info); goto out; } @@ -40,7 +40,7 @@ static int __check_free_space_extents(struct btrfs_trans_handle *trans, extent_count = btrfs_free_space_extent_count(path->nodes[0], info); if (extent_count != num_extents) { - test_msg("Extent count is wrong\n"); + test_err("extent count is wrong"); ret = -EINVAL; goto out; } @@ -99,7 +99,7 @@ static int __check_free_space_extents(struct btrfs_trans_handle *trans, btrfs_release_path(path); return ret; invalid: - test_msg("Free space tree is invalid\n"); + test_err("free space tree is invalid"); ret = -EINVAL; goto out; } @@ -117,7 +117,7 @@ static int check_free_space_extents(struct btrfs_trans_handle *trans, info = search_free_space_info(trans, fs_info, cache, path, 0); if (IS_ERR(info)) { - test_msg("Could not find free space info\n"); + test_err("could not find free space info"); btrfs_release_path(path); return PTR_ERR(info); } @@ -131,15 +131,15 @@ static int check_free_space_extents(struct btrfs_trans_handle *trans, /* Flip it to the other format and check that for good measure. */ if (flags & BTRFS_FREE_SPACE_USING_BITMAPS) { - ret = convert_free_space_to_extents(trans, fs_info, cache, path); + ret = convert_free_space_to_extents(trans, cache, path); if (ret) { - test_msg("Could not convert to extents\n"); + test_err("could not convert to extents"); return ret; } } else { - ret = convert_free_space_to_bitmaps(trans, fs_info, cache, path); + ret = convert_free_space_to_bitmaps(trans, cache, path); if (ret) { - test_msg("Could not convert to bitmaps\n"); + test_err("could not convert to bitmaps"); return ret; } } @@ -170,11 +170,11 @@ static int test_remove_all(struct btrfs_trans_handle *trans, const struct free_space_extent extents[] = {}; int ret; - ret = __remove_from_free_space_tree(trans, fs_info, cache, path, + ret = __remove_from_free_space_tree(trans, cache, path, cache->key.objectid, cache->key.offset); if (ret) { - test_msg("Could not remove free space\n"); + test_err("could not remove free space"); return ret; } @@ -194,10 +194,10 @@ static int test_remove_beginning(struct btrfs_trans_handle *trans, }; int ret; - ret = __remove_from_free_space_tree(trans, fs_info, cache, path, + ret = __remove_from_free_space_tree(trans, cache, path, cache->key.objectid, alignment); if (ret) { - test_msg("Could not remove free space\n"); + test_err("could not remove free space"); return ret; } @@ -217,12 +217,12 @@ static int test_remove_end(struct btrfs_trans_handle *trans, }; int ret; - ret = __remove_from_free_space_tree(trans, fs_info, cache, path, + ret = __remove_from_free_space_tree(trans, cache, path, cache->key.objectid + cache->key.offset - alignment, alignment); if (ret) { - test_msg("Could not remove free space\n"); + test_err("could not remove free space"); return ret; } @@ -243,11 +243,11 @@ static int test_remove_middle(struct btrfs_trans_handle *trans, }; int ret; - ret = __remove_from_free_space_tree(trans, fs_info, cache, path, + ret = __remove_from_free_space_tree(trans, cache, path, cache->key.objectid + alignment, alignment); if (ret) { - test_msg("Could not remove free space\n"); + test_err("could not remove free space"); return ret; } @@ -266,26 +266,26 @@ static int test_merge_left(struct btrfs_trans_handle *trans, }; int ret; - ret = __remove_from_free_space_tree(trans, fs_info, cache, path, + ret = __remove_from_free_space_tree(trans, cache, path, cache->key.objectid, cache->key.offset); if (ret) { - test_msg("Could not remove free space\n"); + test_err("could not remove free space"); return ret; } - ret = __add_to_free_space_tree(trans, fs_info, cache, path, - cache->key.objectid, alignment); + ret = __add_to_free_space_tree(trans, cache, path, cache->key.objectid, + alignment); if (ret) { - test_msg("Could not add free space\n"); + test_err("could not add free space"); return ret; } - ret = __add_to_free_space_tree(trans, fs_info, cache, path, + ret = __add_to_free_space_tree(trans, cache, path, cache->key.objectid + alignment, alignment); if (ret) { - test_msg("Could not add free space\n"); + test_err("could not add free space"); return ret; } @@ -304,27 +304,27 @@ static int test_merge_right(struct btrfs_trans_handle *trans, }; int ret; - ret = __remove_from_free_space_tree(trans, fs_info, cache, path, + ret = __remove_from_free_space_tree(trans, cache, path, cache->key.objectid, cache->key.offset); if (ret) { - test_msg("Could not remove free space\n"); + test_err("could not remove free space"); return ret; } - ret = __add_to_free_space_tree(trans, fs_info, cache, path, + ret = __add_to_free_space_tree(trans, cache, path, cache->key.objectid + 2 * alignment, alignment); if (ret) { - test_msg("Could not add free space\n"); + test_err("could not add free space"); return ret; } - ret = __add_to_free_space_tree(trans, fs_info, cache, path, + ret = __add_to_free_space_tree(trans, cache, path, cache->key.objectid + alignment, alignment); if (ret) { - test_msg("Could not add free space\n"); + test_err("could not add free space"); return ret; } @@ -343,34 +343,34 @@ static int test_merge_both(struct btrfs_trans_handle *trans, }; int ret; - ret = __remove_from_free_space_tree(trans, fs_info, cache, path, + ret = __remove_from_free_space_tree(trans, cache, path, cache->key.objectid, cache->key.offset); if (ret) { - test_msg("Could not remove free space\n"); + test_err("could not remove free space"); return ret; } - ret = __add_to_free_space_tree(trans, fs_info, cache, path, - cache->key.objectid, alignment); + ret = __add_to_free_space_tree(trans, cache, path, cache->key.objectid, + alignment); if (ret) { - test_msg("Could not add free space\n"); + test_err("could not add free space"); return ret; } - ret = __add_to_free_space_tree(trans, fs_info, cache, path, + ret = __add_to_free_space_tree(trans, cache, path, cache->key.objectid + 2 * alignment, alignment); if (ret) { - test_msg("Could not add free space\n"); + test_err("could not add free space"); return ret; } - ret = __add_to_free_space_tree(trans, fs_info, cache, path, + ret = __add_to_free_space_tree(trans, cache, path, cache->key.objectid + alignment, alignment); if (ret) { - test_msg("Could not add free space\n"); + test_err("could not add free space"); return ret; } @@ -391,34 +391,34 @@ static int test_merge_none(struct btrfs_trans_handle *trans, }; int ret; - ret = __remove_from_free_space_tree(trans, fs_info, cache, path, + ret = __remove_from_free_space_tree(trans, cache, path, cache->key.objectid, cache->key.offset); if (ret) { - test_msg("Could not remove free space\n"); + test_err("could not remove free space"); return ret; } - ret = __add_to_free_space_tree(trans, fs_info, cache, path, - cache->key.objectid, alignment); + ret = __add_to_free_space_tree(trans, cache, path, cache->key.objectid, + alignment); if (ret) { - test_msg("Could not add free space\n"); + test_err("could not add free space"); return ret; } - ret = __add_to_free_space_tree(trans, fs_info, cache, path, + ret = __add_to_free_space_tree(trans, cache, path, cache->key.objectid + 4 * alignment, alignment); if (ret) { - test_msg("Could not add free space\n"); + test_err("could not add free space"); return ret; } - ret = __add_to_free_space_tree(trans, fs_info, cache, path, + ret = __add_to_free_space_tree(trans, cache, path, cache->key.objectid + 2 * alignment, alignment); if (ret) { - test_msg("Could not add free space\n"); + test_err("could not add free space"); return ret; } @@ -444,14 +444,14 @@ static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize, fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize); if (!fs_info) { - test_msg("Couldn't allocate dummy fs info\n"); + test_err("couldn't allocate dummy fs info"); ret = -ENOMEM; goto out; } root = btrfs_alloc_dummy_root(fs_info); if (IS_ERR(root)) { - test_msg("Couldn't allocate dummy root\n"); + test_err("couldn't allocate dummy root"); ret = PTR_ERR(root); goto out; } @@ -463,7 +463,7 @@ static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize, root->node = alloc_test_extent_buffer(root->fs_info, nodesize); if (!root->node) { - test_msg("Couldn't allocate dummy buffer\n"); + test_err("couldn't allocate dummy buffer"); ret = -ENOMEM; goto out; } @@ -473,7 +473,7 @@ static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize, cache = btrfs_alloc_dummy_block_group(fs_info, 8 * alignment); if (!cache) { - test_msg("Couldn't allocate dummy block group cache\n"); + test_err("couldn't allocate dummy block group cache"); ret = -ENOMEM; goto out; } @@ -482,26 +482,25 @@ static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize, cache->needs_free_space = 1; cache->fs_info = root->fs_info; - btrfs_init_dummy_trans(&trans); + btrfs_init_dummy_trans(&trans, root->fs_info); path = btrfs_alloc_path(); if (!path) { - test_msg("Couldn't allocate path\n"); + test_err("couldn't allocate path"); ret = -ENOMEM; goto out; } - ret = add_block_group_free_space(&trans, root->fs_info, cache); + ret = add_block_group_free_space(&trans, cache); if (ret) { - test_msg("Could not add block group free space\n"); + test_err("could not add block group free space"); goto out; } if (bitmaps) { - ret = convert_free_space_to_bitmaps(&trans, root->fs_info, - cache, path); + ret = convert_free_space_to_bitmaps(&trans, cache, path); if (ret) { - test_msg("Could not convert block group to bitmaps\n"); + test_err("could not convert block group to bitmaps"); goto out; } } @@ -510,14 +509,14 @@ static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize, if (ret) goto out; - ret = remove_block_group_free_space(&trans, root->fs_info, cache); + ret = remove_block_group_free_space(&trans, cache); if (ret) { - test_msg("Could not remove block group free space\n"); + test_err("could not remove block group free space"); goto out; } if (btrfs_header_nritems(root->node) != 0) { - test_msg("Free space tree has leftover items\n"); + test_err("free space tree has leftover items"); ret = -EINVAL; goto out; } @@ -539,14 +538,16 @@ static int run_test_both_formats(test_func_t test_func, u32 sectorsize, ret = run_test(test_func, 0, sectorsize, nodesize, alignment); if (ret) { - test_msg("%pf failed with extents, sectorsize=%u, nodesize=%u, alignment=%u\n", + test_err( + "%pf failed with extents, sectorsize=%u, nodesize=%u, alignment=%u", test_func, sectorsize, nodesize, alignment); test_ret = ret; } ret = run_test(test_func, 1, sectorsize, nodesize, alignment); if (ret) { - test_msg("%pf failed with bitmaps, sectorsize=%u, nodesize=%u, alignment=%u\n", + test_err( + "%pf failed with bitmaps, sectorsize=%u, nodesize=%u, alignment=%u", test_func, sectorsize, nodesize, alignment); test_ret = ret; } @@ -577,7 +578,7 @@ int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize) */ bitmap_alignment = BTRFS_FREE_SPACE_BITMAP_BITS * PAGE_SIZE; - test_msg("Running free space tree tests\n"); + test_msg("running free space tree tests"); for (i = 0; i < ARRAY_SIZE(tests); i++) { int ret; diff --git a/fs/btrfs/tests/inode-tests.c b/fs/btrfs/tests/inode-tests.c index e0ba799536b48d55d30be6febdbe317917413233..64043f02882067b5ab94695cb0267dacbd837c65 100644 --- a/fs/btrfs/tests/inode-tests.c +++ b/fs/btrfs/tests/inode-tests.c @@ -228,7 +228,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) inode = btrfs_new_test_inode(); if (!inode) { - test_msg("Couldn't allocate inode\n"); + test_err("couldn't allocate inode"); return ret; } @@ -238,19 +238,19 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize); if (!fs_info) { - test_msg("Couldn't allocate dummy fs info\n"); + test_err("couldn't allocate dummy fs info"); goto out; } root = btrfs_alloc_dummy_root(fs_info); if (IS_ERR(root)) { - test_msg("Couldn't allocate root\n"); + test_err("couldn't allocate root"); goto out; } root->node = alloc_dummy_extent_buffer(fs_info, nodesize); if (!root->node) { - test_msg("Couldn't allocate dummy buffer\n"); + test_err("couldn't allocate dummy buffer"); goto out; } @@ -268,11 +268,11 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, 0, sectorsize, 0); if (IS_ERR(em)) { em = NULL; - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { - test_msg("Expected a hole, got %llu\n", em->block_start); + test_err("expected a hole, got %llu", em->block_start); goto out; } free_extent_map(em); @@ -287,20 +287,21 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, 0, (u64)-1, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { - test_msg("Expected a hole, got %llu\n", em->block_start); + test_err("expected a hole, got %llu", 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); + test_err( + "unexpected extent wanted start 0 len 5, got start %llu len %llu", + em->start, em->len); goto out; } if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); + test_err("unexpected flags set, want 0 have %lu", em->flags); goto out; } offset = em->start + em->len; @@ -308,21 +309,22 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start != EXTENT_MAP_INLINE) { - test_msg("Expected an inline, got %llu\n", em->block_start); + test_err("expected an inline, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != (sectorsize - 5)) { - test_msg("Unexpected extent wanted start %llu len 1, got start " - "%llu len %llu\n", offset, em->start, em->len); + test_err( + "unexpected extent wanted start %llu len 1, got start %llu len %llu", + offset, em->start, em->len); goto out; } if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); + test_err("unexpected flags set, want 0 have %lu", em->flags); goto out; } /* @@ -335,20 +337,21 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { - test_msg("Expected a hole, got %llu\n", em->block_start); + test_err("expected a hole, got %llu", 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); + test_err( + "unexpected extent wanted start %llu len 4, got start %llu len %llu", + offset, em->start, em->len); goto out; } if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); + test_err("unexpected flags set, want 0 have %lu", em->flags); goto out; } offset = em->start + em->len; @@ -357,24 +360,25 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) /* Regular extent */ em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); + test_err("expected a real extent, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize - 1) { - test_msg("Unexpected extent wanted start %llu len 4095, got " - "start %llu len %llu\n", offset, em->start, em->len); + test_err( + "unexpected extent wanted start %llu len 4095, got start %llu len %llu", + offset, em->start, em->len); goto out; } if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); + test_err("unexpected flags set, want 0 have %lu", em->flags); goto out; } if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, + test_err("wrong orig offset, want %llu, have %llu", em->start, em->orig_start); goto out; } @@ -384,25 +388,25 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) /* The next 3 are split extents */ em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); + test_err("expected a real extent, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { - test_msg("Unexpected extent start %llu len %u, " - "got start %llu len %llu\n", + test_err( + "unexpected extent start %llu len %u, got start %llu len %llu", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); + test_err("unexpected flags set, want 0 have %lu", em->flags); goto out; } if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, + test_err("wrong orig offset, want %llu, have %llu", em->start, em->orig_start); goto out; } @@ -413,21 +417,21 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { - test_msg("Expected a hole, got %llu\n", em->block_start); + test_err("expected a hole, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { - test_msg("Unexpected extent wanted start %llu len %u, " - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %llu len %u, got start %llu len %llu", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); + test_err("unexpected flags set, want 0 have %lu", em->flags); goto out; } offset = em->start + em->len; @@ -435,31 +439,31 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); + test_err("expected a real extent, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { - test_msg("Unexpected extent wanted start %llu len %u, " - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %llu len %u, got start %llu len %llu", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); + test_err("unexpected flags set, want 0 have %lu", em->flags); goto out; } if (em->orig_start != orig_start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", + test_err("wrong orig offset, want %llu, have %llu", 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", + test_err("wrong block start, want %llu, have %llu", disk_bytenr, em->block_start); goto out; } @@ -469,26 +473,26 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) /* Prealloc extent */ em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); + test_err("expected a real extent, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { - test_msg("Unexpected extent wanted start %llu len %u, " - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %llu len %u, got start %llu len %llu", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != prealloc_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", + test_err("unexpected flags set, want %lu have %lu", prealloc_only, em->flags); goto out; } if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, + test_err("wrong orig offset, want %llu, have %llu", em->start, em->orig_start); goto out; } @@ -498,26 +502,26 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) /* The next 3 are a half written prealloc extent */ em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); + test_err("expected a real extent, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { - test_msg("Unexpected extent wanted start %llu len %u, " - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %llu len %u, got start %llu len %llu", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != prealloc_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", + test_err("unexpected flags set, want %lu have %lu", prealloc_only, em->flags); goto out; } if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, + test_err("wrong orig offset, want %llu, have %llu", em->start, em->orig_start); goto out; } @@ -528,30 +532,30 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start >= EXTENT_MAP_HOLE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); + test_err("expected a real extent, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { - test_msg("Unexpected extent wanted start %llu len %u, " - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %llu len %u, got start %llu len %llu", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); + test_err("unexpected flags set, want 0 have %lu", em->flags); goto out; } if (em->orig_start != orig_start) { - test_msg("Unexpected orig offset, wanted %llu, have %llu\n", + test_err("unexpected orig offset, wanted %llu, have %llu", 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", + test_err("unexpected block start, wanted %llu, have %llu", disk_bytenr + (em->start - em->orig_start), em->block_start); goto out; @@ -561,31 +565,31 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); + test_err("expected a real extent, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { - test_msg("Unexpected extent wanted start %llu len %u, " - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %llu len %u, got start %llu len %llu", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != prealloc_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", + test_err("unexpected flags set, want %lu have %lu", prealloc_only, em->flags); goto out; } if (em->orig_start != orig_start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", orig_start, + test_err("wrong orig offset, want %llu, have %llu", 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", + test_err("unexpected block start, wanted %llu, have %llu", disk_bytenr + (em->start - em->orig_start), em->block_start); goto out; @@ -596,31 +600,31 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) /* Now for the compressed extent */ em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); + test_err("expected a real extent, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { - test_msg("Unexpected extent wanted start %llu len %u," - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %llu len %u, got start %llu len %llu", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != compressed_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", + test_err("unexpected flags set, want %lu have %lu", compressed_only, em->flags); goto out; } if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", + test_err("wrong orig offset, want %llu, have %llu", em->start, em->orig_start); goto out; } if (em->compress_type != BTRFS_COMPRESS_ZLIB) { - test_msg("Unexpected compress type, wanted %d, got %d\n", + test_err("unexpected compress type, wanted %d, got %d", BTRFS_COMPRESS_ZLIB, em->compress_type); goto out; } @@ -630,31 +634,31 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) /* Split compressed extent */ em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); + test_err("expected a real extent, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { - test_msg("Unexpected extent wanted start %llu len %u," - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %llu len %u, got start %llu len %llu", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != compressed_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", + test_err("unexpected flags set, want %lu have %lu", compressed_only, em->flags); goto out; } if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", + test_err("wrong orig offset, want %llu, have %llu", em->start, em->orig_start); goto out; } if (em->compress_type != BTRFS_COMPRESS_ZLIB) { - test_msg("Unexpected compress type, wanted %d, got %d\n", + test_err("unexpected compress type, wanted %d, got %d", BTRFS_COMPRESS_ZLIB, em->compress_type); goto out; } @@ -665,25 +669,25 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); + test_err("expected a real extent, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { - test_msg("Unexpected extent wanted start %llu len %u, " - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %llu len %u, got start %llu len %llu", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); + test_err("unexpected flags set, want 0 have %lu", em->flags); goto out; } if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, + test_err("wrong orig offset, want %llu, have %llu", em->start, em->orig_start); goto out; } @@ -692,32 +696,32 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start != disk_bytenr) { - test_msg("Block start does not match, want %llu got %llu\n", + test_err("block start does not match, want %llu got %llu", disk_bytenr, em->block_start); goto out; } if (em->start != offset || em->len != 2 * sectorsize) { - test_msg("Unexpected extent wanted start %llu len %u, " - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %llu len %u, got start %llu len %llu", offset, 2 * sectorsize, em->start, em->len); goto out; } if (em->flags != compressed_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", + test_err("unexpected flags set, want %lu have %lu", compressed_only, em->flags); goto out; } if (em->orig_start != orig_start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", + test_err("wrong orig offset, want %llu, have %llu", em->start, orig_start); goto out; } if (em->compress_type != BTRFS_COMPRESS_ZLIB) { - test_msg("Unexpected compress type, wanted %d, got %d\n", + test_err("unexpected compress type, wanted %d, got %d", BTRFS_COMPRESS_ZLIB, em->compress_type); goto out; } @@ -728,25 +732,25 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset + 6, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); + test_err("expected a real extent, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { - test_msg("Unexpected extent wanted start %llu len %u, " - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %llu len %u, got start %llu len %llu", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); + test_err("unexpected flags set, want 0 have %lu", em->flags); goto out; } if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, + test_err("wrong orig offset, want %llu, have %llu", em->start, em->orig_start); goto out; } @@ -755,11 +759,11 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, SZ_4M, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { - test_msg("Expected a hole extent, got %llu\n", em->block_start); + test_err("expected a hole extent, got %llu", em->block_start); goto out; } /* @@ -768,18 +772,18 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) * test. */ if (em->start != offset || em->len != 3 * sectorsize) { - test_msg("Unexpected extent wanted start %llu len %u, " - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %llu len %u, got start %llu len %llu", offset, 3 * sectorsize, em->start, em->len); goto out; } if (em->flags != vacancy_only) { - test_msg("Unexpected flags set, want %lu have %lu\n", + test_err("unexpected flags set, want %lu have %lu", vacancy_only, em->flags); goto out; } if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, + test_err("wrong orig offset, want %llu, have %llu", em->start, em->orig_start); goto out; } @@ -788,25 +792,25 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, offset, sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start >= EXTENT_MAP_LAST_BYTE) { - test_msg("Expected a real extent, got %llu\n", em->block_start); + test_err("expected a real extent, got %llu", em->block_start); goto out; } if (em->start != offset || em->len != sectorsize) { - test_msg("Unexpected extent wanted start %llu len %u," - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %llu len %u, got start %llu len %llu", offset, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { - test_msg("Unexpected flags set, want 0 have %lu\n", em->flags); + test_err("unexpected flags set, want 0 have %lu", em->flags); goto out; } if (em->orig_start != em->start) { - test_msg("Wrong orig offset, want %llu, have %llu\n", em->start, + test_err("wrong orig offset, want %llu, have %llu", em->start, em->orig_start); goto out; } @@ -830,7 +834,7 @@ static int test_hole_first(u32 sectorsize, u32 nodesize) inode = btrfs_new_test_inode(); if (!inode) { - test_msg("Couldn't allocate inode\n"); + test_err("couldn't allocate inode"); return ret; } @@ -840,19 +844,19 @@ static int test_hole_first(u32 sectorsize, u32 nodesize) fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize); if (!fs_info) { - test_msg("Couldn't allocate dummy fs info\n"); + test_err("couldn't allocate dummy fs info"); goto out; } root = btrfs_alloc_dummy_root(fs_info); if (IS_ERR(root)) { - test_msg("Couldn't allocate root\n"); + test_err("couldn't allocate root"); goto out; } root->node = alloc_dummy_extent_buffer(fs_info, nodesize); if (!root->node) { - test_msg("Couldn't allocate dummy buffer\n"); + test_err("couldn't allocate dummy buffer"); goto out; } @@ -871,21 +875,21 @@ static int test_hole_first(u32 sectorsize, u32 nodesize) sectorsize, BTRFS_FILE_EXTENT_REG, 0, 1); em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, 0, 2 * sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start != EXTENT_MAP_HOLE) { - test_msg("Expected a hole, got %llu\n", em->block_start); + test_err("expected a hole, got %llu", em->block_start); goto out; } if (em->start != 0 || em->len != sectorsize) { - test_msg("Unexpected extent wanted start 0 len %u, " - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start 0 len %u, got start %llu len %llu", sectorsize, em->start, em->len); goto out; } if (em->flags != vacancy_only) { - test_msg("Wrong flags, wanted %lu, have %lu\n", vacancy_only, + test_err("wrong flags, wanted %lu, have %lu", vacancy_only, em->flags); goto out; } @@ -894,21 +898,21 @@ static int test_hole_first(u32 sectorsize, u32 nodesize) em = btrfs_get_extent(BTRFS_I(inode), NULL, 0, sectorsize, 2 * sectorsize, 0); if (IS_ERR(em)) { - test_msg("Got an error when we shouldn't have\n"); + test_err("got an error when we shouldn't have"); goto out; } if (em->block_start != sectorsize) { - test_msg("Expected a real extent, got %llu\n", em->block_start); + test_err("expected a real extent, got %llu", em->block_start); goto out; } if (em->start != sectorsize || em->len != sectorsize) { - test_msg("Unexpected extent wanted start %u len %u, " - "got start %llu len %llu\n", + test_err( + "unexpected extent wanted start %u len %u, got start %llu len %llu", sectorsize, sectorsize, em->start, em->len); goto out; } if (em->flags != 0) { - test_msg("Unexpected flags set, wanted 0 got %lu\n", + test_err("unexpected flags set, wanted 0 got %lu", em->flags); goto out; } @@ -931,19 +935,19 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) inode = btrfs_new_test_inode(); if (!inode) { - test_msg("Couldn't allocate inode\n"); + test_err("couldn't allocate inode"); return ret; } fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize); if (!fs_info) { - test_msg("Couldn't allocate dummy fs info\n"); + test_err("couldn't allocate dummy fs info"); goto out; } root = btrfs_alloc_dummy_root(fs_info); if (IS_ERR(root)) { - test_msg("Couldn't allocate root\n"); + test_err("couldn't allocate root"); goto out; } @@ -954,12 +958,12 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) ret = btrfs_set_extent_delalloc(inode, 0, BTRFS_MAX_EXTENT_SIZE - 1, 0, NULL, 0); if (ret) { - test_msg("btrfs_set_extent_delalloc returned %d\n", ret); + test_err("btrfs_set_extent_delalloc returned %d", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 1) { ret = -EINVAL; - test_msg("Miscount, wanted 1, got %u\n", + test_err("miscount, wanted 1, got %u", BTRFS_I(inode)->outstanding_extents); goto out; } @@ -969,12 +973,12 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) BTRFS_MAX_EXTENT_SIZE + sectorsize - 1, 0, NULL, 0); if (ret) { - test_msg("btrfs_set_extent_delalloc returned %d\n", ret); + test_err("btrfs_set_extent_delalloc returned %d", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 2) { ret = -EINVAL; - test_msg("Miscount, wanted 2, got %u\n", + test_err("miscount, wanted 2, got %u", BTRFS_I(inode)->outstanding_extents); goto out; } @@ -986,12 +990,12 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) EXTENT_DELALLOC | EXTENT_DIRTY | EXTENT_UPTODATE, 0, 0, NULL); if (ret) { - test_msg("clear_extent_bit returned %d\n", ret); + test_err("clear_extent_bit returned %d", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 2) { ret = -EINVAL; - test_msg("Miscount, wanted 2, got %u\n", + test_err("miscount, wanted 2, got %u", BTRFS_I(inode)->outstanding_extents); goto out; } @@ -1002,12 +1006,12 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) + sectorsize - 1, 0, NULL, 0); if (ret) { - test_msg("btrfs_set_extent_delalloc returned %d\n", ret); + test_err("btrfs_set_extent_delalloc returned %d", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 2) { ret = -EINVAL; - test_msg("Miscount, wanted 2, got %u\n", + test_err("miscount, wanted 2, got %u", BTRFS_I(inode)->outstanding_extents); goto out; } @@ -1020,12 +1024,12 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) (BTRFS_MAX_EXTENT_SIZE << 1) + 3 * sectorsize - 1, 0, NULL, 0); if (ret) { - test_msg("btrfs_set_extent_delalloc returned %d\n", ret); + test_err("btrfs_set_extent_delalloc returned %d", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 4) { ret = -EINVAL; - test_msg("Miscount, wanted 4, got %u\n", + test_err("miscount, wanted 4, got %u", BTRFS_I(inode)->outstanding_extents); goto out; } @@ -1037,12 +1041,12 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) BTRFS_MAX_EXTENT_SIZE + sectorsize, BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, 0, NULL, 0); if (ret) { - test_msg("btrfs_set_extent_delalloc returned %d\n", ret); + test_err("btrfs_set_extent_delalloc returned %d", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 3) { ret = -EINVAL; - test_msg("Miscount, wanted 3, got %u\n", + test_err("miscount, wanted 3, got %u", BTRFS_I(inode)->outstanding_extents); goto out; } @@ -1054,12 +1058,12 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) EXTENT_DIRTY | EXTENT_DELALLOC | EXTENT_UPTODATE, 0, 0, NULL); if (ret) { - test_msg("clear_extent_bit returned %d\n", ret); + test_err("clear_extent_bit returned %d", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 4) { ret = -EINVAL; - test_msg("Miscount, wanted 4, got %u\n", + test_err("miscount, wanted 4, got %u", BTRFS_I(inode)->outstanding_extents); goto out; } @@ -1072,12 +1076,12 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) BTRFS_MAX_EXTENT_SIZE + sectorsize, BTRFS_MAX_EXTENT_SIZE + 2 * sectorsize - 1, 0, NULL, 0); if (ret) { - test_msg("btrfs_set_extent_delalloc returned %d\n", ret); + test_err("btrfs_set_extent_delalloc returned %d", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents != 3) { ret = -EINVAL; - test_msg("Miscount, wanted 3, got %u\n", + test_err("miscount, wanted 3, got %u", BTRFS_I(inode)->outstanding_extents); goto out; } @@ -1087,12 +1091,12 @@ static int test_extent_accounting(u32 sectorsize, u32 nodesize) EXTENT_DIRTY | EXTENT_DELALLOC | EXTENT_UPTODATE, 0, 0, NULL); if (ret) { - test_msg("clear_extent_bit returned %d\n", ret); + test_err("clear_extent_bit returned %d", ret); goto out; } if (BTRFS_I(inode)->outstanding_extents) { ret = -EINVAL; - test_msg("Miscount, wanted 0, got %u\n", + test_err("miscount, wanted 0, got %u", BTRFS_I(inode)->outstanding_extents); goto out; } @@ -1115,14 +1119,14 @@ int btrfs_test_inodes(u32 sectorsize, u32 nodesize) set_bit(EXTENT_FLAG_COMPRESSED, &compressed_only); set_bit(EXTENT_FLAG_PREALLOC, &prealloc_only); - test_msg("Running btrfs_get_extent tests\n"); + test_msg("running btrfs_get_extent tests"); ret = test_btrfs_get_extent(sectorsize, nodesize); if (ret) return ret; - test_msg("Running hole first btrfs_get_extent test\n"); + test_msg("running hole first btrfs_get_extent test"); ret = test_hole_first(sectorsize, nodesize); if (ret) return ret; - test_msg("Running outstanding_extents tests\n"); + test_msg("running outstanding_extents tests"); return test_extent_accounting(sectorsize, nodesize); } diff --git a/fs/btrfs/tests/qgroup-tests.c b/fs/btrfs/tests/qgroup-tests.c index 39b95783f736514ecc1e6e6239bd70de8e8f6086..ace94db09d29dc37bc8273e2ec012a2473bec6a1 100644 --- a/fs/btrfs/tests/qgroup-tests.c +++ b/fs/btrfs/tests/qgroup-tests.c @@ -24,7 +24,7 @@ static int insert_normal_tree_ref(struct btrfs_root *root, u64 bytenr, u32 size = sizeof(*item) + sizeof(*iref) + sizeof(*block_info); int ret; - btrfs_init_dummy_trans(&trans); + btrfs_init_dummy_trans(&trans, NULL); ins.objectid = bytenr; ins.type = BTRFS_EXTENT_ITEM_KEY; @@ -32,14 +32,14 @@ static int insert_normal_tree_ref(struct btrfs_root *root, u64 bytenr, path = btrfs_alloc_path(); if (!path) { - test_msg("Couldn't allocate path\n"); + test_err("couldn't allocate path"); return -ENOMEM; } path->leave_spinning = 1; ret = btrfs_insert_empty_item(&trans, root, path, &ins, size); if (ret) { - test_msg("Couldn't insert ref %d\n", ret); + test_err("couldn't insert ref %d", ret); btrfs_free_path(path); return ret; } @@ -74,7 +74,7 @@ static int add_tree_ref(struct btrfs_root *root, u64 bytenr, u64 num_bytes, u64 refs; int ret; - btrfs_init_dummy_trans(&trans); + btrfs_init_dummy_trans(&trans, NULL); key.objectid = bytenr; key.type = BTRFS_EXTENT_ITEM_KEY; @@ -82,14 +82,14 @@ static int add_tree_ref(struct btrfs_root *root, u64 bytenr, u64 num_bytes, path = btrfs_alloc_path(); if (!path) { - test_msg("Couldn't allocate path\n"); + test_err("couldn't allocate path"); return -ENOMEM; } path->leave_spinning = 1; ret = btrfs_search_slot(&trans, root, &key, path, 0, 1); if (ret) { - test_msg("Couldn't find extent ref\n"); + test_err("couldn't find extent ref"); btrfs_free_path(path); return ret; } @@ -111,7 +111,7 @@ static int add_tree_ref(struct btrfs_root *root, u64 bytenr, u64 num_bytes, ret = btrfs_insert_empty_item(&trans, root, path, &key, 0); if (ret) - test_msg("Failed to insert backref\n"); + test_err("failed to insert backref"); btrfs_free_path(path); return ret; } @@ -124,7 +124,7 @@ static int remove_extent_item(struct btrfs_root *root, u64 bytenr, struct btrfs_path *path; int ret; - btrfs_init_dummy_trans(&trans); + btrfs_init_dummy_trans(&trans, NULL); key.objectid = bytenr; key.type = BTRFS_EXTENT_ITEM_KEY; @@ -132,14 +132,14 @@ static int remove_extent_item(struct btrfs_root *root, u64 bytenr, path = btrfs_alloc_path(); if (!path) { - test_msg("Couldn't allocate path\n"); + test_err("couldn't allocate path"); return -ENOMEM; } path->leave_spinning = 1; ret = btrfs_search_slot(&trans, root, &key, path, -1, 1); if (ret) { - test_msg("Didn't find our key %d\n", ret); + test_err("didn't find our key %d", ret); btrfs_free_path(path); return ret; } @@ -158,7 +158,7 @@ static int remove_extent_ref(struct btrfs_root *root, u64 bytenr, u64 refs; int ret; - btrfs_init_dummy_trans(&trans); + btrfs_init_dummy_trans(&trans, NULL); key.objectid = bytenr; key.type = BTRFS_EXTENT_ITEM_KEY; @@ -166,14 +166,14 @@ static int remove_extent_ref(struct btrfs_root *root, u64 bytenr, path = btrfs_alloc_path(); if (!path) { - test_msg("Couldn't allocate path\n"); + test_err("couldn't allocate path"); return -ENOMEM; } path->leave_spinning = 1; ret = btrfs_search_slot(&trans, root, &key, path, 0, 1); if (ret) { - test_msg("Couldn't find extent ref\n"); + test_err("couldn't find extent ref"); btrfs_free_path(path); return ret; } @@ -195,7 +195,7 @@ static int remove_extent_ref(struct btrfs_root *root, u64 bytenr, ret = btrfs_search_slot(&trans, root, &key, path, -1, 1); if (ret) { - test_msg("Couldn't find backref %d\n", ret); + test_err("couldn't find backref %d", ret); btrfs_free_path(path); return ret; } @@ -213,12 +213,12 @@ static int test_no_shared_qgroup(struct btrfs_root *root, struct ulist *new_roots = NULL; int ret; - btrfs_init_dummy_trans(&trans); + btrfs_init_dummy_trans(&trans, fs_info); - test_msg("Qgroup basic add\n"); + test_msg("qgroup basic add"); ret = btrfs_create_qgroup(NULL, fs_info, BTRFS_FS_TREE_OBJECTID); if (ret) { - test_msg("Couldn't create a qgroup %d\n", ret); + test_err("couldn't create a qgroup %d", ret); return ret; } @@ -231,7 +231,7 @@ static int test_no_shared_qgroup(struct btrfs_root *root, false); if (ret) { ulist_free(old_roots); - test_msg("Couldn't find old roots: %d\n", ret); + test_err("couldn't find old roots: %d", ret); return ret; } @@ -245,20 +245,20 @@ static int test_no_shared_qgroup(struct btrfs_root *root, if (ret) { ulist_free(old_roots); ulist_free(new_roots); - test_msg("Couldn't find old roots: %d\n", ret); + test_err("couldn't find old roots: %d", ret); return ret; } ret = btrfs_qgroup_account_extent(&trans, fs_info, nodesize, nodesize, old_roots, new_roots); if (ret) { - test_msg("Couldn't account space for a qgroup %d\n", ret); + test_err("couldn't account space for a qgroup %d", ret); return ret; } if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FS_TREE_OBJECTID, nodesize, nodesize)) { - test_msg("Qgroup counts didn't match expected values\n"); + test_err("qgroup counts didn't match expected values"); return -EINVAL; } old_roots = NULL; @@ -268,7 +268,7 @@ static int test_no_shared_qgroup(struct btrfs_root *root, false); if (ret) { ulist_free(old_roots); - test_msg("Couldn't find old roots: %d\n", ret); + test_err("couldn't find old roots: %d", ret); return ret; } @@ -281,19 +281,19 @@ static int test_no_shared_qgroup(struct btrfs_root *root, if (ret) { ulist_free(old_roots); ulist_free(new_roots); - test_msg("Couldn't find old roots: %d\n", ret); + test_err("couldn't find old roots: %d", ret); return ret; } ret = btrfs_qgroup_account_extent(&trans, fs_info, nodesize, nodesize, old_roots, new_roots); if (ret) { - test_msg("Couldn't account space for a qgroup %d\n", ret); + test_err("couldn't account space for a qgroup %d", ret); return -EINVAL; } if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FS_TREE_OBJECTID, 0, 0)) { - test_msg("Qgroup counts didn't match expected values\n"); + test_err("qgroup counts didn't match expected values"); return -EINVAL; } @@ -314,9 +314,9 @@ static int test_multiple_refs(struct btrfs_root *root, struct ulist *new_roots = NULL; int ret; - btrfs_init_dummy_trans(&trans); + btrfs_init_dummy_trans(&trans, fs_info); - test_msg("Qgroup multiple refs test\n"); + test_msg("qgroup multiple refs test"); /* * We have BTRFS_FS_TREE_OBJECTID created already from the @@ -324,7 +324,7 @@ static int test_multiple_refs(struct btrfs_root *root, */ ret = btrfs_create_qgroup(NULL, fs_info, BTRFS_FIRST_FREE_OBJECTID); if (ret) { - test_msg("Couldn't create a qgroup %d\n", ret); + test_err("couldn't create a qgroup %d", ret); return ret; } @@ -332,7 +332,7 @@ static int test_multiple_refs(struct btrfs_root *root, false); if (ret) { ulist_free(old_roots); - test_msg("Couldn't find old roots: %d\n", ret); + test_err("couldn't find old roots: %d", ret); return ret; } @@ -346,20 +346,20 @@ static int test_multiple_refs(struct btrfs_root *root, if (ret) { ulist_free(old_roots); ulist_free(new_roots); - test_msg("Couldn't find old roots: %d\n", ret); + test_err("couldn't find old roots: %d", ret); return ret; } ret = btrfs_qgroup_account_extent(&trans, fs_info, nodesize, nodesize, old_roots, new_roots); if (ret) { - test_msg("Couldn't account space for a qgroup %d\n", ret); + test_err("couldn't account space for a qgroup %d", ret); return ret; } if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FS_TREE_OBJECTID, nodesize, nodesize)) { - test_msg("Qgroup counts didn't match expected values\n"); + test_err("qgroup counts didn't match expected values"); return -EINVAL; } @@ -367,7 +367,7 @@ static int test_multiple_refs(struct btrfs_root *root, false); if (ret) { ulist_free(old_roots); - test_msg("Couldn't find old roots: %d\n", ret); + test_err("couldn't find old roots: %d", ret); return ret; } @@ -381,26 +381,26 @@ static int test_multiple_refs(struct btrfs_root *root, if (ret) { ulist_free(old_roots); ulist_free(new_roots); - test_msg("Couldn't find old roots: %d\n", ret); + test_err("couldn't find old roots: %d", ret); return ret; } ret = btrfs_qgroup_account_extent(&trans, fs_info, nodesize, nodesize, old_roots, new_roots); if (ret) { - test_msg("Couldn't account space for a qgroup %d\n", ret); + test_err("couldn't account space for a qgroup %d", ret); return ret; } if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FS_TREE_OBJECTID, nodesize, 0)) { - test_msg("Qgroup counts didn't match expected values\n"); + test_err("qgroup counts didn't match expected values"); return -EINVAL; } if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FIRST_FREE_OBJECTID, nodesize, 0)) { - test_msg("Qgroup counts didn't match expected values\n"); + test_err("qgroup counts didn't match expected values"); return -EINVAL; } @@ -408,7 +408,7 @@ static int test_multiple_refs(struct btrfs_root *root, false); if (ret) { ulist_free(old_roots); - test_msg("Couldn't find old roots: %d\n", ret); + test_err("couldn't find old roots: %d", ret); return ret; } @@ -422,26 +422,26 @@ static int test_multiple_refs(struct btrfs_root *root, if (ret) { ulist_free(old_roots); ulist_free(new_roots); - test_msg("Couldn't find old roots: %d\n", ret); + test_err("couldn't find old roots: %d", ret); return ret; } ret = btrfs_qgroup_account_extent(&trans, fs_info, nodesize, nodesize, old_roots, new_roots); if (ret) { - test_msg("Couldn't account space for a qgroup %d\n", ret); + test_err("couldn't account space for a qgroup %d", ret); return ret; } if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FIRST_FREE_OBJECTID, 0, 0)) { - test_msg("Qgroup counts didn't match expected values\n"); + test_err("qgroup counts didn't match expected values"); return -EINVAL; } if (btrfs_verify_qgroup_counts(fs_info, BTRFS_FS_TREE_OBJECTID, nodesize, nodesize)) { - test_msg("Qgroup counts didn't match expected values\n"); + test_err("qgroup counts didn't match expected values"); return -EINVAL; } @@ -457,13 +457,13 @@ int btrfs_test_qgroups(u32 sectorsize, u32 nodesize) fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize); if (!fs_info) { - test_msg("Couldn't allocate dummy fs info\n"); + test_err("couldn't allocate dummy fs info"); return -ENOMEM; } root = btrfs_alloc_dummy_root(fs_info); if (IS_ERR(root)) { - test_msg("Couldn't allocate root\n"); + test_err("couldn't allocate root"); ret = PTR_ERR(root); goto out; } @@ -485,7 +485,7 @@ int btrfs_test_qgroups(u32 sectorsize, u32 nodesize) */ root->node = alloc_test_extent_buffer(root->fs_info, nodesize); if (!root->node) { - test_msg("Couldn't allocate dummy buffer\n"); + test_err("couldn't allocate dummy buffer"); ret = -ENOMEM; goto out; } @@ -495,7 +495,7 @@ int btrfs_test_qgroups(u32 sectorsize, u32 nodesize) tmp_root = btrfs_alloc_dummy_root(fs_info); if (IS_ERR(tmp_root)) { - test_msg("Couldn't allocate a fs root\n"); + test_err("couldn't allocate a fs root"); ret = PTR_ERR(tmp_root); goto out; } @@ -504,13 +504,13 @@ int btrfs_test_qgroups(u32 sectorsize, u32 nodesize) root->fs_info->fs_root = tmp_root; ret = btrfs_insert_fs_root(root->fs_info, tmp_root); if (ret) { - test_msg("Couldn't insert fs root %d\n", ret); + test_err("couldn't insert fs root %d", ret); goto out; } tmp_root = btrfs_alloc_dummy_root(fs_info); if (IS_ERR(tmp_root)) { - test_msg("Couldn't allocate a fs root\n"); + test_err("couldn't allocate a fs root"); ret = PTR_ERR(tmp_root); goto out; } @@ -518,11 +518,11 @@ int btrfs_test_qgroups(u32 sectorsize, u32 nodesize) tmp_root->root_key.objectid = BTRFS_FIRST_FREE_OBJECTID; ret = btrfs_insert_fs_root(root->fs_info, tmp_root); if (ret) { - test_msg("Couldn't insert fs root %d\n", ret); + test_err("couldn't insert fs root %d", ret); goto out; } - test_msg("Running qgroup tests\n"); + test_msg("running qgroup tests"); ret = test_no_shared_qgroup(root, sectorsize, nodesize); if (ret) goto out; diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index c944b4769e3c7c5a966e421b5c9c9b4635cab2ac..4485eae41e88ad7a487c5bb2e8aa58f950cf7e36 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -877,12 +877,7 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, atomic_dec(&cur_trans->num_writers); extwriter_counter_dec(cur_trans, trans->type); - /* - * Make sure counter is updated before we wake up waiters. - */ - smp_mb(); - if (waitqueue_active(&cur_trans->writer_wait)) - wake_up(&cur_trans->writer_wait); + cond_wake_up(&cur_trans->writer_wait); btrfs_put_transaction(cur_trans); if (current->journal_info == trans) @@ -1250,7 +1245,6 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans) btrfs_free_log(trans, root); btrfs_update_reloc_root(trans, root); - btrfs_orphan_commit_root(trans, root); btrfs_save_ino_cache(root, trans); @@ -1640,15 +1634,14 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, btrfs_abort_transaction(trans, ret); goto fail; } - ret = btrfs_uuid_tree_add(trans, fs_info, new_uuid.b, - BTRFS_UUID_KEY_SUBVOL, objectid); + ret = btrfs_uuid_tree_add(trans, new_uuid.b, BTRFS_UUID_KEY_SUBVOL, + objectid); if (ret) { btrfs_abort_transaction(trans, ret); goto fail; } if (!btrfs_is_empty_uuid(new_root_item->received_uuid)) { - ret = btrfs_uuid_tree_add(trans, fs_info, - new_root_item->received_uuid, + ret = btrfs_uuid_tree_add(trans, new_root_item->received_uuid, BTRFS_UUID_KEY_RECEIVED_SUBVOL, objectid); if (ret && ret != -EEXIST) { diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index d8c0826bc2c7e9aee6ff263884e4621d492f9c1f..94439482a0ec63ad2096df1b7314f4c655503f1a 100644 --- a/fs/btrfs/transaction.h +++ b/fs/btrfs/transaction.h @@ -139,7 +139,6 @@ struct btrfs_pending_snapshot { struct btrfs_path *path; /* block reservation for the operation */ struct btrfs_block_rsv block_rsv; - u64 qgroup_reserved; /* extra metadata reservation for relocation */ int error; bool readonly; diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 8f23a94dab770c00933f39a4ed1fb845edac22dd..f8220ec0203617bd36e9f94e44efe0b3ed62f5c8 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -222,11 +222,8 @@ int btrfs_pin_log_trans(struct btrfs_root *root) void btrfs_end_log_trans(struct btrfs_root *root) { if (atomic_dec_and_test(&root->log_writers)) { - /* - * Implicit memory barrier after atomic_dec_and_test - */ - if (waitqueue_active(&root->log_writer_wait)) - wake_up(&root->log_writer_wait); + /* atomic_dec_and_test implies a barrier */ + cond_wake_up_nomb(&root->log_writer_wait); } } @@ -2988,11 +2985,8 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, mutex_lock(&log_root_tree->log_mutex); if (atomic_dec_and_test(&log_root_tree->log_writers)) { - /* - * Implicit memory barrier after atomic_dec_and_test - */ - if (waitqueue_active(&log_root_tree->log_writer_wait)) - wake_up(&log_root_tree->log_writer_wait); + /* atomic_dec_and_test implies a barrier */ + cond_wake_up_nomb(&log_root_tree->log_writer_wait); } if (ret) { @@ -3116,10 +3110,11 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, mutex_unlock(&log_root_tree->log_mutex); /* - * The barrier before waitqueue_active is implied by mutex_unlock + * The barrier before waitqueue_active (in cond_wake_up) is needed so + * all the updates above are seen by the woken threads. It might not be + * necessary, but proving that seems to be hard. */ - if (waitqueue_active(&log_root_tree->log_commit_wait[index2])) - wake_up(&log_root_tree->log_commit_wait[index2]); + cond_wake_up(&log_root_tree->log_commit_wait[index2]); out: mutex_lock(&root->log_mutex); btrfs_remove_all_log_ctxs(root, index1, ret); @@ -3128,10 +3123,11 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, mutex_unlock(&root->log_mutex); /* - * The barrier before waitqueue_active is implied by mutex_unlock + * The barrier before waitqueue_active (in cond_wake_up) is needed so + * all the updates above are seen by the woken threads. It might not be + * necessary, but proving that seems to be hard. */ - if (waitqueue_active(&root->log_commit_wait[index1])) - wake_up(&root->log_commit_wait[index1]); + cond_wake_up(&root->log_commit_wait[index1]); return ret; } diff --git a/fs/btrfs/uuid-tree.c b/fs/btrfs/uuid-tree.c index 1ba7ca2a4200bee6358c0b3b900063695ca53b15..3b2ae342e6498863ece42e848b1548c60a0682a5 100644 --- a/fs/btrfs/uuid-tree.c +++ b/fs/btrfs/uuid-tree.c @@ -79,10 +79,10 @@ static int btrfs_uuid_tree_lookup(struct btrfs_root *uuid_root, u8 *uuid, return ret; } -int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, u8 *uuid, u8 type, +int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, u8 *uuid, u8 type, u64 subid_cpu) { + struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_root *uuid_root = fs_info->uuid_root; int ret; struct btrfs_path *path = NULL; @@ -144,10 +144,10 @@ int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, return ret; } -int btrfs_uuid_tree_rem(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info, u8 *uuid, u8 type, +int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, u8 *uuid, u8 type, u64 subid) { + struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_root *uuid_root = fs_info->uuid_root; int ret; struct btrfs_path *path = NULL; @@ -239,7 +239,7 @@ static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type, goto out; } - ret = btrfs_uuid_tree_rem(trans, uuid_root->fs_info, uuid, type, subid); + ret = btrfs_uuid_tree_remove(trans, uuid, type, subid); btrfs_end_transaction(trans); out: diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index be3fc701f38948e37e5d776ee93413e0d06e85ff..e034ad9e23b48b42826de6bed1a8f59d6e926a20 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -40,6 +40,9 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 1, .devs_increment = 2, .ncopies = 2, + .raid_name = "raid10", + .bg_flag = BTRFS_BLOCK_GROUP_RAID10, + .mindev_error = BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET, }, [BTRFS_RAID_RAID1] = { .sub_stripes = 1, @@ -49,6 +52,9 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 1, .devs_increment = 2, .ncopies = 2, + .raid_name = "raid1", + .bg_flag = BTRFS_BLOCK_GROUP_RAID1, + .mindev_error = BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET, }, [BTRFS_RAID_DUP] = { .sub_stripes = 1, @@ -58,6 +64,9 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 0, .devs_increment = 1, .ncopies = 2, + .raid_name = "dup", + .bg_flag = BTRFS_BLOCK_GROUP_DUP, + .mindev_error = 0, }, [BTRFS_RAID_RAID0] = { .sub_stripes = 1, @@ -67,6 +76,9 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 0, .devs_increment = 1, .ncopies = 1, + .raid_name = "raid0", + .bg_flag = BTRFS_BLOCK_GROUP_RAID0, + .mindev_error = 0, }, [BTRFS_RAID_SINGLE] = { .sub_stripes = 1, @@ -76,6 +88,9 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 0, .devs_increment = 1, .ncopies = 1, + .raid_name = "single", + .bg_flag = 0, + .mindev_error = 0, }, [BTRFS_RAID_RAID5] = { .sub_stripes = 1, @@ -85,6 +100,9 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 1, .devs_increment = 1, .ncopies = 2, + .raid_name = "raid5", + .bg_flag = BTRFS_BLOCK_GROUP_RAID5, + .mindev_error = BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET, }, [BTRFS_RAID_RAID6] = { .sub_stripes = 1, @@ -94,33 +112,19 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 2, .devs_increment = 1, .ncopies = 3, + .raid_name = "raid6", + .bg_flag = BTRFS_BLOCK_GROUP_RAID6, + .mindev_error = BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET, }, }; -const u64 btrfs_raid_group[BTRFS_NR_RAID_TYPES] = { - [BTRFS_RAID_RAID10] = BTRFS_BLOCK_GROUP_RAID10, - [BTRFS_RAID_RAID1] = BTRFS_BLOCK_GROUP_RAID1, - [BTRFS_RAID_DUP] = BTRFS_BLOCK_GROUP_DUP, - [BTRFS_RAID_RAID0] = BTRFS_BLOCK_GROUP_RAID0, - [BTRFS_RAID_SINGLE] = 0, - [BTRFS_RAID_RAID5] = BTRFS_BLOCK_GROUP_RAID5, - [BTRFS_RAID_RAID6] = BTRFS_BLOCK_GROUP_RAID6, -}; +const char *get_raid_name(enum btrfs_raid_types type) +{ + if (type >= BTRFS_NR_RAID_TYPES) + return NULL; -/* - * Table to convert BTRFS_RAID_* to the error code if minimum number of devices - * condition is not met. Zero means there's no corresponding - * BTRFS_ERROR_DEV_*_NOT_MET value. - */ -const int btrfs_raid_mindev_error[BTRFS_NR_RAID_TYPES] = { - [BTRFS_RAID_RAID10] = BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET, - [BTRFS_RAID_RAID1] = BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET, - [BTRFS_RAID_DUP] = 0, - [BTRFS_RAID_RAID0] = 0, - [BTRFS_RAID_SINGLE] = 0, - [BTRFS_RAID_RAID5] = BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET, - [BTRFS_RAID_RAID6] = BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET, -}; + return btrfs_raid_array[type].raid_name; +} static int init_first_rw_device(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info); @@ -167,12 +171,6 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, * may be used to exclude some operations from running concurrently without any * modifications to the list (see write_all_supers) * - * volume_mutex - * ------------ - * coarse lock owned by a mounted filesystem; used to exclude some operations - * that cannot run in parallel and affect the higher-level properties of the - * filesystem like: device add/deleting/resize/replace, or balance - * * balance_mutex * ------------- * protects balance structures (status, state) and context accessed from @@ -197,6 +195,41 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, * device_list_mutex * chunk_mutex * balance_mutex + * + * + * Exclusive operations, BTRFS_FS_EXCL_OP + * ====================================== + * + * Maintains the exclusivity of the following operations that apply to the + * whole filesystem and cannot run in parallel. + * + * - Balance (*) + * - Device add + * - Device remove + * - Device replace (*) + * - Resize + * + * The device operations (as above) can be in one of the following states: + * + * - Running state + * - Paused state + * - Completed state + * + * Only device operations marked with (*) can go into the Paused state for the + * following reasons: + * + * - ioctl (only Balance can be Paused through ioctl) + * - filesystem remounted as read-only + * - filesystem unmounted and mounted as read-only + * - system power-cycle and filesystem mounted as read-only + * - filesystem or device errors leading to forced read-only + * + * BTRFS_FS_EXCL_OP flag is set and cleared using atomic operations. + * During the course of Paused state, the BTRFS_FS_EXCL_OP remains set. + * A device operation in Paused or Running state can be canceled or resumed + * either by ioctl (Balance only) or when remounted as read-write. + * BTRFS_FS_EXCL_OP flag is cleared when the device operation is canceled or + * completed. */ DEFINE_MUTEX(uuid_mutex); @@ -227,14 +260,14 @@ static struct btrfs_fs_devices *alloc_fs_devices(const u8 *fsid) INIT_LIST_HEAD(&fs_devs->devices); INIT_LIST_HEAD(&fs_devs->resized_devices); INIT_LIST_HEAD(&fs_devs->alloc_list); - INIT_LIST_HEAD(&fs_devs->list); + INIT_LIST_HEAD(&fs_devs->fs_list); if (fsid) memcpy(fs_devs->fsid, fsid, BTRFS_FSID_SIZE); return fs_devs; } -static void free_device(struct btrfs_device *device) +void btrfs_free_device(struct btrfs_device *device) { rcu_string_free(device->name); bio_put(device->flush_bio); @@ -249,7 +282,7 @@ static void free_fs_devices(struct btrfs_fs_devices *fs_devices) device = list_entry(fs_devices->devices.next, struct btrfs_device, dev_list); list_del(&device->dev_list); - free_device(device); + btrfs_free_device(device); } kfree(fs_devices); } @@ -273,8 +306,8 @@ void __exit btrfs_cleanup_fs_uuids(void) while (!list_empty(&fs_uuids)) { fs_devices = list_entry(fs_uuids.next, - struct btrfs_fs_devices, list); - list_del(&fs_devices->list); + struct btrfs_fs_devices, fs_list); + list_del(&fs_devices->fs_list); free_fs_devices(fs_devices); } } @@ -282,7 +315,7 @@ void __exit btrfs_cleanup_fs_uuids(void) /* * Returns a pointer to a new btrfs_device on success; ERR_PTR() on error. * Returned struct is not linked onto any lists and must be destroyed using - * free_device. + * btrfs_free_device. */ static struct btrfs_device *__alloc_device(void) { @@ -327,10 +360,9 @@ static struct btrfs_device *__alloc_device(void) static struct btrfs_device *find_device(struct btrfs_fs_devices *fs_devices, u64 devid, const u8 *uuid) { - struct list_head *head = &fs_devices->devices; struct btrfs_device *dev; - list_for_each_entry(dev, head, dev_list) { + list_for_each_entry(dev, &fs_devices->devices, dev_list) { if (dev->devid == devid && (!uuid || !memcmp(dev->uuid, uuid, BTRFS_UUID_SIZE))) { return dev; @@ -343,7 +375,7 @@ static noinline struct btrfs_fs_devices *find_fsid(u8 *fsid) { struct btrfs_fs_devices *fs_devices; - list_for_each_entry(fs_devices, &fs_uuids, list) { + list_for_each_entry(fs_devices, &fs_uuids, fs_list) { if (memcmp(fsid, fs_devices->fsid, BTRFS_FSID_SIZE) == 0) return fs_devices; } @@ -607,7 +639,7 @@ static void btrfs_free_stale_devices(const char *path, struct btrfs_fs_devices *fs_devs, *tmp_fs_devs; struct btrfs_device *dev, *tmp_dev; - list_for_each_entry_safe(fs_devs, tmp_fs_devs, &fs_uuids, list) { + list_for_each_entry_safe(fs_devs, tmp_fs_devs, &fs_uuids, fs_list) { if (fs_devs->opened) continue; @@ -632,13 +664,13 @@ static void btrfs_free_stale_devices(const char *path, /* delete the stale device */ if (fs_devs->num_devices == 1) { btrfs_sysfs_remove_fsid(fs_devs); - list_del(&fs_devs->list); + list_del(&fs_devs->fs_list); free_fs_devices(fs_devs); break; } else { fs_devs->num_devices--; list_del(&dev->dev_list); - free_device(dev); + btrfs_free_device(dev); } } } @@ -732,7 +764,7 @@ static noinline struct btrfs_device *device_list_add(const char *path, if (IS_ERR(fs_devices)) return ERR_CAST(fs_devices); - list_add(&fs_devices->list, &fs_uuids); + list_add(&fs_devices->fs_list, &fs_uuids); device = NULL; } else { @@ -753,7 +785,7 @@ static noinline struct btrfs_device *device_list_add(const char *path, name = rcu_string_strdup(path, GFP_NOFS); if (!name) { - free_device(device); + btrfs_free_device(device); return ERR_PTR(-ENOMEM); } rcu_assign_pointer(device->name, name); @@ -866,7 +898,7 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig) name = rcu_string_strdup(orig_dev->name->str, GFP_KERNEL); if (!name) { - free_device(device); + btrfs_free_device(device); goto error; } rcu_assign_pointer(device->name, name); @@ -938,7 +970,7 @@ void btrfs_free_extra_devids(struct btrfs_fs_devices *fs_devices, int step) } list_del_init(&device->dev_list); fs_devices->num_devices--; - free_device(device); + btrfs_free_device(device); } if (fs_devices->seed) { @@ -956,7 +988,7 @@ static void free_device_rcu(struct rcu_head *head) struct btrfs_device *device; device = container_of(head, struct btrfs_device, rcu); - free_device(device); + btrfs_free_device(device); } static void btrfs_close_bdev(struct btrfs_device *device) @@ -1005,7 +1037,7 @@ static void btrfs_prepare_close_one_device(struct btrfs_device *device) new_device->fs_devices = device->fs_devices; } -static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) +static int close_fs_devices(struct btrfs_fs_devices *fs_devices) { struct btrfs_device *device, *tmp; struct list_head pending_put; @@ -1050,7 +1082,7 @@ int btrfs_close_devices(struct btrfs_fs_devices *fs_devices) int ret; mutex_lock(&uuid_mutex); - ret = __btrfs_close_devices(fs_devices); + ret = close_fs_devices(fs_devices); if (!fs_devices->opened) { seed_devices = fs_devices->seed; fs_devices->seed = NULL; @@ -1060,23 +1092,22 @@ int btrfs_close_devices(struct btrfs_fs_devices *fs_devices) while (seed_devices) { fs_devices = seed_devices; seed_devices = fs_devices->seed; - __btrfs_close_devices(fs_devices); + close_fs_devices(fs_devices); free_fs_devices(fs_devices); } return ret; } -static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices, +static int open_fs_devices(struct btrfs_fs_devices *fs_devices, fmode_t flags, void *holder) { - struct list_head *head = &fs_devices->devices; struct btrfs_device *device; struct btrfs_device *latest_dev = NULL; int ret = 0; flags |= FMODE_EXCL; - list_for_each_entry(device, head, dev_list) { + list_for_each_entry(device, &fs_devices->devices, dev_list) { /* Just open everything we can; ignore failures here */ if (btrfs_open_one_device(fs_devices, device, flags, holder)) continue; @@ -1115,15 +1146,16 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, { int ret; - mutex_lock(&uuid_mutex); + mutex_lock(&fs_devices->device_list_mutex); if (fs_devices->opened) { fs_devices->opened++; ret = 0; } else { list_sort(NULL, &fs_devices->devices, devid_cmp); - ret = __btrfs_open_devices(fs_devices, flags, holder); + ret = open_fs_devices(fs_devices, flags, holder); } - mutex_unlock(&uuid_mutex); + mutex_unlock(&fs_devices->device_list_mutex); + return ret; } @@ -1201,31 +1233,29 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, */ bytenr = btrfs_sb_offset(0); flags |= FMODE_EXCL; - mutex_lock(&uuid_mutex); bdev = blkdev_get_by_path(path, flags, holder); - if (IS_ERR(bdev)) { - ret = PTR_ERR(bdev); - goto error; - } + if (IS_ERR(bdev)) + return PTR_ERR(bdev); if (btrfs_read_disk_super(bdev, bytenr, &page, &disk_super)) { ret = -EINVAL; goto error_bdev_put; } + mutex_lock(&uuid_mutex); device = device_list_add(path, disk_super); if (IS_ERR(device)) ret = PTR_ERR(device); else *fs_devices_ret = device->fs_devices; + mutex_unlock(&uuid_mutex); btrfs_release_disk_super(page); error_bdev_put: blkdev_put(bdev, flags); -error: - mutex_unlock(&uuid_mutex); + return ret; } @@ -1857,11 +1887,11 @@ static int btrfs_check_raid_min_devices(struct btrfs_fs_info *fs_info, } while (read_seqretry(&fs_info->profiles_lock, seq)); for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) { - if (!(all_avail & btrfs_raid_group[i])) + if (!(all_avail & btrfs_raid_array[i].bg_flag)) continue; if (num_devices < btrfs_raid_array[i].devs_min) { - int ret = btrfs_raid_mindev_error[i]; + int ret = btrfs_raid_array[i].mindev_error; if (ret) return ret; @@ -1917,13 +1947,13 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path, { struct btrfs_device *device; struct btrfs_fs_devices *cur_devices; + struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; u64 num_devices; int ret = 0; - mutex_lock(&fs_info->volume_mutex); mutex_lock(&uuid_mutex); - num_devices = fs_info->fs_devices->num_devices; + num_devices = fs_devices->num_devices; btrfs_dev_replace_read_lock(&fs_info->dev_replace); if (btrfs_dev_replace_is_ongoing(&fs_info->dev_replace)) { WARN_ON(num_devices < 1); @@ -1986,27 +2016,32 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path, * (super_copy) should hold the device list mutex. */ + /* + * In normal cases the cur_devices == fs_devices. But in case + * of deleting a seed device, the cur_devices should point to + * its own fs_devices listed under the fs_devices->seed. + */ cur_devices = device->fs_devices; - mutex_lock(&fs_info->fs_devices->device_list_mutex); + mutex_lock(&fs_devices->device_list_mutex); list_del_rcu(&device->dev_list); - device->fs_devices->num_devices--; - device->fs_devices->total_devices--; + cur_devices->num_devices--; + cur_devices->total_devices--; if (test_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state)) - device->fs_devices->missing_devices--; + cur_devices->missing_devices--; btrfs_assign_next_active_device(fs_info, device, NULL); if (device->bdev) { - device->fs_devices->open_devices--; + cur_devices->open_devices--; /* remove sysfs entry */ - btrfs_sysfs_rm_device_link(fs_info->fs_devices, device); + btrfs_sysfs_rm_device_link(fs_devices, device); } num_devices = btrfs_super_num_devices(fs_info->super_copy) - 1; btrfs_set_super_num_devices(fs_info->super_copy, num_devices); - mutex_unlock(&fs_info->fs_devices->device_list_mutex); + mutex_unlock(&fs_devices->device_list_mutex); /* * at this point, the device is zero sized and detached from @@ -2020,8 +2055,6 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path, call_rcu(&device->rcu, free_device_rcu); if (cur_devices->open_devices == 0) { - struct btrfs_fs_devices *fs_devices; - fs_devices = fs_info->fs_devices; while (fs_devices) { if (fs_devices->seed == cur_devices) { fs_devices->seed = cur_devices->seed; @@ -2030,20 +2063,19 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path, fs_devices = fs_devices->seed; } cur_devices->seed = NULL; - __btrfs_close_devices(cur_devices); + close_fs_devices(cur_devices); free_fs_devices(cur_devices); } out: mutex_unlock(&uuid_mutex); - mutex_unlock(&fs_info->volume_mutex); return ret; error_undo: if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) { mutex_lock(&fs_info->chunk_mutex); list_add(&device->dev_alloc_list, - &fs_info->fs_devices->alloc_list); + &fs_devices->alloc_list); device->fs_devices->rw_devices++; mutex_unlock(&fs_info->chunk_mutex); } @@ -2112,7 +2144,7 @@ void btrfs_rm_dev_replace_free_srcdev(struct btrfs_fs_info *fs_info, tmp_fs_devices = tmp_fs_devices->seed; } fs_devices->seed = NULL; - __btrfs_close_devices(fs_devices); + close_fs_devices(fs_devices); free_fs_devices(fs_devices); } } @@ -2120,23 +2152,23 @@ void btrfs_rm_dev_replace_free_srcdev(struct btrfs_fs_info *fs_info, void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, struct btrfs_device *tgtdev) { - mutex_lock(&uuid_mutex); + struct btrfs_fs_devices *fs_devices = fs_info->fs_devices; + WARN_ON(!tgtdev); - mutex_lock(&fs_info->fs_devices->device_list_mutex); + mutex_lock(&fs_devices->device_list_mutex); - btrfs_sysfs_rm_device_link(fs_info->fs_devices, tgtdev); + btrfs_sysfs_rm_device_link(fs_devices, tgtdev); if (tgtdev->bdev) - fs_info->fs_devices->open_devices--; + fs_devices->open_devices--; - fs_info->fs_devices->num_devices--; + fs_devices->num_devices--; btrfs_assign_next_active_device(fs_info, tgtdev, NULL); list_del_rcu(&tgtdev->dev_list); - mutex_unlock(&fs_info->fs_devices->device_list_mutex); - mutex_unlock(&uuid_mutex); + mutex_unlock(&fs_devices->device_list_mutex); /* * The update_dev_time() with in btrfs_scratch_superblocks() @@ -2188,10 +2220,6 @@ int btrfs_find_device_missing_or_by_path(struct btrfs_fs_info *fs_info, struct btrfs_device *tmp; devices = &fs_info->fs_devices->devices; - /* - * It is safe to read the devices since the volume_mutex - * is held by the caller. - */ list_for_each_entry(tmp, devices, dev_list) { if (test_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &tmp->dev_state) && !tmp->bdev) { @@ -2259,7 +2287,7 @@ static int btrfs_prepare_sprout(struct btrfs_fs_info *fs_info) return PTR_ERR(old_devices); } - list_add(&old_devices->list, &fs_uuids); + list_add(&old_devices->fs_list, &fs_uuids); memcpy(seed_devices, fs_devices, sizeof(*seed_devices)); seed_devices->opened = 1; @@ -2570,7 +2598,7 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path if (trans) btrfs_end_transaction(trans); error_free_device: - free_device(device); + btrfs_free_device(device); error: blkdev_put(bdev, FMODE_EXCL); if (seeding_dev && !unlocked) { @@ -2580,99 +2608,6 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path return ret; } -int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, - const char *device_path, - struct btrfs_device *srcdev, - struct btrfs_device **device_out) -{ - struct btrfs_device *device; - struct block_device *bdev; - struct list_head *devices; - struct rcu_string *name; - u64 devid = BTRFS_DEV_REPLACE_DEVID; - int ret = 0; - - *device_out = NULL; - if (fs_info->fs_devices->seeding) { - btrfs_err(fs_info, "the filesystem is a seed filesystem!"); - return -EINVAL; - } - - bdev = blkdev_get_by_path(device_path, FMODE_WRITE | FMODE_EXCL, - fs_info->bdev_holder); - if (IS_ERR(bdev)) { - btrfs_err(fs_info, "target device %s is invalid!", device_path); - return PTR_ERR(bdev); - } - - filemap_write_and_wait(bdev->bd_inode->i_mapping); - - devices = &fs_info->fs_devices->devices; - list_for_each_entry(device, devices, dev_list) { - if (device->bdev == bdev) { - btrfs_err(fs_info, - "target device is in the filesystem!"); - ret = -EEXIST; - goto error; - } - } - - - if (i_size_read(bdev->bd_inode) < - btrfs_device_get_total_bytes(srcdev)) { - btrfs_err(fs_info, - "target device is smaller than source device!"); - ret = -EINVAL; - goto error; - } - - - device = btrfs_alloc_device(NULL, &devid, NULL); - if (IS_ERR(device)) { - ret = PTR_ERR(device); - goto error; - } - - name = rcu_string_strdup(device_path, GFP_KERNEL); - if (!name) { - free_device(device); - ret = -ENOMEM; - goto error; - } - rcu_assign_pointer(device->name, name); - - mutex_lock(&fs_info->fs_devices->device_list_mutex); - set_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state); - device->generation = 0; - device->io_width = fs_info->sectorsize; - device->io_align = fs_info->sectorsize; - device->sector_size = fs_info->sectorsize; - device->total_bytes = btrfs_device_get_total_bytes(srcdev); - device->disk_total_bytes = btrfs_device_get_disk_total_bytes(srcdev); - device->bytes_used = btrfs_device_get_bytes_used(srcdev); - device->commit_total_bytes = srcdev->commit_total_bytes; - device->commit_bytes_used = device->bytes_used; - device->fs_info = fs_info; - device->bdev = bdev; - set_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state); - set_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state); - device->mode = FMODE_EXCL; - device->dev_stats_valid = 1; - set_blocksize(device->bdev, BTRFS_BDEV_BLOCKSIZE); - device->fs_devices = fs_info->fs_devices; - list_add(&device->dev_list, &fs_info->fs_devices->devices); - fs_info->fs_devices->num_devices++; - fs_info->fs_devices->open_devices++; - mutex_unlock(&fs_info->fs_devices->device_list_mutex); - - *device_out = device; - return ret; - -error: - blkdev_put(bdev, FMODE_EXCL); - return ret; -} - static noinline int btrfs_update_device(struct btrfs_trans_handle *trans, struct btrfs_device *device) { @@ -3273,24 +3208,12 @@ static void update_balance_args(struct btrfs_balance_control *bctl) } /* - * Should be called with both balance and volume mutexes held to - * serialize other volume operations (add_dev/rm_dev/resize) with - * restriper. Same goes for unset_balance_control. + * Clear the balance status in fs_info and delete the balance item from disk. */ -static void set_balance_control(struct btrfs_balance_control *bctl) -{ - struct btrfs_fs_info *fs_info = bctl->fs_info; - - BUG_ON(fs_info->balance_ctl); - - spin_lock(&fs_info->balance_lock); - fs_info->balance_ctl = bctl; - spin_unlock(&fs_info->balance_lock); -} - -static void unset_balance_control(struct btrfs_fs_info *fs_info) +static void reset_balance_state(struct btrfs_fs_info *fs_info) { struct btrfs_balance_control *bctl = fs_info->balance_ctl; + int ret; BUG_ON(!fs_info->balance_ctl); @@ -3299,6 +3222,9 @@ static void unset_balance_control(struct btrfs_fs_info *fs_info) spin_unlock(&fs_info->balance_lock); kfree(bctl); + ret = del_balance_item(fs_info); + if (ret) + btrfs_handle_fs_error(fs_info, ret, NULL); } /* @@ -3835,18 +3761,6 @@ static inline int balance_need_close(struct btrfs_fs_info *fs_info) atomic_read(&fs_info->balance_cancel_req) == 0); } -static void __cancel_balance(struct btrfs_fs_info *fs_info) -{ - int ret; - - unset_balance_control(fs_info); - ret = del_balance_item(fs_info); - if (ret) - btrfs_handle_fs_error(fs_info, ret, NULL); - - clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); -} - /* Non-zero return value signifies invalidity */ static inline int validate_convert_profile(struct btrfs_balance_args *bctl_arg, u64 allowed) @@ -3857,12 +3771,12 @@ static inline int validate_convert_profile(struct btrfs_balance_args *bctl_arg, } /* - * Should be called with both balance and volume mutexes held + * Should be called with balance mutexe held */ -int btrfs_balance(struct btrfs_balance_control *bctl, +int btrfs_balance(struct btrfs_fs_info *fs_info, + struct btrfs_balance_control *bctl, struct btrfs_ioctl_balance_args *bargs) { - struct btrfs_fs_info *fs_info = bctl->fs_info; u64 meta_target, data_target; u64 allowed; int mixed = 0; @@ -3891,7 +3805,7 @@ int btrfs_balance(struct btrfs_balance_control *bctl, !(bctl->flags & BTRFS_BALANCE_METADATA) || memcmp(&bctl->data, &bctl->meta, sizeof(bctl->data))) { btrfs_err(fs_info, - "with mixed groups data and metadata balance options must be the same"); + "balance: mixed groups data and metadata options must be the same"); ret = -EINVAL; goto out; } @@ -3913,23 +3827,29 @@ int btrfs_balance(struct btrfs_balance_control *bctl, allowed |= (BTRFS_BLOCK_GROUP_RAID10 | BTRFS_BLOCK_GROUP_RAID6); if (validate_convert_profile(&bctl->data, allowed)) { + int index = btrfs_bg_flags_to_raid_index(bctl->data.target); + btrfs_err(fs_info, - "unable to start balance with target data profile %llu", - bctl->data.target); + "balance: invalid convert data profile %s", + get_raid_name(index)); ret = -EINVAL; goto out; } if (validate_convert_profile(&bctl->meta, allowed)) { + int index = btrfs_bg_flags_to_raid_index(bctl->meta.target); + btrfs_err(fs_info, - "unable to start balance with target metadata profile %llu", - bctl->meta.target); + "balance: invalid convert metadata profile %s", + get_raid_name(index)); ret = -EINVAL; goto out; } if (validate_convert_profile(&bctl->sys, allowed)) { + int index = btrfs_bg_flags_to_raid_index(bctl->sys.target); + btrfs_err(fs_info, - "unable to start balance with target system profile %llu", - bctl->sys.target); + "balance: invalid convert system profile %s", + get_raid_name(index)); ret = -EINVAL; goto out; } @@ -3950,10 +3870,10 @@ int btrfs_balance(struct btrfs_balance_control *bctl, !(bctl->meta.target & allowed))) { if (bctl->flags & BTRFS_BALANCE_FORCE) { btrfs_info(fs_info, - "force reducing metadata integrity"); + "balance: force reducing metadata integrity"); } else { btrfs_err(fs_info, - "balance will reduce metadata integrity, use force if you want this"); + "balance: reduces metadata integrity, use --force if you want this"); ret = -EINVAL; goto out; } @@ -3967,9 +3887,12 @@ int btrfs_balance(struct btrfs_balance_control *bctl, bctl->data.target : fs_info->avail_data_alloc_bits; if (btrfs_get_num_tolerated_disk_barrier_failures(meta_target) < btrfs_get_num_tolerated_disk_barrier_failures(data_target)) { + int meta_index = btrfs_bg_flags_to_raid_index(meta_target); + int data_index = btrfs_bg_flags_to_raid_index(data_target); + btrfs_warn(fs_info, - "metadata profile 0x%llx has lower redundancy than data profile 0x%llx", - meta_target, data_target); + "balance: metadata profile %s has lower redundancy than data profile %s", + get_raid_name(meta_index), get_raid_name(data_index)); } ret = insert_balance_item(fs_info, bctl); @@ -3978,7 +3901,10 @@ int btrfs_balance(struct btrfs_balance_control *bctl, if (!(bctl->flags & BTRFS_BALANCE_RESUME)) { BUG_ON(ret == -EEXIST); - set_balance_control(bctl); + BUG_ON(fs_info->balance_ctl); + spin_lock(&fs_info->balance_lock); + fs_info->balance_ctl = bctl; + spin_unlock(&fs_info->balance_lock); } else { BUG_ON(ret != -EEXIST); spin_lock(&fs_info->balance_lock); @@ -3986,22 +3912,24 @@ int btrfs_balance(struct btrfs_balance_control *bctl, spin_unlock(&fs_info->balance_lock); } - atomic_inc(&fs_info->balance_running); + ASSERT(!test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)); + set_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags); mutex_unlock(&fs_info->balance_mutex); ret = __btrfs_balance(fs_info); mutex_lock(&fs_info->balance_mutex); - atomic_dec(&fs_info->balance_running); + clear_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags); if (bargs) { memset(bargs, 0, sizeof(*bargs)); - update_ioctl_balance_args(fs_info, 0, bargs); + btrfs_update_ioctl_balance_args(fs_info, bargs); } if ((ret && ret != -ECANCELED && ret != -ENOSPC) || balance_need_close(fs_info)) { - __cancel_balance(fs_info); + reset_balance_state(fs_info); + clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); } wake_up(&fs_info->balance_wait_q); @@ -4009,11 +3937,11 @@ int btrfs_balance(struct btrfs_balance_control *bctl, return ret; out: if (bctl->flags & BTRFS_BALANCE_RESUME) - __cancel_balance(fs_info); - else { + reset_balance_state(fs_info); + else kfree(bctl); - clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); - } + clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); + return ret; } @@ -4022,16 +3950,12 @@ static int balance_kthread(void *data) struct btrfs_fs_info *fs_info = data; int ret = 0; - mutex_lock(&fs_info->volume_mutex); mutex_lock(&fs_info->balance_mutex); - if (fs_info->balance_ctl) { - btrfs_info(fs_info, "continuing balance"); - ret = btrfs_balance(fs_info->balance_ctl, NULL); + btrfs_info(fs_info, "balance: resuming"); + ret = btrfs_balance(fs_info, fs_info->balance_ctl, NULL); } - mutex_unlock(&fs_info->balance_mutex); - mutex_unlock(&fs_info->volume_mutex); return ret; } @@ -4040,15 +3964,15 @@ int btrfs_resume_balance_async(struct btrfs_fs_info *fs_info) { struct task_struct *tsk; - spin_lock(&fs_info->balance_lock); + mutex_lock(&fs_info->balance_mutex); if (!fs_info->balance_ctl) { - spin_unlock(&fs_info->balance_lock); + mutex_unlock(&fs_info->balance_mutex); return 0; } - spin_unlock(&fs_info->balance_lock); + mutex_unlock(&fs_info->balance_mutex); if (btrfs_test_opt(fs_info, SKIP_BALANCE)) { - btrfs_info(fs_info, "force skipping balance"); + btrfs_info(fs_info, "balance: resume skipped"); return 0; } @@ -4100,7 +4024,6 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info) leaf = path->nodes[0]; item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_balance_item); - bctl->fs_info = fs_info; bctl->flags = btrfs_balance_flags(leaf, item); bctl->flags |= BTRFS_BALANCE_RESUME; @@ -4111,15 +4034,26 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info) btrfs_balance_sys(leaf, item, &disk_bargs); btrfs_disk_balance_args_to_cpu(&bctl->sys, &disk_bargs); - WARN_ON(test_and_set_bit(BTRFS_FS_EXCL_OP, &fs_info->flags)); + /* + * This should never happen, as the paused balance state is recovered + * during mount without any chance of other exclusive ops to collide. + * + * This gives the exclusive op status to balance and keeps in paused + * state until user intervention (cancel or umount). If the ownership + * cannot be assigned, show a message but do not fail. The balance + * is in a paused state and must have fs_info::balance_ctl properly + * set up. + */ + if (test_and_set_bit(BTRFS_FS_EXCL_OP, &fs_info->flags)) + btrfs_warn(fs_info, + "balance: cannot set exclusive op status, resume manually"); - mutex_lock(&fs_info->volume_mutex); mutex_lock(&fs_info->balance_mutex); - - set_balance_control(bctl); - + BUG_ON(fs_info->balance_ctl); + spin_lock(&fs_info->balance_lock); + fs_info->balance_ctl = bctl; + spin_unlock(&fs_info->balance_lock); mutex_unlock(&fs_info->balance_mutex); - mutex_unlock(&fs_info->volume_mutex); out: btrfs_free_path(path); return ret; @@ -4135,16 +4069,16 @@ int btrfs_pause_balance(struct btrfs_fs_info *fs_info) return -ENOTCONN; } - if (atomic_read(&fs_info->balance_running)) { + if (test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)) { atomic_inc(&fs_info->balance_pause_req); mutex_unlock(&fs_info->balance_mutex); wait_event(fs_info->balance_wait_q, - atomic_read(&fs_info->balance_running) == 0); + !test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)); mutex_lock(&fs_info->balance_mutex); /* we are good with balance_ctl ripped off from under us */ - BUG_ON(atomic_read(&fs_info->balance_running)); + BUG_ON(test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)); atomic_dec(&fs_info->balance_pause_req); } else { ret = -ENOTCONN; @@ -4156,38 +4090,49 @@ int btrfs_pause_balance(struct btrfs_fs_info *fs_info) int btrfs_cancel_balance(struct btrfs_fs_info *fs_info) { - if (sb_rdonly(fs_info->sb)) - return -EROFS; - mutex_lock(&fs_info->balance_mutex); if (!fs_info->balance_ctl) { mutex_unlock(&fs_info->balance_mutex); return -ENOTCONN; } + /* + * A paused balance with the item stored on disk can be resumed at + * mount time if the mount is read-write. Otherwise it's still paused + * and we must not allow cancelling as it deletes the item. + */ + if (sb_rdonly(fs_info->sb)) { + mutex_unlock(&fs_info->balance_mutex); + return -EROFS; + } + atomic_inc(&fs_info->balance_cancel_req); /* * if we are running just wait and return, balance item is * deleted in btrfs_balance in this case */ - if (atomic_read(&fs_info->balance_running)) { + if (test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)) { mutex_unlock(&fs_info->balance_mutex); wait_event(fs_info->balance_wait_q, - atomic_read(&fs_info->balance_running) == 0); + !test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)); mutex_lock(&fs_info->balance_mutex); } else { - /* __cancel_balance needs volume_mutex */ mutex_unlock(&fs_info->balance_mutex); - mutex_lock(&fs_info->volume_mutex); + /* + * Lock released to allow other waiters to continue, we'll + * reexamine the status again. + */ mutex_lock(&fs_info->balance_mutex); - if (fs_info->balance_ctl) - __cancel_balance(fs_info); - - mutex_unlock(&fs_info->volume_mutex); + if (fs_info->balance_ctl) { + reset_balance_state(fs_info); + clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); + btrfs_info(fs_info, "balance: canceled"); + } } - BUG_ON(fs_info->balance_ctl || atomic_read(&fs_info->balance_running)); + BUG_ON(fs_info->balance_ctl || + test_bit(BTRFS_FS_BALANCE_RUNNING, &fs_info->flags)); atomic_dec(&fs_info->balance_cancel_req); mutex_unlock(&fs_info->balance_mutex); return 0; @@ -4264,8 +4209,7 @@ static int btrfs_uuid_scan_kthread(void *data) } update_tree: if (!btrfs_is_empty_uuid(root_item.uuid)) { - ret = btrfs_uuid_tree_add(trans, fs_info, - root_item.uuid, + ret = btrfs_uuid_tree_add(trans, root_item.uuid, BTRFS_UUID_KEY_SUBVOL, key.objectid); if (ret < 0) { @@ -4276,7 +4220,7 @@ static int btrfs_uuid_scan_kthread(void *data) } if (!btrfs_is_empty_uuid(root_item.received_uuid)) { - ret = btrfs_uuid_tree_add(trans, fs_info, + ret = btrfs_uuid_tree_add(trans, root_item.received_uuid, BTRFS_UUID_KEY_RECEIVED_SUBVOL, key.objectid); @@ -4482,7 +4426,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size) if (!path) return -ENOMEM; - path->reada = READA_FORWARD; + path->reada = READA_BACK; mutex_lock(&fs_info->chunk_mutex); @@ -6043,9 +5987,8 @@ int btrfs_map_sblock(struct btrfs_fs_info *fs_info, enum btrfs_map_op op, return __btrfs_map_block(fs_info, op, logical, length, bbio_ret, 0, 1); } -int btrfs_rmap_block(struct btrfs_fs_info *fs_info, - u64 chunk_start, u64 physical, u64 devid, - u64 **logical, int *naddrs, int *stripe_len) +int btrfs_rmap_block(struct btrfs_fs_info *fs_info, u64 chunk_start, + u64 physical, u64 **logical, int *naddrs, int *stripe_len) { struct extent_map *em; struct map_lookup *map; @@ -6077,8 +6020,6 @@ int btrfs_rmap_block(struct btrfs_fs_info *fs_info, BUG_ON(!buf); /* -ENOMEM */ for (i = 0; i < map->num_stripes; i++) { - if (devid && map->stripes[i].dev->devid != devid) - continue; if (map->stripes[i].physical > physical || map->stripes[i].physical + length <= physical) continue; @@ -6410,7 +6351,7 @@ static struct btrfs_device *add_missing_dev(struct btrfs_fs_devices *fs_devices, * * Return: a pointer to a new &struct btrfs_device on success; ERR_PTR() * on error. Returned struct is not linked onto any lists and must be - * destroyed with free_device. + * destroyed with btrfs_free_device. */ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info, const u64 *devid, @@ -6433,7 +6374,7 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info, ret = find_next_devid(fs_info, &tmp); if (ret) { - free_device(dev); + btrfs_free_device(dev); return ERR_PTR(ret); } } @@ -6684,8 +6625,7 @@ static struct btrfs_fs_devices *open_seed_devices(struct btrfs_fs_info *fs_info, if (IS_ERR(fs_devices)) return fs_devices; - ret = __btrfs_open_devices(fs_devices, FMODE_READ, - fs_info->bdev_holder); + ret = open_fs_devices(fs_devices, FMODE_READ, fs_info->bdev_holder); if (ret) { free_fs_devices(fs_devices); fs_devices = ERR_PTR(ret); @@ -6693,7 +6633,7 @@ static struct btrfs_fs_devices *open_seed_devices(struct btrfs_fs_info *fs_info, } if (!fs_devices->seeding) { - __btrfs_close_devices(fs_devices); + close_fs_devices(fs_devices); free_fs_devices(fs_devices); fs_devices = ERR_PTR(-EINVAL); goto out; @@ -7002,6 +6942,10 @@ int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info) if (!path) return -ENOMEM; + /* + * uuid_mutex is needed only if we are mounting a sprout FS + * otherwise we don't need it. + */ mutex_lock(&uuid_mutex); mutex_lock(&fs_info->chunk_mutex); diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 79096884654fa99171d2509def8143abefaacca4..5139ec8daf4cbfbace23b714fa783af33107870e 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -208,6 +208,7 @@ BTRFS_DEVICE_GETSET_FUNCS(bytes_used); struct btrfs_fs_devices { u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ + struct list_head fs_list; u64 num_devices; u64 open_devices; @@ -229,7 +230,6 @@ struct btrfs_fs_devices { struct list_head resized_devices; /* devices not currently being allocated */ struct list_head alloc_list; - struct list_head list; struct btrfs_fs_devices *seed; int seeding; @@ -329,11 +329,12 @@ struct btrfs_raid_attr { int tolerated_failures; /* max tolerated fail devs */ int devs_increment; /* ndevs has to be a multiple of this */ int ncopies; /* how many copies to data has */ + int mindev_error; /* error code if min devs requisite is unmet */ + const char raid_name[8]; /* name of the raid */ + u64 bg_flag; /* block group flag of the raid */ }; extern const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES]; -extern const int btrfs_raid_mindev_error[BTRFS_NR_RAID_TYPES]; -extern const u64 btrfs_raid_group[BTRFS_NR_RAID_TYPES]; struct map_lookup { u64 type; @@ -351,8 +352,6 @@ struct map_lookup { struct btrfs_balance_args; struct btrfs_balance_progress; struct btrfs_balance_control { - struct btrfs_fs_info *fs_info; - struct btrfs_balance_args data; struct btrfs_balance_args meta; struct btrfs_balance_args sys; @@ -393,9 +392,8 @@ int btrfs_map_block(struct btrfs_fs_info *fs_info, enum btrfs_map_op op, int btrfs_map_sblock(struct btrfs_fs_info *fs_info, enum btrfs_map_op op, u64 logical, u64 *length, struct btrfs_bio **bbio_ret); -int btrfs_rmap_block(struct btrfs_fs_info *fs_info, - u64 chunk_start, u64 physical, u64 devid, - u64 **logical, int *naddrs, int *stripe_len); +int btrfs_rmap_block(struct btrfs_fs_info *fs_info, u64 chunk_start, + u64 physical, u64 **logical, int *naddrs, int *stripe_len); int btrfs_read_sys_array(struct btrfs_fs_info *fs_info); int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info); int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, @@ -421,6 +419,7 @@ int btrfs_find_device_by_devspec(struct btrfs_fs_info *fs_info, u64 devid, struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info, const u64 *devid, const u8 *uuid); +void btrfs_free_device(struct btrfs_device *device); int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path, u64 devid); void __exit btrfs_cleanup_fs_uuids(void); @@ -431,11 +430,8 @@ struct btrfs_device *btrfs_find_device(struct btrfs_fs_info *fs_info, u64 devid, u8 *uuid, u8 *fsid); int btrfs_shrink_device(struct btrfs_device *device, u64 new_size); int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *path); -int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, - const char *device_path, - struct btrfs_device *srcdev, - struct btrfs_device **device_out); -int btrfs_balance(struct btrfs_balance_control *bctl, +int btrfs_balance(struct btrfs_fs_info *fs_info, + struct btrfs_balance_control *bctl, struct btrfs_ioctl_balance_args *bargs); int btrfs_resume_balance_async(struct btrfs_fs_info *fs_info); int btrfs_recover_balance(struct btrfs_fs_info *fs_info); @@ -553,6 +549,8 @@ static inline enum btrfs_raid_types btrfs_bg_flags_to_raid_index(u64 flags) return BTRFS_RAID_SINGLE; /* BTRFS_BLOCK_GROUP_SINGLE */ } +const char *get_raid_name(enum btrfs_raid_types type); + void btrfs_update_commit_device_size(struct btrfs_fs_info *fs_info); void btrfs_update_commit_device_bytes_used(struct btrfs_transaction *trans); diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 965c650a5273cb4310ae460666bf0f4b1437b2b1..39b94ec965be6e5e42dd410d280d71b9365f40e4 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -121,9 +121,9 @@ TRACE_EVENT(btrfs_transaction_commit, __entry->root_objectid = root->root_key.objectid; ), - TP_printk_btrfs("root = %llu(%s), gen = %llu", + TP_printk_btrfs("root=%llu(%s) gen=%llu", show_root_type(__entry->root_objectid), - (unsigned long long)__entry->generation) + __entry->generation) ); DECLARE_EVENT_CLASS(btrfs__inode, @@ -133,7 +133,7 @@ DECLARE_EVENT_CLASS(btrfs__inode, TP_ARGS(inode), TP_STRUCT__entry_btrfs( - __field( ino_t, ino ) + __field( u64, ino ) __field( blkcnt_t, blocks ) __field( u64, disk_i_size ) __field( u64, generation ) @@ -143,7 +143,7 @@ DECLARE_EVENT_CLASS(btrfs__inode, ), TP_fast_assign_btrfs(btrfs_sb(inode->i_sb), - __entry->ino = inode->i_ino; + __entry->ino = btrfs_ino(BTRFS_I(inode)); __entry->blocks = inode->i_blocks; __entry->disk_i_size = BTRFS_I(inode)->disk_i_size; __entry->generation = BTRFS_I(inode)->generation; @@ -153,15 +153,15 @@ DECLARE_EVENT_CLASS(btrfs__inode, BTRFS_I(inode)->root->root_key.objectid; ), - TP_printk_btrfs("root=%llu(%s) gen=%llu ino=%lu blocks=%llu " + TP_printk_btrfs("root=%llu(%s) gen=%llu ino=%llu blocks=%llu " "disk_i_size=%llu last_trans=%llu logged_trans=%llu", show_root_type(__entry->root_objectid), - (unsigned long long)__entry->generation, - (unsigned long)__entry->ino, + __entry->generation, + __entry->ino, (unsigned long long)__entry->blocks, - (unsigned long long)__entry->disk_i_size, - (unsigned long long)__entry->last_trans, - (unsigned long long)__entry->logged_trans) + __entry->disk_i_size, + __entry->last_trans, + __entry->logged_trans) ); DEFINE_EVENT(btrfs__inode, btrfs_inode_new, @@ -244,23 +244,25 @@ TRACE_EVENT_CONDITION(btrfs_get_extent, "block_len=%llu flags=%s refs=%u " "compress_type=%u", show_root_type(__entry->root_objectid), - (unsigned long long)__entry->ino, - (unsigned long long)__entry->start, - (unsigned long long)__entry->len, - (unsigned long long)__entry->orig_start, + __entry->ino, + __entry->start, + __entry->len, + __entry->orig_start, show_map_type(__entry->block_start), - (unsigned long long)__entry->block_len, + __entry->block_len, show_map_flags(__entry->flags), __entry->refs, __entry->compress_type) ); TRACE_EVENT(btrfs_handle_em_exist, - TP_PROTO(const struct extent_map *existing, const struct extent_map *map, u64 start, u64 len), + TP_PROTO(struct btrfs_fs_info *fs_info, + const struct extent_map *existing, const struct extent_map *map, + u64 start, u64 len), - TP_ARGS(existing, map, start, len), + TP_ARGS(fs_info, existing, map, start, len), - TP_STRUCT__entry( + TP_STRUCT__entry_btrfs( __field( u64, e_start ) __field( u64, e_len ) __field( u64, map_start ) @@ -269,7 +271,7 @@ TRACE_EVENT(btrfs_handle_em_exist, __field( u64, len ) ), - TP_fast_assign( + TP_fast_assign_btrfs(fs_info, __entry->e_start = existing->start; __entry->e_len = existing->len; __entry->map_start = map->start; @@ -278,15 +280,15 @@ TRACE_EVENT(btrfs_handle_em_exist, __entry->len = len; ), - TP_printk("start=%llu len=%llu " + TP_printk_btrfs("start=%llu len=%llu " "existing(start=%llu len=%llu) " "em(start=%llu len=%llu)", - (unsigned long long)__entry->start, - (unsigned long long)__entry->len, - (unsigned long long)__entry->e_start, - (unsigned long long)__entry->e_len, - (unsigned long long)__entry->map_start, - (unsigned long long)__entry->map_len) + __entry->start, + __entry->len, + __entry->e_start, + __entry->e_len, + __entry->map_start, + __entry->map_len) ); /* file extent item */ @@ -443,7 +445,7 @@ DECLARE_EVENT_CLASS(btrfs__ordered_extent, TP_ARGS(inode, ordered), TP_STRUCT__entry_btrfs( - __field( ino_t, ino ) + __field( u64, ino ) __field( u64, file_offset ) __field( u64, start ) __field( u64, len ) @@ -457,7 +459,7 @@ DECLARE_EVENT_CLASS(btrfs__ordered_extent, ), TP_fast_assign_btrfs(btrfs_sb(inode->i_sb), - __entry->ino = inode->i_ino; + __entry->ino = btrfs_ino(BTRFS_I(inode)); __entry->file_offset = ordered->file_offset; __entry->start = ordered->start; __entry->len = ordered->len; @@ -477,13 +479,13 @@ DECLARE_EVENT_CLASS(btrfs__ordered_extent, "bytes_left=%llu flags=%s compress_type=%d " "refs=%d", show_root_type(__entry->root_objectid), - (unsigned long long)__entry->ino, - (unsigned long long)__entry->file_offset, - (unsigned long long)__entry->start, - (unsigned long long)__entry->len, - (unsigned long long)__entry->disk_len, - (unsigned long long)__entry->truncated_len, - (unsigned long long)__entry->bytes_left, + __entry->ino, + __entry->file_offset, + __entry->start, + __entry->len, + __entry->disk_len, + __entry->truncated_len, + __entry->bytes_left, show_ordered_flags(__entry->flags), __entry->compress_type, __entry->refs) ); @@ -528,7 +530,7 @@ DECLARE_EVENT_CLASS(btrfs__writepage, TP_ARGS(page, inode, wbc), TP_STRUCT__entry_btrfs( - __field( ino_t, ino ) + __field( u64, ino ) __field( pgoff_t, index ) __field( long, nr_to_write ) __field( long, pages_skipped ) @@ -542,7 +544,7 @@ DECLARE_EVENT_CLASS(btrfs__writepage, ), TP_fast_assign_btrfs(btrfs_sb(inode->i_sb), - __entry->ino = inode->i_ino; + __entry->ino = btrfs_ino(BTRFS_I(inode)); __entry->index = page->index; __entry->nr_to_write = wbc->nr_to_write; __entry->pages_skipped = wbc->pages_skipped; @@ -556,12 +558,12 @@ DECLARE_EVENT_CLASS(btrfs__writepage, BTRFS_I(inode)->root->root_key.objectid; ), - TP_printk_btrfs("root=%llu(%s) ino=%lu page_index=%lu " + TP_printk_btrfs("root=%llu(%s) ino=%llu page_index=%lu " "nr_to_write=%ld pages_skipped=%ld range_start=%llu " "range_end=%llu for_kupdate=%d " "for_reclaim=%d range_cyclic=%d writeback_index=%lu", show_root_type(__entry->root_objectid), - (unsigned long)__entry->ino, __entry->index, + __entry->ino, __entry->index, __entry->nr_to_write, __entry->pages_skipped, __entry->range_start, __entry->range_end, __entry->for_kupdate, @@ -584,7 +586,7 @@ TRACE_EVENT(btrfs_writepage_end_io_hook, TP_ARGS(page, start, end, uptodate), TP_STRUCT__entry_btrfs( - __field( ino_t, ino ) + __field( u64, ino ) __field( pgoff_t, index ) __field( u64, start ) __field( u64, end ) @@ -593,7 +595,7 @@ TRACE_EVENT(btrfs_writepage_end_io_hook, ), TP_fast_assign_btrfs(btrfs_sb(page->mapping->host->i_sb), - __entry->ino = page->mapping->host->i_ino; + __entry->ino = btrfs_ino(BTRFS_I(page->mapping->host)); __entry->index = page->index; __entry->start = start; __entry->end = end; @@ -602,12 +604,12 @@ TRACE_EVENT(btrfs_writepage_end_io_hook, BTRFS_I(page->mapping->host)->root->root_key.objectid; ), - TP_printk_btrfs("root=%llu(%s) ino=%lu page_index=%lu start=%llu " + TP_printk_btrfs("root=%llu(%s) ino=%llu page_index=%lu start=%llu " "end=%llu uptodate=%d", show_root_type(__entry->root_objectid), - (unsigned long)__entry->ino, (unsigned long)__entry->index, - (unsigned long long)__entry->start, - (unsigned long long)__entry->end, __entry->uptodate) + __entry->ino, (unsigned long)__entry->index, + __entry->start, + __entry->end, __entry->uptodate) ); TRACE_EVENT(btrfs_sync_file, @@ -617,8 +619,8 @@ TRACE_EVENT(btrfs_sync_file, TP_ARGS(file, datasync), TP_STRUCT__entry_btrfs( - __field( ino_t, ino ) - __field( ino_t, parent ) + __field( u64, ino ) + __field( u64, parent ) __field( int, datasync ) __field( u64, root_objectid ) ), @@ -628,16 +630,17 @@ TRACE_EVENT(btrfs_sync_file, const struct inode *inode = d_inode(dentry); TP_fast_assign_fsid(btrfs_sb(file->f_path.dentry->d_sb)); - __entry->ino = inode->i_ino; - __entry->parent = d_inode(dentry->d_parent)->i_ino; + __entry->ino = btrfs_ino(BTRFS_I(inode)); + __entry->parent = btrfs_ino(BTRFS_I(d_inode(dentry->d_parent))); __entry->datasync = datasync; __entry->root_objectid = BTRFS_I(inode)->root->root_key.objectid; ), - TP_printk_btrfs("root=%llu(%s) ino=%ld parent=%ld datasync=%d", + TP_printk_btrfs("root=%llu(%s) ino=%llu parent=%llu datasync=%d", show_root_type(__entry->root_objectid), - (unsigned long)__entry->ino, (unsigned long)__entry->parent, + __entry->ino, + __entry->parent, __entry->datasync) ); @@ -655,7 +658,7 @@ TRACE_EVENT(btrfs_sync_fs, __entry->wait = wait; ), - TP_printk_btrfs("wait = %d", __entry->wait) + TP_printk_btrfs("wait=%d", __entry->wait) ); TRACE_EVENT(btrfs_add_block_group, @@ -665,8 +668,7 @@ TRACE_EVENT(btrfs_add_block_group, TP_ARGS(fs_info, block_group, create), - TP_STRUCT__entry( - __array( u8, fsid, BTRFS_FSID_SIZE ) + TP_STRUCT__entry_btrfs( __field( u64, offset ) __field( u64, size ) __field( u64, flags ) @@ -675,8 +677,7 @@ TRACE_EVENT(btrfs_add_block_group, __field( int, create ) ), - TP_fast_assign( - memcpy(__entry->fsid, fs_info->fsid, BTRFS_FSID_SIZE); + TP_fast_assign_btrfs(fs_info, __entry->offset = block_group->key.objectid; __entry->size = block_group->key.offset; __entry->flags = block_group->flags; @@ -686,16 +687,16 @@ TRACE_EVENT(btrfs_add_block_group, __entry->create = create; ), - TP_printk("%pU: block_group offset=%llu size=%llu " + TP_printk_btrfs("block_group offset=%llu size=%llu " "flags=%llu(%s) bytes_used=%llu bytes_super=%llu " - "create=%d", __entry->fsid, - (unsigned long long)__entry->offset, - (unsigned long long)__entry->size, - (unsigned long long)__entry->flags, + "create=%d", + __entry->offset, + __entry->size, + __entry->flags, __print_flags((unsigned long)__entry->flags, "|", BTRFS_GROUP_FLAGS), - (unsigned long long)__entry->bytes_used, - (unsigned long long)__entry->bytes_super, __entry->create) + __entry->bytes_used, + __entry->bytes_super, __entry->create) ); #define show_ref_action(action) \ @@ -740,13 +741,13 @@ DECLARE_EVENT_CLASS(btrfs_delayed_tree_ref, TP_printk_btrfs("bytenr=%llu num_bytes=%llu action=%s " "parent=%llu(%s) ref_root=%llu(%s) level=%d " "type=%s seq=%llu", - (unsigned long long)__entry->bytenr, - (unsigned long long)__entry->num_bytes, + __entry->bytenr, + __entry->num_bytes, show_ref_action(__entry->action), show_root_type(__entry->parent), show_root_type(__entry->ref_root), __entry->level, show_ref_type(__entry->type), - (unsigned long long)__entry->seq) + __entry->seq) ); DEFINE_EVENT(btrfs_delayed_tree_ref, add_delayed_tree_ref, @@ -805,15 +806,15 @@ DECLARE_EVENT_CLASS(btrfs_delayed_data_ref, TP_printk_btrfs("bytenr=%llu num_bytes=%llu action=%s " "parent=%llu(%s) ref_root=%llu(%s) owner=%llu " "offset=%llu type=%s seq=%llu", - (unsigned long long)__entry->bytenr, - (unsigned long long)__entry->num_bytes, + __entry->bytenr, + __entry->num_bytes, show_ref_action(__entry->action), show_root_type(__entry->parent), show_root_type(__entry->ref_root), - (unsigned long long)__entry->owner, - (unsigned long long)__entry->offset, + __entry->owner, + __entry->offset, show_ref_type(__entry->type), - (unsigned long long)__entry->seq) + __entry->seq) ); DEFINE_EVENT(btrfs_delayed_data_ref, add_delayed_data_ref, @@ -859,8 +860,8 @@ DECLARE_EVENT_CLASS(btrfs_delayed_ref_head, ), TP_printk_btrfs("bytenr=%llu num_bytes=%llu action=%s is_data=%d", - (unsigned long long)__entry->bytenr, - (unsigned long long)__entry->num_bytes, + __entry->bytenr, + __entry->num_bytes, show_ref_action(__entry->action), __entry->is_data) ); @@ -923,8 +924,8 @@ DECLARE_EVENT_CLASS(btrfs__chunk, TP_printk_btrfs("root=%llu(%s) offset=%llu size=%llu " "num_stripes=%d sub_stripes=%d type=%s", show_root_type(__entry->root_objectid), - (unsigned long long)__entry->offset, - (unsigned long long)__entry->size, + __entry->offset, + __entry->size, __entry->num_stripes, __entry->sub_stripes, show_chunk_type(__entry->type)) ); @@ -974,9 +975,9 @@ TRACE_EVENT(btrfs_cow_block, "(orig_level=%d) cow_buf=%llu (cow_level=%d)", show_root_type(__entry->root_objectid), __entry->refs, - (unsigned long long)__entry->buf_start, + __entry->buf_start, __entry->buf_level, - (unsigned long long)__entry->cow_start, + __entry->cow_start, __entry->cow_level) ); @@ -1001,7 +1002,7 @@ TRACE_EVENT(btrfs_space_reservation, __entry->reserve = reserve; ), - TP_printk_btrfs("%s: %Lu %s %Lu", __get_str(type), __entry->val, + TP_printk_btrfs("%s: %llu %s %llu", __get_str(type), __entry->val, __entry->reserve ? "reserve" : "release", __entry->bytes) ); @@ -1019,29 +1020,27 @@ TRACE_EVENT(btrfs_trigger_flush, TP_ARGS(fs_info, flags, bytes, flush, reason), - TP_STRUCT__entry( - __array( u8, fsid, BTRFS_FSID_SIZE ) + TP_STRUCT__entry_btrfs( __field( u64, flags ) __field( u64, bytes ) __field( int, flush ) __string( reason, reason ) ), - TP_fast_assign( - memcpy(__entry->fsid, fs_info->fsid, BTRFS_FSID_SIZE); + TP_fast_assign_btrfs(fs_info, __entry->flags = flags; __entry->bytes = bytes; __entry->flush = flush; __assign_str(reason, reason) ), - TP_printk("%pU: %s: flush=%d(%s) flags=%llu(%s) bytes=%llu", - __entry->fsid, __get_str(reason), __entry->flush, + TP_printk_btrfs("%s: flush=%d(%s) flags=%llu(%s) bytes=%llu", + __get_str(reason), __entry->flush, show_flush_action(__entry->flush), - (unsigned long long)__entry->flags, + __entry->flags, __print_flags((unsigned long)__entry->flags, "|", BTRFS_GROUP_FLAGS), - (unsigned long long)__entry->bytes) + __entry->bytes) ); #define show_flush_state(state) \ @@ -1060,29 +1059,27 @@ TRACE_EVENT(btrfs_flush_space, TP_ARGS(fs_info, flags, num_bytes, state, ret), - TP_STRUCT__entry( - __array( u8, fsid, BTRFS_FSID_SIZE ) + TP_STRUCT__entry_btrfs( __field( u64, flags ) __field( u64, num_bytes ) __field( int, state ) __field( int, ret ) ), - TP_fast_assign( - memcpy(__entry->fsid, fs_info->fsid, BTRFS_FSID_SIZE); + TP_fast_assign_btrfs(fs_info, __entry->flags = flags; __entry->num_bytes = num_bytes; __entry->state = state; __entry->ret = ret; ), - TP_printk("%pU: state=%d(%s) flags=%llu(%s) num_bytes=%llu ret=%d", - __entry->fsid, __entry->state, + TP_printk_btrfs("state=%d(%s) flags=%llu(%s) num_bytes=%llu ret=%d", + __entry->state, show_flush_state(__entry->state), - (unsigned long long)__entry->flags, + __entry->flags, __print_flags((unsigned long)__entry->flags, "|", BTRFS_GROUP_FLAGS), - (unsigned long long)__entry->num_bytes, __entry->ret) + __entry->num_bytes, __entry->ret) ); DECLARE_EVENT_CLASS(btrfs__reserved_extent, @@ -1103,8 +1100,8 @@ DECLARE_EVENT_CLASS(btrfs__reserved_extent, TP_printk_btrfs("root=%llu(%s) start=%llu len=%llu", show_root_type(BTRFS_EXTENT_TREE_OBJECTID), - (unsigned long long)__entry->start, - (unsigned long long)__entry->len) + __entry->start, + __entry->len) ); DEFINE_EVENT(btrfs__reserved_extent, btrfs_reserved_extent_alloc, @@ -1140,7 +1137,7 @@ TRACE_EVENT(find_free_extent, __entry->data = data; ), - TP_printk_btrfs("root=%Lu(%s) len=%Lu empty_size=%Lu flags=%Lu(%s)", + TP_printk_btrfs("root=%llu(%s) len=%llu empty_size=%llu flags=%llu(%s)", show_root_type(BTRFS_EXTENT_TREE_OBJECTID), __entry->num_bytes, __entry->empty_size, __entry->data, __print_flags((unsigned long)__entry->data, "|", @@ -1149,11 +1146,10 @@ TRACE_EVENT(find_free_extent, DECLARE_EVENT_CLASS(btrfs__reserve_extent, - TP_PROTO(const struct btrfs_fs_info *fs_info, - const struct btrfs_block_group_cache *block_group, u64 start, + TP_PROTO(const struct btrfs_block_group_cache *block_group, u64 start, u64 len), - TP_ARGS(fs_info, block_group, start, len), + TP_ARGS(block_group, start, len), TP_STRUCT__entry_btrfs( __field( u64, bg_objectid ) @@ -1162,15 +1158,15 @@ DECLARE_EVENT_CLASS(btrfs__reserve_extent, __field( u64, len ) ), - TP_fast_assign_btrfs(fs_info, + TP_fast_assign_btrfs(block_group->fs_info, __entry->bg_objectid = block_group->key.objectid; __entry->flags = block_group->flags; __entry->start = start; __entry->len = len; ), - TP_printk_btrfs("root=%Lu(%s) block_group=%Lu flags=%Lu(%s) " - "start=%Lu len=%Lu", + TP_printk_btrfs("root=%llu(%s) block_group=%llu flags=%llu(%s) " + "start=%llu len=%llu", show_root_type(BTRFS_EXTENT_TREE_OBJECTID), __entry->bg_objectid, __entry->flags, __print_flags((unsigned long)__entry->flags, @@ -1180,20 +1176,18 @@ DECLARE_EVENT_CLASS(btrfs__reserve_extent, DEFINE_EVENT(btrfs__reserve_extent, btrfs_reserve_extent, - TP_PROTO(const struct btrfs_fs_info *fs_info, - const struct btrfs_block_group_cache *block_group, u64 start, + TP_PROTO(const struct btrfs_block_group_cache *block_group, u64 start, u64 len), - TP_ARGS(fs_info, block_group, start, len) + TP_ARGS(block_group, start, len) ); DEFINE_EVENT(btrfs__reserve_extent, btrfs_reserve_extent_cluster, - TP_PROTO(const struct btrfs_fs_info *fs_info, - const struct btrfs_block_group_cache *block_group, u64 start, + TP_PROTO(const struct btrfs_block_group_cache *block_group, u64 start, u64 len), - TP_ARGS(fs_info, block_group, start, len) + TP_ARGS(block_group, start, len) ); TRACE_EVENT(btrfs_find_cluster, @@ -1221,8 +1215,8 @@ TRACE_EVENT(btrfs_find_cluster, __entry->min_bytes = min_bytes; ), - TP_printk_btrfs("block_group=%Lu flags=%Lu(%s) start=%Lu len=%Lu " - "empty_size=%Lu min_bytes=%Lu", __entry->bg_objectid, + TP_printk_btrfs("block_group=%llu flags=%llu(%s) start=%llu len=%llu " + "empty_size=%llu min_bytes=%llu", __entry->bg_objectid, __entry->flags, __print_flags((unsigned long)__entry->flags, "|", BTRFS_GROUP_FLAGS), __entry->start, @@ -1243,7 +1237,7 @@ TRACE_EVENT(btrfs_failed_cluster_setup, __entry->bg_objectid = block_group->key.objectid; ), - TP_printk_btrfs("block_group=%Lu", __entry->bg_objectid) + TP_printk_btrfs("block_group=%llu", __entry->bg_objectid) ); TRACE_EVENT(btrfs_setup_cluster, @@ -1272,8 +1266,8 @@ TRACE_EVENT(btrfs_setup_cluster, __entry->bitmap = bitmap; ), - TP_printk_btrfs("block_group=%Lu flags=%Lu(%s) window_start=%Lu " - "size=%Lu max_size=%Lu bitmap=%d", + TP_printk_btrfs("block_group=%llu flags=%llu(%s) window_start=%llu " + "size=%llu max_size=%llu bitmap=%d", __entry->bg_objectid, __entry->flags, __print_flags((unsigned long)__entry->flags, "|", @@ -1476,7 +1470,7 @@ DECLARE_EVENT_CLASS(btrfs__qgroup_rsv_data, TP_STRUCT__entry_btrfs( __field( u64, rootid ) - __field( unsigned long, ino ) + __field( u64, ino ) __field( u64, start ) __field( u64, len ) __field( u64, reserved ) @@ -1485,14 +1479,14 @@ DECLARE_EVENT_CLASS(btrfs__qgroup_rsv_data, TP_fast_assign_btrfs(btrfs_sb(inode->i_sb), __entry->rootid = BTRFS_I(inode)->root->objectid; - __entry->ino = inode->i_ino; + __entry->ino = btrfs_ino(BTRFS_I(inode)); __entry->start = start; __entry->len = len; __entry->reserved = reserved; __entry->op = op; ), - TP_printk_btrfs("root=%llu ino=%lu start=%llu len=%llu reserved=%llu op=%s", + TP_printk_btrfs("root=%llu ino=%llu start=%llu len=%llu reserved=%llu op=%s", __entry->rootid, __entry->ino, __entry->start, __entry->len, __entry->reserved, __print_flags((unsigned long)__entry->op, "", @@ -1584,12 +1578,14 @@ DEFINE_EVENT(btrfs_qgroup_extent, btrfs_qgroup_trace_extent, TRACE_EVENT(btrfs_qgroup_account_extent, - TP_PROTO(const struct btrfs_fs_info *fs_info, u64 bytenr, + TP_PROTO(const struct btrfs_fs_info *fs_info, u64 transid, u64 bytenr, u64 num_bytes, u64 nr_old_roots, u64 nr_new_roots), - TP_ARGS(fs_info, bytenr, num_bytes, nr_old_roots, nr_new_roots), + TP_ARGS(fs_info, transid, bytenr, num_bytes, nr_old_roots, + nr_new_roots), TP_STRUCT__entry_btrfs( + __field( u64, transid ) __field( u64, bytenr ) __field( u64, num_bytes ) __field( u64, nr_old_roots ) @@ -1597,43 +1593,49 @@ TRACE_EVENT(btrfs_qgroup_account_extent, ), TP_fast_assign_btrfs(fs_info, + __entry->transid = transid; __entry->bytenr = bytenr; __entry->num_bytes = num_bytes; __entry->nr_old_roots = nr_old_roots; __entry->nr_new_roots = nr_new_roots; ), - TP_printk_btrfs("bytenr=%llu num_bytes=%llu nr_old_roots=%llu " - "nr_new_roots=%llu", - __entry->bytenr, - __entry->num_bytes, - __entry->nr_old_roots, - __entry->nr_new_roots) + TP_printk_btrfs( +"transid=%llu bytenr=%llu num_bytes=%llu nr_old_roots=%llu nr_new_roots=%llu", + __entry->transid, + __entry->bytenr, + __entry->num_bytes, + __entry->nr_old_roots, + __entry->nr_new_roots) ); TRACE_EVENT(qgroup_update_counters, - TP_PROTO(const struct btrfs_fs_info *fs_info, u64 qgid, + TP_PROTO(const struct btrfs_fs_info *fs_info, + struct btrfs_qgroup *qgroup, u64 cur_old_count, u64 cur_new_count), - TP_ARGS(fs_info, qgid, cur_old_count, cur_new_count), + TP_ARGS(fs_info, qgroup, cur_old_count, cur_new_count), TP_STRUCT__entry_btrfs( __field( u64, qgid ) + __field( u64, old_rfer ) + __field( u64, old_excl ) __field( u64, cur_old_count ) __field( u64, cur_new_count ) ), TP_fast_assign_btrfs(fs_info, - __entry->qgid = qgid; + __entry->qgid = qgroup->qgroupid; + __entry->old_rfer = qgroup->rfer; + __entry->old_excl = qgroup->excl; __entry->cur_old_count = cur_old_count; __entry->cur_new_count = cur_new_count; ), - TP_printk_btrfs("qgid=%llu cur_old_count=%llu cur_new_count=%llu", - __entry->qgid, - __entry->cur_old_count, - __entry->cur_new_count) + TP_printk_btrfs("qgid=%llu old_rfer=%llu old_excl=%llu cur_old_count=%llu cur_new_count=%llu", + __entry->qgid, __entry->old_rfer, __entry->old_excl, + __entry->cur_old_count, __entry->cur_new_count) ); TRACE_EVENT(qgroup_update_reserve, @@ -1765,14 +1767,14 @@ DECLARE_EVENT_CLASS(btrfs__prelim_ref, ), TP_printk_btrfs("root_id=%llu key=[%llu,%u,%llu] level=%d count=[%d+%d=%d] parent=%llu wanted_disk_byte=%llu nodes=%llu", - (unsigned long long)__entry->root_id, - (unsigned long long)__entry->objectid, __entry->type, - (unsigned long long)__entry->offset, __entry->level, + __entry->root_id, + __entry->objectid, __entry->type, + __entry->offset, __entry->level, __entry->old_count, __entry->mod_count, __entry->old_count + __entry->mod_count, - (unsigned long long)__entry->parent, - (unsigned long long)__entry->bytenr, - (unsigned long long)__entry->tree_size) + __entry->parent, + __entry->bytenr, + __entry->tree_size) ); DEFINE_EVENT(btrfs__prelim_ref, btrfs_prelim_ref_merge, @@ -1808,8 +1810,51 @@ TRACE_EVENT(btrfs_inode_mod_outstanding_extents, TP_printk_btrfs("root=%llu(%s) ino=%llu mod=%d", show_root_type(__entry->root_objectid), - (unsigned long long)__entry->ino, __entry->mod) + __entry->ino, __entry->mod) ); + +DECLARE_EVENT_CLASS(btrfs__block_group, + TP_PROTO(const struct btrfs_block_group_cache *bg_cache), + + TP_ARGS(bg_cache), + + TP_STRUCT__entry_btrfs( + __field( u64, bytenr ) + __field( u64, len ) + __field( u64, used ) + __field( u64, flags ) + ), + + TP_fast_assign_btrfs(bg_cache->fs_info, + __entry->bytenr = bg_cache->key.objectid, + __entry->len = bg_cache->key.offset, + __entry->used = btrfs_block_group_used(&bg_cache->item); + __entry->flags = bg_cache->flags; + ), + + TP_printk_btrfs("bg bytenr=%llu len=%llu used=%llu flags=%llu(%s)", + __entry->bytenr, __entry->len, __entry->used, __entry->flags, + __print_flags(__entry->flags, "|", BTRFS_GROUP_FLAGS)) +); + +DEFINE_EVENT(btrfs__block_group, btrfs_remove_block_group, + TP_PROTO(const struct btrfs_block_group_cache *bg_cache), + + TP_ARGS(bg_cache) +); + +DEFINE_EVENT(btrfs__block_group, btrfs_add_unused_block_group, + TP_PROTO(const struct btrfs_block_group_cache *bg_cache), + + TP_ARGS(bg_cache) +); + +DEFINE_EVENT(btrfs__block_group, btrfs_skip_unused_block_group, + TP_PROTO(const struct btrfs_block_group_cache *bg_cache), + + TP_ARGS(bg_cache) +); + #endif /* _TRACE_BTRFS_H */ /* This part must be outside protection */ diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index c8d99b9ca550e6c741c4fc7503afa1473b45d35c..5ca1d21fc4a7a85db1bd9c8cf15e5f334d8e59d6 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -422,6 +422,21 @@ struct btrfs_ioctl_ino_lookup_args { char name[BTRFS_INO_LOOKUP_PATH_MAX]; }; +#define BTRFS_INO_LOOKUP_USER_PATH_MAX (4080 - BTRFS_VOL_NAME_MAX - 1) +struct btrfs_ioctl_ino_lookup_user_args { + /* in, inode number containing the subvolume of 'subvolid' */ + __u64 dirid; + /* in */ + __u64 treeid; + /* out, name of the subvolume of 'treeid' */ + char name[BTRFS_VOL_NAME_MAX + 1]; + /* + * out, constructed path from the directory with which the ioctl is + * called to dirid + */ + char path[BTRFS_INO_LOOKUP_USER_PATH_MAX]; +}; + /* Search criteria for the btrfs SEARCH ioctl family. */ struct btrfs_ioctl_search_key { /* @@ -725,6 +740,82 @@ struct btrfs_ioctl_send_args { __u64 reserved[4]; /* in */ }; +/* + * Information about a fs tree root. + * + * All items are filled by the ioctl + */ +struct btrfs_ioctl_get_subvol_info_args { + /* Id of this subvolume */ + __u64 treeid; + + /* Name of this subvolume, used to get the real name at mount point */ + char name[BTRFS_VOL_NAME_MAX + 1]; + + /* + * Id of the subvolume which contains this subvolume. + * Zero for top-level subvolume or a deleted subvolume. + */ + __u64 parent_id; + + /* + * Inode number of the directory which contains this subvolume. + * Zero for top-level subvolume or a deleted subvolume + */ + __u64 dirid; + + /* Latest transaction id of this subvolume */ + __u64 generation; + + /* Flags of this subvolume */ + __u64 flags; + + /* UUID of this subvolume */ + __u8 uuid[BTRFS_UUID_SIZE]; + + /* + * UUID of the subvolume of which this subvolume is a snapshot. + * All zero for a non-snapshot subvolume. + */ + __u8 parent_uuid[BTRFS_UUID_SIZE]; + + /* + * UUID of the subvolume from which this subvolume was received. + * All zero for non-received subvolume. + */ + __u8 received_uuid[BTRFS_UUID_SIZE]; + + /* Transaction id indicating when change/create/send/receive happened */ + __u64 ctransid; + __u64 otransid; + __u64 stransid; + __u64 rtransid; + /* Time corresponding to c/o/s/rtransid */ + struct btrfs_ioctl_timespec ctime; + struct btrfs_ioctl_timespec otime; + struct btrfs_ioctl_timespec stime; + struct btrfs_ioctl_timespec rtime; + + /* Must be zero */ + __u64 reserved[8]; +}; + +#define BTRFS_MAX_ROOTREF_BUFFER_NUM 255 +struct btrfs_ioctl_get_subvol_rootref_args { + /* in/out, minimum id of rootref's treeid to be searched */ + __u64 min_treeid; + + /* out */ + struct { + __u64 treeid; + __u64 dirid; + } rootref[BTRFS_MAX_ROOTREF_BUFFER_NUM]; + + /* out, number of found items */ + __u8 num_items; + __u8 align[7]; +}; + /* Error codes as returned by the kernel */ enum btrfs_err_code { BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1, @@ -843,5 +934,11 @@ enum btrfs_err_code { struct btrfs_ioctl_vol_args_v2) #define BTRFS_IOC_LOGICAL_INO_V2 _IOWR(BTRFS_IOCTL_MAGIC, 59, \ struct btrfs_ioctl_logical_ino_args) +#define BTRFS_IOC_GET_SUBVOL_INFO _IOR(BTRFS_IOCTL_MAGIC, 60, \ + struct btrfs_ioctl_get_subvol_info_args) +#define BTRFS_IOC_GET_SUBVOL_ROOTREF _IOWR(BTRFS_IOCTL_MAGIC, 61, \ + struct btrfs_ioctl_get_subvol_rootref_args) +#define BTRFS_IOC_INO_LOOKUP_USER _IOWR(BTRFS_IOCTL_MAGIC, 62, \ + struct btrfs_ioctl_ino_lookup_user_args) #endif /* _UAPI_LINUX_BTRFS_H */