btrfs: fix ->free_chunk_space math in btrfs_shrink_device

There are two bugs in how we adjust ->free_chunk_space in
btrfs_shrink_device.  First we're removing the entire diff between
new_size and old_size from ->free_chunk_space.  This only works if we're
reducing the free area, which we could potentially not be.  So adjust
the math to only subtract the diff in the free space from
->free_chunk_space.

Additionally in the error case we're unconditionally adding the diff
back into ->free_chunk_space, which we need to only do if this device is
writeable.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Josef Bacik 2023-09-27 13:46:59 -04:00 committed by David Sterba
parent efba145449
commit e9fd2c0523

View file

@ -4722,6 +4722,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
u64 old_size = btrfs_device_get_total_bytes(device);
u64 diff;
u64 start;
u64 free_diff = 0;
new_size = round_down(new_size, fs_info->sectorsize);
start = new_size;
@ -4747,7 +4748,19 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
btrfs_device_set_total_bytes(device, new_size);
if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) {
device->fs_devices->total_rw_bytes -= diff;
atomic64_sub(diff, &fs_info->free_chunk_space);
/*
* The new free_chunk_space is new_size - used, so we have to
* subtract the delta of the old free_chunk_space which included
* old_size - used. If used > new_size then just subtract this
* entire device's free space.
*/
if (device->bytes_used < new_size)
free_diff = (old_size - device->bytes_used) -
(new_size - device->bytes_used);
else
free_diff = old_size - device->bytes_used;
atomic64_sub(free_diff, &fs_info->free_chunk_space);
}
/*
@ -4882,9 +4895,10 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
if (ret) {
mutex_lock(&fs_info->chunk_mutex);
btrfs_device_set_total_bytes(device, old_size);
if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state))
if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) {
device->fs_devices->total_rw_bytes += diff;
atomic64_add(diff, &fs_info->free_chunk_space);
atomic64_add(free_diff, &fs_info->free_chunk_space);
}
mutex_unlock(&fs_info->chunk_mutex);
}
return ret;