| From 6ba9fc8e628becf0e3ec94083450d089b0dec5f5 Mon Sep 17 00:00:00 2001 |
| From: Qu Wenruo <wqu@suse.com> |
| Date: Fri, 7 Sep 2018 14:16:24 +0800 |
| Subject: btrfs: Ensure btrfs_trim_fs can trim the whole filesystem |
| |
| From: Qu Wenruo <wqu@suse.com> |
| |
| commit 6ba9fc8e628becf0e3ec94083450d089b0dec5f5 upstream. |
| |
| [BUG] |
| fstrim on some btrfs only trims the unallocated space, not trimming any |
| space in existing block groups. |
| |
| [CAUSE] |
| Before fstrim_range passed to btrfs_trim_fs(), it gets truncated to |
| range [0, super->total_bytes). So later btrfs_trim_fs() will only be |
| able to trim block groups in range [0, super->total_bytes). |
| |
| While for btrfs, any bytenr aligned to sectorsize is valid, since btrfs |
| uses its logical address space, there is nothing limiting the location |
| where we put block groups. |
| |
| For filesystem with frequent balance, it's quite easy to relocate all |
| block groups and bytenr of block groups will start beyond |
| super->total_bytes. |
| |
| In that case, btrfs will not trim existing block groups. |
| |
| [FIX] |
| Just remove the truncation in btrfs_ioctl_fitrim(), so btrfs_trim_fs() |
| can get the unmodified range, which is normally set to [0, U64_MAX]. |
| |
| Reported-by: Chris Murphy <lists@colorremedies.com> |
| Fixes: f4c697e6406d ("btrfs: return EINVAL if start > total_bytes in fitrim ioctl") |
| CC: <stable@vger.kernel.org> # v4.4+ |
| Signed-off-by: Qu Wenruo <wqu@suse.com> |
| Reviewed-by: Nikolay Borisov <nborisov@suse.com> |
| Reviewed-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/btrfs/extent-tree.c | 10 +--------- |
| fs/btrfs/ioctl.c | 11 +++++++---- |
| 2 files changed, 8 insertions(+), 13 deletions(-) |
| |
| --- a/fs/btrfs/extent-tree.c |
| +++ b/fs/btrfs/extent-tree.c |
| @@ -10708,17 +10708,9 @@ int btrfs_trim_fs(struct btrfs_root *roo |
| u64 start; |
| u64 end; |
| u64 trimmed = 0; |
| - u64 total_bytes = btrfs_super_total_bytes(fs_info->super_copy); |
| int ret = 0; |
| |
| - /* |
| - * try to trim all FS space, our block group may start from non-zero. |
| - */ |
| - if (range->len == total_bytes) |
| - cache = btrfs_lookup_first_block_group(fs_info, range->start); |
| - else |
| - cache = btrfs_lookup_block_group(fs_info, range->start); |
| - |
| + cache = btrfs_lookup_first_block_group(fs_info, range->start); |
| while (cache) { |
| if (cache->key.objectid >= (range->start + range->len)) { |
| btrfs_put_block_group(cache); |
| --- a/fs/btrfs/ioctl.c |
| +++ b/fs/btrfs/ioctl.c |
| @@ -378,7 +378,6 @@ static noinline int btrfs_ioctl_fitrim(s |
| struct fstrim_range range; |
| u64 minlen = ULLONG_MAX; |
| u64 num_devices = 0; |
| - u64 total_bytes = btrfs_super_total_bytes(fs_info->super_copy); |
| int ret; |
| |
| if (!capable(CAP_SYS_ADMIN)) |
| @@ -402,11 +401,15 @@ static noinline int btrfs_ioctl_fitrim(s |
| return -EOPNOTSUPP; |
| if (copy_from_user(&range, arg, sizeof(range))) |
| return -EFAULT; |
| - if (range.start > total_bytes || |
| - range.len < fs_info->sb->s_blocksize) |
| + |
| + /* |
| + * NOTE: Don't truncate the range using super->total_bytes. Bytenr of |
| + * block group is in the logical address space, which can be any |
| + * sectorsize aligned bytenr in the range [0, U64_MAX]. |
| + */ |
| + if (range.len < fs_info->sb->s_blocksize) |
| return -EINVAL; |
| |
| - range.len = min(range.len, total_bytes - range.start); |
| range.minlen = max(range.minlen, minlen); |
| ret = btrfs_trim_fs(fs_info->tree_root, &range); |
| if (ret < 0) |