mirror of
https://github.com/kdave/btrfs-progs
synced 2025-02-02 10:51:43 +00:00
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:
parent
9f6c055e38
commit
0ebd1f47c5
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user