diff options
author | Dennis Zhou <dennis@kernel.org> | 2020-01-02 16:26:46 -0500 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2020-01-20 16:41:01 +0100 |
commit | 81b29a3bf7cce4373526ff91a7a89aa6505597f7 (patch) | |
tree | 2fe26501894495a31b10a55b90b11d1e8718d808 /fs | |
parent | 27f0afc7376bf6e1405ee3a0ef0ab85171059ae8 (diff) | |
download | linux-81b29a3bf7cce4373526ff91a7a89aa6505597f7.tar.gz linux-81b29a3bf7cce4373526ff91a7a89aa6505597f7.tar.bz2 linux-81b29a3bf7cce4373526ff91a7a89aa6505597f7.zip |
btrfs: add correction to handle -1 edge case in async discard
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/discard.c | 22 |
1 files changed, 22 insertions, 0 deletions
diff --git a/fs/btrfs/discard.c b/fs/btrfs/discard.c index 6f48ae1589d9..5615320fa659 100644 --- a/fs/btrfs/discard.c +++ b/fs/btrfs/discard.c @@ -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, |