From 29d2b84cf92780b74fd768f5506b0fc8dab56237 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Fri, 30 Mar 2018 12:58:47 +0300 Subject: btrfs: Replace owner argument in add_pinned_bytes with a boolean add_pinned_bytes really cares whether the bytes being pinned are either data or metadata. To that effect it checks whether the 'owner' argument is less than BTRFS_FIRST_FREE_OBJECTID (256). This works because owner can really have 2 types of values: a) For metadata extents it holds the level at which the parent is in the btree. This amounts to owner having the values 0-7 b) In case of modifying data extents, owner is the inode number to which those extents belongs. Let's make this more explicit byt converting the owner parameter to a boolean value and either pass it directly when we know the type of extents we are working with (i.e. in btrfs_free_tree_block). In cases when the parent function can be called on both metadata/data extents perform the check in the caller. This hopefully makes the interface of add_pinned_bytes more intuitive. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 51b5e2da708c..21ccf5c57506 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -744,12 +744,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 +2200,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; } @@ -7266,7 +7269,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 +7323,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; } -- cgit v1.2.3 From c065f5b1cf52d50b9518aa02c7e50415820895af Mon Sep 17 00:00:00 2001 From: Su Yue Date: Mon, 2 Apr 2018 17:24:11 +0800 Subject: btrfs: rename btrfs_get_block_group_info and make it static The function btrfs_get_block_group_info() was introduced by the commit 5af3e8cce8b7 ("Btrfs: make filesystem read-only when submitting barrier fails") which used it in disk-io.c. However, the function is only called in ioctl.c now. Its parameter type btrfs_ioctl_space_info* is only for ioctl. So, make it static and rename it to be original name get_block_group_info. No functional change. Signed-off-by: Su Yue Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 2 -- fs/btrfs/ioctl.c | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 0d422c9908b8..15e34172cdf0 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3267,8 +3267,6 @@ 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, struct btrfs_ioctl_balance_args *bargs); ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen, diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 632e26d6f7ce..61a58214681c 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4007,8 +4007,8 @@ out: 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 +4124,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++; -- cgit v1.2.3 From 41d0bd3b5e73afbcee3cd7dcb6f3f0ec936f54d9 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Wed, 4 Apr 2018 15:57:42 +0300 Subject: btrfs: Drop delayed_refs argument from btrfs_check_delayed_seq It's used to print its pointer in a debug statement but doesn't really bring any useful information to the error message. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/delayed-ref.c | 9 +++------ fs/btrfs/delayed-ref.h | 4 +--- fs/btrfs/extent-tree.c | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index e1b0651686f7..915825b27ffc 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -323,9 +323,7 @@ again: } } -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; } } diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h index 7f00db50bd24..84cc007badd6 100644 --- a/fs/btrfs/delayed-ref.h +++ b/fs/btrfs/delayed-ref.h @@ -269,9 +269,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/extent-tree.c b/fs/btrfs/extent-tree.c index 21ccf5c57506..2f9432beb69c 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2713,7 +2713,7 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, 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; -- cgit v1.2.3 From 89595e80de2e8e35bf3c7035e609f9b99dcfff5d Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Wed, 18 Apr 2018 14:59:25 +0800 Subject: btrfs: add comment about BTRFS_FS_EXCL_OP Adds comments about BTRFS_FS_EXCL_OP to existing comments about the device locks. Signed-off-by: Anand Jain Reviewed-by: David Sterba [ minor updates ] Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index be3fc701f389..a25d5bf4462f 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -197,6 +197,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); -- cgit v1.2.3 From b25f0d0012d11f2fb3df855fb62b86e5f63fdd68 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Wed, 11 Apr 2018 11:21:17 +0300 Subject: btrfs: Use while loop instead of labels in __endio_write_update_ordered Currently __endio_write_update_ordered uses labels to implement what is essentially a simple while loop. This makes the code more cumbersome to follow than it actually has to be. No functional changes. No xfstest regressions were found during testing. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 52 +++++++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 27 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 0b86cf10cf2a..be17cfdcbcf5 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8131,7 +8131,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 +8140,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; + } } } -- cgit v1.2.3 From 1e7a14211bced7ac26f332b16338db88290e0ffd Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Wed, 11 Apr 2018 11:21:18 +0300 Subject: btrfs: Fix lock release order Locks should generally be released in the oppposite order they are acquired. Generally lock acquisiton ordering is used to ensure deadlocks don't happen. However, as becomes more complicated it's best to also maintain proper unlock order so as to avoid possible dead locks. This was found by code inspection and doesn't necessarily lead to a deadlock scenario. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 2f9432beb69c..cd2f5220577f 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2597,8 +2597,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); -- cgit v1.2.3 From 57f1642ec36ac7c3d54f317a2f4882f39aa9ded1 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Wed, 11 Apr 2018 11:21:19 +0300 Subject: btrfs: Consolidate error checking for btrfs_alloc_chunk The second if is really a subcase of ret being less than 0. So introduce a generic if (ret < 0) check, and inside have another if which explicitly handles the -ENOSPC and any other errors. No functional changes. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index cd2f5220577f..686d23727662 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4681,12 +4681,14 @@ again: 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: -- cgit v1.2.3 From 0e08eb9b1c1701f1fda8e8d7f4d2b93e7e54941f Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 3 Apr 2018 21:55:17 +0200 Subject: btrfs: tests: pass fs_info to extent_map tests Preparatory work to pass fs_info to btrfs_add_extent_mapping so we can get a better tracepoint message. Extent maps do not need fs_info for anything so we only add a dummy one without any other initialization. Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/tests/extent-map-tests.c | 52 +++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 16 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/tests/extent-map-tests.c b/fs/btrfs/tests/extent-map-tests.c index 79e0a5f4d9c9..8a39de4453e4 100644 --- a/fs/btrfs/tests/extent-map-tests.c +++ b/fs/btrfs/tests/extent-map-tests.c @@ -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; @@ -112,7 +113,8 @@ out: * 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; @@ -169,7 +171,8 @@ out: 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; @@ -235,14 +238,16 @@ out: * -> 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; @@ -324,30 +329,45 @@ out: * # 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"); + /* + * 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\n"); + 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; } -- cgit v1.2.3 From f46b24c9457143a367c6707eac82d546e2bcf280 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 3 Apr 2018 21:45:57 +0200 Subject: btrfs: use fs_info for btrfs_handle_em_exist tracepoint We really want to know to which filesystem the extent map events belong, but as it cannot be reached from the extent_map pointers, we need to pass it down the callchain. Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/extent_map.c | 6 ++++-- fs/btrfs/extent_map.h | 3 ++- fs/btrfs/inode.c | 2 +- fs/btrfs/tests/extent-map-tests.c | 8 ++++---- include/trace/events/btrfs.h | 12 +++++++----- 5 files changed, 18 insertions(+), 13 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 1b8a078f92eb..6648d55e5339 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 5fcb80a6ce37..25d985e7532a 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/inode.c b/fs/btrfs/inode.c index be17cfdcbcf5..f4447986263a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7083,7 +7083,7 @@ insert: 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: diff --git a/fs/btrfs/tests/extent-map-tests.c b/fs/btrfs/tests/extent-map-tests.c index 8a39de4453e4..9c051c4a3315 100644 --- a/fs/btrfs/tests/extent-map-tests.c +++ b/fs/btrfs/tests/extent-map-tests.c @@ -91,7 +91,7 @@ static void test_case_1(struct btrfs_fs_info *fs_info, 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); if (em && @@ -155,7 +155,7 @@ static void test_case_2(struct btrfs_fs_info *fs_info, 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); if (em && @@ -201,7 +201,7 @@ static void __test_case_3(struct btrfs_fs_info *fs_info, 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", start, start + len, ret); @@ -288,7 +288,7 @@ static void __test_case_4(struct btrfs_fs_info *fs_info, 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", start, len, ret); diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 9be469706d30..d78d8ab4bc86 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -256,11 +256,13 @@ TRACE_EVENT_CONDITION(btrfs_get_extent, 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,7 +280,7 @@ 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)", __entry->start, -- cgit v1.2.3 From 6faa8f475eeaf5d89f985ad3b91b90ab0cf219e6 Mon Sep 17 00:00:00 2001 From: Howard McLauchlan Date: Wed, 18 Apr 2018 18:02:35 -0700 Subject: btrfs: clean up le_bitmap_{set, clear}() le_bitmap_set() is only used by free-space-tree, so move it there and make it static. le_bitmap_clear() is not used, so remove it. Signed-off-by: Howard McLauchlan Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 40 ---------------------------------------- fs/btrfs/extent_io.h | 3 --- fs/btrfs/free-space-tree.c | 20 ++++++++++++++++++++ 3 files changed, 20 insertions(+), 43 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index e99b329002cf..9a521e5e297d 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -5620,46 +5620,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 a53009694b16..d34416c831bf 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -84,9 +84,6 @@ 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; diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 32a0f6cb5594..e03830d83311 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -157,6 +157,26 @@ static u8 *alloc_bitmap(u32 bitmap_size) return ret; } +static 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; + } +} + int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, struct btrfs_block_group_cache *block_group, -- cgit v1.2.3 From a565971ff3e0f584ec163a32abf95196be623041 Mon Sep 17 00:00:00 2001 From: Howard McLauchlan Date: Wed, 18 Apr 2018 18:02:36 -0700 Subject: btrfs: optimize free space tree bitmap conversion Presently, convert_free_space_to_extents() does a linear scan of the bitmap. We can speed this up with find_next_{bit,zero_bit}_le(). This patch replaces the linear scan with find_next_{bit,zero_bit}_le(). Testing shows a 20-33% decrease in execution time for convert_free_space_to_extents(). Since we change bitmap to be unsigned long, we have to do some casting for the bitmap cursor. In le_bitmap_set() it makes sense to use u8, as we are doing bit operations. Everywhere else, we're just using it for pointer arithmetic and not directly accessing it, so char seems more appropriate. Suggested-by: Omar Sandoval Signed-off-by: Howard McLauchlan Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-tree.c | 61 +++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 38 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index e03830d83311..7019afe6e727 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -138,10 +138,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,14 +153,14 @@ 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(u8 *map, unsigned int start, int len) +static void le_bitmap_set(unsigned long *map, unsigned int start, int len) { - u8 *p = map + BIT_BYTE(start); + 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); @@ -186,7 +187,8 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans, 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; @@ -275,7 +277,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) { @@ -324,13 +326,10 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans, 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; @@ -368,7 +367,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); @@ -378,7 +377,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); @@ -412,32 +411,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) @@ -445,6 +428,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) { -- cgit v1.2.3 From 3b079a919a2386f7e080222b25f1cffe9c91666b Mon Sep 17 00:00:00 2001 From: Howard McLauchlan Date: Wed, 18 Apr 2018 18:02:37 -0700 Subject: btrfs: remove unused le_test_bit() With commit b18253ec57c0 ("btrfs: optimize free space tree bitmap conversion"), there are no more callers to le_test_bit(). This patch removes le_test_bit(). Signed-off-by: Howard McLauchlan Signed-off-by: David Sterba --- fs/btrfs/extent_io.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index d34416c831bf..c5e80d60d71b 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -79,11 +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))); -} - struct extent_state; struct btrfs_root; struct btrfs_inode; -- cgit v1.2.3 From ec42f167348a1949ac309532aa34760cfc96c92f Mon Sep 17 00:00:00 2001 From: Misono Tomohiro Date: Wed, 18 Apr 2018 11:34:13 +0900 Subject: btrfs: Move may_destroy_subvol() from ioctl.c to inode.c This is a preparation work to refactor btrfs_ioctl_snap_destroy() and to allow rmdir(2) to delete an empty subvolume. Signed-off-by: Tomohiro Misono Reviewed-by: David Sterba [ minor update of the function comment ] Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 1 + fs/btrfs/inode.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/ioctl.c | 54 ------------------------------------------------------ 3 files changed, 56 insertions(+), 54 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 15e34172cdf0..fe7e5177119d 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3197,6 +3197,7 @@ int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct inode *dir, u64 objectid, const char *name, int name_len); +noinline int may_destroy_subvol(struct btrfs_root *root); 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, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f4447986263a..1e51ca4489da 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4326,6 +4326,61 @@ out: return ret; } +/* + * Helper to check if the subvolume references other subvolumes or if it's + * default. + */ +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 int btrfs_rmdir(struct inode *dir, struct dentry *dentry) { struct inode *inode = d_inode(dentry); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 61a58214681c..592ef10a6604 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1832,60 +1832,6 @@ out: 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) { -- cgit v1.2.3 From f60a2364a4eee4d8c335775a3a0c39aa955aa6b7 Mon Sep 17 00:00:00 2001 From: Misono Tomohiro Date: Wed, 18 Apr 2018 11:34:52 +0900 Subject: btrfs: Factor out the main deletion process from btrfs_ioctl_snap_destroy() Factor out the second half of btrfs_ioctl_snap_destroy() as btrfs_delete_subvolume(), which performs some subvolume specific checks before deletion: 1. send is not in progress 2. the subvolume is not the default subvolume 3. the subvolume does not contain other subvolumes and actual deletion process. btrfs_delete_subvolume() requires inode_lock for both @dir and inode of @dentry. The remaining part of btrfs_ioctl_snap_destroy() is mainly permission checks. Note that call of d_delete() is not included in btrfs_delete_subvolume() as this function will also be used by btrfs_rmdir() to delete an empty subvolume and in that case d_delete() is called in VFS layer. As a result, btrfs_unlink_subvol() and may_destroy_subvol() become static functions. No functional changes. Signed-off-by: Tomohiro Misono Reviewed-by: David Sterba [ minor comment updates ] Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 6 +-- fs/btrfs/inode.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- fs/btrfs/ioctl.c | 131 +------------------------------------------------- 3 files changed, 144 insertions(+), 136 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index fe7e5177119d..3a382ed94030 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3193,11 +3193,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); -noinline int may_destroy_subvol(struct btrfs_root *root); +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, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 1e51ca4489da..6e03e7991a5d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4245,7 +4245,7 @@ out: 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) @@ -4330,7 +4330,7 @@ out: * Helper to check if the subvolume references other subvolumes or if it's * default. */ -noinline int may_destroy_subvol(struct btrfs_root *root) +static noinline int may_destroy_subvol(struct btrfs_root *root) { struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_path *path; @@ -4381,6 +4381,145 @@ out: return ret; } +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; + u64 qgroup_reserved; + 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, &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); + } else { + d_invalidate(dentry); + btrfs_invalidate_inodes(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); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 592ef10a6604..7beec1bf6d4b 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2255,12 +2255,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)) @@ -2344,133 +2339,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: -- cgit v1.2.3 From a79a464d5675dbca49d6121425e8eb3571da29f7 Mon Sep 17 00:00:00 2001 From: Misono Tomohiro Date: Wed, 18 Apr 2018 11:35:31 +0900 Subject: btrfs: Allow rmdir(2) to delete an empty subvolume Change the behavior of rmdir(2) and allow it to delete an empty subvolume by using btrfs_delete_subvolume() which is used by btrfs_ioctl_snap_destroy(). This is a change in behaviour and has been requested by users. Deleting the subvolume by ioctl requires root permissions while the rmdir way does works with standard tools and syscalls for all users that can access the subvolume. The main usecase is to allow 'rm -rf /path/with/subvols' to simply work. We were not able to find any nasty usability surprises, the intention is to do the destructive rm. Without allowing rmdir, this would have to be followed by the ioctl subvolume deletion, which is more of an annoyance. Implementation details: The required lock for @dir and inode of @dentry is already acquired in vfs layer. We need some check before deleting a subvolume. Permission check is done in vfs layer, emptiness check is in btrfs_rmdir() and additional check (i.e. neither the subvolume is a default subvolume nor send is in progress) is in btrfs_delete_subvolume(). Note that in btrfs_ioctl_snap_destroy(), d_delete() is called after btrfs_delete_subvolume(). For rmdir(2), d_delete() is called in vfs layer later. Tested-by: Goffredo Baroncelli Signed-off-by: Tomohiro Misono Reviewed-by: David Sterba [ enhance changelog ] Signed-off-by: David Sterba --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 6e03e7991a5d..d722a30b0b74 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4531,7 +4531,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)) -- cgit v1.2.3 From 477a30ba5f8dfb3fe951ed0352277bb26a616cb8 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 19 Apr 2018 10:46:34 +0300 Subject: btrfs: Sink extent_tree arguments in try_release_extent_mapping This function already gets the page from which the two extent trees are referenced. Simplify its signature by moving the code getting the trees inside the function. No functional changes. Signed-off-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 6 +++--- fs/btrfs/extent_io.h | 4 +--- fs/btrfs/inode.c | 8 +------- 3 files changed, 5 insertions(+), 13 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 9a521e5e297d..c7ae18f8db90 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4238,13 +4238,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) { diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index c5e80d60d71b..29d47383b113 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -270,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); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d722a30b0b74..6853cd836a41 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8913,13 +8913,7 @@ btrfs_readpages(struct file *file, struct address_space *mapping, } 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); -- cgit v1.2.3 From 29c68b2de98c23a2c97fa02c37ce9bf3c15076bd Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 19 Apr 2018 10:46:35 +0300 Subject: btrfs: Remove map argument from try_release_extent_state It's not used in the function so just remove it. No functional changes. Signed-off-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index c7ae18f8db90..d1a4434152da 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4202,8 +4202,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); @@ -4278,7 +4277,7 @@ int try_release_extent_mapping(struct page *page, gfp_t mask) free_extent_map(em); } } - return try_release_extent_state(map, tree, page, mask); + return try_release_extent_state(tree, page, mask); } /* -- cgit v1.2.3 From 2a3ff0adc92069122a75c3e37271d7ab7ce0dc1c Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 19 Apr 2018 10:46:36 +0300 Subject: btrfs: Remove redundant tree argument from extent_readpages This function is called only from btrfs_readpage and is already passed the mapping. Simplify its signature by moving the code obtaining reference to the extent tree in the function. No functional changes. Signed-off-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 6 +++--- fs/btrfs/extent_io.h | 5 ++--- fs/btrfs/inode.c | 5 ++--- 3 files changed, 7 insertions(+), 9 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index d1a4434152da..20bb056b7eca 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4126,9 +4126,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; @@ -4136,6 +4135,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; diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 29d47383b113..752ad87e40d5 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -416,9 +416,8 @@ int extent_writepages(struct extent_io_tree *tree, 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/inode.c b/fs/btrfs/inode.c index 6853cd836a41..53ba8e8f1148 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8907,10 +8907,9 @@ 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) { int ret = try_release_extent_mapping(page, gfp_flags); -- cgit v1.2.3 From 81f1d39035dfc58b265b3ad68e2dcbb61b7d8263 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 19 Apr 2018 10:46:37 +0300 Subject: btrfs: Use list_empty instead of list_empty_careful list_empty_careful usually is a signal of something tricky going on. Its usage in btrfs is actually not needed since both lists it's used on are local to a function and cannot be modified concurrently. So switch to plain list_empty. No functional changes. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 53ba8e8f1148..0aadf17c528f 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10254,7 +10254,7 @@ out: btrfs_wait_and_free_delalloc_work(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); @@ -10316,7 +10316,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); -- cgit v1.2.3 From 8ae225a8a4f9054ffc6566e14aaf05dfc559743e Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 19 Apr 2018 10:46:38 +0300 Subject: btrfs: Remove tree argument from extent_writepages It can be directly referenced from the passed address_space so do that. No functional changes. Signed-off-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/extent_io.c | 5 ++--- fs/btrfs/extent_io.h | 3 +-- fs/btrfs/inode.c | 5 +---- 3 files changed, 4 insertions(+), 9 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 20bb056b7eca..af2f0408c6e4 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4109,14 +4109,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, }; diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 752ad87e40d5..0bfd4aeb822d 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -411,8 +411,7 @@ 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); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 0aadf17c528f..775249f03dd3 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8897,10 +8897,7 @@ 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 -- cgit v1.2.3 From 40012f96b6765e588d8ffd7508d492339f2b9212 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 19 Apr 2018 10:46:39 +0300 Subject: btrfs: Remove btrfs_wait_and_free_delalloc_work This function is called from only 1 place and is effectively a wrapper over wait_completion/kfree. It doesn't really bring any value having those two calls in a separate function. Just open code it and remove it. No functional changes. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 1 - fs/btrfs/inode.c | 9 ++------- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 3a382ed94030..de86f2217816 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3173,7 +3173,6 @@ struct btrfs_delalloc_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, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 775249f03dd3..ce2f9288df3e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10186,12 +10186,6 @@ 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. @@ -10248,7 +10242,8 @@ 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(&splice)) { -- cgit v1.2.3 From 1acda0c28979ef4247d19bb32f65f0547a1bbf33 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 19 Apr 2018 11:06:37 +0300 Subject: btrfs: Drop add_delayed_ref_head fs_info parameter It's provided by the transaction handle. Signed-off-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/delayed-ref.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 915825b27ffc..d2777613cd22 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -532,8 +532,7 @@ update_existing_head_ref(struct btrfs_delayed_ref_root *delayed_refs, * 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, +add_delayed_ref_head(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, @@ -606,14 +605,14 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, qrecord->num_bytes = num_bytes; qrecord->old_roots = NULL; - if(btrfs_qgroup_trace_extent_nolock(fs_info, + 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); @@ -799,8 +798,8 @@ 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, + head_ref = add_delayed_ref_head(trans, head_ref, record, bytenr, + num_bytes, 0, 0, action, 0, is_system, &qrecord_inserted, old_ref_mod, new_ref_mod); @@ -867,8 +866,8 @@ 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, + head_ref = add_delayed_ref_head(trans, head_ref, record, bytenr, + num_bytes, ref_root, reserved, action, 1, 0, &qrecord_inserted, old_ref_mod, new_ref_mod); @@ -904,9 +903,9 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info, * 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, bytenr, num_bytes, 0, 0, + BTRFS_UPDATE_DELAYED_HEAD, extent_op->is_data, + 0, NULL, NULL, NULL); spin_unlock(&delayed_refs->lock); return 0; -- cgit v1.2.3 From f033798d1200469f75d851b3962988d228533368 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 19 Apr 2018 11:06:38 +0300 Subject: btrfs: Drop fs_info parameter from add_delayed_data_ref It's provided by the transaction handle. Signed-off-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/delayed-ref.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index d2777613cd22..2e0bbe4ecc08 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -704,8 +704,7 @@ add_delayed_tree_ref(struct btrfs_fs_info *fs_info, * helper to insert a delayed data ref into the rbtree. */ static noinline void -add_delayed_data_ref(struct btrfs_fs_info *fs_info, - struct btrfs_trans_handle *trans, +add_delayed_data_ref(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, @@ -722,7 +721,7 @@ add_delayed_data_ref(struct btrfs_fs_info *fs_info, delayed_refs = &trans->transaction->delayed_refs; if (is_fstree(ref_root)) - seq = atomic64_read(&fs_info->tree_mod_seq); + seq = atomic64_read(&trans->fs_info->tree_mod_seq); /* first set the basic ref node struct up */ refcount_set(&ref->refs, 1); @@ -747,7 +746,7 @@ add_delayed_data_ref(struct btrfs_fs_info *fs_info, full_ref->objectid = owner; full_ref->offset = offset; - trace_add_delayed_data_ref(fs_info, ref, full_ref, action); + trace_add_delayed_data_ref(trans->fs_info, ref, full_ref, action); ret = insert_delayed_ref(trans, delayed_refs, head_ref, ref); if (ret > 0) @@ -871,9 +870,8 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, action, 1, 0, &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); + add_delayed_data_ref(trans, head_ref, &ref->node, bytenr, num_bytes, + parent, ref_root, owner, offset, action); spin_unlock(&delayed_refs->lock); if (qrecord_inserted) -- cgit v1.2.3 From be97f133b374bd60b7f5f87a4e93ad408bd5fe03 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 19 Apr 2018 11:06:39 +0300 Subject: btrfs: Drop fs_info parameter from btrfs_merge_delayed_refs It's provided by the transaction handle. Signed-off-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/delayed-ref.c | 2 +- fs/btrfs/delayed-ref.h | 1 - fs/btrfs/extent-tree.c | 3 +-- 3 files changed, 2 insertions(+), 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 2e0bbe4ecc08..4fb041e14742 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; diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h index 84cc007badd6..ea1aecb6a50d 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); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 686d23727662..df79340332ad 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2703,8 +2703,7 @@ 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 -- cgit v1.2.3 From c4babc5e38e94e70058218ebfbf7846fd2f8a24d Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 12 Apr 2018 10:29:25 +0800 Subject: btrfs: rename struct btrfs_fs_devices::list btrfs_fs_devices::list is the list of BTRFS fsid in the kernel, a generic name 'list' makes it's search very difficult, rename it to fs_list. Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/sysfs.c | 2 +- fs/btrfs/volumes.c | 16 ++++++++-------- fs/btrfs/volumes.h | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 4848a4318fb5..fa6c8c88b250 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -589,7 +589,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); } } diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index a25d5bf4462f..e1394bbcc540 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -262,7 +262,7 @@ 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); @@ -308,8 +308,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); } } @@ -378,7 +378,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; } @@ -642,7 +642,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; @@ -667,7 +667,7 @@ 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 { @@ -767,7 +767,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 { @@ -2294,7 +2294,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; diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 79096884654f..62a92e472f65 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; -- cgit v1.2.3 From f117e290e8efb5f1b2e5d83e559b73f2f0c1c3aa Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 12 Apr 2018 10:29:26 +0800 Subject: btrfs: cleanup __btrfs_open_devices() drop head pointer __btrfs_open_devices() declares struct list_head *head, however head is used only once, instead use btrfs_fs_devices::devices directly. Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index e1394bbcc540..7fac89f4f316 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1104,14 +1104,13 @@ int btrfs_close_devices(struct btrfs_fs_devices *fs_devices) static int __btrfs_open_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; -- cgit v1.2.3 From 0226e0eb6586c7979f9f2007093f2cabba1d79b9 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 12 Apr 2018 10:29:27 +0800 Subject: btrfs: rename __btrfs_close_devices to close_fs_devices __btrfs_close_devices() is un-exported, drop the __ prefix and rename it to close_fs_devices(). Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 7fac89f4f316..9400f3935d27 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1040,7 +1040,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; @@ -1085,7 +1085,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; @@ -1095,7 +1095,7 @@ 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; @@ -2064,7 +2064,7 @@ 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); } @@ -2146,7 +2146,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); } } @@ -6727,7 +6727,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; -- cgit v1.2.3 From 897fb5734a95454d47b97a9dbb78bc45024de0c1 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 12 Apr 2018 10:29:28 +0800 Subject: btrfs: rename __btrfs_open_devices to open_fs_devices __btrfs_open_devices() is un-exported drop __ prefix and rename it to open_fs_devices(). Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 9400f3935d27..6a78ae24f68a 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1101,7 +1101,7 @@ int btrfs_close_devices(struct btrfs_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 btrfs_device *device; @@ -1155,7 +1155,7 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, 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); return ret; @@ -6718,8 +6718,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); -- cgit v1.2.3 From 636d2c9d63228b3fb3b6a1efece663b06acf6391 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 12 Apr 2018 10:29:29 +0800 Subject: btrfs: cleanup find_device() drop list_head pointer find_device() declares struct list_head *head pointer and used only once, instead just use it directly. Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 6a78ae24f68a..bae1d2a7b232 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -362,10 +362,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; -- cgit v1.2.3 From b51851971343da1ef8533fb6e174c9c539638dd8 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 12 Apr 2018 10:29:30 +0800 Subject: btrfs: cleanup btrfs_rm_device() promote fs_devices pointer This function uses fs_info::fs_devices number of time, however we declare and use it only at the end, instead do it in the beginning of the function and use it. Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index bae1d2a7b232..70a87d4fe5fe 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1950,13 +1950,14 @@ 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); @@ -2020,7 +2021,7 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path, */ 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--; @@ -2034,12 +2035,12 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path, if (device->bdev) { device->fs_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 @@ -2053,8 +2054,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; @@ -2076,7 +2075,7 @@ 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); } -- cgit v1.2.3 From 00251a527a6fae93ccd4322619b23db56ed82986 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 20 Mar 2018 15:35:50 +0100 Subject: btrfs: squeeze btrfs_dev_replace_continue_on_mount to its caller The function is called once and is fairly small, we can merge it with the caller. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/dev-replace.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index f82be266ba4b..db4d08c65131 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) { @@ -810,6 +808,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 +819,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; } -- cgit v1.2.3 From 6fc4749d25738e1a5e5b02d04a0a60bbae516652 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 20 Mar 2018 15:37:08 +0100 Subject: btrfs: make success path out of btrfs_init_dev_replace_tgtdev more clear This is a preparatory cleanup that will make clear that the only successful way out of btrfs_init_dev_replace_tgtdev will also set the device_out to a valid pointer. With this guarantee, the callers can be simplified. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/dev-replace.c | 1 - fs/btrfs/volumes.c | 8 +++++++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index db4d08c65131..e3ec0eb5789b 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -358,7 +358,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, diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 70a87d4fe5fe..a8f8a2e39da6 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2612,6 +2612,12 @@ error: 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 + */ int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, const char *device_path, struct btrfs_device *srcdev, @@ -2698,7 +2704,7 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, mutex_unlock(&fs_info->fs_devices->device_list_mutex); *device_out = device; - return ret; + return 0; error: blkdev_put(bdev, FMODE_EXCL); -- cgit v1.2.3 From a425f9d4755a14fd8b9d2648a23ebe1dea11bd57 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 20 Mar 2018 15:47:33 +0100 Subject: btrfs: export and rename free_device The function will be used outside of volumes.c, the allocation btrfs_alloc_device is also exported. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 24 ++++++++++++------------ fs/btrfs/volumes.h | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index a8f8a2e39da6..364f4e7206f4 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -269,7 +269,7 @@ static struct btrfs_fs_devices *alloc_fs_devices(const u8 *fsid) 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); @@ -284,7 +284,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); } @@ -317,7 +317,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) { @@ -672,7 +672,7 @@ static void btrfs_free_stale_devices(const char *path, } else { fs_devs->num_devices--; list_del(&dev->dev_list); - free_device(dev); + btrfs_free_device(dev); } } } @@ -787,7 +787,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); @@ -900,7 +900,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); @@ -972,7 +972,7 @@ again: } list_del_init(&device->dev_list); fs_devices->num_devices--; - free_device(device); + btrfs_free_device(device); } if (fs_devices->seed) { @@ -990,7 +990,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) @@ -2602,7 +2602,7 @@ error_trans: 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) { @@ -2673,7 +2673,7 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, name = rcu_string_strdup(device_path, GFP_KERNEL); if (!name) { - free_device(device); + btrfs_free_device(device); ret = -ENOMEM; goto error; } @@ -6448,7 +6448,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, @@ -6471,7 +6471,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); } } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 62a92e472f65..316cce159969 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -421,6 +421,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); -- cgit v1.2.3 From d48f39d5a529244f59454386208c6da92bb1c493 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 20 Mar 2018 16:09:48 +0100 Subject: btrfs: move btrfs_init_dev_replace_tgtdev to dev-replace.c and make static The function logically belongs there and there's only a single caller, no need to export it. No code changes. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/dev-replace.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/volumes.c | 99 -------------------------------------------------- fs/btrfs/volumes.h | 4 -- 3 files changed, 99 insertions(+), 103 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index e3ec0eb5789b..8531b5dae777 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -176,6 +176,105 @@ out: 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. diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 364f4e7206f4..488935e66779 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2612,105 +2612,6 @@ error: 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 - */ -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; -} - static noinline int btrfs_update_device(struct btrfs_trans_handle *trans, struct btrfs_device *device) { diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 316cce159969..5737e6e68f8b 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -432,10 +432,6 @@ 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, struct btrfs_ioctl_balance_args *bargs); int btrfs_resume_balance_async(struct btrfs_fs_info *fs_info); -- cgit v1.2.3 From 72b81abf95ae8d2c78c5f38197f47597cf192d2b Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 20 Mar 2018 17:20:45 +0100 Subject: btrfs: move volume_mutex to callers of btrfs_rm_device Move locking and unlocking next to the BTRFS_FS_EXCL_OP bit manipulation so it's obvious that the two happen at the same time. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 4 ++++ fs/btrfs/volumes.c | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 7beec1bf6d4b..937afa8e1613 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2480,6 +2480,7 @@ static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg) ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; goto out; } + mutex_lock(&fs_info->volume_mutex); if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) { ret = btrfs_rm_device(fs_info, NULL, vol_args->devid); @@ -2487,6 +2488,7 @@ static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg) vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0'; ret = btrfs_rm_device(fs_info, vol_args->name, 0); } + mutex_unlock(&fs_info->volume_mutex); clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); if (!ret) { @@ -2522,6 +2524,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; goto out_drop_write; } + mutex_lock(&fs_info->volume_mutex); vol_args = memdup_user(arg, sizeof(*vol_args)); if (IS_ERR(vol_args)) { @@ -2536,6 +2539,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) btrfs_info(fs_info, "disk deleted %s", vol_args->name); kfree(vol_args); out: + mutex_unlock(&fs_info->volume_mutex); clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); out_drop_write: mnt_drop_write_file(file); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 488935e66779..1da46365657b 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1954,7 +1954,6 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path, u64 num_devices; int ret = 0; - mutex_lock(&fs_info->volume_mutex); mutex_lock(&uuid_mutex); num_devices = fs_devices->num_devices; @@ -2068,7 +2067,6 @@ int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path, out: mutex_unlock(&uuid_mutex); - mutex_unlock(&fs_info->volume_mutex); return ret; error_undo: -- cgit v1.2.3 From a17c95df4cc8ade4e0e7276a04c0cc89505c74d7 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 20 Mar 2018 17:28:05 +0100 Subject: btrfs: move clearing of EXCL_OP out of __cancel_balance Make the clearning visible in the callers so we can pair it with the test_and_set part. Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 2 +- fs/btrfs/volumes.c | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 937afa8e1613..f0d0aef3826a 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4462,7 +4462,7 @@ 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. + * free_fs_info. The flag should be cleared after __cancel_balance. */ need_unlock = false; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 1da46365657b..7e3656c9798b 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3780,8 +3780,6 @@ static void __cancel_balance(struct btrfs_fs_info *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 */ @@ -3939,6 +3937,7 @@ int btrfs_balance(struct btrfs_balance_control *bctl, if ((ret && ret != -ECANCELED && ret != -ENOSPC) || balance_need_close(fs_info)) { __cancel_balance(fs_info); + clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); } wake_up(&fs_info->balance_wait_q); @@ -3947,10 +3946,10 @@ int btrfs_balance(struct btrfs_balance_control *bctl, out: if (bctl->flags & BTRFS_BALANCE_RESUME) __cancel_balance(fs_info); - else { + else kfree(bctl); - clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); - } + clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); + return ret; } @@ -4118,8 +4117,10 @@ int btrfs_cancel_balance(struct btrfs_fs_info *fs_info) mutex_lock(&fs_info->volume_mutex); mutex_lock(&fs_info->balance_mutex); - if (fs_info->balance_ctl) + if (fs_info->balance_ctl) { __cancel_balance(fs_info); + clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); + } mutex_unlock(&fs_info->volume_mutex); } -- cgit v1.2.3 From 010a47bde94201d9abdab7ff04bedc17b6e8c357 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 20 Mar 2018 19:51:04 +0100 Subject: btrfs: add proper safety check before resuming dev-replace The device replace is paused by unmount or read only remount, and resumed on next mount or write remount. The exclusive status should be checked properly as it's a global invariant and we must not allow 2 operations run. In this case, the balance can be also paused and resumed under same conditions. It's always checked first so dev-replace could see the EXCL_OP already taken, BUT, the ioctl would never let start both at the same time. Replace the WARN_ON with message and return 0, indicating no error as this is purely theoretical and the user will be informed. Resolving that manually should be possible by waiting for the other operation to finish or cancel the paused state. Reviewed-by: Anand Jain Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/dev-replace.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 8531b5dae777..9fe7be7fdbef 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -896,7 +896,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); } -- cgit v1.2.3 From eee95e3fb0c3bcda4e85fc219964f2f69a218f03 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 20 Mar 2018 20:07:58 +0100 Subject: btrfs: add sanity check when resuming balance after mount Replace a WARN_ON with a proper check and message in case something goes really wrong and resumed balance cannot set up its exclusive status. The check is a user friendly assertion, I don't expect to ever happen under normal circumstances. Also document that the paused balance starts here and owns the exclusive op status. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 7e3656c9798b..585f93013214 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -4047,7 +4047,19 @@ 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, + "cannot set exclusive op status to balance, resume manually"); mutex_lock(&fs_info->volume_mutex); mutex_lock(&fs_info->balance_mutex); -- cgit v1.2.3 From 149196a2aea682ec9d7d50ec00d779a380deb7aa Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 20 Mar 2018 20:23:09 +0100 Subject: btrfs: cleanup helpers that reset balance state The function __cancel_balance name is confusing with the cancel operation of balance and it really resets the state of balance back to zero. The unset_balance_control helper is called only from one place and simple enough to be inlined. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 8 ++++---- fs/btrfs/volumes.c | 29 +++++++++++++---------------- 2 files changed, 17 insertions(+), 20 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index f0d0aef3826a..6c759f2d1301 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4459,10 +4459,10 @@ locked: 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 should be cleared after __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; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 585f93013214..07706c0a5781 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3212,7 +3212,7 @@ 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. + * restriper. Same goes for reset_balance_state. */ static void set_balance_control(struct btrfs_balance_control *bctl) { @@ -3225,9 +3225,13 @@ static void set_balance_control(struct btrfs_balance_control *bctl) spin_unlock(&fs_info->balance_lock); } -static void unset_balance_control(struct btrfs_fs_info *fs_info) +/* + * Clear the balance status in fs_info and delete the balance item from disk. + */ +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); @@ -3236,6 +3240,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); } /* @@ -3772,16 +3779,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); -} - /* Non-zero return value signifies invalidity */ static inline int validate_convert_profile(struct btrfs_balance_args *bctl_arg, u64 allowed) @@ -3936,7 +3933,7 @@ int btrfs_balance(struct btrfs_balance_control *bctl, 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); } @@ -3945,7 +3942,7 @@ int btrfs_balance(struct btrfs_balance_control *bctl, return ret; out: if (bctl->flags & BTRFS_BALANCE_RESUME) - __cancel_balance(fs_info); + reset_balance_state(fs_info); else kfree(bctl); clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); @@ -4124,13 +4121,13 @@ int btrfs_cancel_balance(struct btrfs_fs_info *fs_info) atomic_read(&fs_info->balance_running) == 0); mutex_lock(&fs_info->balance_mutex); } else { - /* __cancel_balance needs volume_mutex */ + /* reset_balance_state needs volume_mutex */ mutex_unlock(&fs_info->balance_mutex); mutex_lock(&fs_info->volume_mutex); mutex_lock(&fs_info->balance_mutex); if (fs_info->balance_ctl) { - __cancel_balance(fs_info); + reset_balance_state(fs_info); clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); } -- cgit v1.2.3 From a0fecc23718aa9ef020b8c86173a0b783ed37dcf Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 20 Mar 2018 23:44:50 +0100 Subject: btrfs: remove wrong use of volume_mutex from btrfs_dev_replace_start The volume mutex does not protect against anything in this case, the comment about scrub is right but not related to locking and looks confusing. The comment in btrfs_find_device_missing_or_by_path is wrong and confusing too. The device_list_mutex is not held here to protect device lookup, but in this case device replace cannot run in parallel with device removal (due to exclusive op protection), so we don't need further locking here. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/dev-replace.c | 7 +------ fs/btrfs/volumes.c | 4 ---- 2 files changed, 1 insertion(+), 10 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 9fe7be7fdbef..d097701d494d 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -414,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; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 07706c0a5781..9e5d27dd00b7 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2218,10 +2218,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) { -- cgit v1.2.3 From dccdb07bc996e9c8de80d06813163ca08288bf73 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 21 Mar 2018 00:20:05 +0100 Subject: btrfs: kill btrfs_fs_info::volume_mutex Mutual exclusion of device add/rm and balance was done by the volume mutex up to version 3.7. The commit 5ac00addc7ac091109 ("Btrfs: disallow mutually exclusive admin operations from user mode") added a bit that essentially tracked the same information. The status bit has an advantage over a mutex that it can be set without restrictions of function context, so it started to be used in the mount-time resuming of balance or device replace. But we don't really need to track the same information in two ways. 1) After the previous cleanups, the main ioctl handlers for add/del/resize copy the EXCL_OP bit next to the volume mutex, here it's clearly safe. 2) Resuming balance during mount or after rw remount will set only the EXCL_OP bit and the volume_mutex is held in the kernel thread that calls btrfs_balance. 3) Resuming device replace during mount or after rw remount is done after balance and is excluded by the EXCL_OP bit. It does not take the volume_mutex at all and completely relies on the EXCL_OP bit. 4) The resuming of balance and dev-replace cannot hapen at the same time as the ioctls cannot be started in parallel. Nevertheless, a crafted image could trigger that and a warning is printed. 5) Balance is normally excluded by EXCL_OP and also uses own mutex to protect against concurrent access to its status data. There's some trickery to maintain the right lock nesting in case we need to reexamine the status in btrfs_ioctl_balance. The volume_mutex is removed and the unlock/lock sequence is left in place as we might expect other waiters to proceed. 6) Similar to 5, the unlock/lock sequence is kept in btrfs_cancel_balance to allow waiters to continue. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 1 - fs/btrfs/disk-io.c | 1 - fs/btrfs/extent-tree.c | 2 +- fs/btrfs/ioctl.c | 17 ++++------------- fs/btrfs/volumes.c | 29 +++++++---------------------- 5 files changed, 12 insertions(+), 38 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index de86f2217816..add0e5a3f415 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -838,7 +838,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 diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index c3504b4d281b..49a990c8493e 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2601,7 +2601,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); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index df79340332ad..60e65df8134d 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4124,7 +4124,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) { diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 6c759f2d1301..c690092e8380 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1457,7 +1457,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 +1564,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; @@ -2432,7 +2430,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); @@ -2447,7 +2444,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; } @@ -2480,7 +2476,6 @@ static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg) ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; goto out; } - mutex_lock(&fs_info->volume_mutex); if (vol_args->flags & BTRFS_DEVICE_SPEC_BY_ID) { ret = btrfs_rm_device(fs_info, NULL, vol_args->devid); @@ -2488,7 +2483,6 @@ static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg) vol_args->name[BTRFS_SUBVOL_NAME_MAX] = '\0'; ret = btrfs_rm_device(fs_info, vol_args->name, 0); } - mutex_unlock(&fs_info->volume_mutex); clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); if (!ret) { @@ -2524,7 +2518,6 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; goto out_drop_write; } - mutex_lock(&fs_info->volume_mutex); vol_args = memdup_user(arg, sizeof(*vol_args)); if (IS_ERR(vol_args)) { @@ -2539,7 +2532,6 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) btrfs_info(fs_info, "disk deleted %s", vol_args->name); kfree(vol_args); out: - mutex_unlock(&fs_info->volume_mutex); clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); out_drop_write: mnt_drop_write_file(file); @@ -4358,7 +4350,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; @@ -4375,8 +4366,10 @@ again: /* this is either (2) or (3) */ if (!atomic_read(&fs_info->balance_running)) { 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 && @@ -4387,7 +4380,6 @@ again: } mutex_unlock(&fs_info->balance_mutex); - mutex_unlock(&fs_info->volume_mutex); goto again; } else { /* this is (2) */ @@ -4480,7 +4472,6 @@ out_bargs: 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: diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 9e5d27dd00b7..0216bc86f476 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -167,12 +167,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 @@ -3206,9 +3200,8 @@ 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 reset_balance_state. + * Should be called with balance mutex held to protect against checking the + * balance status or progress. Same goes for reset_balance_state. */ static void set_balance_control(struct btrfs_balance_control *bctl) { @@ -3785,7 +3778,7 @@ 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, struct btrfs_ioctl_balance_args *bargs) @@ -3951,16 +3944,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); } - mutex_unlock(&fs_info->balance_mutex); - mutex_unlock(&fs_info->volume_mutex); return ret; } @@ -4054,13 +4043,9 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info) btrfs_warn(fs_info, "cannot set exclusive op status to balance, resume manually"); - mutex_lock(&fs_info->volume_mutex); mutex_lock(&fs_info->balance_mutex); - set_balance_control(bctl); - mutex_unlock(&fs_info->balance_mutex); - mutex_unlock(&fs_info->volume_mutex); out: btrfs_free_path(path); return ret; @@ -4117,17 +4102,17 @@ int btrfs_cancel_balance(struct btrfs_fs_info *fs_info) atomic_read(&fs_info->balance_running) == 0); mutex_lock(&fs_info->balance_mutex); } else { - /* reset_balance_state 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) { reset_balance_state(fs_info); clear_bit(BTRFS_FS_EXCL_OP, &fs_info->flags); } - - mutex_unlock(&fs_info->volume_mutex); } BUG_ON(fs_info->balance_ctl || atomic_read(&fs_info->balance_running)); -- cgit v1.2.3 From 3009a62f3b18230a000d1a91e9a676036487e834 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 21 Mar 2018 01:31:04 +0100 Subject: btrfs: track running balance in a simpler way Currently fs_info::balance_running is 0 or 1 and does not use the semantics of atomics. The pause and cancel check for 0, that can happen only after __btrfs_balance exits for whatever reason. Parallel calls to balance ioctl may enter btrfs_ioctl_balance multiple times but will block on the balance_mutex that protects the fs_info::flags bit. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 7 ++++++- fs/btrfs/disk-io.c | 1 - fs/btrfs/ioctl.c | 6 +++--- fs/btrfs/volumes.c | 18 ++++++++++-------- 4 files changed, 19 insertions(+), 13 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index add0e5a3f415..8fdc97312b61 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]; @@ -1003,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; diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 49a990c8493e..7503ff1dd6f0 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2164,7 +2164,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; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index c690092e8380..ffb224b1c051 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4312,7 +4312,7 @@ void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock, 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; @@ -4364,7 +4364,7 @@ again: 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); /* * Lock released to allow other waiters to continue, @@ -4373,7 +4373,7 @@ 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; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 0216bc86f476..ed230247ae5d 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3907,13 +3907,14 @@ 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)); @@ -4061,16 +4062,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; @@ -4096,10 +4097,10 @@ int btrfs_cancel_balance(struct btrfs_fs_info *fs_info) * 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 { mutex_unlock(&fs_info->balance_mutex); @@ -4115,7 +4116,8 @@ int btrfs_cancel_balance(struct btrfs_fs_info *fs_info) } } - 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; -- cgit v1.2.3 From cf7d20f447147105b6c1a98aaf586d03f51bcc25 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 21 Mar 2018 01:45:32 +0100 Subject: btrfs: move and comment read-only check in btrfs_cancel_balance Balance cannot be started on a read-only filesystem and will have to finish/exit before eg. going to read-only via remount. In case the filesystem is forcibly set to read-only after an error, balance will finish anyway and if the cancel call is too fast it will just wait for that to happen. The last case is when the balance is paused after mount but it's read-only and cancelling would want to delete the item. The test is moved after the check if balance is running at all, as it looks more logical to report "no balance running" instead of "read-only filesystem". Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index ed230247ae5d..ff97f4749028 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -4083,15 +4083,22 @@ 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 -- cgit v1.2.3 From 008ef0969dd966ccb567d9c5c6e606f68119a380 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 21 Mar 2018 02:05:27 +0100 Subject: btrfs: drop lock parameter from update_ioctl_balance_args and rename The parameter controls locking of the stats part but we can lock it unconditionally, as this only happens once when balance starts. This is not performance critical. Add the prefix for an exported function. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 4 +++- fs/btrfs/ioctl.c | 14 +++++--------- fs/btrfs/volumes.c | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 8fdc97312b61..9cc90f407cae 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3267,7 +3267,9 @@ 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 update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock, +void btrfs_get_block_group_info(struct list_head *groups_list, + struct btrfs_ioctl_space_info *space); +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); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index ffb224b1c051..397f026b1e4c 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4305,7 +4305,7 @@ out_loi: 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; @@ -4323,13 +4323,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) @@ -4515,7 +4511,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; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index ff97f4749028..197b91b0e15b 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3918,7 +3918,7 @@ int btrfs_balance(struct btrfs_balance_control *bctl, 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) || -- cgit v1.2.3 From 1354e1a13e821040e537d5e63799df7171729945 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 21 Mar 2018 02:29:13 +0100 Subject: btrfs: use mutex in btrfs_resume_balance_async While the spinlock does not cause problems, using the mutex is more correct and consistent with others. The global status of balance is eg. checked from btrfs_pause_balance or btrfs_cancel_balance with mutex. Resuming balance happens during mount or ro->rw remount. In the former case, no other user of the balance_ctl exists, in the latter, balance cannot run until the ro/rw transition is finished. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 197b91b0e15b..447a0c275b43 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3959,12 +3959,12 @@ 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"); -- cgit v1.2.3 From 833aae18fcb768ef8e97faea42e768521154cd21 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 21 Mar 2018 02:41:30 +0100 Subject: btrfs: open code set_balance_control The helper is quite simple and I'd like to see the locking in the caller. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 447a0c275b43..685921ba4c34 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3199,21 +3199,6 @@ static void update_balance_args(struct btrfs_balance_control *bctl) } } -/* - * Should be called with balance mutex held to protect against checking the - * balance status or progress. Same goes for reset_balance_state. - */ -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); -} - /* * Clear the balance status in fs_info and delete the balance item from disk. */ @@ -3899,7 +3884,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); @@ -4045,7 +4033,10 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info) "cannot set exclusive op status to balance, resume manually"); 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); out: btrfs_free_path(path); -- cgit v1.2.3 From b545993694a6e3bf6e801071df00e0ad47526bb8 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 24 Apr 2018 13:03:13 +0800 Subject: btrfs: print-tree: Add eb locking status output for debug build It's pretty handy if we can get the debug output for locking status of an extent buffer, specially for race condition related debugging. So add the following output for btrfs_print_tree() and btrfs_print_leaf(): - refs - write_locks (as w:%d) - read_locks (as r:%d) - blocking_writers (as bw:%d) - blocking_readers (as br:%d) - spinning_writers (as sw:%d) - spinning_readers (as sr:%d) - lock_owner - current->pid Signed-off-by: Qu Wenruo Reviewed-by: David Sterba [ update comment ] Signed-off-by: David Sterba --- fs/btrfs/print-tree.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c index 21a831d3d087..a4e11cf04671 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", -- cgit v1.2.3 From ed23467b180421d37f9a6ce26c8fa8f03aef0a4a Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Wed, 25 Apr 2018 19:01:42 +0800 Subject: btrfs: move btrfs_raid_type_names values to btrfs_raid_attr table Add a new member struct btrfs_raid_attr::raid_name so that btrfs_raid_array can maintain the name of the raid type, and so we can drop btrfs_raid_type_names. Signed-off-by: Anand Jain Reviewed-by: Qu Wenruo Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 18 ------------------ fs/btrfs/volumes.c | 15 +++++++++++++++ fs/btrfs/volumes.h | 3 +++ 3 files changed, 18 insertions(+), 18 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 60e65df8134d..ed262d44ff8a 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -7380,24 +7380,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, diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 685921ba4c34..6abd0ebb3fea 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -40,6 +40,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 1, .devs_increment = 2, .ncopies = 2, + .raid_name = "raid10", }, [BTRFS_RAID_RAID1] = { .sub_stripes = 1, @@ -49,6 +50,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 1, .devs_increment = 2, .ncopies = 2, + .raid_name = "raid1", }, [BTRFS_RAID_DUP] = { .sub_stripes = 1, @@ -58,6 +60,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 0, .devs_increment = 1, .ncopies = 2, + .raid_name = "dup", }, [BTRFS_RAID_RAID0] = { .sub_stripes = 1, @@ -67,6 +70,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 0, .devs_increment = 1, .ncopies = 1, + .raid_name = "raid0", }, [BTRFS_RAID_SINGLE] = { .sub_stripes = 1, @@ -76,6 +80,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 0, .devs_increment = 1, .ncopies = 1, + .raid_name = "single", }, [BTRFS_RAID_RAID5] = { .sub_stripes = 1, @@ -85,6 +90,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 1, .devs_increment = 1, .ncopies = 2, + .raid_name = "raid5", }, [BTRFS_RAID_RAID6] = { .sub_stripes = 1, @@ -94,9 +100,18 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .tolerated_failures = 2, .devs_increment = 1, .ncopies = 3, + .raid_name = "raid6", }, }; +const char *get_raid_name(enum btrfs_raid_types type) +{ + if (type >= BTRFS_NR_RAID_TYPES) + return NULL; + + return btrfs_raid_array[type].raid_name; +} + const u64 btrfs_raid_group[BTRFS_NR_RAID_TYPES] = { [BTRFS_RAID_RAID10] = BTRFS_BLOCK_GROUP_RAID10, [BTRFS_RAID_RAID1] = BTRFS_BLOCK_GROUP_RAID1, diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 5737e6e68f8b..9056a900aace 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -329,6 +329,7 @@ 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 */ + const char raid_name[8]; /* name of the raid */ }; extern const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES]; @@ -550,6 +551,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); -- cgit v1.2.3 From 41a6e8913cdff6b30ac53a24641259d117c0b101 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Wed, 25 Apr 2018 19:01:43 +0800 Subject: btrfs: move btrfs_raid_group values to btrfs_raid_attr table Add a new member struct btrfs_raid_attr::bg_flag so that btrfs_raid_array can maintain the bit map flag of the raid type, and so we can drop btrfs_raid_group. Signed-off-by: Anand Jain Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 2 +- fs/btrfs/extent-tree.c | 2 +- fs/btrfs/volumes.c | 19 ++++++++----------- fs/btrfs/volumes.h | 2 +- 4 files changed, 11 insertions(+), 14 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 7503ff1dd6f0..47dbbe496253 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -3521,7 +3521,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]. diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index ed262d44ff8a..fdd6ac9ee2c6 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4180,7 +4180,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; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 6abd0ebb3fea..4fc22a696206 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -41,6 +41,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .devs_increment = 2, .ncopies = 2, .raid_name = "raid10", + .bg_flag = BTRFS_BLOCK_GROUP_RAID10, }, [BTRFS_RAID_RAID1] = { .sub_stripes = 1, @@ -51,6 +52,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .devs_increment = 2, .ncopies = 2, .raid_name = "raid1", + .bg_flag = BTRFS_BLOCK_GROUP_RAID1, }, [BTRFS_RAID_DUP] = { .sub_stripes = 1, @@ -61,6 +63,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .devs_increment = 1, .ncopies = 2, .raid_name = "dup", + .bg_flag = BTRFS_BLOCK_GROUP_DUP, }, [BTRFS_RAID_RAID0] = { .sub_stripes = 1, @@ -71,6 +74,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .devs_increment = 1, .ncopies = 1, .raid_name = "raid0", + .bg_flag = BTRFS_BLOCK_GROUP_RAID0, }, [BTRFS_RAID_SINGLE] = { .sub_stripes = 1, @@ -81,6 +85,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .devs_increment = 1, .ncopies = 1, .raid_name = "single", + .bg_flag = 0, }, [BTRFS_RAID_RAID5] = { .sub_stripes = 1, @@ -91,6 +96,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .devs_increment = 1, .ncopies = 2, .raid_name = "raid5", + .bg_flag = BTRFS_BLOCK_GROUP_RAID5, }, [BTRFS_RAID_RAID6] = { .sub_stripes = 1, @@ -101,6 +107,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .devs_increment = 1, .ncopies = 3, .raid_name = "raid6", + .bg_flag = BTRFS_BLOCK_GROUP_RAID6, }, }; @@ -112,16 +119,6 @@ const char *get_raid_name(enum btrfs_raid_types type) return btrfs_raid_array[type].raid_name; } -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, -}; - /* * Table to convert BTRFS_RAID_* to the error code if minimum number of devices * condition is not met. Zero means there's no corresponding @@ -1899,7 +1896,7 @@ 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) { diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 9056a900aace..b26f53462e8d 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -330,11 +330,11 @@ struct btrfs_raid_attr { int devs_increment; /* ndevs has to be a multiple of this */ int ncopies; /* how many copies to data has */ 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; -- cgit v1.2.3 From f9fbcaa2a3221f55a8eee319e4b2d532635e002e Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Wed, 25 Apr 2018 19:01:44 +0800 Subject: btrfs: move btrfs_raid_mindev_errorvalues to btrfs_raid_attr table Add a new member struct btrfs_raid_attr::mindev_error so that btrfs_raid_array can maintain the error code to return if the minimum number of devices condition is not met while trying to delete a device in the given raid. And so we can drop btrfs_raid_mindev_error. Signed-off-by: Anand Jain Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 24 ++++++++---------------- fs/btrfs/volumes.h | 2 +- 2 files changed, 9 insertions(+), 17 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 4fc22a696206..7b3b235cf214 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -42,6 +42,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .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, @@ -53,6 +54,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .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, @@ -64,6 +66,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .ncopies = 2, .raid_name = "dup", .bg_flag = BTRFS_BLOCK_GROUP_DUP, + .mindev_error = 0, }, [BTRFS_RAID_RAID0] = { .sub_stripes = 1, @@ -75,6 +78,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .ncopies = 1, .raid_name = "raid0", .bg_flag = BTRFS_BLOCK_GROUP_RAID0, + .mindev_error = 0, }, [BTRFS_RAID_SINGLE] = { .sub_stripes = 1, @@ -86,6 +90,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .ncopies = 1, .raid_name = "single", .bg_flag = 0, + .mindev_error = 0, }, [BTRFS_RAID_RAID5] = { .sub_stripes = 1, @@ -97,6 +102,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .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, @@ -108,6 +114,7 @@ const struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { .ncopies = 3, .raid_name = "raid6", .bg_flag = BTRFS_BLOCK_GROUP_RAID6, + .mindev_error = BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET, }, }; @@ -119,21 +126,6 @@ const char *get_raid_name(enum btrfs_raid_types type) return btrfs_raid_array[type].raid_name; } -/* - * 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, -}; - static int init_first_rw_device(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info); static int btrfs_relocate_sys_chunks(struct btrfs_fs_info *fs_info); @@ -1900,7 +1892,7 @@ static int btrfs_check_raid_min_devices(struct btrfs_fs_info *fs_info, 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; diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index b26f53462e8d..3a6dbbce650a 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -329,12 +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]; struct map_lookup { u64 type; -- cgit v1.2.3 From 9113493e3ad66211967a81fd4498bccfc85d1ef3 Mon Sep 17 00:00:00 2001 From: Gu Jinxiang Date: Thu, 26 Apr 2018 15:49:02 +0800 Subject: btrfs: remove unused fs_info parameter Since the commit c6100a4b4e3d ("Btrfs: replace tree->mapping with tree->private_data"), parameter fs_info in alloc_reloc_control is not used. So remove it. Signed-off-by: Gu Jinxiang Reviewed-by: Nikolay Borisov Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/relocation.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index b041b945a7ae..74656d79e511 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -4299,7 +4299,7 @@ out: 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; @@ -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; -- cgit v1.2.3 From 3dca5c942dac60164e6a6e89172f25b86af07ce7 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 26 Apr 2018 14:24:25 +0800 Subject: btrfs: trace: Remove unnecessary fs_info parameter for btrfs__reserve_extent event class fs_info can be extracted from btrfs_block_group_cache, and all btrfs_block_group_cache is created by btrfs_create_block_group_cache() with fs_info initialized, no need to worry about NULL pointer dereference. Signed-off-by: Qu Wenruo Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 7 +++---- include/trace/events/btrfs.h | 18 ++++++++---------- 2 files changed, 11 insertions(+), 14 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index fdd6ac9ee2c6..295f8298fd9e 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -7651,7 +7651,7 @@ have_block_group: 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) { @@ -7724,7 +7724,7 @@ refill_cluster: 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; @@ -7824,8 +7824,7 @@ checks: 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: diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index d78d8ab4bc86..5af2479e9b16 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -1146,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 ) @@ -1159,7 +1158,7 @@ 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; @@ -1177,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, @@ -1807,6 +1804,7 @@ TRACE_EVENT(btrfs_inode_mod_outstanding_extents, show_root_type(__entry->root_objectid), __entry->ino, __entry->mod) ); + #endif /* _TRACE_BTRFS_H */ /* This part must be outside protection */ -- cgit v1.2.3 From 4ed0a7a3b7c3f8387caf2ad57424d9341f523856 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 26 Apr 2018 17:17:20 +0800 Subject: btrfs: trace: Add trace points for unused block groups This patch will add the following trace events: 1) btrfs_remove_block_group For btrfs_remove_block_group() function. Triggered when a block group is really removed. 2) btrfs_add_unused_block_group Triggered which block group is added to unused_bgs list. 3) btrfs_skip_unused_block_group Triggered which unused block group is not deleted. These trace events is pretty handy to debug case related to block group auto remove. Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 4 ++++ fs/btrfs/scrub.c | 1 + include/trace/events/btrfs.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 295f8298fd9e..888a47894c30 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -6358,6 +6358,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); } @@ -10192,6 +10193,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); } @@ -10379,6 +10381,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. @@ -10743,6 +10746,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; diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 52b39a0924e9..a59005862010 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/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 5af2479e9b16..914ddb7a6d25 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -1805,6 +1805,48 @@ TRACE_EVENT(btrfs_inode_mod_outstanding_extents, __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 */ -- cgit v1.2.3 From 0338dff6e0d91af053ef5d350d7388df666ae78e Mon Sep 17 00:00:00 2001 From: Gu Jinxiang Date: Fri, 27 Apr 2018 16:22:07 +0800 Subject: btrfs: do reverse path readahead in btrfs_shrink_device In btrfs_shrink_device, before btrfs_search_slot, path->reada is set to READA_FORWARD. But I think READA_BACK is correct. Since: 1. key.offset is set to (u64)-1 2. after btrfs_search_slot, btrfs_previous_item is called So, for readahead previous items, READA_BACK is the correct one. Signed-off-by: Gu Jinxiang Reviewed-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 7b3b235cf214..78f6cd1de33a 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -4414,7 +4414,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); -- cgit v1.2.3 From 82b3e53b8da19b25ef36b68316374df47f8fa268 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Mon, 23 Apr 2018 10:54:13 +0300 Subject: btrfs: Remove delayed_iput parameter of btrfs_start_delalloc_roots This parameter was introduced alongside the function in eb73c1b7cea7 ("Btrfs: introduce per-subvolume delalloc inode list") to avoid deadlocks since this function was used in the transaction commit path. However, commit 8d875f95da43 ("btrfs: disable strict file flushes for renames and truncates") removed that usage, rendering the parameter obsolete. Signed-off-by: Nikolay Borisov Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 3 +-- fs/btrfs/dev-replace.c | 2 +- fs/btrfs/extent-tree.c | 4 ++-- fs/btrfs/inode.c | 5 ++--- fs/btrfs/ioctl.c | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 9cc90f407cae..0d15d98a964a 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3205,8 +3205,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, u32 min_type); int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput); -int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput, - int nr); +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); diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index d097701d494d..12f703e127dd 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -594,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; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 888a47894c30..38dd98bc50d7 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4343,7 +4343,7 @@ commit_trans: 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); } @@ -4796,7 +4796,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); } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index ce2f9288df3e..c2bb7c8e1fda 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10269,8 +10269,7 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput) return ret; } -int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int delay_iput, - int nr) +int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int nr) { struct btrfs_root *root; struct list_head splice; @@ -10293,7 +10292,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, 0, nr); btrfs_put_fs_root(root); if (ret < 0) goto out; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 397f026b1e4c..67db5f6b0476 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -5307,7 +5307,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); -- cgit v1.2.3 From 76f32e240ee6a3c1903ce8f1e934b43f2971bffc Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Mon, 23 Apr 2018 10:54:14 +0300 Subject: btrfs: Remove delayed_iput parameter from btrfs_start_delalloc_inodes It's always set to 0, so just remove it and collapse the constant value to the only function we are passing it. Signed-off-by: Nikolay Borisov Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 2 +- fs/btrfs/inode.c | 4 ++-- fs/btrfs/ioctl.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 0d15d98a964a..0dbb5a19d977 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3204,7 +3204,7 @@ 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_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, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index c2bb7c8e1fda..c6ff72a1ff10 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10255,7 +10255,7 @@ out: 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; @@ -10263,7 +10263,7 @@ 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, 0, -1); if (ret > 0) ret = 0; return ret; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 67db5f6b0476..b708f12292b6 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -640,7 +640,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; -- cgit v1.2.3 From 4fbb514785154ac2eff2a85d60fc0974207171b2 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Mon, 23 Apr 2018 10:54:15 +0300 Subject: btrfs: Remove delay_iput parameter from __start_delalloc_inodes It's always set to 0 so remove it. Signed-off-by: Nikolay Borisov Reviewed-by: Qu Wenruo [ rename to start_delalloc_inodes ] Signed-off-by: David Sterba --- fs/btrfs/inode.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index c6ff72a1ff10..20a385b0668f 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10190,8 +10190,7 @@ struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode, * 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; @@ -10219,12 +10218,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, 0); if (!work) { - if (delay_iput) - btrfs_add_delayed_iput(inode); - else - iput(inode); + iput(inode); ret = -ENOMEM; goto out; } @@ -10263,7 +10259,7 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root) if (test_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state)) return -EROFS; - ret = __start_delalloc_inodes(root, 0, -1); + ret = start_delalloc_inodes(root, -1); if (ret > 0) ret = 0; return ret; @@ -10292,7 +10288,7 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, int nr) &fs_info->delalloc_roots); spin_unlock(&fs_info->delalloc_root_lock); - ret = __start_delalloc_inodes(root, 0, nr); + ret = start_delalloc_inodes(root, nr); btrfs_put_fs_root(root); if (ret < 0) goto out; -- cgit v1.2.3 From 076da91cd9ec4032f88bb30a162d576e9e46c2d6 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Mon, 23 Apr 2018 10:54:16 +0300 Subject: btrfs: Remove delayed_iput member from btrfs_delalloc_work When allocating a delalloc work we are always setting the delayed_iput to 0. So remove the delay_iput member of btrfs_delalloc_work, as a result also remove it as a parameter from btrfs_alloc_delalloc_work since it's not used anymore. Signed-off-by: Nikolay Borisov Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 4 +--- fs/btrfs/inode.c | 11 +++-------- 2 files changed, 4 insertions(+), 11 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 0dbb5a19d977..6cdc6d0e8525 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3169,14 +3169,12 @@ void btrfs_extent_item_to_extent_map(struct btrfs_inode *inode, /* 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); +struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode); struct extent_map *btrfs_get_extent_fiemap(struct btrfs_inode *inode, struct page *page, size_t pg_offset, u64 start, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 20a385b0668f..4e10bb1f300e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10159,15 +10159,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) +struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode) { struct btrfs_delalloc_work *work; @@ -10178,7 +10174,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); @@ -10218,7 +10213,7 @@ static int start_delalloc_inodes(struct btrfs_root *root, int nr) } spin_unlock(&root->delalloc_lock); - work = btrfs_alloc_delalloc_work(inode, 0); + work = btrfs_alloc_delalloc_work(inode); if (!work) { iput(inode); ret = -ENOMEM; -- cgit v1.2.3 From 3a2f8c07e1d60739eb6b90ffba41bd1d0de33fc2 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Tue, 24 Apr 2018 17:23:59 +0300 Subject: btrfs: Unexport btrfs_alloc_delalloc_work It's used only in inode.c so makes no sense to have it exported. Also move the definition of btrfs_delalloc_work to inode.c since it's used only this file. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 9 --------- fs/btrfs/inode.c | 9 ++++++++- 2 files changed, 8 insertions(+), 10 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 6cdc6d0e8525..27aa9b58b001 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3167,15 +3167,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; - struct completion completion; - struct list_head list; - struct btrfs_work work; -}; - -struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode); - struct extent_map *btrfs_get_extent_fiemap(struct btrfs_inode *inode, struct page *page, size_t pg_offset, u64 start, u64 len, int create); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 4e10bb1f300e..29374d31f1de 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -10146,6 +10146,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; @@ -10163,7 +10170,7 @@ static void btrfs_run_delalloc_work(struct btrfs_work *work) complete(&delalloc_work->completion); } -struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode) +static struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode) { struct btrfs_delalloc_work *work; -- cgit v1.2.3 From 8b317901da4ab5576d109bf1aed7eb15cbe31d5f Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 30 Apr 2018 15:04:44 +0800 Subject: btrfs: trace: Allow trace_qgroup_update_counters() to record old rfer/excl value Origin trace_qgroup_update_counters() only records qgroup id and its reference count change. It's good enough to debug qgroup accounting change, but when rescan race is involved, it's pretty hard to distinguish which modification belongs to which rescan. So add old_rfer and old_excl trace output to help distinguishing different rescan instance. (Different rescan instance should reset its qgroup->rfer to 0) For trace event parameter, it just changes from u64 qgroup_id to struct btrfs_qgroup *qgroup, so number of parameters is not changed at all. Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/qgroup.c | 4 ++-- include/trace/events/btrfs.h | 18 +++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 9fb758d5077a..ec2339a49ec3 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) { diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 914ddb7a6d25..29f9b14412ad 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -1607,27 +1607,31 @@ TRACE_EVENT(btrfs_qgroup_account_extent, 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, -- cgit v1.2.3 From 63a9c7b9ceaad9b2d8bd482abd44e2068b828b00 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Fri, 4 May 2018 10:53:05 +0300 Subject: btrfs: Remove devid parameter from btrfs_rmap_block This function is used in only one place and devid argument is always passed 0. So just remove it, similarly to how it was removed in the userspace code. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 2 +- fs/btrfs/volumes.c | 7 ++----- fs/btrfs/volumes.h | 5 ++--- 3 files changed, 5 insertions(+), 9 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 38dd98bc50d7..f206f1a65cc1 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -256,7 +256,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; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 78f6cd1de33a..45227c83b742 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -5975,9 +5975,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; @@ -6009,8 +6008,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; diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 3a6dbbce650a..39787a2f5fb4 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -394,9 +394,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, -- cgit v1.2.3 From f5686e3acdfd8b2559ed6988f85374c36e1fed4c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 4 May 2018 12:11:12 +0100 Subject: btrfs: send: fix spelling mistake: "send_in_progres" -> "send_in_progress" Trivial fix to spelling mistake of function name in btrfs_err message Signed-off-by: Colin Ian King Signed-off-by: David Sterba --- fs/btrfs/send.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index c0074d2d7d6d..6e8184f239e0 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -6454,7 +6454,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); } -- cgit v1.2.3 From c9f6f3cd1c6fc4df959ce2bce15e5e6ce660bfd4 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 3 May 2018 09:59:02 +0800 Subject: btrfs: qgroup: Allow trace_btrfs_qgroup_account_extent() to record its transid When debugging quota rescan race, some times btrfs rescan could account some old (committed) leaf and then re-account newly committed leaf in next generation. This race needs extra transid to locate, so add @transid for trace_btrfs_qgroup_account_extent() for such debug. Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/qgroup.c | 4 ++-- include/trace/events/btrfs.h | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index ec2339a49ec3..9fdac5b46aa9 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -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) { diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 29f9b14412ad..39b94ec965be 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -1578,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 ) @@ -1591,18 +1593,20 @@ 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, -- cgit v1.2.3 From 6fcf6e2bffb6cfe26f05795f4fba4e6bb6aa84bf Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 7 May 2018 17:44:03 +0200 Subject: btrfs: remove redundant btrfs_balance_control::fs_info The fs_info is always available from the context so we don't need to store it in the structure. Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 3 +-- fs/btrfs/volumes.c | 7 +++---- fs/btrfs/volumes.h | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index b708f12292b6..48e2ddff32bd 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4428,7 +4428,6 @@ locked: 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)); @@ -4454,7 +4453,7 @@ do_balance: */ need_unlock = false; - ret = btrfs_balance(bctl, bargs); + ret = btrfs_balance(fs_info, bctl, bargs); bctl = NULL; if (arg) { diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 45227c83b742..ebc81766fc86 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3769,10 +3769,10 @@ static inline int validate_convert_profile(struct btrfs_balance_args *bctl_arg, /* * 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; @@ -3940,7 +3940,7 @@ static int balance_kthread(void *data) 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); + ret = btrfs_balance(fs_info, fs_info->balance_ctl, NULL); } mutex_unlock(&fs_info->balance_mutex); @@ -4011,7 +4011,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; diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 39787a2f5fb4..5139ec8daf4c 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -352,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; @@ -432,7 +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_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); -- cgit v1.2.3 From 891f41cb27cf5036e88053bb0ef688f98fcc945b Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Wed, 9 May 2018 21:08:23 +0800 Subject: btrfs: return original error code when failing from option parsing It's not good to overwrite -ENOMEM using -EINVAL when failing from mount option parsing, so just return original error code. Signed-off-by: Chengguang Xu Reviewed-by: David Sterba Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/super.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 0628092b0b1b..c67fafaa2fe7 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -1782,10 +1782,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, -- cgit v1.2.3 From cb49a87b2a4edb469e4d295eca4b1d106f64083e Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Tue, 24 Apr 2018 17:18:17 +0300 Subject: btrfs: Factor out common delayed refs init code THe majority of the init code for struct btrfs_delayed_ref_node is duplicated in add_delayed_data_ref and add_delayed_tree_ref. Factor out the common bits in init_delayed_ref_common. This function is going to be used in future patches to clean that up. No functional changes. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/delayed-ref.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 4fb041e14742..a0dc255792c7 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -644,6 +644,57 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, return head_ref; } +/* + * 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 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) +{ + u64 seq = 0; + + if (action == BTRFS_ADD_DELAYED_EXTENT) + action = BTRFS_ADD_DELAYED_REF; + + if (is_fstree(ref_root)) + seq = atomic64_read(&fs_info->tree_mod_seq); + + 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; + ref->type = ref_type; + RB_CLEAR_NODE(&ref->ref_node); + INIT_LIST_HEAD(&ref->add_list); +} + /* * helper to insert a delayed tree ref into the rbtree. */ -- cgit v1.2.3 From 646f4dd76fb3ac0d1e8677890522d4c044ee2f06 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Tue, 24 Apr 2018 17:18:18 +0300 Subject: btrfs: Use init_delayed_ref_common in add_delayed_tree_ref Use the newly introduced common helper. No functional changes. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/delayed-ref.c | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index a0dc255792c7..1c27d3322198 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -708,38 +708,25 @@ add_delayed_tree_ref(struct btrfs_fs_info *fs_info, { struct btrfs_delayed_tree_ref *full_ref; struct btrfs_delayed_ref_root *delayed_refs; - u64 seq = 0; + u8 ref_type; 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; + ref_type = BTRFS_SHARED_BLOCK_REF_KEY; else - ref->type = BTRFS_TREE_BLOCK_REF_KEY; + ref_type = BTRFS_TREE_BLOCK_REF_KEY; + + init_delayed_ref_common(fs_info, ref, bytenr, num_bytes, ref_root, + action, ref_type); + full_ref->root = ref_root; + full_ref->parent = parent; full_ref->level = level; - trace_add_delayed_tree_ref(fs_info, ref, full_ref, action); + trace_add_delayed_tree_ref(fs_info, ref, full_ref, + action == BTRFS_ADD_DELAYED_EXTENT ? + BTRFS_ADD_DELAYED_REF : action); ret = insert_delayed_ref(trans, delayed_refs, head_ref, ref); -- cgit v1.2.3 From c812c8a857a00acae78341d5d4702eb8d7d02661 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Tue, 24 Apr 2018 17:18:19 +0300 Subject: btrfs: Use init_delayed_ref_common in add_delayed_data_ref Use the newly introduced helper and remove the duplicate code. No functional changes. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/delayed-ref.c | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 1c27d3322198..c891e88d546e 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -750,41 +750,26 @@ add_delayed_data_ref(struct btrfs_trans_handle *trans, { struct btrfs_delayed_data_ref *full_ref; struct btrfs_delayed_ref_root *delayed_refs; - u64 seq = 0; + u8 ref_type; 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(&trans->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; - 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_data_ref(ref); - full_ref->parent = parent; - full_ref->root = ref_root; if (parent) - ref->type = BTRFS_SHARED_DATA_REF_KEY; + ref_type = BTRFS_SHARED_DATA_REF_KEY; else - ref->type = BTRFS_EXTENT_DATA_REF_KEY; + ref_type = BTRFS_EXTENT_DATA_REF_KEY; + init_delayed_ref_common(trans->fs_info, ref, bytenr, num_bytes, + ref_root, action, ref_type); + full_ref->root = ref_root; + full_ref->parent = parent; full_ref->objectid = owner; full_ref->offset = offset; - trace_add_delayed_data_ref(trans->fs_info, ref, full_ref, action); + trace_add_delayed_data_ref(trans->fs_info, ref, full_ref, + action == BTRFS_ADD_DELAYED_EXTENT ? + BTRFS_ADD_DELAYED_REF : action); ret = insert_delayed_ref(trans, delayed_refs, head_ref, ref); if (ret > 0) -- cgit v1.2.3 From 70d640004ab5c2597084f6463dd39b36f4f026f8 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Tue, 24 Apr 2018 17:18:20 +0300 Subject: btrfs: Open-code add_delayed_tree_ref Now that the initialization part and the critical section code have been split it's a lot easier to open code add_delayed_tree_ref. Do so in the following manner: 1. The comming init code is put immediately after memory-to-be-initialized is allocated, followed by the ref-specific member initialization. 2. The only piece of code that remains in the critical section is insert_delayed_ref call. 3. Tracing and memory freeing code is put outside of the critical section as well. The only real change here is an overall shorter critical section when dealing with delayed tree refs. From functional point of view - the code is unchanged. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/delayed-ref.c | 65 ++++++++++++++++---------------------------------- 1 file changed, 20 insertions(+), 45 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index c891e88d546e..184722274bbe 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -695,49 +695,6 @@ static void init_delayed_ref_common(struct btrfs_fs_info *fs_info, INIT_LIST_HEAD(&ref->add_list); } -/* - * 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; - u8 ref_type; - int ret; - - delayed_refs = &trans->transaction->delayed_refs; - full_ref = btrfs_delayed_node_to_tree_ref(ref); - if (parent) - ref_type = BTRFS_SHARED_BLOCK_REF_KEY; - else - ref_type = BTRFS_TREE_BLOCK_REF_KEY; - - init_delayed_ref_common(fs_info, ref, bytenr, num_bytes, ref_root, - action, ref_type); - full_ref->root = ref_root; - full_ref->parent = parent; - full_ref->level = level; - - trace_add_delayed_tree_ref(fs_info, ref, full_ref, - action == BTRFS_ADD_DELAYED_EXTENT ? - BTRFS_ADD_DELAYED_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); -} - /* * helper to insert a delayed data ref into the rbtree. */ @@ -794,12 +751,24 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, struct btrfs_qgroup_extent_record *record = NULL; int qrecord_inserted; int 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; @@ -825,10 +794,16 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, is_system, &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); -- cgit v1.2.3 From cd7f9699b113434467434580ebb8d9b328152fb8 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Tue, 24 Apr 2018 17:18:21 +0300 Subject: btrfs: Open-code add_delayed_data_ref Now that the initialization part and the critical section code have been split it's a lot easier to open code add_delayed_data_ref. Do so in the following manner: 1. The common init function is put immediately after memory-to-be-initialized is allocated, followed by the specific data ref initialization. 2. The only piece of code that remains in the critical section is insert_delayed_ref call. 3. Tracing and memory freeing code is moved outside of the critical section. No functional changes, just an overall shorter critical section. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/delayed-ref.c | 62 ++++++++++++++++++-------------------------------- 1 file changed, 22 insertions(+), 40 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 184722274bbe..3fa8ea5cbbc6 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -695,44 +695,6 @@ static void init_delayed_ref_common(struct btrfs_fs_info *fs_info, INIT_LIST_HEAD(&ref->add_list); } -/* - * helper to insert a delayed data ref into the rbtree. - */ -static noinline void -add_delayed_data_ref(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) -{ - struct btrfs_delayed_data_ref *full_ref; - struct btrfs_delayed_ref_root *delayed_refs; - u8 ref_type; - int ret; - - delayed_refs = &trans->transaction->delayed_refs; - full_ref = btrfs_delayed_node_to_data_ref(ref); - if (parent) - ref_type = BTRFS_SHARED_DATA_REF_KEY; - else - ref_type = BTRFS_EXTENT_DATA_REF_KEY; - - init_delayed_ref_common(trans->fs_info, ref, bytenr, num_bytes, - ref_root, action, ref_type); - full_ref->root = ref_root; - full_ref->parent = parent; - full_ref->objectid = owner; - full_ref->offset = offset; - - trace_add_delayed_data_ref(trans->fs_info, ref, full_ref, - action == BTRFS_ADD_DELAYED_EXTENT ? - BTRFS_ADD_DELAYED_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); -} - /* * add a delayed tree ref. This does all of the accounting required * to make sure the delayed ref is eventually processed before this @@ -832,11 +794,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); @@ -868,10 +844,16 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, action, 1, 0, &qrecord_inserted, old_ref_mod, new_ref_mod); - add_delayed_data_ref(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; -- cgit v1.2.3 From a2e569b3f2b138f2c25b4598cf4b18af8af39abd Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Tue, 24 Apr 2018 17:18:22 +0300 Subject: btrfs: Introduce init_delayed_ref_head add_delayed_ref_head implements the logic to both initialize a head_ref structure as well as perform the necessary operations to add it to the delayed ref machinery. This has resulted in a very cumebrsome interface with loads of parameters and code, which at first glance, looks very unwieldy. Begin untangling it by first extracting the initialization only code in its own function. It's more or less verbatim copy of the first part of add_delayed_ref_head. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/delayed-ref.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 3fa8ea5cbbc6..227094efd050 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -526,6 +526,71 @@ update_existing_head_ref(struct btrfs_delayed_ref_root *delayed_refs, spin_unlock(&existing->lock); } +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) +{ + int count_mod = 1; + int must_insert_reserved = 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 + * should drop the sum in the head node by one. + */ + if (action == BTRFS_UPDATE_DELAYED_HEAD) + count_mod = 0; + else if (action == BTRFS_DROP_DELAYED_REF) + 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. + * + * Once we record must_insert_reserved, switch the action to + * BTRFS_ADD_DELAYED_REF because other special casing is not required. + */ + if (action == BTRFS_ADD_DELAYED_EXTENT) + must_insert_reserved = 1; + else + must_insert_reserved = 0; + + refcount_set(&head_ref->refs, 1); + head_ref->bytenr = bytenr; + head_ref->num_bytes = num_bytes; + head_ref->ref_mod = count_mod; + head_ref->must_insert_reserved = must_insert_reserved; + head_ref->is_data = is_data; + head_ref->is_system = is_system; + head_ref->ref_tree = RB_ROOT; + INIT_LIST_HEAD(&head_ref->ref_add_list); + RB_CLEAR_NODE(&head_ref->href_node); + head_ref->processing = 0; + head_ref->total_ref_mod = count_mod; + head_ref->qgroup_reserved = 0; + head_ref->qgroup_ref_root = 0; + spin_lock_init(&head_ref->lock); + mutex_init(&head_ref->mutex); + + if (qrecord) { + if (ref_root && reserved) { + head_ref->qgroup_ref_root = ref_root; + head_ref->qgroup_reserved = reserved; + } + + 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 -- cgit v1.2.3 From eb86ec73b968b2895ffede893b33bf49bbc9bf5c Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Tue, 24 Apr 2018 17:18:23 +0300 Subject: btrfs: Use init_delayed_ref_head in add_delayed_ref_head Use the newly introduced function when initialising the head_ref in add_delayed_ref_head. No functional changes. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/delayed-ref.c | 63 ++++---------------------------------------------- 1 file changed, 4 insertions(+), 59 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 227094efd050..5d4c39c072a4 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -608,69 +608,14 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, { 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 - * should drop the sum in the head node by one. - */ - if (action == BTRFS_UPDATE_DELAYED_HEAD) - count_mod = 0; - else if (action == BTRFS_DROP_DELAYED_REF) - 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. - * - * Once we record must_insert_reserved, switch the action to - * BTRFS_ADD_DELAYED_REF because other special casing is not required. - */ - if (action == BTRFS_ADD_DELAYED_EXTENT) - must_insert_reserved = 1; - 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; - head_ref->ref_mod = count_mod; - head_ref->must_insert_reserved = must_insert_reserved; - head_ref->is_data = is_data; - head_ref->is_system = is_system; - head_ref->ref_tree = RB_ROOT; - INIT_LIST_HEAD(&head_ref->ref_add_list); - RB_CLEAR_NODE(&head_ref->href_node); - head_ref->processing = 0; - head_ref->total_ref_mod = count_mod; - head_ref->qgroup_reserved = 0; - head_ref->qgroup_ref_root = 0; - spin_lock_init(&head_ref->lock); - mutex_init(&head_ref->mutex); - + init_delayed_ref_head(head_ref, qrecord, bytenr, num_bytes, ref_root, + reserved, action, is_data, is_system); /* Record qgroup extent info if provided */ if (qrecord) { - if (ref_root && reserved) { - head_ref->qgroup_ref_root = ref_root; - head_ref->qgroup_reserved = reserved; - } - - qrecord->bytenr = bytenr; - qrecord->num_bytes = num_bytes; - qrecord->old_roots = NULL; - - if(btrfs_qgroup_trace_extent_nolock(trans->fs_info, + if (btrfs_qgroup_trace_extent_nolock(trans->fs_info, delayed_refs, qrecord)) kfree(qrecord); else @@ -695,7 +640,7 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, } else { if (old_ref_mod) *old_ref_mod = 0; - if (is_data && count_mod < 0) + if (is_data && head_ref->ref_mod < 0) delayed_refs->pending_csums += num_bytes; delayed_refs->num_heads++; delayed_refs->num_heads_ready++; -- cgit v1.2.3 From 2335efafa63f0c675ebb4f8908fff9e972fb8a58 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Tue, 24 Apr 2018 17:18:24 +0300 Subject: btrfs: split delayed ref head initialization and addition add_delayed_ref_head really performed 2 independent operations - initialisting the ref head and adding it to a list. Now that the init part is in a separate function let's complete the separation between both operations. This results in a lot simpler interface for add_delayed_ref_head since the function now deals solely with either adding the newly initialised delayed ref head or merging it into an existing delayed ref head. This results in vastly simplified function signature since 5 arguments are dropped. The only other thing worth mentioning is that due to this split the WARN_ON catching reinit of existing. In this patch the condition is extended such that: qrecord && head_ref->qgroup_ref_root && head_ref->qgroup_reserved is added. This is done because the two qgroup_* prefixed member are set only if both ref_root and reserved are passed. So functionally it's equivalent to the old WARN_ON and allows to remove the two args from add_delayed_ref_head. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/delayed-ref.c | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 5d4c39c072a4..03dec673d12a 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -600,19 +600,15 @@ 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, - u64 bytenr, u64 num_bytes, u64 ref_root, u64 reserved, - int action, int is_data, int is_system, - int *qrecord_inserted_ret, + 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; delayed_refs = &trans->transaction->delayed_refs; - init_delayed_ref_head(head_ref, qrecord, bytenr, num_bytes, ref_root, - reserved, action, is_data, is_system); + /* Record qgroup extent info if provided */ if (qrecord) { if (btrfs_qgroup_trace_extent_nolock(trans->fs_info, @@ -627,7 +623,9 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, 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); @@ -640,8 +638,8 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, } else { if (old_ref_mod) *old_ref_mod = 0; - if (is_data && head_ref->ref_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); @@ -651,6 +649,7 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, *qrecord_inserted_ret = qrecord_inserted; if (new_ref_mod) *new_ref_mod = head_ref->total_ref_mod; + return head_ref; } @@ -722,7 +721,7 @@ 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; @@ -752,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; @@ -761,12 +762,10 @@ 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(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); - ret = insert_delayed_ref(trans, delayed_refs, head_ref, &ref->node); spin_unlock(&delayed_refs->lock); @@ -840,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; @@ -849,9 +850,8 @@ 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(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); ret = insert_delayed_ref(trans, delayed_refs, head_ref, &ref->node); @@ -881,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(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; -- cgit v1.2.3 From 35c8eda12fc69e8a3f67c4615050ca4e76adec32 Mon Sep 17 00:00:00 2001 From: Robbie Ko Date: Tue, 8 May 2018 18:11:37 +0800 Subject: btrfs: incremental send, move allocation until it's needed in orphan_dir_info Move the allocation after the search when it's clear that the new entry will be added. Signed-off-by: Robbie Ko Reviewed-by: Filipe Manana [ update changelog ] Signed-off-by: David Sterba --- fs/btrfs/send.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 6e8184f239e0..29cfc0df1f27 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -2844,12 +2844,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 +2852,16 @@ 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; + rb_link_node(&odi->node, parent, p); rb_insert_color(&odi->node, &sctx->orphan_dirs); return odi; -- cgit v1.2.3 From 0f96f517dcaa58346c32be094aecd610b7d3c008 Mon Sep 17 00:00:00 2001 From: Robbie Ko Date: Tue, 8 May 2018 18:11:38 +0800 Subject: btrfs: incremental send, improve rmdir performance for large directory Currently when checking if a directory can be deleted, we always check if all its children have been processed. Example: A directory with 2,000,000 files was deleted original: 1994m57.071s patch: 1m38.554s [FIX] Instead of checking all children on all calls to can_rmdir(), we keep track of the directory index offset of the child last checked in the last call to can_rmdir(), and then use it as the starting point for future calls to can_rmdir(). Signed-off-by: Robbie Ko Reviewed-by: Filipe Manana [ update changelog ] Signed-off-by: David Sterba --- fs/btrfs/send.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 29cfc0df1f27..c47f62b19226 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 { @@ -2861,6 +2862,7 @@ add_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino) 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); @@ -2916,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. @@ -2930,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; @@ -2957,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; @@ -3258,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) @@ -3275,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: -- cgit v1.2.3 From 7c8a0d363aca1447a0e82e31c54d220e3ecd6a87 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Fri, 27 Apr 2018 12:21:52 +0300 Subject: btrfs: Add assert in __btrfs_del_delalloc_inode The invariant is that when nr_delalloc_inodes is 0 then the root mustn't have any inodes on its delalloc inodes list. Signed-off-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/inode.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 29374d31f1de..a7529827d89c 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1754,6 +1754,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); -- cgit v1.2.3 From 483bce068ea79dde1d4586472759d70cc6ee34c4 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:40 +0300 Subject: btrfs: Make btrfs_init_dummy_trans initialize trans' fs_info field This will be necessary for future cleanups which remove the fs_info argument from some freespace tree functions. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/tests/btrfs-tests.c | 4 +++- fs/btrfs/tests/btrfs-tests.h | 3 ++- fs/btrfs/tests/free-space-tree-tests.c | 2 +- fs/btrfs/tests/qgroup-tests.c | 12 ++++++------ 4 files changed, 12 insertions(+), 9 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/tests/btrfs-tests.c b/fs/btrfs/tests/btrfs-tests.c index 30ed438da2a9..db72b3b6209e 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 a5a0b9500d3e..4c11cffb377c 100644 --- a/fs/btrfs/tests/btrfs-tests.h +++ b/fs/btrfs/tests/btrfs-tests.h @@ -28,7 +28,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/free-space-tree-tests.c b/fs/btrfs/tests/free-space-tree-tests.c index e1f9666c4974..a24ead6ceb2b 100644 --- a/fs/btrfs/tests/free-space-tree-tests.c +++ b/fs/btrfs/tests/free-space-tree-tests.c @@ -482,7 +482,7 @@ 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) { diff --git a/fs/btrfs/tests/qgroup-tests.c b/fs/btrfs/tests/qgroup-tests.c index 39b95783f736..df4bcce20ba5 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; @@ -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; @@ -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; @@ -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; @@ -213,7 +213,7 @@ 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"); ret = btrfs_create_qgroup(NULL, fs_info, BTRFS_FS_TREE_OBJECTID); @@ -314,7 +314,7 @@ 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"); -- cgit v1.2.3 From e4e0711cd96944b47aedf6204427e605674b5e22 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:41 +0300 Subject: btrfs: Remove fs_info argument from add_block_group_free_space We also pass in a transaction handle which has a reference to the fs_info. Just remove the extraneous argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 2 +- fs/btrfs/free-space-tree.c | 2 +- fs/btrfs/free-space-tree.h | 1 - fs/btrfs/tests/free-space-tree-tests.c | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index f206f1a65cc1..c4b5b49b637f 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -10259,7 +10259,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); diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 7019afe6e727..0d4a719c8820 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -1292,9 +1292,9 @@ static int __add_block_group_free_space(struct btrfs_trans_handle *trans, } 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; diff --git a/fs/btrfs/free-space-tree.h b/fs/btrfs/free-space-tree.h index 874b4feecad2..e0abc265dc59 100644 --- a/fs/btrfs/free-space-tree.h +++ b/fs/btrfs/free-space-tree.h @@ -19,7 +19,6 @@ 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, diff --git a/fs/btrfs/tests/free-space-tree-tests.c b/fs/btrfs/tests/free-space-tree-tests.c index a24ead6ceb2b..f3dfd9fc762f 100644 --- a/fs/btrfs/tests/free-space-tree-tests.c +++ b/fs/btrfs/tests/free-space-tree-tests.c @@ -491,7 +491,7 @@ static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize, 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"); goto out; -- cgit v1.2.3 From 9a7e0f9284b14579b9fe5e123711fda31507dfb4 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:42 +0300 Subject: btrfs: Remove fs_info argument from __add_block_group_free_space This function already takes a transaction handle which has a reference to the fs_info. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-tree.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 0d4a719c8820..a1ff604456b7 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); @@ -791,8 +790,7 @@ 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; } @@ -987,8 +985,7 @@ int __add_to_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; } @@ -1274,7 +1271,6 @@ abort: } 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) { @@ -1282,11 +1278,12 @@ 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, trans->fs_info, 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, trans->fs_info, block_group, + path, block_group->key.objectid, block_group->key.offset); } @@ -1311,7 +1308,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); -- cgit v1.2.3 From 2d5cffa1b0d5eca88d01477b27a86f56bd9175b7 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:43 +0300 Subject: btrfs: Remove fs_info argument from __add_to_free_space_tree This function already takes a transaction handle which contains a reference to fs_info. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-tree.c | 14 ++++++-------- fs/btrfs/free-space-tree.h | 1 - fs/btrfs/tests/free-space-tree-tests.c | 26 +++++++++++++------------- 3 files changed, 19 insertions(+), 22 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index a1ff604456b7..501769b941a8 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -976,10 +976,10 @@ out: } 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; @@ -1030,8 +1030,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); @@ -1101,7 +1100,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 - @@ -1126,8 +1125,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; } @@ -1282,8 +1281,7 @@ static int __add_block_group_free_space(struct btrfs_trans_handle *trans, if (ret) return ret; - return __add_to_free_space_tree(trans, trans->fs_info, block_group, - path, + return __add_to_free_space_tree(trans, block_group, path, block_group->key.objectid, block_group->key.offset); } diff --git a/fs/btrfs/free-space-tree.h b/fs/btrfs/free-space-tree.h index e0abc265dc59..7555f64d6731 100644 --- a/fs/btrfs/free-space-tree.h +++ b/fs/btrfs/free-space-tree.h @@ -37,7 +37,6 @@ 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, diff --git a/fs/btrfs/tests/free-space-tree-tests.c b/fs/btrfs/tests/free-space-tree-tests.c index f3dfd9fc762f..6d2b8f736020 100644 --- a/fs/btrfs/tests/free-space-tree-tests.c +++ b/fs/btrfs/tests/free-space-tree-tests.c @@ -274,14 +274,14 @@ static int test_merge_left(struct btrfs_trans_handle *trans, 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"); 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) { @@ -312,7 +312,7 @@ static int test_merge_right(struct btrfs_trans_handle *trans, 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) { @@ -320,7 +320,7 @@ static int test_merge_right(struct btrfs_trans_handle *trans, 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) { @@ -351,14 +351,14 @@ static int test_merge_both(struct btrfs_trans_handle *trans, 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"); 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) { @@ -366,7 +366,7 @@ static int test_merge_both(struct btrfs_trans_handle *trans, 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) { @@ -399,14 +399,14 @@ static int test_merge_none(struct btrfs_trans_handle *trans, 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"); 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) { @@ -414,7 +414,7 @@ static int test_merge_none(struct btrfs_trans_handle *trans, 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) { -- cgit v1.2.3 From 66afee18485932ab1f6000eb37c8605175b5339e Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:44 +0300 Subject: btrfs: Remove fs_info parameter from add_new_free_space_info This function already takes trans handle from where fs_info can be referenced. Remove the redundant parameter. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-tree.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 501769b941a8..4b687e2f97aa 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -44,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; @@ -1067,7 +1066,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; @@ -1277,7 +1276,7 @@ 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, trans->fs_info, block_group, path); + ret = add_new_free_space_info(trans, block_group, path); if (ret) return ret; -- cgit v1.2.3 From 4457c1c702fa1cb2f032bae6dfa0dd2f84ff2b5c Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:45 +0300 Subject: btrfs: Remove fs_info argument from add_new_free_space This function also takes a btrfs_block_group_cache which contains a referene to the fs_info. So use that and remove the extra argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 2 +- fs/btrfs/extent-tree.c | 13 ++++++------- fs/btrfs/free-space-tree.c | 8 +++----- 3 files changed, 10 insertions(+), 13 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 27aa9b58b001..954bfb5054b1 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2832,7 +2832,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, diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index c4b5b49b637f..4d4e064b2897 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -343,8 +343,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 +490,7 @@ next: 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 +508,7 @@ next: } 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; @@ -10162,8 +10162,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); @@ -10300,7 +10299,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); diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 4b687e2f97aa..ecf07fdcf2d8 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -1438,7 +1438,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) { @@ -1452,8 +1451,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++; } @@ -1510,8 +1509,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; -- cgit v1.2.3 From f3f7277995675d632a3e42144e1a4c9b0cc58d44 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:46 +0300 Subject: btrfs: Remove fs_info parameter from remove_block_group_free_space This function always takes a trans handle which contains a reference to the fs_info. Use that and remove the extra argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 2 +- fs/btrfs/free-space-tree.c | 5 ++--- fs/btrfs/free-space-tree.h | 1 - fs/btrfs/tests/free-space-tree-tests.c | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 4d4e064b2897..37ffd4919087 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -10638,7 +10638,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; diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index ecf07fdcf2d8..4a7bef5b8de3 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -1316,10 +1316,9 @@ out: } 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; @@ -1327,7 +1326,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) { diff --git a/fs/btrfs/free-space-tree.h b/fs/btrfs/free-space-tree.h index 7555f64d6731..c4adfd05f34f 100644 --- a/fs/btrfs/free-space-tree.h +++ b/fs/btrfs/free-space-tree.h @@ -21,7 +21,6 @@ int load_free_space_tree(struct btrfs_caching_control *caching_ctl); int add_block_group_free_space(struct btrfs_trans_handle *trans, 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, diff --git a/fs/btrfs/tests/free-space-tree-tests.c b/fs/btrfs/tests/free-space-tree-tests.c index 6d2b8f736020..86955cf8c9be 100644 --- a/fs/btrfs/tests/free-space-tree-tests.c +++ b/fs/btrfs/tests/free-space-tree-tests.c @@ -510,7 +510,7 @@ 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"); goto out; -- cgit v1.2.3 From 719fb4de5508f5fdf230ed4a76f6f830497a8f82 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:47 +0300 Subject: btrfs: Remove fs_info argument from convert_free_space_to_bitmaps This function already takes a transaction handle which contains a reference to fs_info. So use that and remove the extra argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-tree.c | 5 ++--- fs/btrfs/free-space-tree.h | 1 - fs/btrfs/tests/free-space-tree-tests.c | 5 ++--- 3 files changed, 4 insertions(+), 7 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 4a7bef5b8de3..1dea35ac7139 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -177,10 +177,10 @@ static void le_bitmap_set(unsigned long *map, unsigned int start, int len) } 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; @@ -477,8 +477,7 @@ 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, diff --git a/fs/btrfs/free-space-tree.h b/fs/btrfs/free-space-tree.h index c4adfd05f34f..72ff743611ff 100644 --- a/fs/btrfs/free-space-tree.h +++ b/fs/btrfs/free-space-tree.h @@ -43,7 +43,6 @@ int __remove_from_free_space_tree(struct btrfs_trans_handle *trans, 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, diff --git a/fs/btrfs/tests/free-space-tree-tests.c b/fs/btrfs/tests/free-space-tree-tests.c index 86955cf8c9be..d3983ae6173a 100644 --- a/fs/btrfs/tests/free-space-tree-tests.c +++ b/fs/btrfs/tests/free-space-tree-tests.c @@ -137,7 +137,7 @@ static int check_free_space_extents(struct btrfs_trans_handle *trans, 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"); return ret; @@ -498,8 +498,7 @@ static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize, } 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"); goto out; -- cgit v1.2.3 From 5296c2bf51700451062dda22cd03c01d0104ccbc Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:48 +0300 Subject: btrfs: Remove fs_info parameter from convert_free_space_to_extents This function always takes a transaction handle which contains a reference to fs_info. So use that and kill the extra argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-tree.c | 5 ++--- fs/btrfs/free-space-tree.h | 1 - fs/btrfs/tests/free-space-tree-tests.c | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 1dea35ac7139..3937a3baf2f5 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -316,10 +316,10 @@ out: } 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; @@ -480,8 +480,7 @@ static int update_free_space_extent_count(struct btrfs_trans_handle *trans, 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: diff --git a/fs/btrfs/free-space-tree.h b/fs/btrfs/free-space-tree.h index 72ff743611ff..7624f2a1f8e7 100644 --- a/fs/btrfs/free-space-tree.h +++ b/fs/btrfs/free-space-tree.h @@ -46,7 +46,6 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans, 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/tests/free-space-tree-tests.c b/fs/btrfs/tests/free-space-tree-tests.c index d3983ae6173a..176ee997319c 100644 --- a/fs/btrfs/tests/free-space-tree-tests.c +++ b/fs/btrfs/tests/free-space-tree-tests.c @@ -131,7 +131,7 @@ 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"); return ret; -- cgit v1.2.3 From 690d76828a000aadb99ea9d3928d85c8f63b2f82 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:49 +0300 Subject: btrfs: Remove fs_info argument from update_free_space_extent_count This function already takes a transaction handle which has a reference to the fs_info. So use that and remove the extra argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-tree.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 3937a3baf2f5..4f36016b2476 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -449,7 +449,6 @@ out: } 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) @@ -462,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; @@ -683,7 +683,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: @@ -770,7 +770,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: @@ -965,7 +965,7 @@ insert: 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: -- cgit v1.2.3 From 85a7ef130cf5e31da4a4fa22120c1e32f4370cae Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:50 +0300 Subject: btrfs: Remove fs_info argument from modify_free_space_bitmap This function already takes a transaction which has a reference to the fs_info. So use that and remove the extra argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-tree.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 4f36016b2476..e314daeb5072 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -577,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; @@ -799,8 +798,8 @@ int __remove_from_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, 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); @@ -994,8 +993,8 @@ 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); -- cgit v1.2.3 From 5cb1782213006868cf29287f8ac4160491f740f0 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:51 +0300 Subject: btrfs: Remove fs_info argument from add_free_space_extent This function always takes a transaction handle which references the fs_info structure. So use that and remove the extra argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-tree.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index e314daeb5072..4d6f8cbb77d7 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -844,12 +844,11 @@ out: } 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; @@ -996,8 +995,8 @@ int __add_to_free_space_tree(struct btrfs_trans_handle *trans, 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); } } -- cgit v1.2.3 From e581168d1f56ae64e29bd715d34455980212f18d Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:52 +0300 Subject: btrfs: Remove fs_info argument from remove_free_space_extent This function takes a transaction handle which already has a reference to the fs_info. Use it and remove the extra argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-tree.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 4d6f8cbb77d7..f0260cfcf7eb 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -690,12 +690,11 @@ out: } 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; @@ -801,8 +800,8 @@ int __remove_from_free_space_tree(struct btrfs_trans_handle *trans, 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); } } -- cgit v1.2.3 From c31683a6efe5517043c23eada0647c1535f865fe Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:53 +0300 Subject: btrfs: Remove fs_info argument from __remove_from_free_space_tree This function takes a transaction handle which holds a reference to fs_info. So use that and remove the extra argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-tree.c | 8 ++++---- fs/btrfs/free-space-tree.h | 1 - fs/btrfs/tests/free-space-tree-tests.c | 16 ++++++++-------- 3 files changed, 12 insertions(+), 13 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index f0260cfcf7eb..d9d464f0c724 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -776,7 +776,6 @@ out: } 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) { @@ -790,7 +789,8 @@ int __remove_from_free_space_tree(struct btrfs_trans_handle *trans, 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); @@ -830,8 +830,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); diff --git a/fs/btrfs/free-space-tree.h b/fs/btrfs/free-space-tree.h index 7624f2a1f8e7..ca3ad610a8d0 100644 --- a/fs/btrfs/free-space-tree.h +++ b/fs/btrfs/free-space-tree.h @@ -39,7 +39,6 @@ int __add_to_free_space_tree(struct btrfs_trans_handle *trans, 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, diff --git a/fs/btrfs/tests/free-space-tree-tests.c b/fs/btrfs/tests/free-space-tree-tests.c index 176ee997319c..cb92868eec15 100644 --- a/fs/btrfs/tests/free-space-tree-tests.c +++ b/fs/btrfs/tests/free-space-tree-tests.c @@ -170,7 +170,7 @@ 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) { @@ -194,7 +194,7 @@ 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"); @@ -217,7 +217,7 @@ 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); @@ -243,7 +243,7 @@ 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) { @@ -266,7 +266,7 @@ 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) { @@ -304,7 +304,7 @@ 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) { @@ -343,7 +343,7 @@ 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) { @@ -391,7 +391,7 @@ 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) { -- cgit v1.2.3 From 25a356d3f69fe5a1f6477227b9c4f853f763fe78 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:54 +0300 Subject: btrfs: Remove fs_info argument from remove_from_free_space_tree This function alreay takes a transaction handle which holds a reference to the fs_info. Use that and remove the extra argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 6 ++---- fs/btrfs/free-space-tree.c | 5 ++--- fs/btrfs/free-space-tree.h | 1 - 3 files changed, 4 insertions(+), 8 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 37ffd4919087..70f6dc7ca010 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -8173,8 +8173,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; @@ -8256,8 +8255,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, btrfs_mark_buffer_dirty(leaf); btrfs_free_path(path); - ret = remove_from_free_space_tree(trans, fs_info, ins->objectid, - num_bytes); + ret = remove_from_free_space_tree(trans, ins->objectid, num_bytes); if (ret) return ret; diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index d9d464f0c724..36da2c3a28f6 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -806,14 +806,13 @@ int __remove_from_free_space_tree(struct btrfs_trans_handle *trans, } 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(); @@ -822,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; diff --git a/fs/btrfs/free-space-tree.h b/fs/btrfs/free-space-tree.h index ca3ad610a8d0..95916320abec 100644 --- a/fs/btrfs/free-space-tree.h +++ b/fs/btrfs/free-space-tree.h @@ -26,7 +26,6 @@ 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 -- cgit v1.2.3 From e7355e501df48987a6646cb4ae6b0eafb4f48149 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:55 +0300 Subject: btrfs: Remove fs_info argument from add_to_free_space_tree This function takes a transaction handle which already contains a reference to the fs_info. So use it and remove the extra function argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 2 +- fs/btrfs/free-space-tree.c | 5 ++--- fs/btrfs/free-space-tree.h | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 70f6dc7ca010..5be54cedb56f 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -7127,7 +7127,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; diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 36da2c3a28f6..494ff0815988 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -999,14 +999,13 @@ int __add_to_free_space_tree(struct btrfs_trans_handle *trans, } 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(); @@ -1015,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; diff --git a/fs/btrfs/free-space-tree.h b/fs/btrfs/free-space-tree.h index 95916320abec..3133651d7d70 100644 --- a/fs/btrfs/free-space-tree.h +++ b/fs/btrfs/free-space-tree.h @@ -23,7 +23,6 @@ int add_block_group_free_space(struct btrfs_trans_handle *trans, int remove_block_group_free_space(struct btrfs_trans_handle *trans, 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, u64 start, u64 size); -- cgit v1.2.3 From ffa9a9ef2f08c613a798837267e0b4f6c89bf9ad Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 10 May 2018 15:44:56 +0300 Subject: btrfs: Remove fs_info argument from populate_free_space_tree This function always takes a transaction handle which contains a reference to the fs_info. Use that and remove the extra argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/free-space-tree.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index 494ff0815988..b5950aacd697 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -1039,10 +1039,9 @@ out: * 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; @@ -1102,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) { @@ -1158,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); -- cgit v1.2.3 From 21a852b01820bdb543df2728cf2f39ecf565255d Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Fri, 11 May 2018 13:35:25 +0800 Subject: btrfs: Move btrfs_check_super_valid() to avoid forward declaration Move btrfs_check_super_valid() before its single caller to avoid forward declaration. Though such code motion is not recommended as it pollutes git history, in this case the following patches would need to add new forward declarations for static functions that we want to avoid. Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 299 ++++++++++++++++++++++++++--------------------------- 1 file changed, 149 insertions(+), 150 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 47dbbe496253..6dff0028d69a 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); @@ -2441,6 +2440,155 @@ out: return ret; } +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; +} + int open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_devices, char *options) @@ -3973,155 +4121,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 */ -- cgit v1.2.3 From 069ec957c35e03ba3beb40973379899cfdbf1ee1 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Fri, 11 May 2018 13:35:26 +0800 Subject: btrfs: Refactor btrfs_check_super_valid Refactor btrfs_check_super_valid: 1) Rename it to btrfs_validate_mount_super() Now it's more obvious when the function should be called. 2) Extract core check routine into validate_super() Later write time check can reuse it, and if needed, we could also use validate_super() to check each super block. 3) Add more comments about btrfs_validate_mount_super() Mostly about what it doesn't check and when it should be called. Signed-off-by: Qu Wenruo Reviewed-by: David Sterba [ rename to validate_super ] Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 6dff0028d69a..eff867370036 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2440,9 +2440,19 @@ out: return ret; } -static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info) +/* + * 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) { - struct btrfs_super_block *sb = fs_info->super_copy; u64 nodesize = btrfs_super_nodesize(sb); u64 sectorsize = btrfs_super_sectorsize(sb); int ret = 0; @@ -2545,7 +2555,8 @@ static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info) ret = -EINVAL; } - if (btrfs_super_bytenr(sb) != BTRFS_SUPER_INFO_OFFSET) { + 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; @@ -2589,6 +2600,16 @@ static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info) 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); +} + int open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_devices, char *options) @@ -2814,7 +2835,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; -- cgit v1.2.3 From 75cb857d2618cca810b8bf13ba5b2ceaaf26ba3d Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Fri, 11 May 2018 13:35:27 +0800 Subject: btrfs: Do super block verification before writing it to disk There are already 2 reports about strangely corrupted super blocks, where csum still matches but extra garbage gets slipped into super block. The corruption would looks like: ------ superblock: bytenr=65536, device=/dev/sdc1 --------------------------------------------------------- csum_type 41700 (INVALID) csum 0x3b252d3a [match] bytenr 65536 flags 0x1 ( WRITTEN ) magic _BHRfS_M [match] ... incompat_flags 0x5b22400000000169 ( MIXED_BACKREF | COMPRESS_LZO | BIG_METADATA | EXTENDED_IREF | SKINNY_METADATA | unknown flag: 0x5b22400000000000 ) ... ------ Or ------ superblock: bytenr=65536, device=/dev/mapper/x --------------------------------------------------------- csum_type 35355 (INVALID) csum_size 32 csum 0xf0dbeddd [match] bytenr 65536 flags 0x1 ( WRITTEN ) magic _BHRfS_M [match] ... incompat_flags 0x176d200000000169 ( MIXED_BACKREF | COMPRESS_LZO | BIG_METADATA | EXTENDED_IREF | SKINNY_METADATA | unknown flag: 0x176d200000000000 ) ------ Obviously, csum_type and incompat_flags get some garbage, but its csum still matches, which means kernel calculates the csum based on corrupted super block memory. And after manually fixing these values, the filesystem is completely healthy without any problem exposed by btrfs check. Although the cause is still unknown, at least detect it and prevent further corruption. Both reports have same symptoms, there's an overwrite on offset 192 of the superblock, by 4 bytes. The superblock structure is not allocated or freed and stays in the memory for the whole filesystem lifetime, so it's not a use-after-free kind of error on someone else's leaked page. As a vague point for the problable cause is mentioning of other system freezing related to graphic card drivers. Reported-by: Ken Swenson Reported-by: Ben Parsons <9parsonsb@gmail.com> Signed-off-by: Qu Wenruo Reviewed-by: David Sterba [ add brief analysis of the reports ] Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index eff867370036..a16385091572 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2610,6 +2610,41 @@ 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) @@ -3770,6 +3805,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++; -- cgit v1.2.3 From 7a1b1e70280a9e0185a8ef80a8b4dbf4dc87772a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 13 May 2018 19:03:18 +0100 Subject: btrfs: take the last remnants of ->d_fsdata use out [spotted while going through ->d_fsdata handling around d_splice_alias(); don't really care which tree that goes through] The only thing even looking at ->d_fsdata in there (since 2012) had been kfree(dentry->d_fsdata) in btrfs_dentry_delete(). Which, incidentally, is all btrfs_dentry_delete() does. Signed-off-by: Al Viro Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a7529827d89c..6757fe136177 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6045,11 +6045,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) { @@ -10844,5 +10839,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, }; -- cgit v1.2.3 From b6debf15d4753e0075a85ff119b0bb3c7e172782 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 14 May 2018 09:38:12 +0800 Subject: btrfs: qgroup: Search commit root for rescan to avoid missing extent When doing qgroup rescan using the following script (modified from btrfs/017 test case), we can sometimes hit qgroup corruption. ------ umount $dev &> /dev/null umount $mnt &> /dev/null mkfs.btrfs -f -n 64k $dev mount $dev $mnt extent_size=8192 xfs_io -f -d -c "pwrite 0 $extent_size" $mnt/foo > /dev/null btrfs subvolume snapshot $mnt $mnt/snap xfs_io -f -c "reflink $mnt/foo" $mnt/foo-reflink > /dev/null xfs_io -f -c "reflink $mnt/foo" $mnt/snap/foo-reflink > /dev/null xfs_io -f -c "reflink $mnt/foo" $mnt/snap/foo-reflink2 > /dev/unll btrfs quota enable $mnt # -W is the new option to only wait rescan while not starting new one btrfs quota rescan -W $mnt btrfs qgroup show -prce $mnt umount $mnt # Need to patch btrfs-progs to report qgroup mismatch as error btrfs check $dev || _fail ------ For fast machine, we can hit some corruption which missed accounting tree blocks: ------ qgroupid rfer excl max_rfer max_excl parent child -------- ---- ---- -------- -------- ------ ----- 0/5 8.00KiB 0.00B none none --- --- 0/257 8.00KiB 0.00B none none --- --- ------ This is due to the fact that we're always searching commit root for btrfs_find_all_roots() at qgroup_rescan_leaf(), but the leaf we get is from current transaction, not commit root. And if our tree blocks get modified in current transaction, we won't find any owner in commit root, thus causing the corruption. Fix it by searching commit root for extent tree for qgroup_rescan_leaf(). Reported-by: Nikolay Borisov Signed-off-by: Qu Wenruo Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/qgroup.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 9fdac5b46aa9..641ef2679bb1 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -2590,7 +2590,6 @@ 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; int slot; int ret; @@ -2625,7 +2624,6 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path, 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,7 +2662,6 @@ out: btrfs_tree_read_unlock_blocking(scratch_leaf); free_extent_buffer(scratch_leaf); } - btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem); return ret; } @@ -2681,6 +2678,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)) { -- cgit v1.2.3 From ff3d27a048d926b3920ccdb75d98788c567cae0d Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 14 May 2018 09:38:13 +0800 Subject: btrfs: qgroup: Finish rescan when hit the last leaf of extent tree Under the following case, qgroup rescan can double account cowed tree blocks: In this case, extent tree only has one tree block. - | transid=5 last committed=4 | btrfs_qgroup_rescan_worker() | |- btrfs_start_transaction() | | transid = 5 | |- qgroup_rescan_leaf() | |- btrfs_search_slot_for_read() on extent tree | Get the only extent tree block from commit root (transid = 4). | Scan it, set qgroup_rescan_progress to the last | EXTENT/META_ITEM + 1 | now qgroup_rescan_progress = A + 1. | | fs tree get CoWed, new tree block is at A + 16K | transid 5 get committed - | transid=6 last committed=5 | btrfs_qgroup_rescan_worker() | btrfs_qgroup_rescan_worker() | |- btrfs_start_transaction() | | transid = 5 | |- qgroup_rescan_leaf() | |- btrfs_search_slot_for_read() on extent tree | Get the only extent tree block from commit root (transid = 5). | scan it using qgroup_rescan_progress (A + 1). | found new tree block beyong A, and it's fs tree block, | account it to increase qgroup numbers. - In above case, tree block A, and tree block A + 16K get accounted twice, while qgroup rescan should stop when it already reach the last leaf, other than continue using its qgroup_rescan_progress. Such case could happen by just looping btrfs/017 and with some possibility it can hit such double qgroup accounting problem. Fix it by checking the path to determine if we should finish qgroup rescan, other than relying on next loop to exit. Reported-by: Nikolay Borisov Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/qgroup.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 641ef2679bb1..457219d6a16b 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -2579,6 +2579,21 @@ out: 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. @@ -2591,6 +2606,7 @@ qgroup_rescan_leaf(struct btrfs_fs_info *fs_info, struct btrfs_path *path, struct extent_buffer *scratch_leaf = NULL; struct ulist *roots = NULL; u64 num_bytes; + bool done; int slot; int ret; @@ -2619,6 +2635,7 @@ 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); @@ -2663,6 +2680,8 @@ out: free_extent_buffer(scratch_leaf); } + if (done && !ret) + ret = 1; return ret; } -- cgit v1.2.3 From 110a21feedd78d398598d91be57db60e19b76fe0 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Feb 2018 15:43:18 +0100 Subject: btrfs: introduce conditional wakeup helpers Add convenience wrappers for the waitqueue management that involves memory barriers to prevent deadlocks. The helpers will let us remove barriers and the necessary comments in several places. Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 954bfb5054b1..3d6b2dc86c8f 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3755,4 +3755,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 -- cgit v1.2.3 From 3d3a2e610ea5e7c6d4f9481ecce5d8e2d8317843 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 24 Apr 2018 14:53:56 +0200 Subject: btrfs: add barriers to btrfs_sync_log before log_commit_wait wakeups Currently the code assumes that there's an implied barrier by the sequence of code preceding the wakeup, namely the mutex unlock. As Nikolay pointed out: I think this is wrong (not your code) but the original assumption that the RELEASE semantics provided by mutex_unlock is sufficient. According to memory-barriers.txt: Section 'LOCK ACQUISITION FUNCTIONS' states: (2) RELEASE operation implication: Memory operations issued before the RELEASE will be completed before the RELEASE operation has completed. Memory operations issued after the RELEASE *may* be completed before the RELEASE operation has completed. (I've bolded the may portion) The example given there: As an example, consider the following: *A = a; *B = b; ACQUIRE *C = c; *D = d; RELEASE *E = e; *F = f; The following sequence of events is acceptable: ACQUIRE, {*F,*A}, *E, {*C,*D}, *B, RELEASE So if we assume that *C is modifying the flag which the waitqueue is checking, and *E is the actual wakeup, then those accesses can be re-ordered... IMHO this code should be considered broken... --- To be on the safe side, add the barriers. The synchronization logic around log using the mutexes and several other threads does not make it easy to reason for/against the barrier. CC: Nikolay Borisov Link: https://lkml.kernel.org/r/6ee068d8-1a69-3728-00d1-d86293d43c9f@suse.com Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/tree-log.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 8f23a94dab77..2009cea65d89 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -3116,8 +3116,11 @@ out_wake_log_root: mutex_unlock(&log_root_tree->log_mutex); /* - * The barrier before waitqueue_active is implied by mutex_unlock + * The barrier before waitqueue_active 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. */ + smp_mb(); if (waitqueue_active(&log_root_tree->log_commit_wait[index2])) wake_up(&log_root_tree->log_commit_wait[index2]); out: @@ -3128,8 +3131,11 @@ out: mutex_unlock(&root->log_mutex); /* - * The barrier before waitqueue_active is implied by mutex_unlock + * The barrier before waitqueue_active 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. */ + smp_mb(); if (waitqueue_active(&root->log_commit_wait[index1])) wake_up(&root->log_commit_wait[index1]); return ret; -- cgit v1.2.3 From 093258e6ebaf178bb25da514f0d1f744968cc900 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Feb 2018 16:15:17 +0100 Subject: btrfs: replace waitqueue_actvie with cond_wake_up Use the wrappers and reduce the amount of low-level details about the waitqueue management. Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/compression.c | 7 +------ fs/btrfs/delayed-inode.c | 9 +++------ fs/btrfs/dev-replace.c | 10 ++++------ fs/btrfs/extent-tree.c | 7 +------ fs/btrfs/inode.c | 9 +++------ fs/btrfs/locking.c | 34 +++++++++++----------------------- fs/btrfs/ordered-data.c | 14 ++++---------- fs/btrfs/transaction.c | 7 +------ fs/btrfs/tree-log.c | 34 ++++++++++++---------------------- 9 files changed, 40 insertions(+), 91 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 1061575a7d25..d3e447b45bf7 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/delayed-inode.c b/fs/btrfs/delayed-inode.c index a8d492dbd3e7..fe6caa7e698b 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/dev-replace.c b/fs/btrfs/dev-replace.c index 12f703e127dd..89946285203d 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -1009,9 +1009,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) @@ -1022,9 +1022,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/extent-tree.c b/fs/btrfs/extent-tree.c index 5be54cedb56f..fa2ed14532c1 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -11081,12 +11081,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/inode.c b/fs/btrfs/inode.c index 6757fe136177..563e63fa2fce 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1156,13 +1156,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); diff --git a/fs/btrfs/locking.c b/fs/btrfs/locking.c index e4faefac9d16..1da768e5ef75 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/ordered-data.c b/fs/btrfs/ordered-data.c index 6db8bb2f2c28..2e1a1694a33d 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 @@ have_entry: 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/transaction.c b/fs/btrfs/transaction.c index c944b4769e3c..ff841abb756e 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) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 2009cea65d89..f8220ec02036 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,13 +3110,11 @@ out_wake_log_root: mutex_unlock(&log_root_tree->log_mutex); /* - * The barrier before waitqueue_active 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. + * 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. */ - smp_mb(); - 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); @@ -3131,13 +3123,11 @@ out: mutex_unlock(&root->log_mutex); /* - * The barrier before waitqueue_active 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. + * 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. */ - smp_mb(); - 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; } -- cgit v1.2.3 From 20a68004022d5b894efdf5959ebd538b8e91ec73 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Fri, 27 Apr 2018 14:36:24 +0300 Subject: btrfs: Unexport and rename btrfs_invalidate_inodes This function is no longer used outside of inode.c so just make it static. At the same time give a more becoming name, since it's not really invalidating the inodes but just calling d_prune_alias. Last, but not least - move the function above the sole caller to avoid introducing yet-another-pointless forward declaration. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Reviewed-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 1 - fs/btrfs/inode.c | 129 ++++++++++++++++++++++++++++--------------------------- 2 files changed, 65 insertions(+), 65 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 3d6b2dc86c8f..bfa96697209a 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3231,7 +3231,6 @@ 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, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 563e63fa2fce..766bd286869d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4379,6 +4379,70 @@ out: 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); @@ -4505,7 +4569,7 @@ out_up_write: spin_unlock(&dest->root_item_lock); } else { d_invalidate(dentry); - btrfs_invalidate_inodes(dest); + btrfs_prune_dentries(dest); ASSERT(dest->send_in_progress == 0); /* the last ref */ @@ -5818,69 +5882,6 @@ static void inode_tree_del(struct inode *inode) } } -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); - } - spin_unlock(&root->inode_lock); -} static int btrfs_init_locked_inode(struct inode *inode, void *p) { -- cgit v1.2.3 From b6ed73bcb1c4c8c742452a4cc03868ce4e7455c8 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 12 Apr 2018 10:29:24 +0800 Subject: btrfs: reduce uuid_mutex critical section while scanning devices The generic block device lookup or cleanup does not need the uuid mutex, that's only for the device_list_add. Signed-off-by: Anand Jain Reviewed-by: David Sterba [ update changelog ] Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index ebc81766fc86..c1d09da40d73 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1232,31 +1232,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; } -- cgit v1.2.3 From 41a52a0f1bdcb3a17e2b384d6b6368a9590f9a58 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 12 Apr 2018 10:29:31 +0800 Subject: btrfs: use existing cur_devices, cleanup btrfs_rm_device Instead of de-referencing the device->fs_devices use cur_devices which points to the same fs_devices and does not change. Signed-off-by: Anand Jain Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index c1d09da40d73..a382d53c560a 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2015,20 +2015,25 @@ 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_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_devices, device); } -- cgit v1.2.3 From 3dd0f7a3644eed9017f62ccd0067a91e8260d097 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 12 Apr 2018 10:29:32 +0800 Subject: btrfs: document uuid_mutex uasge in read_chunk_tree read_chunk_tree() calls read_one_dev(), but for seed device we have to search the fs_uuids list, so we need the uuid_mutex. Add a comment comment, so that we can improve this part. Signed-off-by: Anand Jain Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index a382d53c560a..6eec1e3f9083 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -6932,6 +6932,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); -- cgit v1.2.3 From 542c5908abfe84f7b4c1717492ecc92ea0ea328d Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 12 Apr 2018 10:29:34 +0800 Subject: btrfs: replace uuid_mutex by device_list_mutex in btrfs_open_devices btrfs_open_devices() is using the uuid_mutex, but as btrfs_open_devices is just limited to openning all the devices under for given fsid, so we don't need uuid_mutex. Instead it should hold the device_list_mutex as it updates the members of the btrfs_fs_devices and btrfs_device and not the whole fs_devs list. Signed-off-by: Anand Jain Reviewed-by: David Sterba [ update changelog ] Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 6eec1e3f9083..271d9be6e1da 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1146,7 +1146,7 @@ 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; @@ -1154,7 +1154,8 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, list_sort(NULL, &fs_devices->devices, devid_cmp); ret = open_fs_devices(fs_devices, flags, holder); } - mutex_unlock(&uuid_mutex); + mutex_unlock(&fs_devices->device_list_mutex); + return ret; } -- cgit v1.2.3 From b25e59e2b2ee394a2a7e981b99cbd92ef6c8be85 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 12 Apr 2018 10:29:36 +0800 Subject: btrfs: drop uuid_mutex in btrfs_dev_replace_finishing btrfs_dev_replace_finishing updates devices (soruce and target) which are within the btrfs_fs_devices::devices or withint the cloned seed devices (btrfs_fs_devices::seed::devices), so we don't need the global uuid_mutex. The device replace context is also locked by its own locks. Signed-off-by: Anand Jain Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/dev-replace.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 89946285203d..e2ba0419297a 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -609,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); @@ -636,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); @@ -687,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); -- cgit v1.2.3 From ab5c2f65debc23d6f76cbb7e743ff4d10e66dce0 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 12 Apr 2018 10:29:37 +0800 Subject: btrfs: drop uuid_mutex in btrfs_destroy_dev_replace_tgtdev Delete the uuid_mutex lock here as this thread accesses the btrfs_fs_devices::devices only (counters or called functions do a list traversal). And the device_list_mutex lock is already taken. Signed-off-by: Anand Jain Reviewed-by: David Sterba [ update changelog ] Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 271d9be6e1da..28715e4c3097 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2152,7 +2152,6 @@ 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); WARN_ON(!tgtdev); mutex_lock(&fs_info->fs_devices->device_list_mutex); @@ -2168,7 +2167,6 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, list_del_rcu(&tgtdev->dev_list); mutex_unlock(&fs_info->fs_devices->device_list_mutex); - mutex_unlock(&uuid_mutex); /* * The update_dev_time() with in btrfs_scratch_superblocks() -- cgit v1.2.3 From d9a071f0086d22a6bdb57dc5ecbbf65fa20b6e22 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 12 Apr 2018 10:29:38 +0800 Subject: btrfs: use common variable for fs_devices in btrfs_destroy_dev_replace_tgtdev Use a local btrfs_fs_devices variable to access the structure. Signed-off-by: Anand Jain Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 28715e4c3097..8ed90107b727 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2152,21 +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) { + 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(&fs_devices->device_list_mutex); /* * The update_dev_time() with in btrfs_scratch_superblocks() -- cgit v1.2.3 From 7b6a221e5b21fdd4fd0f8a2ebf3f251338a10fea Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Mar 2018 18:40:21 +0200 Subject: btrfs: rename btrfs_update_iflags to reflect which flags it touches The btrfs inode flag flavour is now simply called 'inode flags' and the vfs inode are i_flags. Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 2 +- fs/btrfs/inode.c | 4 ++-- fs/btrfs/ioctl.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index bfa96697209a..71aecf0b7cf5 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3249,7 +3249,7 @@ 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, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 766bd286869d..cbcda4e296f8 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3929,7 +3929,7 @@ cache_acl: break; } - btrfs_update_iflags(inode); + btrfs_sync_inode_flags_to_i_flags(inode); return 0; make_bad: @@ -6458,7 +6458,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, diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 48e2ddff32bd..47e7e2822c60 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -136,7 +136,7 @@ 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); unsigned int new_fl = 0; @@ -317,7 +317,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); -- cgit v1.2.3 From 1905a0f7c7de3cec4a61b9f0053da8cd07b512bf Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Mar 2018 18:52:15 +0200 Subject: btrfs: rename btrfs_mask_flags to reflect which flags it touches The FS_*_FL flags cannot be easily identified by a variable name prefix but we still need to recognize them so the 'fsflags' should be closer to the naming scheme but again the 'fs' part sounds like it's a filesystem flag. I don't have a better idea for now. Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 47e7e2822c60..f5b83b4cc6bf 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -93,11 +93,12 @@ 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); @@ -218,7 +219,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) i_oldflags = inode->i_flags; mode = inode->i_mode; - flags = btrfs_mask_flags(inode->i_mode, flags); + flags = btrfs_mask_fsflags_for_type(inode, flags); oldflags = btrfs_flags_to_ioctl(ip->flags); if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) { -- cgit v1.2.3 From 5ba76abfb233661f4d890b7069aacc65aa65e34c Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Mar 2018 18:52:15 +0200 Subject: btrfs: rename check_flags to reflect which flags it touches The FS_*_FL flags cannot be easily identified by a prefix but we still need to recognize them so the 'fsflags' should be closer to the naming scheme but again the 'fs' part sounds like it's a filesystem flag. I don't have a better idea for now. Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index f5b83b4cc6bf..ba62dc3d59ec 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -168,7 +168,8 @@ static int btrfs_ioctl_getflags(struct file *file, void __user *arg) 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 | \ @@ -205,7 +206,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) if (copy_from_user(&flags, arg, sizeof(flags))) return -EFAULT; - ret = check_flags(flags); + ret = check_fsflags(flags); if (ret) return ret; -- cgit v1.2.3 From a157d4fd81dc7b7d04b6b5fc1a8e063dba2ffe5a Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Mar 2018 19:12:25 +0200 Subject: btrfs: rename btrfs_flags_to_ioctl to reflect which flags it touches Converts btrfs_inode::flags to the FS_*_FL flags. Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index ba62dc3d59ec..47c1b96df099 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -105,9 +105,10 @@ static unsigned int btrfs_mask_fsflags_for_type(struct inode *inode, } /* - * 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; @@ -161,7 +162,7 @@ void btrfs_sync_inode_flags_to_i_flags(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); + unsigned int flags = btrfs_inode_flags_to_fsflags(ip->flags); if (copy_to_user(arg, &flags, sizeof(flags))) return -EFAULT; @@ -221,7 +222,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) mode = inode->i_mode; flags = btrfs_mask_fsflags_for_type(inode, flags); - oldflags = btrfs_flags_to_ioctl(ip->flags); + oldflags = btrfs_inode_flags_to_fsflags(ip->flags); if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { if (!capable(CAP_LINUX_IMMUTABLE)) { ret = -EPERM; -- cgit v1.2.3 From 19f93b3cd8c24f84e752faabf0ed9026f9377da2 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Mar 2018 19:42:05 +0200 Subject: btrfs: add helpers for FS_XFLAG_* conversion Preparatory work for the FS_IOC_FSGETXATTR ioctl, basic conversions and checking helpers. Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 47c1b96df099..054fa20ef193 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -338,6 +338,38 @@ 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; +} + static int btrfs_ioctl_getversion(struct file *file, int __user *arg) { struct inode *inode = file_inode(file); -- cgit v1.2.3 From e4202ac927eb0994f18e1305fe46c34341334f8a Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Mar 2018 19:51:16 +0200 Subject: btrfs: add FS_IOC_FSGETXATTR ioctl The new ioctl is an extension to the FS_IOC_GETFLAGS and adds new flags and is extensible. This patch allows to return the xflags portion of the fsxattr structure, other items have no meaning for btrfs or can be added later. The original patch was written by Chandan Jay Sharma but was incomplete and no further revision has been sent. Several cleanups were necessary to avoid confusion with other ioctls, as we have another flavor of flags. Based-on-patches-by: Chandan Jay Sharma Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 054fa20ef193..c3100f3988e2 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -370,6 +370,24 @@ static int check_xflags(unsigned int flags) 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_getversion(struct file *file, int __user *arg) { struct inode *inode = file_inode(file); @@ -5409,6 +5427,8 @@ 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); } return -ENOTTY; -- cgit v1.2.3 From 025f2121488ec4362394c691d3e3b3576bd7868a Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 26 Mar 2018 19:51:16 +0200 Subject: btrfs: add FS_IOC_FSSETXATTR ioctl The new ioctl is an extension to the FS_IOC_SETFLAGS and adds new flags and is extensible. Don't get fooled by the XATTR in the name, it does not have anything in common with the extended attributes, incidentally also abbreviated as XATTRs. This patch allows to set the xflags portion of the fsxattr structure, other items have no meaning and non-zero values will result in EOPNOTSUPP. Currently supported xflags: - APPEND - IMMUTABLE - NOATIME - NODUMP - SYNC The structure of btrfs_ioctl_fssetxattr copies btrfs_ioctl_setflags but is simpler on the flag setting side. The original patch was written by Chandan Jay Sharma but was incomplete and no further revision has been sent. Based-on-patches-by: Chandan Jay Sharma Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index c3100f3988e2..28a13b3c7503 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -388,6 +388,98 @@ static int btrfs_ioctl_fsgetxattr(struct file *file, void __user *arg) 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); @@ -5429,6 +5521,8 @@ long btrfs_ioctl(struct file *file, unsigned int 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); } return -ENOTTY; -- cgit v1.2.3 From 5c57b8b6a496641172328c4b71697656de8cebd5 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 23 Apr 2018 15:45:18 +0200 Subject: btrfs: unify naming of flags variables for SETFLAGS and XFLAGS * The simple 'flags' refer to the btrfs inode * ... that's in 'binode * the FS_*_FL variables are 'fsflags' * the old copies of the variable are prefixed by 'old_' * Struct inode flags contain 'i_flags'. Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 106 +++++++++++++++++++++++++++---------------------------- 1 file changed, 53 insertions(+), 53 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 28a13b3c7503..ad1752bc80a7 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -140,18 +140,18 @@ static unsigned int btrfs_inode_flags_to_fsflags(unsigned int flags) */ 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, @@ -161,8 +161,8 @@ void btrfs_sync_inode_flags_to_i_flags(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_inode_flags_to_fsflags(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; @@ -189,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)) @@ -204,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_fsflags(flags); + ret = check_fsflags(fsflags); if (ret) return ret; @@ -217,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_fsflags_for_type(inode, flags); - oldflags = btrfs_inode_flags_to_fsflags(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. @@ -262,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 { /* @@ -273,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; } } @@ -285,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) @@ -311,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); @@ -328,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: -- cgit v1.2.3 From 6dac13f8e233e50b69df831f6e598a1efb6d3aaa Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Wed, 16 May 2018 10:51:26 +0800 Subject: btrfs: add prefix "balance:" for log messages Kernel logs are very important for the forensic investigations of the issues in general make it easy to use it. This patch adds 'balance:' prefix so that it can be easily searched. Signed-off-by: Anand Jain Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 8ed90107b727..b6757b53c297 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3805,7 +3805,7 @@ int btrfs_balance(struct btrfs_fs_info *fs_info, !(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; } @@ -3827,23 +3827,29 @@ int btrfs_balance(struct btrfs_fs_info *fs_info, 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; } @@ -3864,10 +3870,10 @@ int btrfs_balance(struct btrfs_fs_info *fs_info, !(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; } @@ -3881,9 +3887,12 @@ int btrfs_balance(struct btrfs_fs_info *fs_info, 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); @@ -3943,7 +3952,7 @@ static int balance_kthread(void *data) mutex_lock(&fs_info->balance_mutex); if (fs_info->balance_ctl) { - btrfs_info(fs_info, "continuing balance"); + btrfs_info(fs_info, "balance: resuming"); ret = btrfs_balance(fs_info, fs_info->balance_ctl, NULL); } mutex_unlock(&fs_info->balance_mutex); @@ -3963,7 +3972,7 @@ int btrfs_resume_balance_async(struct btrfs_fs_info *fs_info) 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; } @@ -4037,7 +4046,7 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info) */ if (test_and_set_bit(BTRFS_FS_EXCL_OP, &fs_info->flags)) btrfs_warn(fs_info, - "cannot set exclusive op status to balance, resume manually"); + "balance: cannot set exclusive op status, resume manually"); mutex_lock(&fs_info->balance_mutex); BUG_ON(fs_info->balance_ctl); @@ -4118,6 +4127,7 @@ int btrfs_cancel_balance(struct btrfs_fs_info *fs_info) 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"); } } -- cgit v1.2.3 From 6c52157fa9378efc0ff24c5f2602d500997f59db Mon Sep 17 00:00:00 2001 From: Tomohiro Misono Date: Wed, 16 May 2018 17:09:26 +0900 Subject: btrfs: sysfs: Use enum/define value for feature array definitions Use existing named values instead of the raw numbers. Signed-off-by: Tomohiro Misono Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/sysfs.c | 11 ++++++----- fs/btrfs/sysfs.h | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index fa6c8c88b250..217d401fe8ae 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -514,10 +514,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, @@ -609,7 +610,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 +674,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; diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h index b567560d9aa9..c6ee600aff89 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, -- cgit v1.2.3 From f902bd3a5e19284698ce6d70209d316df15116c6 Mon Sep 17 00:00:00 2001 From: Misono Tomohiro Date: Thu, 17 May 2018 14:24:51 +0900 Subject: btrfs: sysfs: Add entry which shows if rmdir can work on subvolumes Deletion of a subvolume by rmdir(2) has become allowed by the 'commit cd2decf640b1 ("btrfs: Allow rmdir(2) to delete an empty subvolume")'. It is a kind of new feature and this commits add a sysfs entry /sys/fs/btrfs/features/rmdir_subvol to indicate the availability of the feature so that a user program (e.g. fstests) can detect it. Prior to this commit, all entries in /sys/fs/btrfs/features are feature which depend on feature bits of superblock (i.e. each feature affects on-disk format) and managed by attribute_group "btrfs_feature_attr_group". For each fs, entries in /sys/fs/btrfs/UUID/features indicate which features are enabled (or can be changed online) for the fs. However, rmdir_subvol feature only depends on kernel module. Therefore new attribute_group "btrfs_static_feature_attr_group" is introduced and sysfs_merge_group() is used to share /sys/fs/btrfs/features directory. Features in "btrfs_static_feature_attr_group" won't be listed in each /sys/fs/btrfs/UUID/features. Signed-off-by: Tomohiro Misono Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/sysfs.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 217d401fe8ae..4a4e960c7c66 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; @@ -901,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: @@ -913,6 +950,8 @@ out1: 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); -- cgit v1.2.3 From c442793e6722158372d65388e2cdbd56170e7ad0 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 17 May 2018 14:16:29 +0300 Subject: btrfs: Remove stale comment about select_delayed_ref select_delayed_ref really just gets the next delayed ref which has to be processed - either an add ref or drop ref. We never go back for anything. So the comment is actually bogus, just remove it. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index fa2ed14532c1..47edb3af3f9f 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2705,10 +2705,6 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, spin_lock(&locked_ref->lock); 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 && -- cgit v1.2.3 From d1342aadbd9fcc82fd6e24c7f0443a43fe4714c7 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 11 May 2018 13:13:29 -0700 Subject: Btrfs: update stale comments referencing vmtruncate() Commit a41ad394a03b ("Btrfs: convert to the new truncate sequence") changed btrfs_setsize() to call truncate_setsize() instead of vmtruncate() but didn't update the comment above it. truncate_setsize() never fails (the IS_SWAPFILE() check happens elsewhere), so remove the comment. Additionally, the comment above btrfs_page_mkwrite() references vmtruncate(), but truncate_setsize() does the size write and page locking now. Signed-off-by: Omar Sandoval Signed-off-by: David Sterba --- fs/btrfs/inode.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index cbcda4e296f8..a1d352fbe647 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5369,7 +5369,6 @@ static int btrfs_setsize(struct inode *inode, struct iattr *attr) 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 */ @@ -9044,8 +9043,8 @@ again: * * 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. */ -- cgit v1.2.3 From fd86a3a31506fe4602ad056bc06a16f20e8eb30f Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 11 May 2018 13:13:30 -0700 Subject: Btrfs: fix error handling in btrfs_truncate_inode_items() btrfs_truncate_inode_items() uses two variables for error handling, ret and err. These are not handled consistently, leading to a couple of bugs. - Errors from btrfs_del_items() are handled but not propagated to the caller - If btrfs_run_delayed_refs() fails and aborts the transaction, we continue running Just use ret everywhere and simplify things a bit, fixing both of these issues. Fixes: 79787eaab461 ("btrfs: replace many BUG_ONs with proper error handling") Fixes: 1262133b8d6f ("Btrfs: account for crcs in delayed ref processing") Reviewed-by: Nikolay Borisov Signed-off-by: Omar Sandoval Signed-off-by: David Sterba --- fs/btrfs/inode.c | 55 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a1d352fbe647..e0863deea1a5 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4705,7 +4705,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; @@ -4757,22 +4756,19 @@ search_again: * 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 */ @@ -4883,7 +4879,7 @@ search_again: * We have to bail so the last_size is set to * just before this extent. */ - err = NEED_TRUNCATE_BLOCK; + ret = NEED_TRUNCATE_BLOCK; break; } @@ -4950,7 +4946,7 @@ delete: pending_del_nr); if (ret) { btrfs_abort_transaction(trans, ret); - goto error; + break; } pending_del_nr = 0; } @@ -4961,8 +4957,8 @@ delete: trans->delayed_ref_updates = 0; ret = btrfs_run_delayed_refs(trans, updates * 2); - if (ret && !err) - err = ret; + if (ret) + break; } } /* @@ -4970,8 +4966,8 @@ delete: * and let the transaction restart */ if (should_end) { - err = -EAGAIN; - goto error; + ret = -EAGAIN; + break; } goto search_again; } else { @@ -4979,32 +4975,37 @@ delete: } } 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; } /* -- cgit v1.2.3 From 0552210997badb6a60740a26ff9d976a416510f0 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 11 May 2018 13:13:31 -0700 Subject: Btrfs: don't BUG_ON() in btrfs_truncate_inode_items() btrfs_free_extent() can fail because of ENOMEM. There's no reason to panic here, we can just abort the transaction. Fixes: f4b9aa8d3b87 ("btrfs_truncate") Reviewed-by: Nikolay Borisov Signed-off-by: Omar Sandoval Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/inode.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index e0863deea1a5..c966fd6e2cef 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4918,7 +4918,10 @@ delete: 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, -- cgit v1.2.3 From f7e9e8fc792fe2f823ff7d64d23f4363b3f2203a Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 11 May 2018 13:13:32 -0700 Subject: Btrfs: stop creating orphan items for truncate Currently, we insert an orphan item during a truncate so that if there's a crash, we don't leak extents past the on-disk i_size. However, since commit 7f4f6e0a3f6d ("Btrfs: only update disk_i_size as we remove extents"), we keep disk_i_size in sync with the extent items as we truncate, so orphan cleanup will never have any extents to remove. Don't bother with the superfluous orphan item. Reviewed-by: Josef Bacik Signed-off-by: Omar Sandoval Signed-off-by: David Sterba --- fs/btrfs/free-space-cache.c | 6 +- fs/btrfs/inode.c | 159 ++++++++++++++------------------------------ 2 files changed, 51 insertions(+), 114 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index e5b569bebc73..d5f80cb300be 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/inode.c b/fs/btrfs/inode.c index c966fd6e2cef..74caec73b7c2 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3346,8 +3346,8 @@ void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans, } /* - * This creates an orphan entry for the given inode in case something goes - * wrong in the middle of an unlink/truncate. + * This creates an orphan entry for the given inode in case something goes wrong + * in the middle of an unlink. * * NOTE: caller of this function should reserve 5 units of metadata for * this function. @@ -3410,7 +3410,7 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, } } - /* insert an orphan item to track this unlinked/truncated file */ + /* insert an orphan item to track this unlinked file */ if (insert) { ret = btrfs_insert_orphan_item(trans, root, btrfs_ino(inode)); if (ret) { @@ -3439,8 +3439,8 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, } /* - * 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) @@ -3484,7 +3484,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; @@ -3584,12 +3584,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); @@ -3613,34 +3632,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) &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); @@ -3665,8 +3657,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) @@ -5350,29 +5340,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; - truncate_setsize(inode, newsize); /* Disable nonlocked read DIO to avoid the end less truncate */ @@ -5384,29 +5351,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); } } @@ -9224,39 +9178,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 - * - * 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. + * Yes ladies and gentlemen, this is indeed ugly. We have a couple of + * things going on here: * - * 2) We need to reserve space to update our inode. + * 1) 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); @@ -9345,13 +9291,6 @@ 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) { trans->block_rsv = &fs_info->trans_block_rsv; ret = btrfs_update_inode(trans, root, inode); -- cgit v1.2.3 From 7b40b695b4d06666c4665f6b5718085f2c6a5da2 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 11 May 2018 13:13:33 -0700 Subject: Btrfs: get rid of BTRFS_INODE_HAS_ORPHAN_ITEM Now that we don't add orphan items for truncate, there can't be races on adding or deleting an orphan item, so this bit is unnecessary. Reviewed-by: Nikolay Borisov Signed-off-by: Omar Sandoval Signed-off-by: David Sterba --- fs/btrfs/btrfs_inode.h | 1 - fs/btrfs/inode.c | 76 +++++++++++++------------------------------------- 2 files changed, 20 insertions(+), 57 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 234bae55b85d..cb7dc0aa4253 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -23,7 +23,6 @@ #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 diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 74caec73b7c2..c78e91619c76 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3359,7 +3359,6 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, 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) { @@ -3369,10 +3368,6 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, 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; @@ -3386,8 +3381,7 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, block_rsv = NULL; } - if (insert) - atomic_inc(&root->orphan_inodes); + atomic_inc(&root->orphan_inodes); spin_unlock(&root->orphan_lock); /* grab metadata reservation from transaction handle */ @@ -3403,36 +3397,28 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, 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 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 = 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) { + btrfs_abort_transaction(trans, ret); + return ret; } - ret = 0; } return 0; @@ -3446,14 +3432,9 @@ 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) + if (trans) ret = btrfs_del_orphan_item(trans, root, btrfs_ino(inode)); if (test_and_clear_bit(BTRFS_INODE_ORPHAN_META_RESERVED, @@ -3465,8 +3446,7 @@ static int btrfs_orphan_del(struct btrfs_trans_handle *trans, * to zero, in order to avoid that, decrease ->orphan_inodes after * everything is done. */ - if (delete_item) - atomic_dec(&root->orphan_inodes); + atomic_dec(&root->orphan_inodes); return ret; } @@ -3624,12 +3604,6 @@ 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); nr_unlink++; @@ -5527,11 +5501,8 @@ void btrfs_evict_inode(struct inode *inode) 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 && @@ -9442,13 +9413,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) -- cgit v1.2.3 From 05a5bd7c4decf828f79b62863e520c9ff86eef72 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 11 May 2018 13:13:34 -0700 Subject: Btrfs: delete dead code in btrfs_orphan_commit_root() btrfs_orphan_commit_root() tries to delete an orphan item for a subvolume in the tree root, but we don't actually insert that item in the first place. See commit 0a0d4415e338 ("Btrfs: delete dead code in btrfs_orphan_add()"). We can get rid of it. Reviewed-by: Josef Bacik Signed-off-by: Omar Sandoval Signed-off-by: David Sterba --- fs/btrfs/inode.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index c78e91619c76..06506cd00e4a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3307,7 +3307,6 @@ void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans, { 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) @@ -3328,17 +3327,6 @@ void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans, 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); -- cgit v1.2.3 From c08db7d8d295a4f3a10faaca376de011afff7950 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 11 May 2018 13:13:35 -0700 Subject: Btrfs: don't return ino to ino cache if inode item removal fails In btrfs_evict_inode(), if btrfs_truncate_inode_items() fails, the inode item will still be in the tree but we still return the ino to the ino cache. That will blow up later when someone tries to allocate that ino, so don't return it to the cache. Fixes: 581bb050941b ("Btrfs: Cache free inode numbers in memory") Reviewed-by: Josef Bacik Signed-off-by: Omar Sandoval Signed-off-by: David Sterba --- fs/btrfs/inode.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 06506cd00e4a..73783492a0d1 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5593,13 +5593,18 @@ void btrfs_evict_inode(struct inode *inode) trans->block_rsv = rsv; ret = btrfs_truncate_inode_items(trans, root, inode, 0, 0); - if (ret != -ENOSPC && ret != -EAGAIN) + if (ret) { + trans->block_rsv = &fs_info->trans_block_rsv; + btrfs_end_transaction(trans); + btrfs_btree_balance_dirty(fs_info); + if (ret != -ENOSPC && ret != -EAGAIN) { + btrfs_orphan_del(NULL, BTRFS_I(inode)); + btrfs_free_block_rsv(fs_info, rsv); + goto no_delete; + } + } else { break; - - trans->block_rsv = &fs_info->trans_block_rsv; - btrfs_end_transaction(trans); - trans = NULL; - btrfs_btree_balance_dirty(fs_info); + } } btrfs_free_block_rsv(fs_info, rsv); @@ -5608,12 +5613,8 @@ void btrfs_evict_inode(struct inode *inode) * 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 (ret == 0) { - trans->block_rsv = root->orphan_block_rsv; - btrfs_orphan_del(trans, BTRFS_I(inode)); - } else { - btrfs_orphan_del(NULL, BTRFS_I(inode)); - } + trans->block_rsv = root->orphan_block_rsv; + btrfs_orphan_del(trans, BTRFS_I(inode)); trans->block_rsv = &fs_info->trans_block_rsv; if (!(root == fs_info->tree_root || -- cgit v1.2.3 From 4b9d7b59bfcb62a7d438876eac5a054db32904dd Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 11 May 2018 13:13:36 -0700 Subject: Btrfs: refactor btrfs_evict_inode() reserve refill dance The truncate loop in btrfs_evict_inode() does two things at once: - It refills the temporary block reserve, potentially stealing from the global reserve or committing - It calls btrfs_truncate_inode_items() The tangle of continues hides the fact that these two steps are actually separate. Split the first step out into a separate function both for clarity and so that we can reuse it in a later patch. Reviewed-by: Nikolay Borisov Signed-off-by: Omar Sandoval Signed-off-by: David Sterba --- fs/btrfs/inode.c | 113 +++++++++++++++++++++---------------------------------- 1 file changed, 42 insertions(+), 71 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 73783492a0d1..f70284acb6ac 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5452,13 +5452,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; @@ -5511,85 +5550,17 @@ void btrfs_evict_inode(struct inode *inode) } 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); + trans = evict_refill_and_join(root, rsv, min_size); 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->block_rsv = rsv; ret = btrfs_truncate_inode_items(trans, root, inode, 0, 0); -- cgit v1.2.3 From 27919067f182c35f825406ad76a0e2e5644d33af Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 11 May 2018 13:13:37 -0700 Subject: Btrfs: fix ENOSPC caused by orphan items reservations Currently, we keep space reserved for all inode orphan items until the inode is evicted (i.e., all references to it are dropped). We hit an issue where an application would keep a bunch of deleted files open (by design) and thus keep a large amount of space reserved, causing ENOSPC errors when other operations tried to reserve space. This long-standing reservation isn't absolutely necessary for a couple of reasons: - We can almost always make the reservation we need or steal from the global reserve for the orphan item - If we can't, it's not the end of the world if we drop the orphan item on the floor and let the next mount clean it up So, get rid of persistent reservation and just reserve space in btrfs_evict_inode(). Reviewed-by: Nikolay Borisov Signed-off-by: Omar Sandoval Signed-off-by: David Sterba --- fs/btrfs/inode.c | 158 +++++++++++++------------------------------------------ 1 file changed, 38 insertions(+), 120 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f70284acb6ac..f8af40d02595 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3336,77 +3336,16 @@ void btrfs_orphan_commit_root(struct btrfs_trans_handle *trans, /* * This creates an orphan entry for the given inode in case something goes wrong * in the middle of an unlink. - * - * NOTE: caller of this function should reserve 5 units of metadata for - * this function. */ 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; 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_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; - } - - 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); - return ret; - } - } - - /* insert an orphan item to track this unlinked file */ - 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) { - btrfs_abort_transaction(trans, ret); - return ret; - } + ret = btrfs_insert_orphan_item(trans, inode->root, btrfs_ino(inode)); + if (ret && ret != -EEXIST) { + btrfs_abort_transaction(trans, ret); + return ret; } return 0; @@ -3419,24 +3358,7 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, static int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct btrfs_inode *inode) { - struct btrfs_root *root = inode->root; - int ret = 0; - - if (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. - */ - atomic_dec(&root->orphan_inodes); - - return ret; + return btrfs_del_orphan_item(trans, inode->root, btrfs_ino(inode)); } /* @@ -3592,8 +3514,6 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) continue; } - atomic_inc(&root->orphan_inodes); - nr_unlink++; /* this will do delete_inode and everything for us */ @@ -5518,10 +5438,8 @@ 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); @@ -5538,16 +5456,12 @@ 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; @@ -5555,46 +5469,50 @@ void btrfs_evict_inode(struct inode *inode) while (1) { trans = evict_refill_and_join(root, rsv, min_size); - if (IS_ERR(trans)) { - btrfs_orphan_del(NULL, BTRFS_I(inode)); - btrfs_free_block_rsv(fs_info, rsv); - goto no_delete; - } + if (IS_ERR(trans)) + goto free_rsv; trans->block_rsv = rsv; ret = btrfs_truncate_inode_items(trans, root, inode, 0, 0); - if (ret) { - trans->block_rsv = &fs_info->trans_block_rsv; - btrfs_end_transaction(trans); - btrfs_btree_balance_dirty(fs_info); - if (ret != -ENOSPC && ret != -EAGAIN) { - btrfs_orphan_del(NULL, BTRFS_I(inode)); - btrfs_free_block_rsv(fs_info, rsv); - goto no_delete; - } - } else { + trans->block_rsv = &fs_info->trans_block_rsv; + btrfs_end_transaction(trans); + 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. */ - trans->block_rsv = root->orphan_block_rsv; - btrfs_orphan_del(trans, BTRFS_I(inode)); + trans = evict_refill_and_join(root, rsv, min_size); + if (!IS_ERR(trans)) { + trans->block_rsv = rsv; + btrfs_orphan_del(trans, 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); } -- cgit v1.2.3 From a575ceeb1338e7eae6d14e223b077b3c6fd3bb6b Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 11 May 2018 13:13:38 -0700 Subject: Btrfs: get rid of unused orphan infrastructure Now that we don't keep long-standing reservations for orphan items, root->orphan_block_rsv isn't used. We can git rid of it, along with: - root->orphan_lock, which was used to protect root->orphan_block_rsv - root->orphan_inodes, which was used as a refcount for root->orphan_block_rsv - BTRFS_INODE_ORPHAN_META_RESERVED, which was used to track reservations in root->orphan_block_rsv - btrfs_orphan_commit_root(), which was the last user of any of these and does nothing else Reviewed-by: Nikolay Borisov Signed-off-by: Omar Sandoval Signed-off-by: David Sterba --- fs/btrfs/btrfs_inode.h | 1 - fs/btrfs/ctree.h | 8 -------- fs/btrfs/disk-io.c | 9 --------- fs/btrfs/extent-tree.c | 38 -------------------------------------- fs/btrfs/inode.c | 43 +------------------------------------------ fs/btrfs/transaction.c | 1 - 6 files changed, 1 insertion(+), 99 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index cb7dc0aa4253..4807cde0313d 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -20,7 +20,6 @@ * 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_ASYNC_EXTENT 5 diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 71aecf0b7cf5..bbb358143ded 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1223,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; @@ -2768,9 +2765,6 @@ 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, @@ -3228,8 +3222,6 @@ 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_add_delayed_iput(struct inode *inode); void btrfs_run_delayed_iputs(struct btrfs_fs_info *fs_info); diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index a16385091572..d8d3b73680ef 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1184,7 +1184,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); @@ -1194,7 +1193,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); @@ -1215,7 +1213,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; @@ -3884,8 +3881,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) @@ -3976,7 +3971,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); @@ -4072,9 +4066,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; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 47edb3af3f9f..ccf2690f7ca1 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -5949,44 +5949,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 diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f8af40d02595..b247ea31c436 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3297,42 +3297,6 @@ void btrfs_run_delayed_iputs(struct btrfs_fs_info *fs_info) spin_unlock(&fs_info->delayed_iput_lock); } -/* - * 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; - - 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 (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. @@ -3526,12 +3490,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); diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index ff841abb756e..2544acc33045 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -1245,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); -- cgit v1.2.3 From 7efc3e349c1b557563eb61ef054653ce8486de58 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 11 May 2018 13:13:39 -0700 Subject: Btrfs: renumber BTRFS_INODE_ runtime flags and switch to enums We got rid of BTRFS_INODE_HAS_ORPHAN_ITEM and BTRFS_INODE_ORPHAN_META_RESERVED, so we can renumber the flags to make them consecutive again. Signed-off-by: Omar Sandoval [ switch them enums so we don't have to do that again ] Signed-off-by: David Sterba --- fs/btrfs/btrfs_inode.h | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 4807cde0313d..7e075343daa5 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -19,15 +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_DUMMY 2 -#define BTRFS_INODE_IN_DEFRAG 3 -#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 { -- cgit v1.2.3 From 399b0bbf5f680797d3599fa14f16706ffc470145 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 11 May 2018 13:13:40 -0700 Subject: Btrfs: reserve space for O_TMPFILE orphan item deletion btrfs_link() calls btrfs_orphan_del() if it's linking an O_TMPFILE but it doesn't reserve space to do so. Even before the removal of the orphan_block_rsv it wasn't using it. Fixes: ef3b9af50bfa ("Btrfs: implement inode_operations callback tmpfile") Reviewed-by: Filipe Manana Signed-off-by: Omar Sandoval Signed-off-by: David Sterba --- fs/btrfs/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index b247ea31c436..6e9cb5338964 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6658,8 +6658,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; -- cgit v1.2.3 From 3973909d926c093fd13d56953e591635d56ab7b6 Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Wed, 2 May 2018 08:15:36 +0300 Subject: Btrfs: split btrfs_extent_same Split btrfs_extent_same() to two parts where one is the main EXTENT_SAME entry and a helper that can be repeatedly called on a range. This will be used in following patches. Signed-off-by: Timofey Titovets Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 64 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 28 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index ad1752bc80a7..ec0f380f85a1 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3027,8 +3027,8 @@ 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) { int ret; u64 len = olen; @@ -3037,21 +3037,13 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, 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) { /* @@ -3068,32 +3060,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); if (ret) - goto out_unlock; + return ret; if (same_inode) ret = lock_extent_range(src, same_lock_start, same_lock_len, @@ -3143,6 +3124,33 @@ again: btrfs_double_extent_unlock(src, loff, dst, dst_loff, len); btrfs_cmp_data_free(&cmp); + + return ret; +} + +static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, + struct inode *dst, u64 dst_loff) +{ + int ret; + bool same_inode = (src == dst); + + 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; + } + + ret = btrfs_extent_same_range(src, loff, olen, dst, dst_loff); + out_unlock: if (same_inode) inode_unlock(src); -- cgit v1.2.3 From b67287682688a60d03e8c2c6d4689ea3a25d5d3c Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Wed, 2 May 2018 08:15:37 +0300 Subject: Btrfs: dedupe_file_range ioctl: remove 16MiB restriction Currently btrfs_dedupe_file_range silently restricts the dedupe range to to 16MiB to limit locking and working memory size and is documented in manual page as implementation specific. Let's remove that restriction by iterating over the dedup range in 16MiB steps. This is backward compatible and will not change anything for requests smaller then 16MiB. Signed-off-by: Timofey Titovets Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index ec0f380f85a1..a247991856a0 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3128,11 +3128,14 @@ again: 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; bool same_inode = (src == dst); + u64 i, tail_len, chunk_count; if (olen == 0) return 0; @@ -3149,7 +3152,21 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, goto out_unlock; } - ret = btrfs_extent_same_range(src, loff, olen, dst, dst_loff); + tail_len = olen % BTRFS_MAX_DEDUPE_LEN; + chunk_count = div_u64(olen, BTRFS_MAX_DEDUPE_LEN); + + for (i = 0; i < chunk_count; i++) { + ret = btrfs_extent_same_range(src, loff, BTRFS_MAX_DEDUPE_LEN, + dst, dst_loff); + 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); out_unlock: if (same_inode) @@ -3160,8 +3177,6 @@ out_unlock: 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) { @@ -3170,9 +3185,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 -- cgit v1.2.3 From 67b07bd4bec53be0aa5a8496aef7a4ee2c7f97bf Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Wed, 2 May 2018 08:15:38 +0300 Subject: Btrfs: reuse cmp workspace in EXTENT_SAME ioctl We support big dedup requests by splitting range to smaller parts, and call dedupe logic on each of them. Instead of repeated allocation and deallocation, allocate once at the beginning and reuse in the iteration. Signed-off-by: Timofey Titovets Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 79 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 39 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a247991856a0..99eab7b3e5e1 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2914,8 +2914,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, @@ -2924,40 +2922,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) @@ -3028,11 +3000,11 @@ static int extent_same_check_offsets(struct inode *inode, u64 off, u64 *plen, } static int btrfs_extent_same_range(struct inode *src, u64 loff, u64 olen, - struct inode *dst, u64 dst_loff) + 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; @@ -3072,7 +3044,7 @@ static int btrfs_extent_same_range(struct inode *src, u64 loff, u64 olen, } 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) return ret; @@ -3095,7 +3067,7 @@ again: * 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); @@ -3108,12 +3080,12 @@ again: 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); @@ -3123,7 +3095,7 @@ again: else btrfs_double_extent_unlock(src, loff, dst, dst_loff, len); - btrfs_cmp_data_free(&cmp); + btrfs_cmp_data_free(cmp); return ret; } @@ -3134,6 +3106,8 @@ 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; @@ -3154,10 +3128,33 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, 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 = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); + cmp.dst_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); + if (!cmp.src_pages || !cmp.dst_pages) { + kfree(cmp.src_pages); + kfree(cmp.dst_pages); + return -ENOMEM; + } for (i = 0; i < chunk_count; i++) { ret = btrfs_extent_same_range(src, loff, BTRFS_MAX_DEDUPE_LEN, - dst, dst_loff); + dst, dst_loff, &cmp); if (ret) goto out_unlock; @@ -3166,7 +3163,8 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, } if (tail_len > 0) - ret = btrfs_extent_same_range(src, loff, tail_len, dst, dst_loff); + ret = btrfs_extent_same_range(src, loff, tail_len, dst, + dst_loff, &cmp); out_unlock: if (same_inode) @@ -3174,6 +3172,9 @@ out_unlock: else btrfs_double_inode_unlock(src, dst); + kfree(cmp.src_pages); + kfree(cmp.dst_pages); + return ret; } -- cgit v1.2.3 From bf5091c8d69e95c34dab2224b98a9cb0ccff1aa8 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Fri, 11 May 2018 17:57:54 +0200 Subject: btrfs: use kvzalloc for EXTENT_SAME temporary data The dedupe range is 16 MiB, with 4 KiB pages and 8 byte pointers, the arrays can be 32KiB large. To avoid allocation failures due to fragmented memory, use the allocation with fallback to vmalloc. The arrays are allocated and freed only inside btrfs_extent_same and reused for all the ranges. Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 99eab7b3e5e1..aeef6cd8aaeb 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3144,12 +3144,13 @@ static int btrfs_extent_same(struct inode *src, u64 loff, u64 olen, * 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 = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); - cmp.dst_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); + 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) { - kfree(cmp.src_pages); - kfree(cmp.dst_pages); - return -ENOMEM; + ret = -ENOMEM; + goto out_free; } for (i = 0; i < chunk_count; i++) { @@ -3172,8 +3173,9 @@ out_unlock: else btrfs_double_inode_unlock(src, dst); - kfree(cmp.src_pages); - kfree(cmp.dst_pages); +out_free: + kvfree(cmp.src_pages); + kvfree(cmp.dst_pages); return ret; } -- cgit v1.2.3 From b78e2b78a88ce4982294ceb03c1227810e659a7b Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 17 May 2018 21:25:12 +0800 Subject: btrfs: fix describe_relocation when printing unknown flags Looks like the original idea was to print the hex of the flags which is not coded with their flag name. So use the current buf pointer bp instead of buf. Reaching the uknown flags should never happen, it's there just in case. Fixes: ebce0e01b930b ("btrfs: make block group flags in balance printks human-readable") Signed-off-by: Anand Jain Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/relocation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 74656d79e511..879b76fa881a 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -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 } -- cgit v1.2.3 From 37becec95ac31b209eb1c8e096f1093a7db00f32 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Mon, 21 May 2018 17:07:19 -0700 Subject: Btrfs: allow empty subvol= again I got a report that after upgrading to 4.16, someone's filesystems weren't mounting: [ 23.845852] BTRFS info (device loop0): unrecognized mount option 'subvol=' Before 4.16, this mounted the default subvolume. It turns out that this empty "subvol=" is actually an application bug, but it was causing the application to fail, so it's an ABI break if you squint. The generic parsing code we use for mount options (match_token()) doesn't match an empty string as "%s". Previously, setup_root_args() removed the "subvol=" string, but the mount path was cleaned up to not need that. Add a dummy Opt_subvol_empty to fix this. The simple workaround is to use / or . for the value of 'subvol=' . Fixes: 312c89fbca06 ("btrfs: cleanup btrfs_mount() using btrfs_mount_root()") CC: stable@vger.kernel.org # 4.16+ Signed-off-by: Omar Sandoval Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/super.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index c67fafaa2fe7..81107ad49f3a 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: -- cgit v1.2.3 From ad1e3d5672ddce03eaa811c3f8d728acefac9a19 Mon Sep 17 00:00:00 2001 From: Misono Tomohiro Date: Mon, 21 May 2018 13:57:27 +0900 Subject: btrfs: use error code returned by btrfs_read_fs_root_no_name in search ioctl btrfs_read_fs_root_no_name() may return ERR_PTR(-ENOENT) or ERR_PTR(-ENOMEM) and therefore search_ioctl() and btrfs_search_path_in_tree() should use PTR_ERR() instead of -ENOENT, which all other callers of btrfs_read_fs_root_no_name() do. Drop the error message as it would be confusing, the caller of ioctl will likely interpret the error code and not look into the syslog. Signed-off-by: Tomohiro Misono Reviewed-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index aeef6cd8aaeb..743c4f1b8001 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2155,7 +2155,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); } } @@ -2289,8 +2289,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; } -- cgit v1.2.3 From 3c7251f2f8888086c5769f83651018a5494f784b Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 17 May 2018 00:00:42 +0200 Subject: btrfs: tests: add helper for error messages and update them The test failures are not clearly visible in the system log as they're printed at INFO level. Add a new helper that is level ERROR. As this touches almost all strings, I took the opportunity to unify them: - decapitalize the first letter as there's a prefix and the text continues after ":" - glue strings split to more lines and un-indent so they fit to 80 columns - use %llu instead of %Lu - drop \n from the modified messages (test_msg is left untouched) Signed-off-by: David Sterba --- fs/btrfs/tests/btrfs-tests.h | 1 + fs/btrfs/tests/extent-buffer-tests.c | 56 +++--- fs/btrfs/tests/extent-io-tests.c | 75 ++++---- fs/btrfs/tests/extent-map-tests.c | 30 ++-- fs/btrfs/tests/free-space-tests.c | 177 ++++++++++--------- fs/btrfs/tests/free-space-tree-tests.c | 74 ++++---- fs/btrfs/tests/inode-tests.c | 312 +++++++++++++++++---------------- fs/btrfs/tests/qgroup-tests.c | 88 +++++----- 8 files changed, 412 insertions(+), 401 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/tests/btrfs-tests.h b/fs/btrfs/tests/btrfs-tests.h index 4c11cffb377c..47b5d2eac790 100644 --- a/fs/btrfs/tests/btrfs-tests.h +++ b/fs/btrfs/tests/btrfs-tests.h @@ -10,6 +10,7 @@ int btrfs_run_sanity_tests(void); #define test_msg(fmt, ...) pr_info("BTRFS: selftest: " fmt, ##__VA_ARGS__) +#define test_err(fmt, ...) pr_err("BTRFS: selftest: " fmt "\n", ##__VA_ARGS__) struct btrfs_root; struct btrfs_trans_handle; diff --git a/fs/btrfs/tests/extent-buffer-tests.c b/fs/btrfs/tests/extent-buffer-tests.c index 31e8a9ec228c..2fa440cf7874 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\n"); 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 @@ out: 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\n"); 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 76aa5a678a96..f17e2e31d64f 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\n"); 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\n"); /* * 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\n"); 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\n"); return ret; } diff --git a/fs/btrfs/tests/extent-map-tests.c b/fs/btrfs/tests/extent-map-tests.c index 9c051c4a3315..d55266e01cad 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)); @@ -93,12 +93,12 @@ static void test_case_1(struct btrfs_fs_info *fs_info, em->block_len = 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); @@ -157,12 +157,12 @@ static void test_case_2(struct btrfs_fs_info *fs_info, em->block_len = (u64)-1; 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); @@ -203,7 +203,7 @@ static void __test_case_3(struct btrfs_fs_info *fs_info, em->block_len = SZ_16K; 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 @@ -212,8 +212,8 @@ static void __test_case_3(struct btrfs_fs_info *fs_info, 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); @@ -290,12 +290,12 @@ static void __test_case_4(struct btrfs_fs_info *fs_info, em->block_len = SZ_32K; 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); @@ -341,7 +341,7 @@ 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\n"); /* * Note: the fs_info is not set up completely, we only need diff --git a/fs/btrfs/tests/free-space-tests.c b/fs/btrfs/tests/free-space-tests.c index d3c9f8a59ba5..7cbad3e666d3 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\n"); /* 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\n"); 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\n"); /* * 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\n"); /* * 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\n"); 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 @@ out: 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\n"); return ret; } diff --git a/fs/btrfs/tests/free-space-tree-tests.c b/fs/btrfs/tests/free-space-tree-tests.c index cb92868eec15..8c0b395257ea 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 @@ out: 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); } @@ -133,13 +133,13 @@ static int check_free_space_extents(struct btrfs_trans_handle *trans, if (flags & BTRFS_FREE_SPACE_USING_BITMAPS) { 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, cache, path); if (ret) { - test_msg("Could not convert to bitmaps\n"); + test_err("could not convert to bitmaps"); return ret; } } @@ -174,7 +174,7 @@ static int test_remove_all(struct btrfs_trans_handle *trans, 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; } @@ -197,7 +197,7 @@ static int test_remove_beginning(struct btrfs_trans_handle *trans, 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; } @@ -222,7 +222,7 @@ static int test_remove_end(struct btrfs_trans_handle *trans, cache->key.offset - alignment, alignment); if (ret) { - test_msg("Could not remove free space\n"); + test_err("could not remove free space"); return ret; } @@ -247,7 +247,7 @@ static int test_remove_middle(struct btrfs_trans_handle *trans, cache->key.objectid + alignment, alignment); if (ret) { - test_msg("Could not remove free space\n"); + test_err("could not remove free space"); return ret; } @@ -270,14 +270,14 @@ static int test_merge_left(struct btrfs_trans_handle *trans, 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, 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; } @@ -285,7 +285,7 @@ static int test_merge_left(struct btrfs_trans_handle *trans, cache->key.objectid + alignment, alignment); if (ret) { - test_msg("Could not add free space\n"); + test_err("could not add free space"); return ret; } @@ -308,7 +308,7 @@ static int test_merge_right(struct btrfs_trans_handle *trans, 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; } @@ -316,7 +316,7 @@ static int test_merge_right(struct btrfs_trans_handle *trans, 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; } @@ -324,7 +324,7 @@ static int test_merge_right(struct btrfs_trans_handle *trans, cache->key.objectid + alignment, alignment); if (ret) { - test_msg("Could not add free space\n"); + test_err("could not add free space"); return ret; } @@ -347,14 +347,14 @@ static int test_merge_both(struct btrfs_trans_handle *trans, 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, 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; } @@ -362,7 +362,7 @@ static int test_merge_both(struct btrfs_trans_handle *trans, 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; } @@ -370,7 +370,7 @@ static int test_merge_both(struct btrfs_trans_handle *trans, cache->key.objectid + alignment, alignment); if (ret) { - test_msg("Could not add free space\n"); + test_err("could not add free space"); return ret; } @@ -395,14 +395,14 @@ static int test_merge_none(struct btrfs_trans_handle *trans, 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, 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; } @@ -410,7 +410,7 @@ static int test_merge_none(struct btrfs_trans_handle *trans, 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; } @@ -418,7 +418,7 @@ static int test_merge_none(struct btrfs_trans_handle *trans, 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; } @@ -486,21 +486,21 @@ static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize, 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, 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, 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; } } @@ -511,12 +511,12 @@ static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize, 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; } @@ -538,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; } @@ -576,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\n"); 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 e0ba799536b4..2f2f9dc30f4c 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\n"); 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\n"); ret = test_hole_first(sectorsize, nodesize); if (ret) return ret; - test_msg("Running outstanding_extents tests\n"); + test_msg("running outstanding_extents tests\n"); return test_extent_accounting(sectorsize, nodesize); } diff --git a/fs/btrfs/tests/qgroup-tests.c b/fs/btrfs/tests/qgroup-tests.c index df4bcce20ba5..578c6c461897 100644 --- a/fs/btrfs/tests/qgroup-tests.c +++ b/fs/btrfs/tests/qgroup-tests.c @@ -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; } @@ -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; } @@ -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; } @@ -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; } @@ -215,10 +215,10 @@ static int test_no_shared_qgroup(struct btrfs_root *root, btrfs_init_dummy_trans(&trans, fs_info); - test_msg("Qgroup basic add\n"); + test_msg("qgroup basic add\n"); 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; } @@ -316,7 +316,7 @@ static int test_multiple_refs(struct btrfs_root *root, btrfs_init_dummy_trans(&trans, fs_info); - test_msg("Qgroup multiple refs test\n"); + test_msg("qgroup multiple refs test\n"); /* * 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\n"); ret = test_no_shared_qgroup(root, sectorsize, nodesize); if (ret) goto out; -- cgit v1.2.3 From 315b76b46232758836f2d431e0963567f796116f Mon Sep 17 00:00:00 2001 From: David Sterba Date: Thu, 17 May 2018 00:00:44 +0200 Subject: btrfs: tests: drop newline from test_msg strings Now that test_err strings do not need the newline, remove them also from the test_msg. Signed-off-by: David Sterba --- fs/btrfs/tests/btrfs-tests.h | 2 +- fs/btrfs/tests/extent-buffer-tests.c | 4 ++-- fs/btrfs/tests/extent-io-tests.c | 8 ++++---- fs/btrfs/tests/extent-map-tests.c | 4 ++-- fs/btrfs/tests/free-space-tests.c | 12 ++++++------ fs/btrfs/tests/free-space-tree-tests.c | 2 +- fs/btrfs/tests/inode-tests.c | 6 +++--- fs/btrfs/tests/qgroup-tests.c | 6 +++--- 8 files changed, 22 insertions(+), 22 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/tests/btrfs-tests.h b/fs/btrfs/tests/btrfs-tests.h index 47b5d2eac790..70ff9f9d86a1 100644 --- a/fs/btrfs/tests/btrfs-tests.h +++ b/fs/btrfs/tests/btrfs-tests.h @@ -9,7 +9,7 @@ #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; diff --git a/fs/btrfs/tests/extent-buffer-tests.c b/fs/btrfs/tests/extent-buffer-tests.c index 2fa440cf7874..7d72eab6d32c 100644 --- a/fs/btrfs/tests/extent-buffer-tests.c +++ b/fs/btrfs/tests/extent-buffer-tests.c @@ -26,7 +26,7 @@ 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) { @@ -220,6 +220,6 @@ out: 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 f17e2e31d64f..d9269a531a4d 100644 --- a/fs/btrfs/tests/extent-io-tests.c +++ b/fs/btrfs/tests/extent-io-tests.c @@ -68,7 +68,7 @@ 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) { @@ -377,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 @@ -425,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) @@ -433,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 d55266e01cad..385a5316e4bf 100644 --- a/fs/btrfs/tests/extent-map-tests.c +++ b/fs/btrfs/tests/extent-map-tests.c @@ -341,7 +341,7 @@ 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 @@ -349,7 +349,7 @@ int btrfs_test_extent_map(void) */ fs_info = btrfs_alloc_dummy_fs_info(PAGE_SIZE, PAGE_SIZE); if (!fs_info) { - test_msg("Couldn't allocate dummy fs info\n"); + test_msg("Couldn't allocate dummy fs info"); return -ENOMEM; } diff --git a/fs/btrfs/tests/free-space-tests.c b/fs/btrfs/tests/free-space-tests.c index 7cbad3e666d3..5c2f77e9439b 100644 --- a/fs/btrfs/tests/free-space-tests.c +++ b/fs/btrfs/tests/free-space-tests.c @@ -20,7 +20,7 @@ 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); @@ -92,7 +92,7 @@ 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) { @@ -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 @@ -404,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 @@ -832,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; @@ -874,6 +874,6 @@ out: 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 8c0b395257ea..89346da890cf 100644 --- a/fs/btrfs/tests/free-space-tree-tests.c +++ b/fs/btrfs/tests/free-space-tree-tests.c @@ -578,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 2f2f9dc30f4c..64043f028820 100644 --- a/fs/btrfs/tests/inode-tests.c +++ b/fs/btrfs/tests/inode-tests.c @@ -1119,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 578c6c461897..ace94db09d29 100644 --- a/fs/btrfs/tests/qgroup-tests.c +++ b/fs/btrfs/tests/qgroup-tests.c @@ -215,7 +215,7 @@ static int test_no_shared_qgroup(struct btrfs_root *root, 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_err("couldn't create a qgroup %d", ret); @@ -316,7 +316,7 @@ static int test_multiple_refs(struct btrfs_root *root, 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 @@ -522,7 +522,7 @@ int btrfs_test_qgroups(u32 sectorsize, u32 nodesize) 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; -- cgit v1.2.3 From 9dcdbe0144f97c4567e022b75ba8e888e8199660 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Mon, 21 May 2018 12:27:20 +0300 Subject: btrfs: Remove fs_info argument from alloc_reserved_tree_block This function already takes a transaction handle which contains a reference to the fs_info. So use this and remove the extra argument. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index ccf2690f7ca1..6605fd374f50 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -66,7 +66,6 @@ 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); @@ -2461,8 +2460,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, + ret = alloc_reserved_tree_block(trans, parent, ref_root, extent_op->flags_to_set, &extent_op->key, ref->level, &ins); @@ -8146,11 +8144,11 @@ 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_fs_info *fs_info = trans->fs_info; int ret; struct btrfs_extent_item *extent_item; struct btrfs_tree_block_info *block_info; -- cgit v1.2.3 From 4e6bd4e0aaf8e00d9ea4472d91252715c4b4bd5d Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Mon, 21 May 2018 12:27:21 +0300 Subject: btrfs: Simplify alloc_reserved_tree_block interface This function currently takes 7 parameters, most of which are proxies for values from btrfs_delayed_ref_node struct which is not passed. This patch simplifies the interface of the function by simply passing said delayed ref node struct to the function. This enables us to: 1. Move locals variables and init code related to them from run_delayed_tree_ref which should only be used inside alloc_reserved_tree_block, such as skinny_metadata and the btrfs_key, representing the extent being inserted. This removes the need for the "ins" argument. Instead, it's replaced by a local var with a more verbose name - extent_key. 2. Now that we have a reference to the node in alloc_reserved_tree_block the delayed_tree_ref struct can be referenced inside the function and this enable removing the "ref->level", "parent" and "ref_root" arguments. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 71 +++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 32 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 6605fd374f50..2d232e23450e 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -66,9 +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, - u64 parent, u64 root_objectid, - u64 flags, struct btrfs_disk_key *key, - int level, struct btrfs_key *ins); + struct btrfs_delayed_ref_node *node, + u64 flags, struct btrfs_disk_key *key); static int do_chunk_alloc(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, u64 flags, int force); @@ -2430,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); @@ -2442,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", @@ -2460,10 +2448,9 @@ 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, parent, ref_root, + ret = alloc_reserved_tree_block(trans, node, extent_op->flags_to_set, - &extent_op->key, - ref->level, &ins); + &extent_op->key); } else if (node->action == BTRFS_ADD_DELAYED_REF) { ret = __btrfs_inc_extent_ref(trans, fs_info, node, parent, ref_root, @@ -8144,37 +8131,57 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans, } static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, - u64 parent, u64 root_objectid, - u64 flags, struct btrfs_disk_key *key, - int level, struct btrfs_key *ins) + struct btrfs_delayed_ref_node *node, + u64 flags, struct btrfs_disk_key *key) { 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 parent; bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA); - if (!skinny_metadata) + ref = btrfs_delayed_node_to_tree_ref(node); + + if (node->type == BTRFS_SHARED_BLOCK_REF_KEY) + parent = ref->parent; + else + parent = 0; + + 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; } @@ -8189,11 +8196,10 @@ 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_level(leaf, block_info, ref->level); iref = (struct btrfs_extent_inline_ref *)(block_info + 1); } @@ -8205,25 +8211,26 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, } 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, ins->objectid, num_bytes); + 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; } -- cgit v1.2.3 From 21ebfbe7e06cafb05c41cc39b058be35acb6c5da Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Mon, 21 May 2018 12:27:22 +0300 Subject: btrfs: Pass btrfs_delayed_extent_op to alloc_reserved_tree_block Instead of taking only specific member of this structure, which results in 2 extra arguments, just take the delayed_extent_op struct and reference the arguments inside the functions. No functional changes. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 2d232e23450e..1e666961f0e4 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -67,7 +67,7 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans, struct btrfs_key *ins, int ref_mod); static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, struct btrfs_delayed_ref_node *node, - u64 flags, struct btrfs_disk_key *key); + 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); @@ -2448,9 +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, node, - extent_op->flags_to_set, - &extent_op->key); + 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, @@ -8132,7 +8130,7 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans, static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, struct btrfs_delayed_ref_node *node, - u64 flags, struct btrfs_disk_key *key) + struct btrfs_delayed_extent_op *extent_op) { struct btrfs_fs_info *fs_info = trans->fs_info; int ret; @@ -8146,6 +8144,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, u32 size = sizeof(*extent_item) + sizeof(*iref); u64 num_bytes; u64 parent; + u64 flags = extent_op->flags_to_set; bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA); ref = btrfs_delayed_node_to_tree_ref(node); @@ -8198,7 +8197,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, iref = (struct btrfs_extent_inline_ref *)(extent_item + 1); } else { block_info = (struct btrfs_tree_block_info *)(extent_item + 1); - btrfs_set_tree_block_key(leaf, block_info, key); + 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); } -- cgit v1.2.3 From d4b20733d203eea307986bd0502ba8d54afc2891 Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Mon, 21 May 2018 12:27:23 +0300 Subject: btrfs: Streamline shared ref check in alloc_reserved_tree_block Instead of setting "parent" to ref->parent only when dealing with a shared ref and subsequently performing another check to see if (parent > 0), check the "node->type" directly and act accordingly. This makes the code more streamline. No functional changes. Signed-off-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 1e666961f0e4..c25308722e81 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -8143,17 +8143,11 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, struct btrfs_delayed_tree_ref *ref; u32 size = sizeof(*extent_item) + sizeof(*iref); u64 num_bytes; - u64 parent; u64 flags = extent_op->flags_to_set; bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA); ref = btrfs_delayed_node_to_tree_ref(node); - if (node->type == BTRFS_SHARED_BLOCK_REF_KEY) - parent = ref->parent; - else - parent = 0; - extent_key.objectid = node->bytenr; if (skinny_metadata) { extent_key.offset = ref->level; @@ -8202,11 +8196,11 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, 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); -- cgit v1.2.3 From b89311efe60d47f8fc23b6f027c6b3f30067c93e Mon Sep 17 00:00:00 2001 From: Gu Jinxiang Date: Tue, 22 May 2018 17:46:51 +0800 Subject: btrfs: propagate failures of __exclude_logged_extent to upper caller Function btrfs_exclude_logged_extents may call __exclude_logged_extent which may fail. Propagate the failures of __exclude_logged_extent to upper caller. Signed-off-by: Gu Jinxiang Reviewed-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index c25308722e81..7273dcddcdf4 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -6457,6 +6457,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; @@ -6473,10 +6474,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 -- cgit v1.2.3 From b5c40d598f5408bd0ca22dfffa82f03cd9433f23 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Tue, 22 May 2018 15:02:12 -0700 Subject: Btrfs: fix clone vs chattr NODATASUM race In btrfs_clone_files(), we must check the NODATASUM flag while the inodes are locked. Otherwise, it's possible that btrfs_ioctl_setflags() will change the flags after we check and we can end up with a party checksummed file. The race window is only a few instructions in size, between the if and the locks which is: 3834 if (S_ISDIR(src->i_mode) || S_ISDIR(inode->i_mode)) 3835 return -EISDIR; where the setflags must be run and toggle the NODATASUM flag (provided the file size is 0). The clone will block on the inode lock, segflags takes the inode lock, changes flags, releases log and clone continues. Not impossible but still needs a lot of bad luck to hit unintentionally. Fixes: 0e7b824c4ef9 ("Btrfs: don't make a file partly checksummed through file clone") CC: stable@vger.kernel.org # 4.4+ Signed-off-by: Omar Sandoval Reviewed-by: Nikolay Borisov Reviewed-by: David Sterba [ update changelog ] Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 743c4f1b8001..b9b779a4ab6e 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3808,11 +3808,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; @@ -3822,6 +3817,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) -- cgit v1.2.3 From d5c1d68fdeeecd130d60007522c5db179e47e3d2 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 17 May 2018 13:52:22 +0800 Subject: btrfs: compression: Add linux/sizes.h for compression.h Since compression.h is using the SZ_* macros, and if some file includes only compression.h without linux/sizes.h, it will cause compile error. One example is lzo.c, if it uses BTRFS_MAX_COMPRESSED. Fix it by adding linux/sizes.h in compression.h Signed-off-by: Qu Wenruo Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/compression.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h index cc605f7b23fb..ddda9b80bf20 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 -- cgit v1.2.3 From 2a1f7c0cbddae68a34096d056fc7ff7b171bab17 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 17 May 2018 13:10:01 +0800 Subject: btrfs: lzo: document the compressed data format Although it's not that complex, but such comment could still save several minutes for newer reader/reviewer instead of inferring that from the code. Signed-off-by: Qu Wenruo Reviewed-by: David Sterba [ minor wording updates ] Signed-off-by: David Sterba --- fs/btrfs/lzo.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 0667ea07f766..9591927022ba 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 */ -- cgit v1.2.3 From 314bfa473b6b6d3efe68011899bd718b349f29d7 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 15 May 2018 14:57:51 +0800 Subject: btrfs: lzo: Add header length check to avoid potential out-of-bounds access James Harvey reported that some corrupted compressed extent data can lead to various kernel memory corruption. Such corrupted extent data belongs to inode with NODATASUM flags, thus data csum won't help us detecting such bug. If lucky enough, KASAN could catch it like: BUG: KASAN: slab-out-of-bounds in lzo_decompress_bio+0x384/0x7a0 [btrfs] Write of size 4096 at addr ffff8800606cb0f8 by task kworker/u16:0/2338 CPU: 3 PID: 2338 Comm: kworker/u16:0 Tainted: G O 4.17.0-rc5-custom+ #50 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 Workqueue: btrfs-endio btrfs_endio_helper [btrfs] Call Trace: dump_stack+0xc2/0x16b print_address_description+0x6a/0x270 kasan_report+0x260/0x380 memcpy+0x34/0x50 lzo_decompress_bio+0x384/0x7a0 [btrfs] end_compressed_bio_read+0x99f/0x10b0 [btrfs] bio_endio+0x32e/0x640 normal_work_helper+0x15a/0xea0 [btrfs] process_one_work+0x7e3/0x1470 worker_thread+0x1b0/0x1170 kthread+0x2db/0x390 ret_from_fork+0x22/0x40 ... The offending compressed data has the following info: Header: length 32768 (looks completely valid) Segment 0 Header: length 3472882419 (obviously out of bounds) Then when handling segment 0, since it's over the current page, we need the copy the compressed data to temporary buffer in workspace, then such large size would trigger out-of-bounds memory access, screwing up the whole kernel. Fix it by adding extra checks on header and segment headers to ensure we won't access out-of-bounds, and even checks the decompressed data won't be out-of-bounds. Reported-by: James Harvey Signed-off-by: Qu Wenruo Reviewed-by: Misono Tomohiro Reviewed-by: David Sterba [ updated comments ] Signed-off-by: David Sterba --- fs/btrfs/lzo.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 9591927022ba..464fbb96af1a 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -295,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; @@ -308,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; @@ -322,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; @@ -372,7 +396,7 @@ cont: } } - 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) -- cgit v1.2.3 From de885e3ee281a88f52283c7e8994e762e3a5f6bd Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Thu, 17 May 2018 14:10:29 +0800 Subject: btrfs: lzo: Harden inline lzo compressed extent decompression For inlined extent, we only have one segment, thus less things to check. And further more, inlined extent always has the csum in its leaf header, it's less probable to have corrupted data. Anyway, still check header and segment header. Signed-off-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/lzo.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 464fbb96af1a..b6a4cc178bee 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -430,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; -- cgit v1.2.3 From fd4e994bd1f9dc9628e168a7f619bf69f6984635 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Tue, 22 May 2018 15:44:01 -0700 Subject: Btrfs: fix memory and mount leak in btrfs_ioctl_rm_dev_v2() If we have invalid flags set, when we error out we must drop our writer counter and free the buffer we allocated for the arguments. This bug is trivially reproduced with the following program on 4.7+: #include #include #include #include #include #include #include #include #include #include int main(int argc, char **argv) { struct btrfs_ioctl_vol_args_v2 vol_args = { .flags = UINT64_MAX, }; int ret; int fd; if (argc != 2) { fprintf(stderr, "usage: %s PATH\n", argv[0]); return EXIT_FAILURE; } fd = open(argv[1], O_WRONLY); if (fd == -1) { perror("open"); return EXIT_FAILURE; } ret = ioctl(fd, BTRFS_IOC_RM_DEV_V2, &vol_args); if (ret == -1) perror("ioctl"); close(fd); return EXIT_SUCCESS; } When unmounting the filesystem, we'll hit the WARN_ON(mnt_get_writers(mnt)) in cleanup_mnt() and also may prevent the filesystem to be remounted read-only as the writer count will stay lifted. Fixes: 6b526ed70cf1 ("btrfs: introduce device delete by devid") CC: stable@vger.kernel.org # 4.9+ Signed-off-by: Omar Sandoval Reviewed-by: Su Yue Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index b9b779a4ab6e..3b49b5eb98f1 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2613,8 +2613,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; -- cgit v1.2.3 From 9593bf49675efc5567c9bdb8c51cdb204b6f85b3 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Wed, 2 May 2018 13:28:03 +0800 Subject: btrfs: qgroup: show more meaningful qgroup_rescan_init error message Error message from qgroup_rescan_init() mostly looks like: BTRFS info (device nvme0n1p1): qgroup_rescan_init failed with -115 Which is far from meaningful, and sometimes confusing as for above -EINPROGRESS it's mostly (despite the init race) harmless, but sometimes it can also indicate problem if the return value is -EINVAL. Change it to some more meaningful messages like: BTRFS info (device nvme0n1p1): qgroup rescan is already in progress And BTRFS err(device nvme0n1p1): qgroup rescan init failed, qgroup is not enabled Signed-off-by: Qu Wenruo Reviewed-by: Nikolay Borisov [ update the messages and level ] Signed-off-by: David Sterba --- fs/btrfs/qgroup.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 457219d6a16b..1874a6d2e6f5 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -2782,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; } @@ -2820,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; } -- cgit v1.2.3 From ff76a864cc94d055b4239fb2cd13e8e633dc7aac Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Fri, 18 May 2018 10:59:35 +0800 Subject: Btrfs: add parent_transid parameter to veirfy_level_key As verify_level_key() is checked after verify_parent_transid(), i.e. if (verify_parent_transid()) ret = -EIO; else if (verify_level_key()) ret = -EUCLEAN; if parent_transid is 0, verify_parent_transid() skips verifying parent_transid and considers eb as valid, and if verify_level_key() reports something wrong, we're not going to know if it's caused by corrupted metadata or non-checkecd eb (e.g. stale eb). The stale eb can be from an outdated raid1 mirror after a degraded mount, see eg "btrfs: fix reading stale metadata blocks after degraded raid1 mounts" (02a3307aa9c20b4f66262) for more details. @parent_transid is able to tell whether the eb's generation has been verified by the caller. Signed-off-by: Liu Bo Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index d8d3b73680ef..205092dc9390 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -415,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; @@ -453,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; @@ -492,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; -- cgit v1.2.3 From 4ca616832783ce6563b59b2d4764fdbb272e0362 Mon Sep 17 00:00:00 2001 From: Lu Fengqi Date: Mon, 28 May 2018 14:30:27 +0800 Subject: btrfs: drop unused space_info parameter from create_space_info Since commit dc2d3005d27d ("btrfs: remove dead create_space_info calls"), there is only one caller btrfs_init_space_info. However, it doesn't need create_space_info to return space_info at all. Signed-off-by: Lu Fengqi Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 7273dcddcdf4..7ebb05fe2cd8 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4007,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; @@ -4046,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; @@ -10825,7 +10823,6 @@ next: 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; @@ -10841,21 +10838,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; -- cgit v1.2.3 From ca19b4a69962fa850d5a22aa7a335106fcba0473 Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Fri, 18 May 2018 11:00:19 +0800 Subject: Btrfs: remove superfluous free_extent_buffer in read_block_for_search read_block_for_search() can be simplified as: tmp = find_extent_buffer(); if (tmp) return; ... free_extent_buffer(); read_tree_block(); Apparently, @tmp must be NULL at this point, free_extent_buffer() is not needed. Signed-off-by: Liu Bo Reviewed-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 8c68961925b1..820226d42d5d 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -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); -- cgit v1.2.3 From e6a1d6fd276965db0ca91e91dffc0a6fb7d89254 Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Fri, 18 May 2018 11:00:20 +0800 Subject: Btrfs: use more straightforward extent_buffer_uptodate check If parent_transid "0" is passed to btrfs_buffer_uptodate(), btrfs_buffer_uptodate() is equivalent to extent_buffer_uptodate(), but extent_buffer_uptodate() is preferred since we don't have to look into verify_parent_transid(). Signed-off-by: Liu Bo Reviewed-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 820226d42d5d..46afc9743ad0 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -2445,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 { -- cgit v1.2.3 From 1fc28d8e2e9bf22044f1bacd17fe941cd0df5ba6 Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Fri, 18 May 2018 11:00:21 +0800 Subject: Btrfs: move get root out of btrfs_search_slot to a helper It's good to have a helper instead of having all get-root details open-coded. The new helper locks (if necessary) and sets root node of the path. Also invert the checks to make the code flow easier to read. There is no functional change in this commit. Signed-off-by: Liu Bo Reviewed-by: Qu Wenruo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.c | 110 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 65 insertions(+), 45 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 46afc9743ad0..44dd1950f88a 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -2598,6 +2598,70 @@ 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); + if (!p->skip_locking) + btrfs_tree_read_lock(b); + + goto out; + } + + if (p->skip_locking) { + b = btrfs_root_node(root); + level = btrfs_header_level(b); + goto out; + } + + /* + * 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. @@ -2634,7 +2698,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; @@ -2672,50 +2735,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); -- cgit v1.2.3 From 662c653bfda58698cf48d7143a39bd3a063fd9c6 Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Fri, 18 May 2018 11:00:23 +0800 Subject: Btrfs: grab write lock directly if write_lock_level is the max level Typically, when acquiring root node's lock, btrfs tries its best to get read lock and trade for write lock if @write_lock_level implies to do so. In case of (cow && (p->keep_locks || p->lowest_level)), write_lock_level is set to BTRFS_MAX_LEVEL, which means we need to acquire root node's write lock directly. In this particular case, the dance of acquiring read lock and then trading for write lock can be saved. Signed-off-by: Liu Bo Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 44dd1950f88a..239682330929 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -2632,19 +2632,24 @@ static struct extent_buffer *btrfs_search_slot_get_root(struct btrfs_root *root, } /* - * We don't know the level of the root node until we actually have it - * read locked + * If the level is set to maximum, we can skip trying to get the read + * lock. */ - b = btrfs_read_lock_root_node(root); - level = btrfs_header_level(b); - if (level > write_lock_level) - goto out; + 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); + } - /* - * 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; -- cgit v1.2.3 From d80bb3f905ccf70b2c4dde541cff7286e6f936e8 Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Fri, 18 May 2018 11:00:24 +0800 Subject: Btrfs: remove always true check in unlock_up As unlock_up() is written as for () { if (!path->locks[i]) break; ... if (... && path->locks[i]) { } } Apparently, @path->locks[i] is always true at this 'if'. Signed-off-by: Liu Bo Reviewed-by: David Sterba Reviewed-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/ctree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 239682330929..973912ec8992 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 && -- cgit v1.2.3 From f9ddfd0592acf9bf01814e7d1d60134af7fd0a4d Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Tue, 29 May 2018 21:27:06 +0800 Subject: Btrfs: remove unused check of skip_locking The check is superfluous since all callers who set search_for_commit also have skip_locking set. ASSERT() is put in place to ensure skip_locking is set by new callers. Reviewed-by: Qu Wenruo Signed-off-by: Liu Bo Signed-off-by: David Sterba --- fs/btrfs/ctree.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 973912ec8992..4bc326df472e 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -2619,8 +2619,11 @@ static struct extent_buffer *btrfs_search_slot_get_root(struct btrfs_root *root, 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); + /* + * Ensure that all callers have set skip_locking when + * p->search_commit_root = 1. + */ + ASSERT(p->skip_locking == 1); goto out; } -- cgit v1.2.3 From cdb345a877205849042a18cc568a17620935b8f9 Mon Sep 17 00:00:00 2001 From: Lu Fengqi Date: Tue, 29 May 2018 15:01:53 +0800 Subject: btrfs: Remove fs_info argument from btrfs_uuid_tree_add This function always takes a transaction handle which contains a reference to the fs_info. Use that and remove the extra argument. Signed-off-by: Lu Fengqi Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 3 +-- fs/btrfs/ioctl.c | 4 ++-- fs/btrfs/transaction.c | 7 +++---- fs/btrfs/uuid-tree.c | 4 ++-- fs/btrfs/volumes.c | 5 ++--- 5 files changed, 10 insertions(+), 13 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index bbb358143ded..3dc1db981be7 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3040,8 +3040,7 @@ 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, diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 3b49b5eb98f1..4ba8cabedeec 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -718,7 +718,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); @@ -5043,7 +5043,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) { diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 2544acc33045..4485eae41e88 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -1634,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/uuid-tree.c b/fs/btrfs/uuid-tree.c index 1ba7ca2a4200..8be956ed4603 100644 --- a/fs/btrfs/uuid-tree.c +++ b/fs/btrfs/uuid-tree.c @@ -79,10 +79,10 @@ out: 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; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index b6757b53c297..e034ad9e23b4 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -4209,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) { @@ -4221,7 +4220,7 @@ update_tree: } 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); -- cgit v1.2.3 From d19577912d57c143bb592a061e3dbd7b6f78f71a Mon Sep 17 00:00:00 2001 From: Lu Fengqi Date: Tue, 29 May 2018 15:01:54 +0800 Subject: btrfs: Remove fs_info argument from btrfs_uuid_tree_rem This function always takes a transaction handle which contains a reference to the fs_info. Use that and remove the extra argument. Signed-off-by: Lu Fengqi Reviewed-by: Nikolay Borisov [ rename the function ] Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 3 +-- fs/btrfs/inode.c | 4 ++-- fs/btrfs/ioctl.c | 3 +-- fs/btrfs/uuid-tree.c | 6 +++--- 4 files changed, 7 insertions(+), 9 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 3dc1db981be7..9a62bc59cc39 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3042,8 +3042,7 @@ void btrfs_update_root_times(struct btrfs_trans_handle *trans, /* uuid-tree.c */ 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, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 6e9cb5338964..5d370bbdf826 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4361,7 +4361,7 @@ int btrfs_delete_subvolume(struct inode *dir, struct dentry *dentry) } } - ret = btrfs_uuid_tree_rem(trans, fs_info, dest->root_item.uuid, + ret = btrfs_uuid_tree_remove(trans, dest->root_item.uuid, BTRFS_UUID_KEY_SUBVOL, dest->root_key.objectid); if (ret && ret != -ENOENT) { @@ -4370,7 +4370,7 @@ int btrfs_delete_subvolume(struct inode *dir, struct dentry *dentry) goto out_end_trans; } if (!btrfs_is_empty_uuid(dest->root_item.received_uuid)) { - ret = btrfs_uuid_tree_rem(trans, fs_info, + ret = btrfs_uuid_tree_remove(trans, dest->root_item.received_uuid, BTRFS_UUID_KEY_RECEIVED_SUBVOL, dest->root_key.objectid); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 4ba8cabedeec..a58ab372091e 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -5018,8 +5018,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) { diff --git a/fs/btrfs/uuid-tree.c b/fs/btrfs/uuid-tree.c index 8be956ed4603..3b2ae342e649 100644 --- a/fs/btrfs/uuid-tree.c +++ b/fs/btrfs/uuid-tree.c @@ -144,10 +144,10 @@ out: 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: -- cgit v1.2.3 From 78d4295b1eeed4d857f11333caac8b4894ac4412 Mon Sep 17 00:00:00 2001 From: Ethan Lien Date: Thu, 17 May 2018 14:58:29 +0800 Subject: btrfs: lift some btrfs_cross_ref_exist checks in nocow path In nocow path, we check if the extent is snapshotted in btrfs_cross_ref_exist(). We can do the similar check earlier and avoid unnecessary search into extent tree. A fio test on a Intel D-1531, 16GB RAM, SSD RAID-5 machine as follows: [global] group_reporting time_based thread=1 ioengine=libaio bs=4k iodepth=32 size=64G runtime=180 numjobs=8 rw=randwrite [file1] filename=/mnt/nocow/testfile IOPS result: unpatched patched 1 fio round: 46670 46958 snapshot 2 fio round: 51826 54498 3 fio round: 59767 61289 After snapshot, the first fio get about 5% performance gain. As we continually write to the same file, all writes will resume to nocow mode and eventually we have no performance gain. Signed-off-by: Ethan Lien Reviewed-by: David Sterba [ update comments ] Signed-off-by: David Sterba --- fs/btrfs/inode.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 5d370bbdf826..27293ff7174d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1370,6 +1370,13 @@ next_slot: 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)) @@ -7322,6 +7329,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) { -- cgit v1.2.3 From e73e81b6d0114d4a303205a952ab2e87c44bd279 Mon Sep 17 00:00:00 2001 From: Ethan Lien Date: Mon, 28 May 2018 13:48:20 +0800 Subject: btrfs: balance dirty metadata pages in btrfs_finish_ordered_io [Problem description and how we fix it] We should balance dirty metadata pages at the end of btrfs_finish_ordered_io, since a small, unmergeable random write can potentially produce dirty metadata which is multiple times larger than the data itself. For example, a small, unmergeable 4KiB write may produce: 16KiB dirty leaf (and possibly 16KiB dirty node) in subvolume tree 16KiB dirty leaf (and possibly 16KiB dirty node) in checksum tree 16KiB dirty leaf (and possibly 16KiB dirty node) in extent tree Although we do call balance dirty pages in write side, but in the buffered write path, most metadata are dirtied only after we reach the dirty background limit (which by far only counts dirty data pages) and wakeup the flusher thread. If there are many small, unmergeable random writes spread in a large btree, we'll find a burst of dirty pages exceeds the dirty_bytes limit after we wakeup the flusher thread - which is not what we expect. In our machine, it caused out-of-memory problem since a page cannot be dropped if it is marked dirty. Someone may worry about we may sleep in btrfs_btree_balance_dirty_nodelay, but since we do btrfs_finish_ordered_io in a separate worker, it will not stop the flusher consuming dirty pages. Also, we use different worker for metadata writeback endio, sleep in btrfs_finish_ordered_io help us throttle the size of dirty metadata pages. [Reproduce steps] To reproduce the problem, we need to do 4KiB write randomly spread in a large btree. In our 2GiB RAM machine: 1) Create 4 subvolumes. 2) Run fio on each subvolume: [global] direct=0 rw=randwrite ioengine=libaio bs=4k iodepth=16 numjobs=1 group_reporting size=128G runtime=1800 norandommap time_based randrepeat=0 3) Take snapshot on each subvolume and repeat fio on existing files. 4) Repeat step (3) until we get large btrees. In our case, by observing btrfs_root_item->bytes_used, we have 2GiB of metadata in each subvolume tree and 12GiB of metadata in extent tree. 5) Stop all fio, take snapshot again, and wait until all delayed work is completed. 6) Start all fio. Few seconds later we hit OOM when the flusher starts to work. It can be reproduced even when using nocow write. Signed-off-by: Ethan Lien Reviewed-by: David Sterba [ add comment ] Signed-off-by: David Sterba --- fs/btrfs/inode.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 27293ff7174d..c431ae05cbe4 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3163,6 +3163,9 @@ out: /* 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; } -- cgit v1.2.3 From c4c129db5da8f070147f1757e16196d377ccf20b Mon Sep 17 00:00:00 2001 From: Gu JinXiang Date: Wed, 30 May 2018 11:00:38 +0800 Subject: btrfs: drop unused parameter qgroup_reserved Since commit 7775c8184ec0 ("btrfs: remove unused parameter from btrfs_subvolume_release_metadata") parameter qgroup_reserved is not used by caller of function btrfs_subvolume_reserve_metadata. So remove it. Signed-off-by: Gu JinXiang Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 3 +-- fs/btrfs/extent-tree.c | 7 ++----- fs/btrfs/inode.c | 4 +--- fs/btrfs/ioctl.c | 5 +---- 4 files changed, 5 insertions(+), 14 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 9a62bc59cc39..f4bf7874c24a 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2767,8 +2767,7 @@ void btrfs_free_reserved_data_space_noquota(struct inode *inode, u64 start, void btrfs_trans_release_chunk_metadata(struct btrfs_trans_handle *trans); 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, diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 7ebb05fe2cd8..9a65df02a13f 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -5947,7 +5947,6 @@ void btrfs_trans_release_chunk_metadata(struct btrfs_trans_handle *trans) 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; @@ -5965,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); @@ -5976,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; } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index c431ae05cbe4..7148abaf1da3 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4293,7 +4293,6 @@ int btrfs_delete_subvolume(struct inode *dir, struct dentry *dentry) struct btrfs_trans_handle *trans; struct btrfs_block_rsv block_rsv; u64 root_flags; - u64 qgroup_reserved; int ret; int err; @@ -4328,8 +4327,7 @@ int btrfs_delete_subvolume(struct inode *dir, struct dentry *dentry) * two for dir entries, * two for root ref/backref. */ - err = btrfs_subvolume_reserve_metadata(root, &block_rsv, - 5, &qgroup_reserved, true); + err = btrfs_subvolume_reserve_metadata(root, &block_rsv, 5, true); if (err) goto out_up_write; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a58ab372091e..4db2446c7015 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -569,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); @@ -594,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; @@ -803,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; -- cgit v1.2.3 From 6b0cb1f90147908f7d85dd15e4dec56c8b6b632f Mon Sep 17 00:00:00 2001 From: Gu JinXiang Date: Wed, 30 May 2018 11:00:39 +0800 Subject: btrfs: drop useless member qgroup_reserved of btrfs_pending_snapshot Since there is no more use of qgroup_reserved member in struct btrfs_pending_snapshot, remove it. Signed-off-by: Gu JinXiang Signed-off-by: David Sterba --- fs/btrfs/transaction.h | 1 - 1 file changed, 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index d8c0826bc2c7..94439482a0ec 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; -- cgit v1.2.3 From 090a127afa8f73e9618d4058d6755f7ec7453dd6 Mon Sep 17 00:00:00 2001 From: Su Yue Date: Wed, 30 May 2018 16:48:56 +0800 Subject: btrfs: return error value if create_io_em failed in cow_file_range In cow_file_range(), create_io_em() may fail, but its return value is not recorded. Then return value may be 0 even it failed which is a wrong behavior. Let cow_file_range() return PTR_ERR(em) if create_io_em() failed. Fixes: 6f9994dbabe5 ("Btrfs: create a helper to create em for IO") CC: stable@vger.kernel.org # 4.11+ Signed-off-by: Su Yue Reviewed-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/inode.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 7148abaf1da3..f81a48c47fe0 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, -- cgit v1.2.3 From 1389053e1bed93b75b0c3cd292f61032334c81b3 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 29 May 2018 16:44:59 -0700 Subject: btrfs: raid56: Remove VLA usage In the quest to remove all stack VLA usage from the kernel[1], this allocates the working buffers during regular init, instead of using stack space. This refactors the allocation code a bit to make it easier to review. [1] https://lkml.kernel.org/r/CA+55aFzCG-zNmZwX4A2FQpadafLfEzK6CC=qPXydAacU1RqZWA@mail.gmail.com Signed-off-by: Kees Cook Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/raid56.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index 9abd950e7f78..5e4ad134b9ad 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; -- cgit v1.2.3 From 9132c4ff6f9557db3ba10fb321317b4f67626ee8 Mon Sep 17 00:00:00 2001 From: Su Yue Date: Wed, 30 May 2018 14:49:10 +0800 Subject: btrfs: return ENOMEM if path allocation fails in btrfs_cross_ref_exist The error code does not match the reason of failure and may confuse the callers. Signed-off-by: Su Yue Reviewed-by: Nikolay Borisov Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 9a65df02a13f..3d9fe58c0080 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3272,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, -- cgit v1.2.3 From 1c8d0175df47364aa55c568b65ed7a3aee5b9a6d Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Wed, 2 May 2018 15:19:32 +0300 Subject: btrfs: Factor out read portion of btrfs_get_blocks_direct Currently this function handles both the READ and WRITE dio cases. This is facilitated by a bunch of 'if' statements, a goto short-circuit statement and a very perverse aliasing of "!created"(READ) case by setting lockstart = lockend and checking for lockstart < lockend for detecting the write. Let's simplify this mess by extracting the READ-only code into a separate __btrfs_get_block_direct_read function. This is only the first step, the next one will be to factor out the write side as well. The end goal will be to have the common locking/ unlocking code in btrfs_get_blocks_direct and then it will call either the read|write subvariants. No functional changes. Signed-off-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/inode.c | 56 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 13 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f81a48c47fe0..4f8fb1130cf3 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7540,6 +7540,27 @@ 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(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { @@ -7608,11 +7629,29 @@ 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))) { + if (!create) { + 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; + } + /* + * We need to unlock only the end area that we aren't using. + * The rest is going to be unlocked by the endio routine. + */ + 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); - goto unlock_err; + return 0; } /* @@ -7624,12 +7663,6 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, * 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)) { @@ -7716,10 +7749,7 @@ unlock: 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); return 0; -- cgit v1.2.3 From c5794e51784a0a96dd82e8f955570a7eccf27e5d Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Wed, 2 May 2018 15:19:33 +0300 Subject: btrfs: Factor out write portion of btrfs_get_blocks_direct Now that the read side is extracted into its own function, do the same to the write side. This leaves btrfs_get_blocks_direct_write with the sole purpose of handling common locking required. Also flip the condition in btrfs_get_blocks_direct_write so that the write case comes first and we check for if (Create) rather than if (!create). This is purely subjective but I believe makes reading a bit more "linear". No functional changes. Signed-off-by: Nikolay Borisov Signed-off-by: David Sterba --- fs/btrfs/inode.c | 207 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 108 insertions(+), 99 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 4f8fb1130cf3..880431ae5e59 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7561,6 +7561,104 @@ static int btrfs_get_blocks_direct_read(struct extent_map *em, 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) { @@ -7629,7 +7727,16 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, goto unlock_err; } - if (!create) { + if (create) { + 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 */ @@ -7650,106 +7757,8 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, } else { free_extent_state(cached_state); } - free_extent_map(em); - return 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); - 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); - - /* - * 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; - } - - /* - * 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); - } free_extent_map(em); return 0; -- cgit v1.2.3 From ad7e1a740d940cf7da1beb332a1095bcda40c747 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Tue, 22 May 2018 09:59:50 -0700 Subject: Btrfs: clean up error handling in btrfs_truncate() btrfs_truncate() uses two variables for error handling, ret and err (if this sounds familiar, it's because btrfs_truncate_inode_items() did something similar). This is error prone, as was made evident by "Btrfs: fix error handling in btrfs_truncate()". We only have err because we don't want to mask an error if we call btrfs_update_inode() and btrfs_end_transaction(), so let's make that its own scoped return variable and use ret everywhere else. Reviewed-by: Nikolay Borisov Signed-off-by: Omar Sandoval Signed-off-by: David Sterba --- fs/btrfs/inode.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) (limited to 'fs/btrfs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 880431ae5e59..89b208201783 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -9030,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); @@ -9083,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; } @@ -9107,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; } @@ -9158,21 +9152,22 @@ static int btrfs_truncate(struct inode *inode, bool skip_writeback) } 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; } /* -- cgit v1.2.3 From b64ec075bded2b30bcd90af5aa5256d2237c885d Mon Sep 17 00:00:00 2001 From: Tomohiro Misono Date: Mon, 21 May 2018 10:09:42 +0900 Subject: btrfs: Add unprivileged ioctl which returns subvolume information Add new unprivileged ioctl BTRFS_IOC_GET_SUBVOL_INFO which returns the information of subvolume containing this inode. (i.e. returns the information in ROOT_ITEM and ROOT_BACKREF.) Reviewed-by: Gu Jinxiang Tested-by: Gu Jinxiang Signed-off-by: Tomohiro Misono [ minor style fixes, update struct comments ] Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 121 +++++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/btrfs.h | 62 +++++++++++++++++++++++ 2 files changed, 183 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 4db2446c7015..42ed752288e6 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2383,6 +2383,125 @@ out: 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; + +out: + btrfs_free_path(path); + kzfree(subvol_info); + return ret; +} + static noinline int btrfs_ioctl_snap_destroy(struct file *file, void __user *arg) { @@ -5545,6 +5664,8 @@ long btrfs_ioctl(struct file *file, unsigned int 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); } return -ENOTTY; diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index c8d99b9ca550..f8f20d72b852 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -725,6 +725,66 @@ 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]; +}; + /* Error codes as returned by the kernel */ enum btrfs_err_code { BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET = 1, @@ -843,5 +903,7 @@ 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) #endif /* _UAPI_LINUX_BTRFS_H */ -- cgit v1.2.3 From 42e4b520c812daaf5e6177c2e4beec012ce1e2ce Mon Sep 17 00:00:00 2001 From: Tomohiro Misono Date: Mon, 21 May 2018 10:09:43 +0900 Subject: btrfs: Add unprivileged ioctl which returns subvolume's ROOT_REF Add unprivileged ioctl BTRFS_IOC_GET_SUBVOL_ROOTREF which returns ROOT_REF information of the subvolume containing this inode except the subvolume name (this is because to prevent potential name leak). The subvolume name will be gained by user version of ino_lookup ioctl (BTRFS_IOC_INO_LOOKUP_USER) which also performs permission check. The min id of root ref's subvolume to be searched is specified by @min_id in struct btrfs_ioctl_get_subvol_rootref_args. After the search ends, @min_id is set to the last searched root ref's subvolid + 1. Also, if there are more root refs than BTRFS_MAX_ROOTREF_BUFFER_NUM, -EOVERFLOW is returned. Therefore the caller can just call this ioctl again without changing the argument to continue search. Reviewed-by: Qu Wenruo Reviewed-by: Gu Jinxiang Tested-by: Gu Jinxiang Signed-off-by: Tomohiro Misono [ style fixes and struct item renames ] Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/btrfs.h | 18 +++++++++ 2 files changed, 117 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 42ed752288e6..be9b3f39183c 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2502,6 +2502,103 @@ out: return ret; } +/* + * 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_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; + + 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; + + key.objectid = objectid; + key.type = BTRFS_ROOT_REF_KEY; + key.offset = rootrefs->min_treeid; + found = 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]; + + btrfs_item_key_to_cpu(leaf, &key, slot); + if (key.objectid != objectid || key.type != BTRFS_ROOT_REF_KEY) { + ret = 0; + goto out; + } + + 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 || 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); + + return ret; +} + static noinline int btrfs_ioctl_snap_destroy(struct file *file, void __user *arg) { @@ -5666,6 +5763,8 @@ long btrfs_ioctl(struct file *file, unsigned int 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); } return -ENOTTY; diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index f8f20d72b852..f90d10478235 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -785,6 +785,22 @@ struct btrfs_ioctl_get_subvol_info_args { __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, @@ -905,5 +921,7 @@ enum btrfs_err_code { 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) #endif /* _UAPI_LINUX_BTRFS_H */ -- cgit v1.2.3 From 23d0b79dfaed2305b500b0215b0421701ada6b1a Mon Sep 17 00:00:00 2001 From: Tomohiro Misono Date: Mon, 21 May 2018 10:09:44 +0900 Subject: btrfs: Add unprivileged version of ino_lookup ioctl Add unprivileged version of ino_lookup ioctl BTRFS_IOC_INO_LOOKUP_USER to allow normal users to call "btrfs subvolume list/show" etc. in combination with BTRFS_IOC_GET_SUBVOL_INFO/BTRFS_IOC_GET_SUBVOL_ROOTREF. This can be used like BTRFS_IOC_INO_LOOKUP but the argument is different. This is because it always searches the fs/file tree correspoinding to the fd with which this ioctl is called and also returns the name of bottom subvolume. The main differences from original 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. Note that the maximum length of path is shorter 256 (BTRFS_VOL_NAME_MAX+1) bytes than ino_lookup ioctl because of space of subvolume's name. Reviewed-by: Gu Jinxiang Reviewed-by: Qu Wenruo Tested-by: Gu Jinxiang Signed-off-by: Tomohiro Misono [ style fixes ] Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 204 +++++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/btrfs.h | 17 ++++ 2 files changed, 221 insertions(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index be9b3f39183c..d29992f7dc63 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2341,6 +2341,165 @@ out: 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) { @@ -2383,6 +2542,49 @@ out: 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) { @@ -5765,6 +5967,8 @@ long btrfs_ioctl(struct file *file, unsigned int 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/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index f90d10478235..5ca1d21fc4a7 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 { /* @@ -923,5 +938,7 @@ enum btrfs_err_code { 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 */ -- cgit v1.2.3