]> git.baikalelectronics.ru Git - kernel.git/commitdiff
btrfs: add correction to handle -1 edge case in async discard
authorDennis Zhou <dennis@kernel.org>
Thu, 2 Jan 2020 21:26:46 +0000 (16:26 -0500)
committerDavid Sterba <dsterba@suse.com>
Mon, 20 Jan 2020 15:41:01 +0000 (16:41 +0100)
From Dave's testing described below, it's possible to drive a file
system to have bogus values of discardable_extents and _bytes.  As
btrfs_discard_calc_delay() is the only user of discardable_extents, we
can correct here for any negative discardable_extents/discardable_bytes.

The problem is not reliably reproducible. The workload that created it
was based on linux git tree, switching between release tags, then
everytihng deleted followed by a full rebalance. At this state the
values of discardable_bytes was 16K and discardable_extents was -1,
expected values 0 and 0.

Repeating the workload again did not correct the bogus values so the
offset seems to be stable once it happens.

Reported-by: David Sterba <dsterba@suse.com>
Signed-off-by: Dennis Zhou <dennis@kernel.org>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/discard.c

index 6f48ae1589d90b0eab78d33ca14d2e3f8e9d8cd2..5615320fa659f31835f9415d0208a15b80404b0b 100644 (file)
@@ -516,6 +516,7 @@ bool btrfs_run_discard_work(struct btrfs_discard_ctl *discard_ctl)
 void btrfs_discard_calc_delay(struct btrfs_discard_ctl *discard_ctl)
 {
        s32 discardable_extents;
+       s64 discardable_bytes;
        u32 iops_limit;
        unsigned long delay;
        unsigned long lower_limit = BTRFS_DISCARD_MIN_DELAY_MSEC;
@@ -526,6 +527,27 @@ void btrfs_discard_calc_delay(struct btrfs_discard_ctl *discard_ctl)
 
        spin_lock(&discard_ctl->lock);
 
+       /*
+        * The following is to fix a potential -1 discrepenancy that we're not
+        * sure how to reproduce. But given that this is the only place that
+        * utilizes these numbers and this is only called by from
+        * btrfs_finish_extent_commit() which is synchronized, we can correct
+        * here.
+        */
+       if (discardable_extents < 0)
+               atomic_add(-discardable_extents,
+                          &discard_ctl->discardable_extents);
+
+       discardable_bytes = atomic64_read(&discard_ctl->discardable_bytes);
+       if (discardable_bytes < 0)
+               atomic64_add(-discardable_bytes,
+                            &discard_ctl->discardable_bytes);
+
+       if (discardable_extents <= 0) {
+               spin_unlock(&discard_ctl->lock);
+               return;
+       }
+
        iops_limit = READ_ONCE(discard_ctl->iops_limit);
        if (iops_limit)
                lower_limit = max_t(unsigned long, lower_limit,