btrfs-progs: check: add the ability to reset btrfs_dev_item::bytes_used

There is a report from the mailing list that one user got its filesystem
with device item bytes_used mismatch.

This problem leaves the device item with some ghost bytes_used, meaning
even if we delete all device extents of that device, the bytes_used
still won't be 0.

This itself is not a big deal, but when the user used up all its
unallocated space, write time tree-checker can be triggered and make the
fs RO, as the new device::bytes_used can be larger than
device::total_bytes.

Thus we need to fix the problem in btrfs-check to avoid above write-time
tree check warning.

This patch will add the ability to reset a device's bytes_used to both
original mode and lowmem mode.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Qu Wenruo 2021-06-09 14:27:42 +08:00 committed by David Sterba
parent 9f6c055e38
commit 0ebd1f47c5
4 changed files with 80 additions and 3 deletions

View File

@ -8397,11 +8397,17 @@ static int check_device_used(struct device_record *dev_rec,
}
if (total_byte != dev_rec->byte_used) {
int ret = -1;
fprintf(stderr,
"Dev extent's total-byte(%llu) is not equal to byte-used(%llu) in dev[%llu, %u, %llu]\n",
total_byte, dev_rec->byte_used, dev_rec->objectid,
dev_rec->type, dev_rec->offset);
return -1;
if (repair) {
ret = repair_dev_item_bytes_used(gfs_info,
dev_rec->devid, total_byte);
}
return ret;
} else {
return 0;
}

View File

@ -21,6 +21,7 @@
#include "kernel-shared/transaction.h"
#include "common/utils.h"
#include "kernel-shared/disk-io.h"
#include "kernel-shared/volumes.h"
#include "common/repair.h"
#include "check/mode-common.h"
@ -1243,3 +1244,60 @@ out:
btrfs_release_path(&path);
return ret;
}
int repair_dev_item_bytes_used(struct btrfs_fs_info *fs_info,
u64 devid, u64 bytes_used_expected)
{
struct btrfs_trans_handle *trans;
struct btrfs_device *device;
int ret;
device = btrfs_find_device_by_devid(fs_info->fs_devices, devid, 0);
if (!device) {
error("failed to find device with devid %llu", devid);
return -ENOENT;
}
/* Bytes_used matches, not what we can repair */
if (device->bytes_used == bytes_used_expected)
return -ENOTSUP;
/*
* We have to set the device bytes_used right now, before starting a
* new transaction, as it may allocate a new chunk and modify
* device->bytes_used.
*/
device->bytes_used = bytes_used_expected;
trans = btrfs_start_transaction(fs_info->chunk_root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
errno = -ret;
error("failed to start transaction: %m");
return ret;
}
/* Manually update the device item in chunk tree */
ret = btrfs_update_device(trans, device);
if (ret < 0) {
errno = -ret;
error("failed to update device item for devid %llu: %m", devid);
goto error;
}
/*
* Commit transaction not only to save the above change but also update
* the device item in super block.
*/
ret = btrfs_commit_transaction(trans, fs_info->chunk_root);
if (ret < 0) {
errno = -ret;
error("failed to commit transaction: %m");
} else {
printf("reset devid %llu bytes_used to %llu\n", devid,
device->bytes_used);
}
return ret;
error:
btrfs_abort_transaction(trans, ret);
return ret;
}

View File

@ -192,4 +192,7 @@ static inline void btrfs_check_subpage_eb_alignment(u64 start, u32 len)
start, start + len);
}
int repair_dev_item_bytes_used(struct btrfs_fs_info *fs_info,
u64 devid, u64 bytes_used_expected);
#endif

View File

@ -4454,7 +4454,8 @@ out:
/*
* Check if the used space is correct with the dev item
*/
static int check_dev_item(struct extent_buffer *eb, int slot)
static int check_dev_item(struct extent_buffer *eb, int slot,
u64 *bytes_used_expected)
{
struct btrfs_root *dev_root = gfs_info->dev_root;
struct btrfs_dev_item *dev_item;
@ -4543,6 +4544,7 @@ next:
}
btrfs_release_path(&path);
*bytes_used_expected = total;
if (used != total) {
btrfs_item_key_to_cpu(eb, &key, slot);
error(
@ -4744,6 +4746,7 @@ static int repair_chunk_item(struct btrfs_root *chunk_root,
static int check_leaf_items(struct btrfs_root *root, struct btrfs_path *path,
struct node_refs *nrefs, int account_bytes)
{
u64 bytes_used_expected = (u64)-1;
struct btrfs_key key;
struct extent_buffer *eb;
int slot;
@ -4782,7 +4785,14 @@ again:
err |= ret;
break;
case BTRFS_DEV_ITEM_KEY:
ret = check_dev_item(eb, slot);
ret = check_dev_item(eb, slot, &bytes_used_expected);
if (repair && (ret & ACCOUNTING_MISMATCH) &&
bytes_used_expected != (u64)-1) {
ret = repair_dev_item_bytes_used(root->fs_info,
key.offset, bytes_used_expected);
if (ret < 0)
ret = ACCOUNTING_MISMATCH;
}
err |= ret;
break;
case BTRFS_CHUNK_ITEM_KEY: