]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
blk/KernelDevice: Introduce a cap on the number of pending discards 62221/head
authorJoshua Baergen <jbaergen@digitalocean.com>
Wed, 18 Dec 2024 17:27:58 +0000 (10:27 -0700)
committerIgor Fedotov <igor.fedotov@croit.io>
Tue, 11 Mar 2025 10:11:53 +0000 (13:11 +0300)
Some disks have a discard performance that is too low to keep up with
write workloads. Using async discard in this case will cause the OSD to
run out of capacity due to the number of outstanding discards preventing
allocations from being freed. While sync discard could be used in this
case to cause backpressure, this might have unacceptable performance
implications.

For the most part, as long as enough discards are getting through to a
device, then it will stay trimmed enough to maintain acceptable
performance. Thus, we can introduce a cap on the pending discard count,
ensuring that the queue of allocations to be freed doesn't get too long
while also issuing sufficient discards to disk. The default value of
1000000 has ample room for discard spikes (e.g. from snaptrim); it could
result in multiple minutes of discards being queued up, but at least
it's not unbounded (though if a user really wants unbounded behaviour,
they can choose it by setting the new configuration option to 0).

Fixes: https://tracker.ceph.com/issues/69604
Signed-off-by: Joshua Baergen <jbaergen@digitalocean.com>
(cherry picked from commit 1dee8837959075687ea8a81c4eec2e1c6625e486)

src/blk/kernel/KernelDevice.cc
src/blk/kernel/KernelDevice.h
src/common/options/global.yaml.in

index 64be2466540ff61ddd83934ed80834e49621fde2..e0ca14f2731e07e961d1d7e191594a517810504e 100644 (file)
@@ -799,14 +799,21 @@ void KernelDevice::_discard_thread(uint64_t tid)
 
 // this is private and is expected that the caller checks that discard
 // threads are running via _discard_started()
-void KernelDevice::_queue_discard(interval_set<uint64_t> &to_release)
+bool KernelDevice::_queue_discard(interval_set<uint64_t> &to_release)
 {
   if (to_release.empty())
-    return;
+    return false;
+
+  auto max_pending = cct->_conf->bdev_async_discard_max_pending;
 
   std::lock_guard l(discard_lock);
+
+  if (max_pending > 0 && discard_queued.num_intervals() >= max_pending)
+    return false;
+
   discard_queued.insert(to_release);
   discard_cond.notify_one();
+  return true;
 }
 
 // return true only if discard was queued, so caller won't have to do
@@ -817,8 +824,7 @@ bool KernelDevice::try_discard(interval_set<uint64_t> &to_release, bool async)
     return false;
 
   if (async && _discard_started()) {
-    _queue_discard(to_release);
-    return true;
+    return _queue_discard(to_release);
   } else {
     for (auto p = to_release.begin(); p != to_release.end(); ++p) {
       _discard(p.get_start(), p.get_len());
index e3ecaa036d0880dcc7ef2d0215a287d62050d263..ede56d140d3385cc1235026cdf556747bb7c79e0 100644 (file)
@@ -86,7 +86,7 @@ private:
 
   void _aio_thread();
   void _discard_thread(uint64_t tid);
-  void _queue_discard(interval_set<uint64_t> &to_release);
+  bool _queue_discard(interval_set<uint64_t> &to_release);
   bool try_discard(interval_set<uint64_t> &to_release, bool async = true) override;
 
   int _aio_start();
index 8de8f25f5f0a02eb2cb1018bad132de2ed54f891..b69b19f37bdca3cf19cec6af1cd0d8e7452b5f7b 100644 (file)
@@ -4035,6 +4035,23 @@ options:
   - runtime
   see_also:
   - bdev_enable_discard
+  - bdev_async_discard_max_pending
+- name: bdev_async_discard_max_pending
+  desc: maximum number of pending discards
+  long_desc: The maximum number of pending async discards that can be queued and not claimed by an
+    async discard thread. Discards will not be issued once the queue is full and blocks will be
+    freed back to the allocator immediately instead. This is useful if you have a device with slow
+    discard performance that can't keep up to a consistently high write workload. 0 means
+    'unlimited'.
+  type: uint
+  level: advanced
+  default: 1000000
+  min: 0
+  with_legacy: true
+  flags:
+  - runtime
+  see_also:
+  - bdev_async_discard_threads
 - name: bdev_flock_retry_interval
   type: float
   level: advanced