Commit e9fd2c05 authored by Josef Bacik's avatar Josef Bacik Committed by David Sterba

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: default avatarJosef Bacik <josef@toxicpanda.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent efba1454
...@@ -4722,6 +4722,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size) ...@@ -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 old_size = btrfs_device_get_total_bytes(device);
u64 diff; u64 diff;
u64 start; u64 start;
u64 free_diff = 0;
new_size = round_down(new_size, fs_info->sectorsize); new_size = round_down(new_size, fs_info->sectorsize);
start = new_size; start = new_size;
...@@ -4747,7 +4748,19 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 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); btrfs_device_set_total_bytes(device, new_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; 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) ...@@ -4882,9 +4895,10 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
if (ret) { if (ret) {
mutex_lock(&fs_info->chunk_mutex); mutex_lock(&fs_info->chunk_mutex);
btrfs_device_set_total_bytes(device, old_size); 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; 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); mutex_unlock(&fs_info->chunk_mutex);
} }
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment