btrfs-progs: change-csum: fix the wrong metadata space reservation

[BUG]
'btrfstune --csum' would always fail for a newly created btrfs:

  # truncate -s 1G test.img
  # ./mkfs.btrfs -f test.img
  # ./btrsftune --csum xxhash test.img
  WARNING: Experimental build with unstable or unfinished features
  WARNING: Switching checksums is experimental, do not use for valuable data!

  Proceed to switch checksums
  ERROR: failed to start transaction: Unknown error -28
  ERROR: failed to start transaction: No space left on device
  ERROR: failed to generate new data csums: No space left on device
  ERROR: btrfstune failed

[CAUSE]
After commit e79f18a4a7 ("btrfs-progs: introduce a basic metadata free
space reservation check"), btrfs_start_transaction() would check the
metadata space.

But at the time of introduction of csum conversion, the parameter for
btrfs_start_transaction() was incorrect.

The 2nd parameter is the *number* of items to be added (if we're
deleting items, just pass 1).
However commit 08a3bd7694 ("btrfs-progs: tune: add the ability to
generate new data checksums") is using the item size, not the number of
items to be added.

This means we're passing a number 8 * nodesize times larger than the
original size, no wonder we would error out with -ENOSPC.

[FIX]
Use proper calcuation to convert the new csum item size to number of
leaves needed, and double it just in case.

Pull-request: #820
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Qu Wenruo 2024-06-18 15:11:39 +09:30
parent 5cdaf19529
commit 3644eb7148

View file

@ -224,14 +224,25 @@ out:
* item. * item.
*/ */
#define CSUM_CHANGE_BYTES_THRESHOLD (SZ_2M) #define CSUM_CHANGE_BYTES_THRESHOLD (SZ_2M)
static unsigned int calc_csum_change_nr_items(struct btrfs_fs_info *fs_info,
u16 new_csum_type)
{
const u32 new_csum_size = btrfs_csum_type_size(new_csum_type);
const u32 csum_item_size = CSUM_CHANGE_BYTES_THRESHOLD /
fs_info->sectorsize * new_csum_size;
return round_up(csum_item_size, fs_info->nodesize) / fs_info->nodesize * 2;
}
static int generate_new_data_csums_range(struct btrfs_fs_info *fs_info, u64 start, static int generate_new_data_csums_range(struct btrfs_fs_info *fs_info, u64 start,
u16 new_csum_type) u16 new_csum_type)
{ {
const unsigned int nr_items = calc_csum_change_nr_items(fs_info, new_csum_type);
struct btrfs_root *csum_root = btrfs_csum_root(fs_info, 0); struct btrfs_root *csum_root = btrfs_csum_root(fs_info, 0);
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
struct btrfs_path path = { 0 }; struct btrfs_path path = { 0 };
struct btrfs_key key; struct btrfs_key key;
const u32 new_csum_size = btrfs_csum_type_size(new_csum_type);
void *csum_buffer; void *csum_buffer;
u64 converted_bytes = 0; u64 converted_bytes = 0;
u64 last_csum; u64 last_csum;
@ -248,9 +259,7 @@ static int generate_new_data_csums_range(struct btrfs_fs_info *fs_info, u64 star
if (!csum_buffer) if (!csum_buffer)
return -ENOMEM; return -ENOMEM;
trans = btrfs_start_transaction(csum_root, trans = btrfs_start_transaction(csum_root, nr_items);
CSUM_CHANGE_BYTES_THRESHOLD / fs_info->sectorsize *
new_csum_size);
if (IS_ERR(trans)) { if (IS_ERR(trans)) {
ret = PTR_ERR(trans); ret = PTR_ERR(trans);
errno = -ret; errno = -ret;
@ -306,9 +315,7 @@ static int generate_new_data_csums_range(struct btrfs_fs_info *fs_info, u64 star
return -EUCLEAN; return -EUCLEAN;
if (ret < 0) if (ret < 0)
goto out; goto out;
trans = btrfs_start_transaction(csum_root, trans = btrfs_start_transaction(csum_root, nr_items);
CSUM_CHANGE_BYTES_THRESHOLD /
fs_info->sectorsize * new_csum_size);
if (IS_ERR(trans)) { if (IS_ERR(trans)) {
ret = PTR_ERR(trans); ret = PTR_ERR(trans);
goto out; goto out;