@@ -449,7 +449,7 @@ static int cmd_rescue_clear_ino_cache(const struct cmd_struct *cmd,
errno = -ret;
error("failed to clear ino cache: %m");
} else {
- pr_verbose(LOG_DEFAULT, "Successfully cleared ino cache");
+ pr_verbose(LOG_DEFAULT, "Successfully cleared ino cache\n");
}
close_ctree(fs_info->tree_root);
out:
@@ -555,69 +555,85 @@ out:
return ret;
}
-int clear_ino_cache_items(struct btrfs_fs_info *fs_info)
+/*
+ * Find a root item whose key.objectid >= @rootid, and save the found
+ * key into @found_key.
+ *
+ * Return 0 if a root item is found.
+ * Return >0 if no more root item is found.
+ * Return <0 for error.
+ */
+static int find_next_root(struct btrfs_fs_info *fs_info, u64 rootid,
+ struct btrfs_key *found_key)
{
- int ret;
+ struct btrfs_key key = {
+ .objectid = rootid,
+ .type = BTRFS_ROOT_ITEM_KEY,
+ .offset = 0,
+ };
struct btrfs_path path = { 0 };
- struct btrfs_key key;
-
- key.objectid = BTRFS_FS_TREE_OBJECTID;
- key.type = BTRFS_ROOT_ITEM_KEY;
- key.offset = 0;
+ int ret;
ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, &path, 0, 0);
if (ret < 0)
return ret;
-
- while(1) {
- struct btrfs_key found_key;
-
- btrfs_item_key_to_cpu(path.nodes[0], &found_key, path.slots[0]);
- if (found_key.type == BTRFS_ROOT_ITEM_KEY &&
- is_fstree(found_key.objectid)) {
- struct btrfs_root *root;
-
- found_key.offset = (u64)-1;
- root = btrfs_read_fs_root(fs_info, &found_key);
- if (IS_ERR(root))
- goto next;
- ret = truncate_free_ino_items(root);
- if (ret)
- goto out;
- printf("Successfully cleaned up ino cache for root id: %lld\n",
- root->objectid);
- } else {
- /* If we get a negative tree this means it's the last one */
- if ((s64)found_key.objectid < 0 &&
- found_key.type == BTRFS_ROOT_ITEM_KEY)
- goto out;
- }
-
- /*
- * Only fs roots contain an ino cache information - either
- * FS_TREE_OBJECTID or subvol id >= BTRFS_FIRST_FREE_OBJECTID
- */
-next:
- if (key.objectid == BTRFS_FS_TREE_OBJECTID) {
- key.objectid = BTRFS_FIRST_FREE_OBJECTID;
- btrfs_release_path(&path);
- ret = btrfs_search_slot(NULL, fs_info->tree_root, &key,
- &path, 0, 0);
- if (ret < 0)
- return ret;
- } else {
- ret = btrfs_next_item(fs_info->tree_root, &path);
- if (ret < 0) {
- goto out;
- } else if (ret > 0) {
- ret = 0;
- goto out;
- }
+ while (true) {
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (key.type == BTRFS_ROOT_ITEM_KEY && key.objectid >= rootid) {
+ memcpy(found_key, &key, sizeof(key));
+ ret = 0;
+ goto out;
}
+ ret = btrfs_next_item(fs_info->tree_root, &path);
+ if (ret)
+ goto out;
}
-
out:
btrfs_release_path(&path);
return ret;
}
+int clear_ino_cache_items(struct btrfs_fs_info *fs_info)
+{
+ u64 cur_subvol = BTRFS_FS_TREE_OBJECTID;
+ int ret;
+
+ while (1) {
+ struct btrfs_key key = { 0 };
+ struct btrfs_root *root;
+
+ ret = find_next_root(fs_info, cur_subvol, &key);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to find the next root item: %m");
+ break;
+ }
+ if (ret > 0 || !is_fstree(key.objectid)) {
+ ret = 0;
+ break;
+ }
+ root = btrfs_read_fs_root(fs_info, &key);
+ if (IS_ERR(root)) {
+ ret = PTR_ERR(root);
+ errno = -ret;
+ error("failed to read root %llu: %m", key.objectid);
+ break;
+ }
+ ret = truncate_free_ino_items(root);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to clean up ino cache for root %llu: %m",
+ key.objectid);
+ break;
+ }
+ printf("Successfully cleaned up ino cache for root id: %lld\n",
+ root->objectid);
+
+ if (cur_subvol == BTRFS_FS_TREE_OBJECTID)
+ cur_subvol = BTRFS_FIRST_FREE_OBJECTID;
+ else
+ cur_subvol = root->objectid + 1;
+ }
+ return ret;
+}
+
[BUG] There is one report about `btrfs rescue clear-ino-cache` failed with tree block level mismatch: # btrfs rescue clear-ino-cache /dev/mapper/rootext Successfully cleaned up ino cache for root id: 5 Successfully cleaned up ino cache for root id: 257 Successfully cleaned up ino cache for root id: 258 corrupt node: root=7 block=647369064448 slot=0, invalid level for leaf, have 1 expect 0 node 647369064448 level 1 items 252 free space 241 generation 6065173 owner CSUM_TREE node 647369064448 flags 0x1(WRITTEN) backref revision 1 fs uuid e6614f01-6f56-4776-8b0a-c260089c35e7 chunk uuid f665f535-4cfd-49e0-8be9-7f94bf59b75d key (EXTENT_CSUM EXTENT_CSUM 3714473984) block 677126111232 gen 6065002 [...] key (EXTENT_CSUM EXTENT_CSUM 6192357376) block 646396493824 gen 6065032 ERROR: failed to clear ino cache: Input/output error [CAUSE] During `btrfs rescue clear-ino-cache`, btrfs-progs will iterate through all the subvolumes, and clear the inode cache inode from each subvolume. The problem is in how we iterate the subvolumes. We hold a path of tree root, and go modifiy the fs for each found subvolume, then call btrfs_next_item(). This is not safe, because the path to tree root is not longer reliable if we modified the fs. So the btrfs_next_item() call will fail because the fs is modified halfway, resulting the above problem. [FIX] Instead of holding a path to a subvolume root item, and modify the fs halfway, here introduce a helper, find_next_root(), to locate the root item whose objectid >= our target rootid, and return the found item key. The path to root tree is only hold then released inside find_next_root(). By this, we won't hold any unrelated path while modifying the filesystem. And since we're here, also adding back the missing new line when all ino cache is cleared. Reported-by: Archange <archange@archlinux.org> Link: https://lore.kernel.org/linux-btrfs/4803f696-2dc5-4987-a353-fce1272e93e7@archlinux.org/ Signed-off-by: Qu Wenruo <wqu@suse.com> --- cmds/rescue.c | 2 +- common/clear-cache.c | 122 ++++++++++++++++++++++++------------------- 2 files changed, 70 insertions(+), 54 deletions(-)