btrfs: Honour FITRIM range constraints during free space trim
authorNikolay Borisov <nborisov@suse.com>
Mon, 25 Mar 2019 12:31:21 +0000 (14:31 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 25 May 2019 16:26:57 +0000 (18:26 +0200)
commit c2d1b3aae33605a61cbab445d8ae1c708ccd2698 upstream.

Up until now trimming the freespace was done irrespective of what the
arguments of the FITRIM ioctl were. For example fstrim's -o/-l arguments
will be entirely ignored. Fix it by correctly handling those paramter.
This requires breaking if the found freespace extent is after the end of
the passed range as well as completing trim after trimming
fstrim_range::len bytes.

Fixes: 499f377f49f0 ("btrfs: iterate over unused chunk space in FITRIM")
CC: stable@vger.kernel.org # 4.4+
Signed-off-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

index 7938c48c72ff0d3e8651e540f6b19514305f159d..6b29165f766f1862316226a5176841786c4b35da 100644 (file)
@@ -11150,9 +11150,9 @@ int btrfs_error_unpin_extent_range(struct btrfs_root *root, u64 start, u64 end)
  * transaction.
  */
 static int btrfs_trim_free_extents(struct btrfs_device *device,
-                                  u64 minlen, u64 *trimmed)
+                                  struct fstrim_range *range, u64 *trimmed)
 {
-       u64 start = 0, len = 0;
+       u64 start = range->start, len = 0;
        int ret;
 
        *trimmed = 0;
@@ -11188,8 +11188,8 @@ static int btrfs_trim_free_extents(struct btrfs_device *device,
                        atomic_inc(&trans->use_count);
                spin_unlock(&fs_info->trans_lock);
 
-               ret = find_free_dev_extent_start(trans, device, minlen, start,
-                                                &start, &len);
+               ret = find_free_dev_extent_start(trans, device, range->minlen,
+                                                start, &start, &len);
                if (trans)
                        btrfs_put_transaction(trans);
 
@@ -11201,6 +11201,16 @@ static int btrfs_trim_free_extents(struct btrfs_device *device,
                        break;
                }
 
+               /* If we are out of the passed range break */
+               if (start > range->start + range->len - 1) {
+                       mutex_unlock(&fs_info->chunk_mutex);
+                       ret = 0;
+                       break;
+               }
+
+               start = max(range->start, start);
+               len = min(range->len, len);
+
                ret = btrfs_issue_discard(device->bdev, start, len, &bytes);
                up_read(&fs_info->commit_root_sem);
                mutex_unlock(&fs_info->chunk_mutex);
@@ -11211,6 +11221,10 @@ static int btrfs_trim_free_extents(struct btrfs_device *device,
                start += len;
                *trimmed += bytes;
 
+               /* We've trimmed enough */
+               if (*trimmed >= range->len)
+                       break;
+
                if (fatal_signal_pending(current)) {
                        ret = -ERESTARTSYS;
                        break;
@@ -11295,8 +11309,7 @@ int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range)
        mutex_lock(&fs_info->fs_devices->device_list_mutex);
        devices = &fs_info->fs_devices->devices;
        list_for_each_entry(device, devices, dev_list) {
-               ret = btrfs_trim_free_extents(device, range->minlen,
-                                             &group_trimmed);
+               ret = btrfs_trim_free_extents(device, range, &group_trimmed);
                if (ret) {
                        dev_failed++;
                        dev_ret = ret;