]> git.hungrycats.org Git - linux/commitdiff
btrfs: add preferred_metadata mode (with last btrfs_find_device argument, without...
authorGoffredo Baroncelli <kreijack@inwind.it>
Thu, 28 May 2020 18:34:51 +0000 (20:34 +0200)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Tue, 29 Dec 2020 02:58:34 +0000 (21:58 -0500)
When this mode is enabled, the allocation policy of the chunk
is so modified:
- allocation of metadata chunk: priority is given to preferred_metadata
  disks.
- allocation of data chunk: priority is given to a non preferred_metadata
  disk.

When a striped profile is involved (like RAID0,5,6), the logic
is a bit more complex. If there are enough disks, the data profiles
are stored on the non preferred_metadata disks; instead the metadata
profiles are stored on the preferred_metadata disk.
If the disks are not enough, then the profile is allocated on all
the disks.

Example: assuming that sda, sdb, sdc are ssd disks, and sde, sdf are
non preferred_metadata ones.
A data profile raid6, will be stored on sda, sdb, sdc, sde, sdf (sde
and sdf are not enough to host a raid5 profile).
A metadata profile raid6, will be stored on sda, sdb, sdc (these
are enough to host a raid6 profile).

To enable this mode pass -o dedicated_metadata at mount time.

Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it>
(cherry picked from commit fc8c568d8d1982558aca72dada291dd087d6cb7a)
(cherry picked from commit 1d4c7f65a47eadff2ce33d1562cbb65a58996138)
(cherry picked from commit b2a92a3bc07a05907626f4d518a6af0c53a2a9d2)

zygo: btrfs: fix btrfs_ioctl_dev_properties extra argument for btrfs_find_device

(cherry picked from commit dbd08fd8d91377423abee53cce4de95249825c71)

zygo: btrfs: remove unnecessary preferred_metadata mount option

(cherry picked from commit 3d3b483295ab700874fc492ebfa944674e32f95e)

Revert "zygo: btrfs: fix btrfs_ioctl_dev_properties extra argument for btrfs_find_device"

This reverts commit dbd08fd8d91377423abee53cce4de95249825c71.

(cherry picked from commit 7ff6cdcc37319fd4407f4ec92cb962c0114ac53f)

fs/btrfs/super.c
fs/btrfs/volumes.c
fs/btrfs/volumes.h

index 25967ecaaf0af97fca8ba95830bdfa5599996706..4235272341bff7f1c2608cf0b19b926339bd2317 100644 (file)
@@ -375,6 +375,7 @@ enum {
 #ifdef CONFIG_BTRFS_FS_REF_VERIFY
        Opt_ref_verify,
 #endif
+       Opt_preferred_metadata,
        Opt_err,
 };
 
@@ -449,6 +450,7 @@ static const match_table_t tokens = {
 #ifdef CONFIG_BTRFS_FS_REF_VERIFY
        {Opt_ref_verify, "ref_verify"},
 #endif
+       {Opt_preferred_metadata, "preferred_metadata"},
        {Opt_err, NULL},
 };
 
index 8855db6a013d091c183ab5c8f597653568e0def8..74c02887e89a9ad096f3024dc802d63a75096a0c 100644 (file)
@@ -4812,6 +4812,56 @@ static int btrfs_cmp_device_info(const void *a, const void *b)
        return 0;
 }
 
+/*
+ * sort the devices in descending order by preferred_metadata,
+ * max_avail, total_avail
+ */
+static int btrfs_cmp_device_info_metadata(const void *a, const void *b)
+{
+       const struct btrfs_device_info *di_a = a;
+       const struct btrfs_device_info *di_b = b;
+
+       /* metadata -> preferred_metadata first */
+       if (di_a->preferred_metadata && !di_b->preferred_metadata)
+               return -1;
+       if (!di_a->preferred_metadata && di_b->preferred_metadata)
+               return 1;
+       if (di_a->max_avail > di_b->max_avail)
+               return -1;
+       if (di_a->max_avail < di_b->max_avail)
+               return 1;
+       if (di_a->total_avail > di_b->total_avail)
+               return -1;
+       if (di_a->total_avail < di_b->total_avail)
+               return 1;
+       return 0;
+}
+
+/*
+ * sort the devices in descending order by !preferred_metadata,
+ * max_avail, total_avail
+ */
+static int btrfs_cmp_device_info_data(const void *a, const void *b)
+{
+       const struct btrfs_device_info *di_a = a;
+       const struct btrfs_device_info *di_b = b;
+
+       /* data -> preferred_metadata last */
+       if (di_a->preferred_metadata && !di_b->preferred_metadata)
+               return 1;
+       if (!di_a->preferred_metadata && di_b->preferred_metadata)
+               return -1;
+       if (di_a->max_avail > di_b->max_avail)
+               return -1;
+       if (di_a->max_avail < di_b->max_avail)
+               return 1;
+       if (di_a->total_avail > di_b->total_avail)
+               return -1;
+       if (di_a->total_avail < di_b->total_avail)
+               return 1;
+       return 0;
+}
+
 static void check_raid56_incompat_flag(struct btrfs_fs_info *info, u64 type)
 {
        if (!(type & BTRFS_BLOCK_GROUP_RAID56_MASK))
@@ -4927,6 +4977,7 @@ static int gather_device_info(struct btrfs_fs_devices *fs_devices,
        int ndevs = 0;
        u64 max_avail;
        u64 dev_offset;
+       int nr_preferred_metadata = 0;
 
        /*
         * in the first pass through the devices list, we gather information
@@ -4979,15 +5030,49 @@ static int gather_device_info(struct btrfs_fs_devices *fs_devices,
                devices_info[ndevs].max_avail = max_avail;
                devices_info[ndevs].total_avail = total_avail;
                devices_info[ndevs].dev = device;
+               devices_info[ndevs].preferred_metadata = !!(device->type &
+                       BTRFS_DEV_PREFERRED_METADATA);
+               if (devices_info[ndevs].preferred_metadata)
+                       nr_preferred_metadata++;
                ++ndevs;
        }
        ctl->ndevs = ndevs;
 
+       BUG_ON(nr_preferred_metadata > ndevs);
        /*
         * now sort the devices by hole size / available space
         */
-       sort(devices_info, ndevs, sizeof(struct btrfs_device_info),
-            btrfs_cmp_device_info, NULL);
+       if ((ctl->type & BTRFS_BLOCK_GROUP_DATA) &&
+           (ctl->type & BTRFS_BLOCK_GROUP_METADATA)
+           ) {
+               /* mixed bg or PREFERRED_METADATA not set */
+               sort(devices_info, ctl->ndevs, sizeof(struct btrfs_device_info),
+                            btrfs_cmp_device_info, NULL);
+       } else {
+               /*
+                * if PREFERRED_METADATA is set, sort the device considering
+                * also the kind (preferred_metadata or not). Limit the
+                * availables devices to the ones of the same kind, to avoid
+                * that a striped profile, like raid5, spreads to all kind of
+                * devices.
+                * It is allowed to use different kinds of devices if the ones
+                * of the same kind are not enough alone.
+                */
+               if (ctl->type & BTRFS_BLOCK_GROUP_DATA) {
+                       int nr_data = ctl->ndevs - nr_preferred_metadata;
+                       sort(devices_info, ctl->ndevs,
+                                    sizeof(struct btrfs_device_info),
+                                    btrfs_cmp_device_info_data, NULL);
+                       if (nr_data >= ctl->devs_min)
+                               ctl->ndevs = nr_data;
+               } else { /* non data -> metadata and system */
+                       sort(devices_info, ctl->ndevs,
+                                    sizeof(struct btrfs_device_info),
+                                    btrfs_cmp_device_info_metadata, NULL);
+                       if (nr_preferred_metadata >= ctl->devs_min)
+                               ctl->ndevs = nr_preferred_metadata;
+               }
+       }
 
        return 0;
 }
index 06ca13b19abdd6b5f09d58677da48be328ee8216..9a24976ddc22b2facfe36546408b9782712e7df9 100644 (file)
@@ -348,6 +348,7 @@ struct btrfs_device_info {
        u64 dev_offset;
        u64 max_avail;
        u64 total_avail;
+       int preferred_metadata:1;
 };
 
 struct btrfs_raid_attr {