Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e165aa3

Browse files
committedSep 12, 2024
btrfs-progs: fix a false failure for inode cache cleanup
[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>
1 parent 9a2e024 commit e165aa3

File tree

2 files changed

+69
-52
lines changed

2 files changed

+69
-52
lines changed
 

‎cmds/rescue.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ static int cmd_rescue_clear_ino_cache(const struct cmd_struct *cmd,
449449
errno = -ret;
450450
error("failed to clear ino cache: %m");
451451
} else {
452-
pr_verbose(LOG_DEFAULT, "Successfully cleared ino cache");
452+
pr_verbose(LOG_DEFAULT, "Successfully cleared ino cache\n");
453453
}
454454
close_ctree(fs_info->tree_root);
455455
out:

‎common/clear-cache.c

+68-51
Original file line numberDiff line numberDiff line change
@@ -555,69 +555,86 @@ int truncate_free_ino_items(struct btrfs_root *root)
555555
return ret;
556556
}
557557

558-
int clear_ino_cache_items(struct btrfs_fs_info *fs_info)
558+
/*
559+
* Find a root item whose key.objectid >= @rootid, and save the found
560+
* key into @found_key.
561+
*
562+
* Return 0 if a root item is found.
563+
* Return >0 if no more root item is found.
564+
* Return <0 for error.
565+
*/
566+
static int find_next_root(struct btrfs_fs_info *fs_info, u64 rootid,
567+
struct btrfs_key *found_key)
559568
{
560-
int ret;
569+
struct btrfs_key key = {
570+
.objectid = rootid,
571+
.type = BTRFS_ROOT_ITEM_KEY,
572+
.offset = 0,
573+
};
561574
struct btrfs_path path = { 0 };
562-
struct btrfs_key key;
563-
564-
key.objectid = BTRFS_FS_TREE_OBJECTID;
565-
key.type = BTRFS_ROOT_ITEM_KEY;
566-
key.offset = 0;
575+
int ret;
567576

568577
ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, &path, 0, 0);
569578
if (ret < 0)
570579
return ret;
580+
while (true) {
581+
btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
582+
if (key.type == BTRFS_ROOT_ITEM_KEY && key.objectid >= rootid) {
583+
memcpy(found_key, &key, sizeof(key));
584+
ret = 0;
585+
goto out;
586+
}
587+
ret = btrfs_next_item(fs_info->tree_root, &path);
588+
if (ret)
589+
goto out;
590+
}
591+
out:
592+
btrfs_release_path(&path);
593+
return ret;
594+
}
571595

572-
while(1) {
573-
struct btrfs_key found_key;
596+
int clear_ino_cache_items(struct btrfs_fs_info *fs_info)
597+
{
598+
u64 cur_subvol = BTRFS_FS_TREE_OBJECTID;
599+
int ret;
574600

575-
btrfs_item_key_to_cpu(path.nodes[0], &found_key, path.slots[0]);
576-
if (found_key.type == BTRFS_ROOT_ITEM_KEY &&
577-
is_fstree(found_key.objectid)) {
578-
struct btrfs_root *root;
601+
while (1) {
602+
struct btrfs_key key = { 0 };
603+
struct btrfs_root *root;
579604

580-
found_key.offset = (u64)-1;
581-
root = btrfs_read_fs_root(fs_info, &found_key);
582-
if (IS_ERR(root))
583-
goto next;
584-
ret = truncate_free_ino_items(root);
585-
if (ret)
586-
goto out;
587-
printf("Successfully cleaned up ino cache for root id: %lld\n",
588-
root->objectid);
589-
} else {
590-
/* If we get a negative tree this means it's the last one */
591-
if ((s64)found_key.objectid < 0 &&
592-
found_key.type == BTRFS_ROOT_ITEM_KEY)
593-
goto out;
605+
ret = find_next_root(fs_info, cur_subvol, &key);
606+
if (ret < 0) {
607+
errno = -ret;
608+
error("failed to find the next root item for rootid %llu: %m",
609+
cur_subvol);
610+
break;
594611
}
595-
596-
/*
597-
* Only fs roots contain an ino cache information - either
598-
* FS_TREE_OBJECTID or subvol id >= BTRFS_FIRST_FREE_OBJECTID
599-
*/
600-
next:
601-
if (key.objectid == BTRFS_FS_TREE_OBJECTID) {
602-
key.objectid = BTRFS_FIRST_FREE_OBJECTID;
603-
btrfs_release_path(&path);
604-
ret = btrfs_search_slot(NULL, fs_info->tree_root, &key,
605-
&path, 0, 0);
606-
if (ret < 0)
607-
return ret;
608-
} else {
609-
ret = btrfs_next_item(fs_info->tree_root, &path);
610-
if (ret < 0) {
611-
goto out;
612-
} else if (ret > 0) {
613-
ret = 0;
614-
goto out;
615-
}
612+
if (ret > 0 || !is_fstree(key.objectid)) {
613+
ret = 0;
614+
break;
616615
}
617-
}
616+
root = btrfs_read_fs_root(fs_info, &key);
617+
if (IS_ERR(root)) {
618+
ret = PTR_ERR(root);
619+
errno = -ret;
620+
error("failed to read root %llu: %m", key.objectid);
621+
break;
622+
}
623+
ret = truncate_free_ino_items(root);
624+
if (ret < 0) {
625+
errno = -ret;
626+
error("failed to clean up ino cache for root %llu: %m",
627+
key.objectid);
628+
break;
629+
}
630+
printf("Successfully cleaned up ino cache for root id: %lld\n",
631+
root->objectid);
618632

619-
out:
620-
btrfs_release_path(&path);
633+
if (cur_subvol == BTRFS_FS_TREE_OBJECTID)
634+
cur_subvol = BTRFS_FIRST_FREE_OBJECTID;
635+
else
636+
cur_subvol = root->objectid + 1;
637+
}
621638
return ret;
622639
}
623640

0 commit comments

Comments
 (0)
Please sign in to comment.