btrfs: add kbps discard rate limit for async discard
authorDennis Zhou <dennis@kernel.org>
Thu, 2 Jan 2020 21:26:36 +0000 (16:26 -0500)
committerDavid Sterba <dsterba@suse.com>
Mon, 20 Jan 2020 15:40:59 +0000 (16:40 +0100)
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: Dennis Zhou <dennis@kernel.org>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/ctree.h
fs/btrfs/discard.c
fs/btrfs/sysfs.c

index 451dade0a4bb83ee9039d48ea699a558d759c7a1..f58b1cfeeca683fb03ca1f47aa5b87575330cd72 100644 (file)
@@ -466,10 +466,12 @@ struct btrfs_discard_ctl {
        spinlock_t lock;
        struct btrfs_block_group *block_group;
        struct list_head discard_list[BTRFS_NR_DISCARD_LISTS];
+       u64 prev_discard;
        atomic_t discardable_extents;
        atomic64_t discardable_bytes;
        unsigned long delay;
        u32 iops_limit;
+       u32 kbps_limit;
 };
 
 /* delayed seq elem */
index 29645676427bfe6d0634738fb85068c7df2a836c..565931f9a9aa897a51abaede60148aec3b6c1ee9 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/kernel.h>
 #include <linux/ktime.h>
 #include <linux/list.h>
+#include <linux/math64.h>
 #include <linux/sizes.h>
 #include <linux/workqueue.h>
 #include "ctree.h"
@@ -222,8 +223,8 @@ void btrfs_discard_queue_work(struct btrfs_discard_ctl *discard_ctl,
  * @override: override the current timer
  *
  * Discards are issued by a delayed workqueue item.  @override is used to
- * update the current delay as the baseline delay interview is reevaluated
- * on transaction commit.  This is also maxed with any other rate limit.
+ * update the current delay as the baseline delay interval is reevaluated on
+ * transaction commit.  This is also maxed with any other rate limit.
  */
 void btrfs_discard_schedule_work(struct btrfs_discard_ctl *discard_ctl,
                                 bool override)
@@ -242,6 +243,20 @@ void btrfs_discard_schedule_work(struct btrfs_discard_ctl *discard_ctl,
        block_group = find_next_block_group(discard_ctl, now);
        if (block_group) {
                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
@@ -316,6 +331,8 @@ static void btrfs_discard_workfn(struct work_struct *work)
                                       btrfs_block_group_end(block_group),
                                       0, true);
 
+       discard_ctl->prev_discard = trimmed;
+
        /* Determine next steps for a block_group */
        if (block_group->discard_cursor >= btrfs_block_group_end(block_group)) {
                if (discard_state == BTRFS_DISCARD_BITMAPS) {
@@ -507,10 +524,12 @@ void btrfs_discard_init(struct btrfs_fs_info *fs_info)
        for (i = 0; i < BTRFS_NR_DISCARD_LISTS; i++)
                INIT_LIST_HEAD(&discard_ctl->discard_list[i]);
 
+       discard_ctl->prev_discard = 0;
        atomic_set(&discard_ctl->discardable_extents, 0);
        atomic64_set(&discard_ctl->discardable_bytes, 0);
        discard_ctl->delay = BTRFS_DISCARD_MAX_DELAY_MSEC;
        discard_ctl->iops_limit = BTRFS_DISCARD_MAX_IOPS;
+       discard_ctl->kbps_limit = 0;
 }
 
 void btrfs_discard_cleanup(struct btrfs_fs_info *fs_info)
index 4155174245ffdf9dcfd0e97397b81701bd46d1d8..0168bc12875ef4eaf35efe0ffbe56b755efc8d9a 100644 (file)
@@ -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_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[] = {
        BTRFS_ATTR_PTR(discard, discardable_bytes),
        BTRFS_ATTR_PTR(discard, discardable_extents),
        BTRFS_ATTR_PTR(discard, iops_limit),
+       BTRFS_ATTR_PTR(discard, kbps_limit),
        NULL,
 };