]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
os/bluestore: add perf counter for allocator fragmentation. 21377/head
authorIgor Fedotov <ifedotov@suse.com>
Thu, 12 Apr 2018 12:35:05 +0000 (15:35 +0300)
committerIgor Fedotov <ifedotov@suse.com>
Thu, 12 Apr 2018 14:45:16 +0000 (17:45 +0300)
Signed-off-by: Igor Fedotov <ifedotov@suse.com>
src/os/bluestore/Allocator.h
src/os/bluestore/BlueStore.cc
src/os/bluestore/BlueStore.h
src/os/bluestore/StupidAllocator.cc
src/os/bluestore/StupidAllocator.h
src/test/objectstore/Allocator_test.cc

index e12fe88b7aed52e93401c0515d2201487f022ca7..d3a48db650117e56e8beb3bafb8858e7a52b051b 100644 (file)
@@ -51,6 +51,10 @@ public:
   virtual void init_rm_free(uint64_t offset, uint64_t length) = 0;
 
   virtual uint64_t get_free() = 0;
+  virtual double get_fragmentation(uint64_t alloc_unit)
+  {
+    return 0.0;
+  }
 
   virtual void shutdown() = 0;
   static Allocator *create(CephContext* cct, string type, int64_t size,
index d863ccf0f2943adab8650fe734341e78a2b4129a..ca6cae54d7d98e972f87b764e364d6f7691aeb3b 100644 (file)
@@ -4107,6 +4107,8 @@ void BlueStore::_init_logger()
                    "collection");
   b.add_u64_counter(l_bluestore_read_eio, "bluestore_read_eio",
                     "Read EIO errors propagated to high level callers");
+  b.add_u64(l_bluestore_fragmentation, "bluestore_fragmentation_micros",
+            "How fragmented bluestore free space is (free extents / max possible number of free extents) * 1000");
   logger = b.create_perf_counters();
   cct->get_perfcounters_collection()->add(logger);
 }
@@ -8740,6 +8742,8 @@ void BlueStore::_txc_finish(TransContext *txc)
       dout(10) << __func__ << " empty zombie osr " << osr << " already reaped" << dendl;
     }
   }
+  logger->set(l_bluestore_fragmentation,
+    (uint64_t)(alloc->get_fragmentation(min_alloc_size) * 1000));
 }
 
 void BlueStore::_txc_release_alloc(TransContext *txc)
index e6ec52bc2a0de521db5b5744b2f3b919a9e5396d..a1e10712a597b078a88bf67bf09e626dd05dd4d7 100644 (file)
@@ -119,6 +119,7 @@ enum {
   l_bluestore_extent_compress,
   l_bluestore_gc_merged,
   l_bluestore_read_eio,
+  l_bluestore_fragmentation,
   l_bluestore_last
 };
 
index 9aba90bb03ec83c2313ca434547cce62b5ad17e4..99b9d030a166a8868445cdb29589c88d77aafb5b 100644 (file)
@@ -273,6 +273,31 @@ uint64_t StupidAllocator::get_free()
   return num_free;
 }
 
+double StupidAllocator::get_fragmentation(uint64_t alloc_unit)
+{
+  assert(alloc_unit);
+  double res;
+  uint64_t max_intervals = 0;
+  uint64_t intervals = 0;
+  {
+    std::lock_guard<std::mutex> l(lock);
+    max_intervals = num_free / alloc_unit;
+    for (unsigned bin = 0; bin < free.size(); ++bin) {
+      intervals += free[bin].num_intervals();
+    }
+  }
+  ldout(cct, 30) << __func__ << " " << intervals << "/" << max_intervals 
+                 << dendl;
+  assert(intervals <= max_intervals);
+  if (!intervals || max_intervals <= 1) {
+    return 0.0;
+  }
+  intervals--;
+  max_intervals--;
+  res = (double)intervals / max_intervals;
+  return res;
+}
+
 void StupidAllocator::dump()
 {
   std::lock_guard<std::mutex> l(lock);
index e3d86531f3f867f9e50b584eee9a6154f40e5932..a41feddc1ded3affca871f313798b427c117ce8a 100644 (file)
@@ -53,6 +53,7 @@ public:
     const interval_set<uint64_t>& release_set) override;
 
   uint64_t get_free() override;
+  double get_fragmentation(uint64_t alloc_unit) override;
 
   void dump() override;
 
index 66a5567238a4cd15c486919b3ac663fcfa817413..a53c2f7ff3a135d1b1cb6c4b91dcedf9e2aec59f 100644 (file)
@@ -290,6 +290,58 @@ TEST_P(AllocTest, test_alloc_non_aligned_len)
   EXPECT_EQ(want_size, alloc->allocate(want_size, alloc_unit, 0, &extents));
 }
 
+TEST_P(AllocTest, test_alloc_fragmentation)
+{
+  if (GetParam() == std::string("bitmap")) {
+    return;
+  }
+  uint64_t capacity = 4 * 1024 * 1024;
+  uint64_t alloc_unit = 4096;
+  uint64_t want_size = alloc_unit;
+  PExtentVector allocated, tmp;
+  
+  init_alloc(capacity, alloc_unit);
+  alloc->init_add_free(0, capacity);
+  
+  EXPECT_EQ(0.0, alloc->get_fragmentation(alloc_unit));
+  for (size_t i = 0; i < capacity / alloc_unit; ++i)
+  {
+    tmp.clear();
+    EXPECT_EQ(0, alloc->reserve(want_size));
+    EXPECT_EQ(want_size, alloc->allocate(want_size, alloc_unit, 0, 0, &tmp));
+    allocated.insert(allocated.end(), tmp.begin(), tmp.end());
+    EXPECT_EQ(0.0, alloc->get_fragmentation(alloc_unit));
+  }
+  EXPECT_EQ(-ENOSPC, alloc->reserve(want_size));
+
+  for (size_t i = 0; i < allocated.size(); i += 2)
+  {
+    interval_set<uint64_t> release_set;
+    release_set.insert(allocated[i].offset, allocated[i].length);
+    alloc->release(release_set);
+  }
+  EXPECT_EQ(1.0, alloc->get_fragmentation(alloc_unit));
+  for (size_t i = 1; i < allocated.size() / 2; i += 2)
+  {
+    interval_set<uint64_t> release_set;
+    release_set.insert(allocated[i].offset, allocated[i].length);
+    alloc->release(release_set);
+  }
+  // fragmentation approx = 257 intervals / 768 max intervals
+  EXPECT_EQ(33, uint64_t(alloc->get_fragmentation(alloc_unit) * 100));
+
+  for (size_t i = allocated.size() / 2 + 1; i < allocated.size(); i += 2)
+  {
+    interval_set<uint64_t> release_set;
+    release_set.insert(allocated[i].offset, allocated[i].length);
+    alloc->release(release_set);
+  }
+  // doing some rounding trick as stupid allocator doesn't merge all the 
+  // extents that causes some minor fragmentation (minor bug or by-design behavior?).
+  // Hence leaving just two 
+  // digits after decimal point due to this.
+  EXPECT_EQ(0, uint64_t(alloc->get_fragmentation(alloc_unit) * 100));
+}
 
 INSTANTIATE_TEST_CASE_P(
   Allocator,