|
14 | 14 | #include "common/debug.h" |
15 | 15 | #include "common/admin_socket.h" |
16 | 16 | #define dout_subsys ceph_subsys_bluestore |
| 17 | +using TOPNSPC::common::cmd_getval; |
17 | 18 |
|
18 | 19 | using std::string; |
19 | 20 | using std::to_string; |
@@ -52,6 +53,13 @@ class Allocator::SocketHook : public AdminSocketHook { |
52 | 53 | this, |
53 | 54 | "give allocator fragmentation (0-no fragmentation, 1-absolute fragmentation)"); |
54 | 55 | ceph_assert(r == 0); |
| 56 | + r = admin_socket->register_command( |
| 57 | + ("bluestore allocator fragmentation histogram " + name + |
| 58 | + " name=alloc_unit,type=CephInt,req=false" + |
| 59 | + " name=num_buckets,type=CephInt,req=false").c_str(), |
| 60 | + this, |
| 61 | + "build allocator free regions state histogram"); |
| 62 | + ceph_assert(r == 0); |
55 | 63 | } |
56 | 64 | } |
57 | 65 | } |
@@ -100,6 +108,39 @@ class Allocator::SocketHook : public AdminSocketHook { |
100 | 108 | f->open_object_section("fragmentation"); |
101 | 109 | f->dump_float("fragmentation_rating", alloc->get_fragmentation()); |
102 | 110 | f->close_section(); |
| 111 | + } else if (command == "bluestore allocator fragmentation histogram " + name) { |
| 112 | + int64_t alloc_unit = 4096; |
| 113 | + cmd_getval(cmdmap, "alloc_unit", alloc_unit); |
| 114 | + if (alloc_unit == 0 || |
| 115 | + p2align(alloc_unit, alloc->get_block_size()) != alloc_unit) { |
| 116 | + ss << "Invalid allocation unit: '" << alloc_unit |
| 117 | + << ", to be aligned with: '" << alloc->get_block_size() |
| 118 | + << std::endl; |
| 119 | + return -EINVAL; |
| 120 | + } |
| 121 | + int64_t num_buckets = 8; |
| 122 | + cmd_getval(cmdmap, "num_buckets", num_buckets); |
| 123 | + if (num_buckets < 2) { |
| 124 | + ss << "Invalid amount of buckets (min=2): '" << num_buckets |
| 125 | + << std::endl; |
| 126 | + return -EINVAL; |
| 127 | + } |
| 128 | + |
| 129 | + Allocator::FreeStateHistogram hist; |
| 130 | + hist.resize(num_buckets); |
| 131 | + alloc->build_free_state_histogram(alloc_unit, hist); |
| 132 | + f->open_array_section("extent_counts"); |
| 133 | + for(int i = 0; i < num_buckets; i++) { |
| 134 | + f->open_object_section("c"); |
| 135 | + f->dump_unsigned("max_len", |
| 136 | + hist[i].get_max(i, num_buckets) |
| 137 | + ); |
| 138 | + f->dump_unsigned("total", hist[i].total); |
| 139 | + f->dump_unsigned("aligned", hist[i].aligned); |
| 140 | + f->dump_unsigned("units", hist[i].alloc_units); |
| 141 | + f->close_section(); |
| 142 | + } |
| 143 | + f->close_section(); |
103 | 144 | } else { |
104 | 145 | ss << "Invalid command" << std::endl; |
105 | 146 | r = -ENOSYS; |
@@ -234,3 +275,51 @@ double Allocator::get_fragmentation_score() |
234 | 275 | double terrible = (sum / block_size) * get_score(block_size); |
235 | 276 | return (ideal - score_sum) / (ideal - terrible); |
236 | 277 | } |
| 278 | + |
| 279 | +void Allocator::build_free_state_histogram( |
| 280 | + size_t alloc_unit, Allocator::FreeStateHistogram& hist) |
| 281 | +{ |
| 282 | + auto num_buckets = hist.size(); |
| 283 | + ceph_assert(num_buckets); |
| 284 | + |
| 285 | + auto base = free_state_hist_bucket::base; |
| 286 | + auto base_bits = free_state_hist_bucket::base_bits; |
| 287 | + auto mux = free_state_hist_bucket::mux; |
| 288 | + // maximum chunk size we track, |
| 289 | + // provided by the bucket before the last one |
| 290 | + size_t max = |
| 291 | + free_state_hist_bucket::get_max(num_buckets - 2, num_buckets); |
| 292 | + |
| 293 | + auto iterated_allocation = [&](size_t off, size_t len) { |
| 294 | + size_t idx; |
| 295 | + if (len <= base) { |
| 296 | + idx = 0; |
| 297 | + } else if (len > max) { |
| 298 | + idx = num_buckets - 1; |
| 299 | + } else { |
| 300 | + size_t most_bit = cbits(uint64_t(len-1)) - 1; |
| 301 | + idx = 1 + ((most_bit - base_bits) / mux); |
| 302 | + } |
| 303 | + ceph_assert(idx < num_buckets); |
| 304 | + ++hist[idx].total; |
| 305 | + |
| 306 | + // now calculate the bucket for the chunk after alignment, |
| 307 | + // resulting chunks shorter than alloc_unit are discarded |
| 308 | + auto delta = p2roundup(off, alloc_unit) - off; |
| 309 | + if (len >= delta + alloc_unit) { |
| 310 | + len -= delta; |
| 311 | + if (len <= base) { |
| 312 | + idx = 0; |
| 313 | + } else if (len > max) { |
| 314 | + idx = num_buckets - 1; |
| 315 | + } else { |
| 316 | + size_t most_bit = cbits(uint64_t(len-1)) - 1; |
| 317 | + idx = 1 + ((most_bit - base_bits) / mux); |
| 318 | + } |
| 319 | + ++hist[idx].aligned; |
| 320 | + hist[idx].alloc_units += len / alloc_unit; |
| 321 | + } |
| 322 | + }; |
| 323 | + |
| 324 | + foreach(iterated_allocation); |
| 325 | +} |
0 commit comments