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. Pull-request: #890 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>
This commit is contained in:
parent
18ecbfd3dd
commit
85225ea00a
|
@ -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,86 @@ 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 for rootid %llu: %m",
|
||||
cur_subvol);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue