If subvolume is deleted, we add drop_key into the corresponding root item, so we know where to start processing the deleted subvolume. However, we don't skip the keys prior to the drop_key in corresponding relocation tree of the deleted subvolume. As a result, we might run into block that is freed and being used again. This cause btrfs check to report false alarm.
Fix this by adding drop_key for deleted subvolume to its corresponding relocation tree. Signed-off-by: ethanwu <etha...@synology.com> --- cmds-check.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/cmds-check.c b/cmds-check.c index bf6398d..4d7cb68 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -92,6 +92,13 @@ struct extent_backref { unsigned int broken:1; }; +static int add_root_item_to_list(struct list_head *head, + u64 objectid, u64 bytenr, u64 last_snapshot, + u8 level, u8 drop_level, + int level_size, struct btrfs_key *drop_key); + +static void free_root_item_list(struct list_head *list); + static inline struct extent_backref* to_extent_backref(struct list_head *entry) { return list_entry(entry, struct extent_backref, list); @@ -3761,6 +3768,10 @@ static int check_fs_roots(struct btrfs_root *root, struct btrfs_root *tree_root = root->fs_info->tree_root; int ret; int err = 0; + struct list_head dropping_trees; + struct btrfs_disk_key *drop_progress; + + INIT_LIST_HEAD(&dropping_trees); if (ctx.progress_enabled) { ctx.tp = TASK_FS_ROOTS; @@ -3818,6 +3829,32 @@ again: err = 1; goto next; } + + drop_progress = &tmp_root->root_item.drop_progress; + if (btrfs_disk_key_objectid(drop_progress) != 0) { + struct btrfs_key drop_key; + + btrfs_disk_key_to_cpu(&drop_key, drop_progress); + ret = add_root_item_to_list(&dropping_trees, + tmp_root->root_key.objectid, + 0, 0, 0, tmp_root->root_item.drop_level, + 0, &drop_key); + if (ret < 0) { + err = 1; + goto out; + } + } else if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) { + struct root_item_record *ri_rec; + + list_for_each_entry(ri_rec, &dropping_trees, list) { + if (ri_rec->objectid == key.offset) { + btrfs_cpu_key_to_disk(drop_progress, &ri_rec->drop_key); + tmp_root->root_item.drop_level = ri_rec->drop_level; + break; + } + } + } + ret = check_fs_root(tmp_root, root_cache, &wc); if (ret == -EAGAIN) { free_root_recs_tree(root_cache); @@ -3837,6 +3874,7 @@ next: path.slots[0]++; } out: + free_root_item_list(&dropping_trees); btrfs_release_path(&path); if (err) free_extent_cache_tree(&wc.shared); @@ -8536,6 +8574,9 @@ again: if (found_key.type == BTRFS_ROOT_ITEM_KEY) { unsigned long offset; u64 last_snapshot; + struct root_item_record *ri_rec; + struct btrfs_key drop_key = {0}; + u8 drop_level = 0; offset = btrfs_item_ptr_offset(leaf, path.slots[0]); read_extent_buffer(leaf, &ri, offset, sizeof(ri)); @@ -8543,11 +8584,20 @@ again: if (btrfs_disk_key_objectid(&ri.drop_progress) == 0) { level = btrfs_root_level(&ri); level_size = root->nodesize; + if (found_key.objectid == BTRFS_TREE_RELOC_OBJECTID) { + list_for_each_entry(ri_rec, &dropping_trees, list) { + if (ri_rec->objectid == found_key.offset) { + drop_key = ri_rec->drop_key; + drop_level = ri_rec->drop_level; + break; + } + } + } ret = add_root_item_to_list(&normal_trees, found_key.objectid, btrfs_root_bytenr(&ri), last_snapshot, level, - 0, level_size, NULL); + drop_level, level_size, &drop_key); if (ret < 0) goto out; } else { -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html