From: Goffredo Baroncelli Date: Thu, 28 May 2020 18:34:51 +0000 (+0200) Subject: btrfs: add preferred_metadata mode (with last btrfs_find_device argument, without... X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4bfc908ec7c8c20b4e3ff581b8deea72b5a02a85;p=linux btrfs: add preferred_metadata mode (with last btrfs_find_device argument, without mount option) 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 (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) --- diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index f8435641b9126..11b550a6f3e0f 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -382,6 +382,7 @@ enum { #ifdef CONFIG_BTRFS_FS_REF_VERIFY Opt_ref_verify, #endif + Opt_preferred_metadata, Opt_err, }; @@ -456,6 +457,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}, }; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 021bc3907ef6d..10b1ce15969e7 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -4893,6 +4893,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)) @@ -5042,6 +5092,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 @@ -5094,15 +5145,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; } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index e36a7ae7a5f5c..fde95cf2cf9ba 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -366,6 +366,7 @@ struct btrfs_device_info { u64 dev_offset; u64 max_avail; u64 total_avail; + int preferred_metadata:1; }; struct btrfs_raid_attr {