@@ -1236,6 +1236,7 @@ static inline u32 BTRFS_MAX_XATTR_SIZE(const struct btrfs_fs_info *info)
#define BTRFS_MOUNT_NOLOGREPLAY (1 << 27)
#define BTRFS_MOUNT_REF_VERIFY (1 << 28)
#define BTRFS_MOUNT_DISCARD_ASYNC (1 << 29)
+#define BTRFS_MOUNT_SSD_METADATA (1 << 29)
#define BTRFS_DEFAULT_COMMIT_INTERVAL (30)
#define BTRFS_DEFAULT_MAX_INLINE (2048)
@@ -350,6 +350,7 @@ enum {
#ifdef CONFIG_BTRFS_FS_REF_VERIFY
Opt_ref_verify,
#endif
+ Opt_ssd_metadata,
Opt_err,
};
@@ -421,6 +422,7 @@ static const match_table_t tokens = {
#ifdef CONFIG_BTRFS_FS_REF_VERIFY
{Opt_ref_verify, "ref_verify"},
#endif
+ {Opt_ssd_metadata, "ssd_metadata"},
{Opt_err, NULL},
};
@@ -872,6 +874,10 @@ int btrfs_parse_options(struct btrfs_fs_info *info, char *options,
btrfs_set_opt(info->mount_opt, REF_VERIFY);
break;
#endif
+ case Opt_ssd_metadata:
+ btrfs_set_and_info(info, SSD_METADATA,
+ "enabling ssd_metadata");
+ break;
case Opt_err:
btrfs_info(info, "unrecognized mount option '%s'", p);
ret = -EINVAL;
@@ -1390,6 +1396,8 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
#endif
if (btrfs_test_opt(info, REF_VERIFY))
seq_puts(seq, ",ref_verify");
+ if (btrfs_test_opt(info, SSD_METADATA))
+ seq_puts(seq, ",ssd_metadata");
seq_printf(seq, ",subvolid=%llu",
BTRFS_I(d_inode(dentry))->root->root_key.objectid);
seq_puts(seq, ",subvol=");
@@ -4761,6 +4761,58 @@ static int btrfs_cmp_device_info(const void *a, const void *b)
return 0;
}
+/*
+ * sort the devices in descending order by rotational,
+ * 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 -> non rotational first */
+ if (!di_a->rotational && di_b->rotational)
+ return -1;
+ if (di_a->rotational && !di_b->rotational)
+ 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 !rotational,
+ * 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 -> non rotational last */
+ if (!di_a->rotational && di_b->rotational)
+ return 1;
+ if (di_a->rotational && !di_b->rotational)
+ 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))
@@ -4808,6 +4860,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
int i;
int j;
int index;
+ int nr_rotational;
BUG_ON(!alloc_profile_is_valid(type, 0));
@@ -4863,6 +4916,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
* about the available holes on each device.
*/
ndevs = 0;
+ nr_rotational = 0;
list_for_each_entry(device, &fs_devices->alloc_list, dev_alloc_list) {
u64 max_avail;
u64 dev_offset;
@@ -4914,14 +4968,45 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
devices_info[ndevs].max_avail = max_avail;
devices_info[ndevs].total_avail = total_avail;
devices_info[ndevs].dev = device;
+ devices_info[ndevs].rotational = !test_bit(QUEUE_FLAG_NONROT,
+ &(bdev_get_queue(device->bdev)->queue_flags));
+ if (devices_info[ndevs].rotational)
+ nr_rotational++;
++ndevs;
}
+ BUG_ON(nr_rotational > 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 (((type & BTRFS_BLOCK_GROUP_DATA) &&
+ (type & BTRFS_BLOCK_GROUP_METADATA)) ||
+ !btrfs_test_opt(info, SSD_METADATA)) {
+ /* mixed bg or SSD_METADATA not set */
+ sort(devices_info, ndevs, sizeof(struct btrfs_device_info),
+ btrfs_cmp_device_info, NULL);
+ } else {
+ /*
+ * if SSD_METADATA is set, sort the device considering also the
+ * kind (ssd or not). Limit the availables devices to the ones
+ * of the same kind, to avoid that a striped profile like raid5
+ * spread to all kind of devices (ssd and rotational).
+ * It is allowed to use different kinds of devices if the ones
+ * of the same kind are not enough alone.
+ */
+ if (type & BTRFS_BLOCK_GROUP_DATA) {
+ sort(devices_info, ndevs, sizeof(struct btrfs_device_info),
+ btrfs_cmp_device_info_data, NULL);
+ if (nr_rotational >= devs_min)
+ ndevs = nr_rotational;
+ } else {
+ int nr_norot = ndevs - nr_rotational;
+ sort(devices_info, ndevs, sizeof(struct btrfs_device_info),
+ btrfs_cmp_device_info_metadata, NULL);
+ if (nr_norot >= devs_min)
+ ndevs = nr_norot;
+ }
+ }
/*
* Round down to number of usable stripes, devs_increment can be any
@@ -343,6 +343,7 @@ struct btrfs_device_info {
u64 dev_offset;
u64 max_avail;
u64 total_avail;
+ int rotational:1;
};
struct btrfs_raid_attr {