Commit e93591bb authored by Dennis Zhou's avatar Dennis Zhou Committed by David Sterba

btrfs: add kbps discard rate limit for async discard

Provide the ability to rate limit based on kbps in addition to iops as
additional guides for the target discard rate. The delay used ends up
being max(kbps_delay, iops_delay).
Signed-off-by: default avatarDennis Zhou <dennis@kernel.org>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent a2309300
...@@ -466,10 +466,12 @@ struct btrfs_discard_ctl { ...@@ -466,10 +466,12 @@ struct btrfs_discard_ctl {
spinlock_t lock; spinlock_t lock;
struct btrfs_block_group *block_group; struct btrfs_block_group *block_group;
struct list_head discard_list[BTRFS_NR_DISCARD_LISTS]; struct list_head discard_list[BTRFS_NR_DISCARD_LISTS];
u64 prev_discard;
atomic_t discardable_extents; atomic_t discardable_extents;
atomic64_t discardable_bytes; atomic64_t discardable_bytes;
unsigned long delay; unsigned long delay;
u32 iops_limit; u32 iops_limit;
u32 kbps_limit;
}; };
/* delayed seq elem */ /* delayed seq elem */
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/math64.h>
#include <linux/sizes.h> #include <linux/sizes.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include "ctree.h" #include "ctree.h"
...@@ -222,8 +223,8 @@ void btrfs_discard_queue_work(struct btrfs_discard_ctl *discard_ctl, ...@@ -222,8 +223,8 @@ void btrfs_discard_queue_work(struct btrfs_discard_ctl *discard_ctl,
* @override: override the current timer * @override: override the current timer
* *
* Discards are issued by a delayed workqueue item. @override is used to * Discards are issued by a delayed workqueue item. @override is used to
* update the current delay as the baseline delay interview is reevaluated * update the current delay as the baseline delay interval is reevaluated on
* on transaction commit. This is also maxed with any other rate limit. * transaction commit. This is also maxed with any other rate limit.
*/ */
void btrfs_discard_schedule_work(struct btrfs_discard_ctl *discard_ctl, void btrfs_discard_schedule_work(struct btrfs_discard_ctl *discard_ctl,
bool override) bool override)
...@@ -242,6 +243,20 @@ void btrfs_discard_schedule_work(struct btrfs_discard_ctl *discard_ctl, ...@@ -242,6 +243,20 @@ void btrfs_discard_schedule_work(struct btrfs_discard_ctl *discard_ctl,
block_group = find_next_block_group(discard_ctl, now); block_group = find_next_block_group(discard_ctl, now);
if (block_group) { if (block_group) {
unsigned long delay = discard_ctl->delay; unsigned long delay = discard_ctl->delay;
u32 kbps_limit = READ_ONCE(discard_ctl->kbps_limit);
/*
* A single delayed workqueue item is responsible for
* discarding, so we can manage the bytes rate limit by keeping
* track of the previous discard.
*/
if (kbps_limit && discard_ctl->prev_discard) {
u64 bps_limit = ((u64)kbps_limit) * SZ_1K;
u64 bps_delay = div64_u64(discard_ctl->prev_discard *
MSEC_PER_SEC, bps_limit);
delay = max(delay, msecs_to_jiffies(bps_delay));
}
/* /*
* This timeout is to hopefully prevent immediate discarding * This timeout is to hopefully prevent immediate discarding
...@@ -316,6 +331,8 @@ static void btrfs_discard_workfn(struct work_struct *work) ...@@ -316,6 +331,8 @@ static void btrfs_discard_workfn(struct work_struct *work)
btrfs_block_group_end(block_group), btrfs_block_group_end(block_group),
0, true); 0, true);
discard_ctl->prev_discard = trimmed;
/* Determine next steps for a block_group */ /* Determine next steps for a block_group */
if (block_group->discard_cursor >= btrfs_block_group_end(block_group)) { if (block_group->discard_cursor >= btrfs_block_group_end(block_group)) {
if (discard_state == BTRFS_DISCARD_BITMAPS) { if (discard_state == BTRFS_DISCARD_BITMAPS) {
...@@ -507,10 +524,12 @@ void btrfs_discard_init(struct btrfs_fs_info *fs_info) ...@@ -507,10 +524,12 @@ void btrfs_discard_init(struct btrfs_fs_info *fs_info)
for (i = 0; i < BTRFS_NR_DISCARD_LISTS; i++) for (i = 0; i < BTRFS_NR_DISCARD_LISTS; i++)
INIT_LIST_HEAD(&discard_ctl->discard_list[i]); INIT_LIST_HEAD(&discard_ctl->discard_list[i]);
discard_ctl->prev_discard = 0;
atomic_set(&discard_ctl->discardable_extents, 0); atomic_set(&discard_ctl->discardable_extents, 0);
atomic64_set(&discard_ctl->discardable_bytes, 0); atomic64_set(&discard_ctl->discardable_bytes, 0);
discard_ctl->delay = BTRFS_DISCARD_MAX_DELAY_MSEC; discard_ctl->delay = BTRFS_DISCARD_MAX_DELAY_MSEC;
discard_ctl->iops_limit = BTRFS_DISCARD_MAX_IOPS; discard_ctl->iops_limit = BTRFS_DISCARD_MAX_IOPS;
discard_ctl->kbps_limit = 0;
} }
void btrfs_discard_cleanup(struct btrfs_fs_info *fs_info) void btrfs_discard_cleanup(struct btrfs_fs_info *fs_info)
......
...@@ -396,10 +396,41 @@ static ssize_t btrfs_discard_iops_limit_store(struct kobject *kobj, ...@@ -396,10 +396,41 @@ static ssize_t btrfs_discard_iops_limit_store(struct kobject *kobj,
BTRFS_ATTR_RW(discard, iops_limit, btrfs_discard_iops_limit_show, BTRFS_ATTR_RW(discard, iops_limit, btrfs_discard_iops_limit_show,
btrfs_discard_iops_limit_store); btrfs_discard_iops_limit_store);
static ssize_t btrfs_discard_kbps_limit_show(struct kobject *kobj,
struct kobj_attribute *a,
char *buf)
{
struct btrfs_fs_info *fs_info = discard_to_fs_info(kobj);
return snprintf(buf, PAGE_SIZE, "%u\n",
READ_ONCE(fs_info->discard_ctl.kbps_limit));
}
static ssize_t btrfs_discard_kbps_limit_store(struct kobject *kobj,
struct kobj_attribute *a,
const char *buf, size_t len)
{
struct btrfs_fs_info *fs_info = discard_to_fs_info(kobj);
struct btrfs_discard_ctl *discard_ctl = &fs_info->discard_ctl;
u32 kbps_limit;
int ret;
ret = kstrtou32(buf, 10, &kbps_limit);
if (ret)
return -EINVAL;
WRITE_ONCE(discard_ctl->kbps_limit, kbps_limit);
return len;
}
BTRFS_ATTR_RW(discard, kbps_limit, btrfs_discard_kbps_limit_show,
btrfs_discard_kbps_limit_store);
static const struct attribute *discard_debug_attrs[] = { static const struct attribute *discard_debug_attrs[] = {
BTRFS_ATTR_PTR(discard, discardable_bytes), BTRFS_ATTR_PTR(discard, discardable_bytes),
BTRFS_ATTR_PTR(discard, discardable_extents), BTRFS_ATTR_PTR(discard, discardable_extents),
BTRFS_ATTR_PTR(discard, iops_limit), BTRFS_ATTR_PTR(discard, iops_limit),
BTRFS_ATTR_PTR(discard, kbps_limit),
NULL, NULL,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment