]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/os/bluestore/Allocator.cc
import ceph 14.2.5
[ceph.git] / ceph / src / os / bluestore / Allocator.cc
index b6026353165cba9d1250aa85cff1dc65ca634603..748589fac22d9aa55326deea51ac65941ed3c9c9 100644 (file)
 #include "StupidAllocator.h"
 #include "BitmapAllocator.h"
 #include "common/debug.h"
-
+#include "common/admin_socket.h"
 #define dout_subsys ceph_subsys_bluestore
 
+class Allocator::SocketHook : public AdminSocketHook {
+  Allocator *alloc;
+
+  std::string name;
+public:
+  explicit SocketHook(Allocator *alloc, const std::string& _name) : alloc(alloc), name(_name)
+  {
+    AdminSocket *admin_socket = g_ceph_context->get_admin_socket();
+    if (name.empty()) {
+      name = to_string((uintptr_t)this);
+    }
+    if (admin_socket) {
+      int r = admin_socket->register_command(("bluestore allocator dump " + name).c_str(),
+                                           ("bluestore allocator dump " + name).c_str(),
+                                           this,
+                                           "dump allocator free regions");
+      if (r != 0)
+        alloc = nullptr; //some collision, disable
+      if (alloc) {
+        r = admin_socket->register_command(("bluestore allocator score " + name).c_str(),
+                                           ("bluestore allocator score " + name).c_str(),
+                                           this,
+                                           "give score on allocator fragmentation (0-no fragmentation, 1-absolute fragmentation)");
+        ceph_assert(r == 0);
+      }
+    }
+  }
+  ~SocketHook()
+  {
+    AdminSocket *admin_socket = g_ceph_context->get_admin_socket();
+    if (admin_socket && alloc) {
+      int r = admin_socket->unregister_command(("bluestore allocator dump " + name).c_str());
+      ceph_assert(r == 0);
+      r = admin_socket->unregister_command(("bluestore allocator score " + name).c_str());
+      ceph_assert(r == 0);
+    }
+  }
+
+  bool call(std::string_view command, const cmdmap_t& cmdmap,
+            std::string_view format, bufferlist& out) override {
+    stringstream ss;
+    bool r = true;
+    if (command == "bluestore allocator dump " + name) {
+      Formatter *f = Formatter::create(format, "json-pretty", "json-pretty");
+      f->open_array_section("free_regions");
+      auto iterated_allocation = [&](size_t off, size_t len) {
+        ceph_assert(len > 0);
+        f->open_object_section("free");
+        char off_hex[30];
+        char len_hex[30];
+        snprintf(off_hex, sizeof(off_hex) - 1, "0x%lx", off);
+        snprintf(len_hex, sizeof(len_hex) - 1, "0x%lx", len);
+        f->dump_string("offset", off_hex);
+        f->dump_string("length", len_hex);
+        f->close_section();
+      };
+      alloc->dump(iterated_allocation);
+
+
+      f->close_section();
+      f->flush(ss);
+    } else if (command == "bluestore allocator score " + name) {
+      Formatter *f = Formatter::create(format, "json-pretty", "json-pretty");
+      f->open_object_section("fragmentation_score");
+      f->dump_float("fragmentation_rating", alloc->get_fragmentation_score());
+      f->close_section();
+      f->flush(ss);
+      delete f;
+    } else {
+      ss << "Invalid command" << std::endl;
+      r = false;
+    }
+    out.append(ss);
+    return r;
+  }
+
+};
+Allocator::Allocator(const std::string& name)
+{
+  asok_hook = new SocketHook(this, name);
+}
+
+
+Allocator::~Allocator()
+{
+  delete asok_hook;
+}
+
+
 Allocator *Allocator::create(CephContext* cct, string type,
-                             int64_t size, int64_t block_size)
+                             int64_t size, int64_t block_size, const std::string& name)
 {
+  Allocator* alloc = nullptr;
   if (type == "stupid") {
-    return new StupidAllocator(cct);
+    alloc = new StupidAllocator(cct, name);
   } else if (type == "bitmap") {
-    return new BitmapAllocator(cct, size, block_size);
+    alloc = new BitmapAllocator(cct, size, block_size, name);
   }
-  lderr(cct) << "Allocator::" << __func__ << " unknown alloc type "
+  if (alloc == nullptr) {
+    lderr(cct) << "Allocator::" << __func__ << " unknown alloc type "
             << type << dendl;
-  return nullptr;
+  }
+  return alloc;
 }
 
 void Allocator::release(const PExtentVector& release_vec)
@@ -29,3 +121,55 @@ void Allocator::release(const PExtentVector& release_vec)
   }
   release(release_set);
 }
+
+/**
+ * Gives fragmentation a numeric value.
+ *
+ * Following algorithm applies value to each existing free unallocated block.
+ * Value of single block is a multiply of size and per-byte-value.
+ * Per-byte-value is greater for larger blocks.
+ * Assume block size X has value per-byte p; then block size 2*X will have per-byte value 1.1*p.
+ *
+ * This could be expressed in logarithms, but for speed this is interpolated inside ranges.
+ * [1]  [2..3] [4..7] [8..15] ...
+ * ^    ^      ^      ^
+ * 1.1  1.1^2  1.1^3  1.1^4 ...
+ *
+ * Final score is obtained by proportion between score that would have been obtained
+ * in condition of absolute fragmentation and score in no fragmentation at all.
+ */
+double Allocator::get_fragmentation_score()
+{
+  // this value represents how much worth is 2X bytes in one chunk then in X + X bytes
+  static const double double_size_worth = 1.1 ;
+  std::vector<double> scales{1};
+  double score_sum = 0;
+  size_t sum = 0;
+
+  auto get_score = [&](size_t v) -> double {
+    size_t sc = sizeof(v) * 8 - clz(v) - 1; //assign to grade depending on log2(len)
+    while (scales.size() <= sc + 1) {
+      //unlikely expand scales vector
+      scales.push_back(scales[scales.size() - 1] * double_size_worth);
+    }
+
+    size_t sc_shifted = size_t(1) << sc;
+    double x = double(v - sc_shifted) / sc_shifted; //x is <0,1) in its scale grade
+    // linear extrapolation in its scale grade
+    double score = (sc_shifted    ) * scales[sc]   * (1-x) +
+                   (sc_shifted * 2) * scales[sc+1] * x;
+    return score;
+  };
+
+  auto iterated_allocation = [&](size_t off, size_t len) {
+    ceph_assert(len > 0);
+    score_sum += get_score(len);
+    sum += len;
+  };
+  dump(iterated_allocation);
+
+
+  double ideal = get_score(sum);
+  double terrible = sum * get_score(1);
+  return (ideal - score_sum) / (ideal - terrible);
+}