From: Josef Bacik <josef@toxicpanda.com>
To: Stefan Roesch <shr@fb.com>
Cc: linux-btrfs@vger.kernel.org, kernel-team@fb.com
Subject: Re: [PATCH] btrfs: sysfs: set / query btrfs stripe size
Date: Tue, 26 Oct 2021 14:29:50 -0400 [thread overview]
Message-ID: <YXhJHqqyx4+FUPRj@localhost.localdomain> (raw)
In-Reply-To: <20211026165915.553834-1-shr@fb.com>
On Tue, Oct 26, 2021 at 09:59:15AM -0700, Stefan Roesch wrote:
> Motivation:
> The btrfs allocator is currently not ideal for all workloads. It tends
> to suffer from overallocating data block groups and underallocating
> metadata block groups. This results in filesystems becoming read-only
> even though there is plenty of "free" space.
> This is naturally confusing and distressing to users.
>
> Changes:
> - Increase the default chunk size allocation for metadata to 5GB
> for devices with > 50GB storage space.
>
> - Add a new sysfs setting to control the stripe size
> /sys/fs/btrfs/<UUID>/allocation/<block_type>/stripe_size
>
> - For testing add a new sysfs setting to force a chunk allocation.
> This setting is only available if the kernel is compiled with
> CONFIG_BTRFS_DEBUG.
> /sys/fs/btrfs/<UUID>/allocation/<block_type>/force_chunk_alloc
>
> Testing:
> A new test is being added to the xfstest suite. For reference the
> corresponding patch has the title:
> [PATCH] btrfs: Test chunk allocation with different sizes
>
> In addition also manual testing has been performed.
> - Run xfstests with the changes and the new test. It does not
> show new diffs.
> - Test with storage devices 10G, 20G, 30G, 50G, 60G
> - Default allocation
> - Increase of chunk size
> - If the stripe size is > the free space, it allocates
> free space - 1MB. The 1MB is left as free space.
> - If the device has a storage size > 50G, it uses a 5GB
> chunk size for new allocations.
>
> Signed-off-by: Stefan Roesch <shr@fb.com>
This should be 4 patches, 1 to do the force allocation sysfs thing, 1 to
implement the new chunk size stuff, and then another to add the sysfs interface
to make the alloc sizes adjustable, and finally 1 to change the default from
1gib metadata chunks to 5gib metadata chunks.
> ---
> fs/btrfs/space-info.c | 52 +++++++++++++++
> fs/btrfs/space-info.h | 4 ++
> fs/btrfs/sysfs.c | 145 ++++++++++++++++++++++++++++++++++++++++++
> fs/btrfs/volumes.c | 30 ++++-----
> 4 files changed, 212 insertions(+), 19 deletions(-)
>
> diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c
> index 48d77f360a24..e082654c85f1 100644
> --- a/fs/btrfs/space-info.c
> +++ b/fs/btrfs/space-info.c
> @@ -181,6 +181,54 @@ void btrfs_clear_space_info_full(struct btrfs_fs_info *info)
> found->full = 0;
> }
>
> +/*
> + * Compute stripe size depending on block type.
> + */
> +static u64 compute_stripe_size(struct btrfs_fs_info *info, u64 flags)
> +{
> + if (flags & BTRFS_BLOCK_GROUP_DATA) {
> + return SZ_1G;
> + } else if (flags & BTRFS_BLOCK_GROUP_METADATA) {
> + /* For larger filesystems, use larger metadata chunks */
> + return info->fs_devices->total_rw_bytes > 50ULL * SZ_1G
> + ? 5ULL * SZ_1G
> + : SZ_256M;
> + } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) {
> + return SZ_32M;
For the last one we prefer to just fall through, so
if (flags & BTRFS_BLOCK_GROUP_DATA)
return SZ_1G;
else if (flags & BTRFS_BLOCK_GROUP_SYSTEM)
return SZ_32M;
/* BTRFS_BLOCK_GROUP_METADATA. */
if (info->fs_devices->total_rw_bytes > 50ULL * SZ_1G)
return 5ULL * SZ_1G;
return SZ_256M;
> + }
> +
> + BUG();
We want to avoid adding BUG()'s to new code, instead add this to the beginning
ASSERT(flags & BTRFS_BLOCK_GROUP_TYPE_MASK);
> +}
> +
> +/*
> + * Compute chunk size depending on block type and stripe size.
> + */
> +static u64 compute_chunk_size(u64 flags, u64 max_stripe_size)
> +{
> + if (flags & BTRFS_BLOCK_GROUP_DATA)
> + return BTRFS_MAX_DATA_CHUNK_SIZE;
> + else if (flags & BTRFS_BLOCK_GROUP_METADATA)
> + return max_stripe_size;
> + else if (flags & BTRFS_BLOCK_GROUP_SYSTEM)
> + return 2 * max_stripe_size;
> +
> + BUG();
> +}
Same comment as above.
> +
> +/*
> + * Update maximum stripe size and chunk size.
> + *
> + */
> +void btrfs_update_space_info_max_alloc_sizes(struct btrfs_space_info *space_info,
> + u64 flags, u64 max_stripe_size)
> +{
> + spin_lock(&space_info->lock);
> + space_info->max_stripe_size = max_stripe_size;
> + space_info->max_chunk_size = compute_chunk_size(flags,
> + space_info->max_stripe_size);
> + spin_unlock(&space_info->lock);
> +}
> +
> static int create_space_info(struct btrfs_fs_info *info, u64 flags)
> {
>
> @@ -203,6 +251,10 @@ static int create_space_info(struct btrfs_fs_info *info, u64 flags)
> INIT_LIST_HEAD(&space_info->priority_tickets);
> space_info->clamp = 1;
>
> + space_info->max_stripe_size = compute_stripe_size(info, flags);
> + space_info->max_chunk_size = compute_chunk_size(flags,
> + space_info->max_stripe_size);
> +
> ret = btrfs_sysfs_add_space_info_type(info, space_info);
> if (ret)
> return ret;
> diff --git a/fs/btrfs/space-info.h b/fs/btrfs/space-info.h
> index cb5056472e79..5ee3e381de38 100644
> --- a/fs/btrfs/space-info.h
> +++ b/fs/btrfs/space-info.h
> @@ -23,6 +23,8 @@ struct btrfs_space_info {
> u64 max_extent_size; /* This will hold the maximum extent size of
> the space info if we had an ENOSPC in the
> allocator. */
> + u64 max_chunk_size; /* maximum chunk size in bytes */
> + u64 max_stripe_size; /* maximum stripe size in bytes */
>
> int clamp; /* Used to scale our threshold for preemptive
> flushing. The value is >> clamp, so turns
> @@ -115,6 +117,8 @@ void btrfs_update_space_info(struct btrfs_fs_info *info, u64 flags,
> u64 total_bytes, u64 bytes_used,
> u64 bytes_readonly, u64 bytes_zone_unusable,
> struct btrfs_space_info **space_info);
> +void btrfs_update_space_info_max_alloc_sizes(struct btrfs_space_info *space_info,
> + u64 flags, u64 max_stripe_size);
> struct btrfs_space_info *btrfs_find_space_info(struct btrfs_fs_info *info,
> u64 flags);
> u64 __pure btrfs_space_info_used(struct btrfs_space_info *s_info,
> diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c
> index 25a6f587852b..df3913027df9 100644
> --- a/fs/btrfs/sysfs.c
> +++ b/fs/btrfs/sysfs.c
> @@ -21,6 +21,7 @@
> #include "space-info.h"
> #include "block-group.h"
> #include "qgroup.h"
> +#include "misc.h"
>
> /*
> * Structure name Path
> @@ -92,6 +93,7 @@ static struct btrfs_feature_attr btrfs_attr_features_##_name = { \
>
> static inline struct btrfs_fs_info *to_fs_info(struct kobject *kobj);
> static inline struct btrfs_fs_devices *to_fs_devs(struct kobject *kobj);
> +static inline struct kobject *get_btrfs_kobj(struct kobject *kobj);
>
> static struct btrfs_feature_attr *to_btrfs_feature_attr(struct kobj_attribute *a)
> {
> @@ -709,6 +711,125 @@ static ssize_t btrfs_space_info_show_##field(struct kobject *kobj, \
> } \
> BTRFS_ATTR(space_info, field, btrfs_space_info_show_##field)
>
> +/*
> + * Return space info stripe size.
> + */
> +static ssize_t btrfs_stripe_size_show(struct kobject *kobj,
> + struct kobj_attribute *a, char *buf)
> +{
> + struct btrfs_space_info *sinfo = to_space_info(kobj);
> +
> + return btrfs_show_u64(&sinfo->max_stripe_size, &sinfo->lock, buf);
> +}
> +
> +/*
> + * Store new user supplied stripe size in space info.
> + *
> + * Note: If the new stripe size value is larger than 10% of free space it is
> + * reduced to match that limit.
> + */
> +static ssize_t btrfs_stripe_size_store(struct kobject *kobj,
> + struct kobj_attribute *a,
> + const char *buf, size_t len)
> +{
> + struct btrfs_space_info *space_info = to_space_info(kobj);
> + struct btrfs_fs_info *fs_info = to_fs_info(get_btrfs_kobj(kobj));
> + u64 val;
> + int ret;
> +
> + if (!capable(CAP_SYS_ADMIN))
> + return -EPERM;
> +
> + if (!fs_info) {
> + pr_err("couldn't get fs_info\n");
> + return -EPERM;
> + }
> +
> + if (sb_rdonly(fs_info->sb))
> + return -EROFS;
> +
> + if (!fs_info->fs_devices)
> + return -EINVAL;
> +
> + if (fs_info->fs_devices->chunk_alloc_policy == BTRFS_CHUNK_ALLOC_ZONED)
> + return -EINVAL;
> +
> + if (!space_info) {
> + btrfs_err(fs_info, "couldn't get space_info\n");
> + return -EPERM;
> + }
> +
> + ret = kstrtoull(buf, 10, &val);
> + if (ret)
> + return ret;
> +
> + /*
> + * Limit stripe size to 10% of available space.
> + */
> + val = min(div_factor(fs_info->fs_devices->total_rw_bytes, 1), val);
> + btrfs_update_space_info_max_alloc_sizes(space_info, space_info->flags, val);
> +
> + return val;
> +}
> +
> +#ifdef CONFIG_BTRFS_DEBUG
> +/*
> + * Return if space info force allocation chunk flag is set.
> + */
> +static ssize_t btrfs_force_chunk_alloc_show(struct kobject *kobj,
> + struct kobj_attribute *a,
> + char *buf)
> +{
> + return snprintf(buf, PAGE_SIZE, "0\n");
> +}
> +
> +/*
> + * Request chunk allocation with current chunk size.
> + */
> +static ssize_t btrfs_force_chunk_alloc_store(struct kobject *kobj,
> + struct kobj_attribute *a,
> + const char *buf, size_t len)
> +{
> + struct btrfs_space_info *space_info = to_space_info(kobj);
> + struct btrfs_fs_info *fs_info = to_fs_info(get_btrfs_kobj(kobj));
> + struct btrfs_trans_handle *trans;
> + unsigned long val;
> + int ret;
> +
> + if (!fs_info) {
> + pr_err("couldn't get fs_info\n");
> + return -EPERM;
> + }
> +
> + if (!capable(CAP_SYS_ADMIN))
> + return -EPERM;
> +
> + if (sb_rdonly(fs_info->sb))
> + return -EROFS;
> +
> + ret = kstrtoul(buf, 10, &val);
> + if (ret)
> + return ret;
> +
> + if (val == 0)
> + return -EINVAL;
> +
> + /*
> + * Allocate new chunk.
> + */
> + trans = btrfs_start_transaction(fs_info->extent_root, 0);
> + if (!trans)
> + return PTR_ERR(trans);
> + ret = btrfs_force_chunk_alloc(trans, space_info->flags);
> + btrfs_end_transaction(trans);
> +
> + if (ret == 1)
> + return len;
> +
> + return -ENOSPC;
> +}
> +#endif
> +
> SPACE_INFO_ATTR(flags);
> SPACE_INFO_ATTR(total_bytes);
> SPACE_INFO_ATTR(bytes_used);
> @@ -719,6 +840,12 @@ SPACE_INFO_ATTR(bytes_readonly);
> SPACE_INFO_ATTR(bytes_zone_unusable);
> SPACE_INFO_ATTR(disk_used);
> SPACE_INFO_ATTR(disk_total);
> +BTRFS_ATTR_RW(space_info, stripe_size, btrfs_stripe_size_show,
> + btrfs_stripe_size_store);
> +#ifdef CONFIG_BTRFS_DEBUG
> +BTRFS_ATTR_RW(space_info, force_chunk_alloc, btrfs_force_chunk_alloc_show,
> + btrfs_force_chunk_alloc_store);
> +#endif
>
> /*
> * Allocation information about block group types.
> @@ -736,6 +863,10 @@ static struct attribute *space_info_attrs[] = {
> BTRFS_ATTR_PTR(space_info, bytes_zone_unusable),
> BTRFS_ATTR_PTR(space_info, disk_used),
> BTRFS_ATTR_PTR(space_info, disk_total),
> + BTRFS_ATTR_PTR(space_info, stripe_size),
> +#ifdef CONFIG_BTRFS_DEBUG
> + BTRFS_ATTR_PTR(space_info, force_chunk_alloc),
> +#endif
> NULL,
> };
> ATTRIBUTE_GROUPS(space_info);
> @@ -1103,6 +1234,20 @@ static inline struct btrfs_fs_info *to_fs_info(struct kobject *kobj)
> return to_fs_devs(kobj)->fs_info;
> }
>
> +/*
> + * Get btrfs sysfs kobject.
> + */
> +static inline struct kobject *get_btrfs_kobj(struct kobject *kobj)
> +{
> + while (kobj) {
> + if (kobj->ktype == &btrfs_ktype)
> + return kobj;
> + kobj = kobj->parent;
> + }
> +
> + return NULL;
> +}
> +
> #define NUM_FEATURE_BITS 64
> #define BTRFS_FEATURE_NAME_MAX 13
> static char btrfs_unknown_feature_names[FEAT_MAX][NUM_FEATURE_BITS][BTRFS_FEATURE_NAME_MAX];
> diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
> index 6031e2f4c6bc..4ab581c03cda 100644
> --- a/fs/btrfs/volumes.c
> +++ b/fs/btrfs/volumes.c
> @@ -4990,7 +4990,9 @@ struct alloc_chunk_ctl {
> int ncopies;
> /* Number of stripes worth of bytes to store parity information */
> int nparity;
> + /* Maximum stripe size */
> u64 max_stripe_size;
> + /* Maximum chunk size */
> u64 max_chunk_size;
> u64 dev_extent_min;
> u64 stripe_size;
This is superflous, you can drop this bit. Thanks,
Josef
next prev parent reply other threads:[~2021-10-26 18:29 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-10-26 16:59 [PATCH] btrfs: sysfs: set / query btrfs stripe size Stefan Roesch
2021-10-26 18:08 ` Hugo Mills
2021-10-26 18:29 ` Josef Bacik [this message]
2021-10-27 6:28 ` Johannes Thumshirn
2021-10-27 6:51 ` Damien Le Moal
2021-10-27 6:58 ` Stefan Roesch
2021-10-27 7:09 ` Johannes Thumshirn
2021-10-27 7:19 ` Stefan Roesch
2021-10-27 7:37 ` Johannes Thumshirn
2021-10-27 7:20 ` Johannes Thumshirn
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=YXhJHqqyx4+FUPRj@localhost.localdomain \
--to=josef@toxicpanda.com \
--cc=kernel-team@fb.com \
--cc=linux-btrfs@vger.kernel.org \
--cc=shr@fb.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.