]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
os/bluestore: fix unexpected ENOSPC in Avl/Hybrid allocators. 41658/head
authorIgor Fedotov <ifed@suse.com>
Mon, 17 May 2021 19:23:26 +0000 (22:23 +0300)
committerNeha Ojha <nojha@redhat.com>
Wed, 2 Jun 2021 16:24:41 +0000 (16:24 +0000)
Avl allocator mode was returning unexpected ENOSPC in first-fit mode if all size-
matching available extents were unaligned but applying the alignment made all of
them shorter than required. Since no lookup retry with smaller size -
ENOSPC is returned.
Additionally we should proceed with a lookup in best-fit mode even when
original size has been truncated to match the avail size.
(force_range_size_alloc==true)

Fixes: https://tracker.ceph.com/issues/50656
Signed-off-by: Igor Fedotov <ifedotov@suse.com>
(cherry picked from commit 0eed13a4969d02eeb23681519f2a23130e51ac59)

src/os/bluestore/AvlAllocator.cc
src/test/objectstore/Allocator_test.cc

index 0ac70baa80c3acd6cd87c03bfb59d06158730842..fc7f8a47af976fa0bb8e8eeebdd1080ddf6761bc 100644 (file)
@@ -219,16 +219,6 @@ int AvlAllocator::_allocate(
     ceph_assert(size > 0);
     force_range_size_alloc = true;
   }
-  /*
-   * 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.
-   */
-  const uint64_t align = size & -size;
-  ceph_assert(align != 0);
-  uint64_t *cursor = &lbas[cbits(align) - 1];
 
   const int free_pct = num_free * 100 / num_total;
   uint64_t start = 0;
@@ -239,10 +229,11 @@ int AvlAllocator::_allocate(
   if (force_range_size_alloc ||
       max_size < range_size_alloc_threshold ||
       free_pct < range_size_alloc_free_pct) {
-    *cursor = 0;
+    uint64_t fake_cursor = 0;
     do {
-      start = _block_picker(range_size_tree, cursor, size, unit);
-      if (start != -1ULL || !force_range_size_alloc) {
+      start = _block_picker(range_size_tree, &fake_cursor, size, unit);
+      dout(20) << __func__ << " best fit=" << start << " size=" << size << dendl;
+      if (start != uint64_t(-1ULL)) {
         break;
       }
       // try to collect smaller extents as we could fail to retrieve
@@ -250,7 +241,27 @@ int AvlAllocator::_allocate(
       size = p2align(size >> 1, unit);
     } while (size >= unit);
   } else {
-    start = _block_picker(range_tree, cursor, size, unit);
+    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 align = size & -size;
+      ceph_assert(align != 0);
+      uint64_t* cursor = &lbas[cbits(align) - 1];
+
+      start = _block_picker(range_tree, 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;
index f6f0c38bec20abc5e9e00bbb3a3bb8328e45891b..b571edd7c9ed922c2aa7ed28d95ea6708b00324f 100644 (file)
@@ -498,6 +498,47 @@ TEST_P(AllocTest, test_alloc_47883)
   EXPECT_EQ(got, 0x630000);
 }
 
+TEST_P(AllocTest, test_alloc_50656_best_fit)
+{
+  uint64_t block = 0x1000;
+  uint64_t size = 0x3b9e400000;
+
+  init_alloc(size, block);
+
+  // too few free extents - causes best fit mode for avls
+  for (size_t i = 0; i < 0x10; i++) {
+    alloc->init_add_free(i * 2 * 0x100000, 0x100000);
+  }
+
+  alloc->init_add_free(0x1e1bd13000, 0x404000);
+
+  PExtentVector extents;
+  auto need = 0x400000;
+  auto got = alloc->allocate(need, 0x10000, 0, (int64_t)0, &extents);
+  EXPECT_GT(got, 0);
+  EXPECT_EQ(got, 0x400000);
+}
+
+TEST_P(AllocTest, test_alloc_50656_first_fit)
+{
+  uint64_t block = 0x1000;
+  uint64_t size = 0x3b9e400000;
+
+  init_alloc(size, block);
+
+  for (size_t i = 0; i < 0x10000; i += 2) {
+    alloc->init_add_free(i * 0x100000, 0x100000);
+  }
+
+  alloc->init_add_free(0x1e1bd13000, 0x404000);
+
+  PExtentVector extents;
+  auto need = 0x400000;
+  auto got = alloc->allocate(need, 0x10000, 0, (int64_t)0, &extents);
+  EXPECT_GT(got, 0);
+  EXPECT_EQ(got, 0x400000);
+}
+
 INSTANTIATE_TEST_SUITE_P(
   Allocator,
   AllocTest,