]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
os/bluestore/AvlAllocator: introduce bluestore_avl_alloc_ff_max_search_count
authorKefu Chai <kchai@redhat.com>
Wed, 2 Jun 2021 07:57:04 +0000 (15:57 +0800)
committerMauricio Faria de Oliveira <mfo@canonical.com>
Wed, 10 Nov 2021 15:07:56 +0000 (12:07 -0300)
so AvlAllocator can switch from the first-first mode to best-fit mode
without walking through the whole space map tree. in the
highly-fragmented system, iterating the whole tree could hurt the
performance of fast storage system a lot.

the idea comes from openzfs's metaslab allocator.

Signed-off-by: Kefu Chai <kchai@redhat.com>
(cherry picked from commit 40f05b971f5a8064cf9819f80fc3bbf21d5206da)
Signed-off-by: Mauricio Faria de Oliveira <mfo@canonical.com>
 Conflicts:
src/common/options/global.yaml.in
- Moved new option into src/common/options.cc

src/common/options.cc
src/os/bluestore/AvlAllocator.cc
src/os/bluestore/AvlAllocator.h

index 97175f8eb7886e08227ccbe5d0027e4fe13c6867..e4fd613f7c22a097bfb85b2fdf82b7a41ecc18cb 100644 (file)
@@ -4716,6 +4716,10 @@ std::vector<Option> get_global_options() {
     .set_description("Enforces specific hw profile settings")
     .set_long_description("'hdd' enforces settings intended for BlueStore above a rotational drive. 'ssd' enforces settings intended for BlueStore above a solid drive. 'default' - using settings for the actual hardware."),
 
+    Option("bluestore_avl_alloc_ff_max_search_count", Option::TYPE_UINT, Option::LEVEL_DEV)
+    .set_default(100)
+    .set_description("Search for this many ranges in first-fit mode before switching over to to best-fit mode. 0 to iterate through all ranges for required chunk."),
+
     Option("bluestore_avl_alloc_bf_threshold", Option::TYPE_UINT, Option::LEVEL_DEV)
     .set_default(131072)
     .set_description(""),
index 7c620ade31d6509523453ae66d15f3d71732e775..751e52d58175c109e081a2a9ffb18b00f5d95fa8 100644 (file)
@@ -34,6 +34,7 @@ uint64_t AvlAllocator::_pick_block_after(uint64_t *cursor,
                                         uint64_t align)
 {
   const auto compare = range_tree.key_comp();
+  uint32_t search_count = 0;
   auto rs_start = range_tree.lower_bound(range_t{*cursor, size}, compare);
   for (auto rs = rs_start; rs != range_tree.end(); ++rs) {
     uint64_t offset = p2roundup(rs->start, align);
@@ -41,6 +42,9 @@ uint64_t AvlAllocator::_pick_block_after(uint64_t *cursor,
       *cursor = offset + size;
       return offset;
     }
+    if (max_search_count > 0 && ++search_count > max_search_count) {
+      return -1ULL;
+    }
   }
   if (*cursor == 0) {
     // If we already started from beginning, don't bother with searching from beginning
@@ -53,6 +57,9 @@ uint64_t AvlAllocator::_pick_block_after(uint64_t *cursor,
       *cursor = offset + size;
       return offset;
     }
+    if (max_search_count > 0 && ++search_count > max_search_count) {
+      return -1ULL;
+    }
   }
   return -1ULL;
 }
@@ -240,13 +247,27 @@ int AvlAllocator::_allocate(
 
   const int free_pct = num_free * 100 / num_total;
   uint64_t start = 0;
-  /*
-   * If we're running low on space switch to using the size
-   * sorted AVL tree (best-fit).
-   */
+  // If we're running low on space, find a range by size by looking up in the size
+  // sorted tree (best-fit), instead of searching in the area pointed by cursor
   if (force_range_size_alloc ||
       max_size < range_size_alloc_threshold ||
       free_pct < range_size_alloc_free_pct) {
+    start = -1ULL;
+  } else {
+    /*
+     * Find the largest power of 2 block size that evenly divides the
+     * requested size. This is used to try to allocate blocks with similar
+     * alignment from the same area (i.e. same cursor bucket) but it does
+     * not guarantee that other allocations sizes may exist in the same
+     * region.
+     */
+    uint64_t align = size & -size;
+    ceph_assert(align != 0);
+    uint64_t* cursor = &lbas[cbits(align) - 1];
+    start = _pick_block_after(cursor, size, unit);
+    dout(20) << __func__ << " first fit=" << start << " size=" << size << dendl;
+  }
+  if (start == -1ULL) {
     do {
       start = _pick_block_fits(size, unit);
       dout(20) << __func__ << " best fit=" << start << " size=" << size << dendl;
@@ -257,25 +278,6 @@ int AvlAllocator::_allocate(
       // that large block due to misaligned extents
       size = p2align(size >> 1, unit);
     } while (size >= unit);
-  } else {
-    do {
-      /*
-       * Find the largest power of 2 block size that evenly divides the
-       * requested size. This is used to try to allocate blocks with similar
-       * alignment from the same area (i.e. same cursor bucket) but it does
-       * not guarantee that other allocations sizes may exist in the same
-       * region.
-       */
-      uint64_t* cursor = &lbas[cbits(size) - 1];
-      start = _pick_block_after(cursor, size, unit);
-      dout(20) << __func__ << " first fit=" << start << " size=" << size << dendl;
-      if (start != uint64_t(-1ULL)) {
-        break;
-      }
-      // try to collect smaller extents as we could fail to retrieve
-      // that large block due to misaligned extents
-      size = p2align(size >> 1, unit);
-    } while (size >= unit);
   }
   if (start == -1ULL) {
     return -ENOSPC;
@@ -329,6 +331,8 @@ AvlAllocator::AvlAllocator(CephContext* cct,
     cct->_conf.get_val<uint64_t>("bluestore_avl_alloc_bf_threshold")),
   range_size_alloc_free_pct(
     cct->_conf.get_val<uint64_t>("bluestore_avl_alloc_bf_free_pct")),
+  max_search_count(
+    cct->_conf.get_val<uint64_t>("bluestore_avl_alloc_ff_max_search_count")),
   range_count_cap(max_mem / sizeof(range_seg_t)),
   cct(cct)
 {}
index e3deb729d106be1acc5b8d6fb9d661dea40bfcf1..023c1a15255dded0be6a54893e40b1ca6e71d3d9 100644 (file)
@@ -163,7 +163,12 @@ private:
    * switch to using best-fit allocations.
    */
   int range_size_alloc_free_pct = 0;
-
+  /*
+   * Maximum number of segments to check in the first-fit mode, without this
+   * limit, fragmented device can see lots of iterations and _block_picker()
+   * becomes the performance limiting factor on high-performance storage.
+   */
+  const uint32_t max_search_count;
   /*
   * Max amount of range entries allowed. 0 - unlimited
   */