btrfs: don't miss async discards after scheduled work override
authorPavel Begunkov <asml.silence@gmail.com>
Wed, 4 Nov 2020 09:45:53 +0000 (09:45 +0000)
committerDavid Sterba <dsterba@suse.com>
Tue, 8 Dec 2020 14:54:05 +0000 (15:54 +0100)
If btrfs_discard_schedule_work() is called with override=true, it sets
delay anew regardless how much time is left until the timer should have
fired. If delays are long (that can happen, for example, with low
kbps_limit), they might get constantly overridden without having a
chance to run the discard work.

Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/ctree.h
fs/btrfs/discard.c

index fbbfe8a..461d1d5 100644 (file)
@@ -469,6 +469,7 @@ struct btrfs_discard_ctl {
        struct btrfs_block_group *block_group;
        struct list_head discard_list[BTRFS_NR_DISCARD_LISTS];
        u64 prev_discard;
+       u64 prev_discard_time;
        atomic_t discardable_extents;
        atomic64_t discardable_bytes;
        u64 max_discard_size;
index 819eaf5..1db966b 100644 (file)
@@ -381,6 +381,15 @@ void btrfs_discard_schedule_work(struct btrfs_discard_ctl *discard_ctl,
                        delay = max(delay, bg_timeout);
                }
 
+               if (override && discard_ctl->prev_discard) {
+                       u64 elapsed = now - discard_ctl->prev_discard_time;
+
+                       if (delay > elapsed)
+                               delay -= elapsed;
+                       else
+                               delay = 0;
+               }
+
                mod_delayed_work(discard_ctl->discard_workers,
                                 &discard_ctl->work, nsecs_to_jiffies(delay));
        }
@@ -465,7 +474,12 @@ static void btrfs_discard_workfn(struct work_struct *work)
                discard_ctl->discard_extent_bytes += trimmed;
        }
 
+       /*
+        * Updated without locks as this is inside the workfn and nothing else
+        * is reading the values
+        */
        discard_ctl->prev_discard = trimmed;
+       discard_ctl->prev_discard_time = ktime_get_ns();
 
        /* Determine next steps for a block_group */
        if (block_group->discard_cursor >= btrfs_block_group_end(block_group)) {
@@ -685,6 +699,7 @@ void btrfs_discard_init(struct btrfs_fs_info *fs_info)
                INIT_LIST_HEAD(&discard_ctl->discard_list[i]);
 
        discard_ctl->prev_discard = 0;
+       discard_ctl->prev_discard_time = 0;
        atomic_set(&discard_ctl->discardable_extents, 0);
        atomic64_set(&discard_ctl->discardable_bytes, 0);
        discard_ctl->max_discard_size = BTRFS_ASYNC_DISCARD_DEFAULT_MAX_SIZE;