aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDennis Zhou <dennis@kernel.org>2019-12-27 21:12:54 -0800
committerDennis Zhou <dennis@kernel.org>2020-01-02 12:00:48 -0800
commitf00ffdb40462c1dd9b611ee06cf19b2d495e398b (patch)
treed09fbad920f55a663652fe1e2991e0df55d560b1
parentfe33c76ede300fd4cb03d0a7878cac0cbee47c42 (diff)
downloadmisc-async-discard.tar.gz
btrfs: add correction to handle -1 edge case in async discardasync-discard
From Dave's testing, it's possible to drive a file system to have -1 discardable_extents and a corresponding negative discardable_bytes. As btrfs_discard_calc_delay() is the only user of discardable_extents, we can correct here for any negative discardable_extents/discardable_bytes. Reported-by: David Sterba <dsterba@suse.com> Signed-off-by: Dennis Zhou <dennis@kernel.org>
-rw-r--r--fs/btrfs/discard.c24
1 files changed, 21 insertions, 3 deletions
diff --git a/fs/btrfs/discard.c b/fs/btrfs/discard.c
index d5a89e3755ed19..d2c7851e31defb 100644
--- a/fs/btrfs/discard.c
+++ b/fs/btrfs/discard.c
@@ -518,14 +518,32 @@ void btrfs_discard_calc_delay(struct btrfs_discard_ctl *discard_ctl)
{
s32 discardable_extents =
atomic_read(&discard_ctl->discardable_extents);
+ s64 discardable_bytes = atomic64_read(&discard_ctl->discardable_bytes);
unsigned iops_limit;
unsigned long delay, lower_limit = BTRFS_DISCARD_MIN_DELAY_MSEC;
- if (!discardable_extents)
- return;
-
spin_lock(&discard_ctl->lock);
+ /*
+ * The following is to fix a potential -1 discrepenancy that I'm 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);
+
+ 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,