]> git.ipfire.org Git - people/arne_f/kernel.git/commitdiff
Btrfs: fix fitrim discarding device area reserved for boot loader's use
authorFilipe Manana <fdmanana@suse.com>
Wed, 6 Jan 2016 22:42:35 +0000 (22:42 +0000)
committerFilipe Manana <fdmanana@suse.com>
Thu, 7 Jan 2016 21:16:03 +0000 (21:16 +0000)
As of the 4.3 kernel release, the fitrim ioctl can now discard any region
of a disk that is not allocated to any chunk/block group, including the
first megabyte which is used for our primary superblock and by the boot
loader (grub for example).

Fix this by not allowing to trim/discard any region in the device starting
with an offset not greater than min(alloc_start_mount_option, 1Mb), just
as it was not possible before 4.3.

A reproducer test case for xfstests follows.

  seq=`basename $0`
  seqres=$RESULT_DIR/$seq
  echo "QA output created by $seq"
  tmp=/tmp/$$
  status=1 # failure is the default!
  trap "_cleanup; exit \$status" 0 1 2 3 15

  _cleanup()
  {
      cd /
      rm -f $tmp.*
  }

  # get standard environment, filters and checks
  . ./common/rc
  . ./common/filter

  # real QA test starts here
  _need_to_be_root
  _supported_fs btrfs
  _supported_os Linux
  _require_scratch

  rm -f $seqres.full

  _scratch_mkfs >>$seqres.full 2>&1

  # Write to the [0, 64Kb[ and [68Kb, 1Mb[ ranges of the device. These ranges are
  # reserved for a boot loader to use (GRUB for example) and btrfs should never
  # use them - neither for allocating metadata/data nor should trim/discard them.
  # The range [64Kb, 68Kb[ is used for the primary superblock of the filesystem.
  $XFS_IO_PROG -c "pwrite -S 0xfd 0 64K" $SCRATCH_DEV | _filter_xfs_io
  $XFS_IO_PROG -c "pwrite -S 0xfd 68K 956K" $SCRATCH_DEV | _filter_xfs_io

  # Now mount the filesystem and perform a fitrim against it.
  _scratch_mount
  _require_batched_discard $SCRATCH_MNT
  $FSTRIM_PROG $SCRATCH_MNT

  # Now unmount the filesystem and verify the content of the ranges was not
  # modified (no trim/discard happened on them).
  _scratch_unmount
  echo "Content of the ranges [0, 64Kb] and [68Kb, 1Mb[ after fitrim:"
  od -t x1 -N $((64 * 1024)) $SCRATCH_DEV
  od -t x1 -j $((68 * 1024)) -N $((956 * 1024)) $SCRATCH_DEV

  status=0
  exit

Reported-by: Vincent Petry <PVince81@yahoo.fr>
Reported-by: Andrei Borzenkov <arvidjaar@gmail.com>
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=109341
Fixes: 499f377f49f0 (btrfs: iterate over unused chunk space in FITRIM)
Cc: stable@vger.kernel.org # 4.3+
Signed-off-by: Filipe Manana <fdmanana@suse.com>
fs/btrfs/volumes.c

index a114b7bb87a0290bc20d184ebed5f757d6ec1ee0..b0a0eb242473c91882deaf627556e42aedbe528f 100644 (file)
@@ -1258,6 +1258,15 @@ int find_free_dev_extent_start(struct btrfs_transaction *transaction,
        int ret;
        int slot;
        struct extent_buffer *l;
+       u64 min_search_start;
+
+       /*
+        * We don't want to overwrite the superblock on the drive nor any area
+        * used by the boot loader (grub for example), so we make sure to start
+        * at an offset of at least 1MB.
+        */
+       min_search_start = max(root->fs_info->alloc_start, 1024ull * 1024);
+       search_start = max(search_start, min_search_start);
 
        path = btrfs_alloc_path();
        if (!path)
@@ -1398,18 +1407,9 @@ int find_free_dev_extent(struct btrfs_trans_handle *trans,
                         struct btrfs_device *device, u64 num_bytes,
                         u64 *start, u64 *len)
 {
-       struct btrfs_root *root = device->dev_root;
-       u64 search_start;
-
        /* FIXME use last free of some kind */
-
-       /*
-        * we don't want to overwrite the superblock on the drive,
-        * so we make sure to start at an offset of at least 1MB
-        */
-       search_start = max(root->fs_info->alloc_start, 1024ull * 1024);
        return find_free_dev_extent_start(trans->transaction, device,
-                                         num_bytes, search_start, start, len);
+                                         num_bytes, 0, start, len);
 }
 
 static int btrfs_free_dev_extent(struct btrfs_trans_handle *trans,