]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "Allocator.h" | |
5 | #include "StupidAllocator.h" | |
a8e16298 | 6 | #include "BitmapAllocator.h" |
9f95a23c | 7 | #include "AvlAllocator.h" |
7c673cae | 8 | #include "common/debug.h" |
eafe8130 | 9 | #include "common/admin_socket.h" |
7c673cae FG |
10 | #define dout_subsys ceph_subsys_bluestore |
11 | ||
eafe8130 TL |
12 | class Allocator::SocketHook : public AdminSocketHook { |
13 | Allocator *alloc; | |
14 | ||
15 | std::string name; | |
16 | public: | |
9f95a23c TL |
17 | explicit SocketHook(Allocator *alloc, |
18 | const std::string& _name) : | |
19 | alloc(alloc), name(_name) | |
eafe8130 TL |
20 | { |
21 | AdminSocket *admin_socket = g_ceph_context->get_admin_socket(); | |
22 | if (name.empty()) { | |
23 | name = to_string((uintptr_t)this); | |
24 | } | |
25 | if (admin_socket) { | |
9f95a23c TL |
26 | int r = admin_socket->register_command( |
27 | ("bluestore allocator dump " + name).c_str(), | |
28 | this, | |
29 | "dump allocator free regions"); | |
eafe8130 TL |
30 | if (r != 0) |
31 | alloc = nullptr; //some collision, disable | |
32 | if (alloc) { | |
9f95a23c TL |
33 | r = admin_socket->register_command( |
34 | ("bluestore allocator score " + name).c_str(), | |
35 | this, | |
36 | "give score on allocator fragmentation (0-no fragmentation, 1-absolute fragmentation)"); | |
37 | ceph_assert(r == 0); | |
38 | r = admin_socket->register_command( | |
39 | ("bluestore allocator fragmentation " + name).c_str(), | |
40 | this, | |
41 | "give allocator fragmentation (0-no fragmentation, 1-absolute fragmentation)"); | |
eafe8130 TL |
42 | ceph_assert(r == 0); |
43 | } | |
44 | } | |
45 | } | |
46 | ~SocketHook() | |
47 | { | |
48 | AdminSocket *admin_socket = g_ceph_context->get_admin_socket(); | |
49 | if (admin_socket && alloc) { | |
9f95a23c | 50 | admin_socket->unregister_commands(this); |
eafe8130 TL |
51 | } |
52 | } | |
53 | ||
9f95a23c TL |
54 | int call(std::string_view command, |
55 | const cmdmap_t& cmdmap, | |
56 | Formatter *f, | |
57 | std::ostream& ss, | |
58 | bufferlist& out) override { | |
59 | int r = 0; | |
eafe8130 | 60 | if (command == "bluestore allocator dump " + name) { |
eafe8130 TL |
61 | f->open_array_section("free_regions"); |
62 | auto iterated_allocation = [&](size_t off, size_t len) { | |
63 | ceph_assert(len > 0); | |
64 | f->open_object_section("free"); | |
65 | char off_hex[30]; | |
66 | char len_hex[30]; | |
67 | snprintf(off_hex, sizeof(off_hex) - 1, "0x%lx", off); | |
68 | snprintf(len_hex, sizeof(len_hex) - 1, "0x%lx", len); | |
69 | f->dump_string("offset", off_hex); | |
70 | f->dump_string("length", len_hex); | |
71 | f->close_section(); | |
72 | }; | |
73 | alloc->dump(iterated_allocation); | |
eafe8130 | 74 | f->close_section(); |
eafe8130 | 75 | } else if (command == "bluestore allocator score " + name) { |
eafe8130 TL |
76 | f->open_object_section("fragmentation_score"); |
77 | f->dump_float("fragmentation_rating", alloc->get_fragmentation_score()); | |
78 | f->close_section(); | |
9f95a23c TL |
79 | } else if (command == "bluestore allocator fragmentation " + name) { |
80 | f->open_object_section("fragmentation"); | |
81 | f->dump_float("fragmentation_rating", alloc->get_fragmentation()); | |
82 | f->close_section(); | |
eafe8130 TL |
83 | } else { |
84 | ss << "Invalid command" << std::endl; | |
9f95a23c | 85 | r = -ENOSYS; |
eafe8130 | 86 | } |
eafe8130 TL |
87 | return r; |
88 | } | |
89 | ||
90 | }; | |
91 | Allocator::Allocator(const std::string& name) | |
92 | { | |
93 | asok_hook = new SocketHook(this, name); | |
94 | } | |
95 | ||
96 | ||
97 | Allocator::~Allocator() | |
98 | { | |
99 | delete asok_hook; | |
100 | } | |
101 | ||
102 | ||
7c673cae | 103 | Allocator *Allocator::create(CephContext* cct, string type, |
eafe8130 | 104 | int64_t size, int64_t block_size, const std::string& name) |
7c673cae | 105 | { |
eafe8130 | 106 | Allocator* alloc = nullptr; |
7c673cae | 107 | if (type == "stupid") { |
9f95a23c | 108 | alloc = new StupidAllocator(cct, name, block_size); |
7c673cae | 109 | } else if (type == "bitmap") { |
eafe8130 | 110 | alloc = new BitmapAllocator(cct, size, block_size, name); |
9f95a23c TL |
111 | } else if (type == "avl") { |
112 | return new AvlAllocator(cct, size, block_size, name); | |
7c673cae | 113 | } |
eafe8130 TL |
114 | if (alloc == nullptr) { |
115 | lderr(cct) << "Allocator::" << __func__ << " unknown alloc type " | |
7c673cae | 116 | << type << dendl; |
eafe8130 TL |
117 | } |
118 | return alloc; | |
7c673cae | 119 | } |
a8e16298 TL |
120 | |
121 | void Allocator::release(const PExtentVector& release_vec) | |
122 | { | |
123 | interval_set<uint64_t> release_set; | |
124 | for (auto e : release_vec) { | |
125 | release_set.insert(e.offset, e.length); | |
126 | } | |
127 | release(release_set); | |
128 | } | |
eafe8130 TL |
129 | |
130 | /** | |
131 | * Gives fragmentation a numeric value. | |
132 | * | |
133 | * Following algorithm applies value to each existing free unallocated block. | |
134 | * Value of single block is a multiply of size and per-byte-value. | |
135 | * Per-byte-value is greater for larger blocks. | |
136 | * Assume block size X has value per-byte p; then block size 2*X will have per-byte value 1.1*p. | |
137 | * | |
138 | * This could be expressed in logarithms, but for speed this is interpolated inside ranges. | |
139 | * [1] [2..3] [4..7] [8..15] ... | |
140 | * ^ ^ ^ ^ | |
141 | * 1.1 1.1^2 1.1^3 1.1^4 ... | |
142 | * | |
143 | * Final score is obtained by proportion between score that would have been obtained | |
144 | * in condition of absolute fragmentation and score in no fragmentation at all. | |
145 | */ | |
146 | double Allocator::get_fragmentation_score() | |
147 | { | |
148 | // this value represents how much worth is 2X bytes in one chunk then in X + X bytes | |
149 | static const double double_size_worth = 1.1 ; | |
150 | std::vector<double> scales{1}; | |
151 | double score_sum = 0; | |
152 | size_t sum = 0; | |
153 | ||
154 | auto get_score = [&](size_t v) -> double { | |
155 | size_t sc = sizeof(v) * 8 - clz(v) - 1; //assign to grade depending on log2(len) | |
156 | while (scales.size() <= sc + 1) { | |
157 | //unlikely expand scales vector | |
158 | scales.push_back(scales[scales.size() - 1] * double_size_worth); | |
159 | } | |
160 | ||
161 | size_t sc_shifted = size_t(1) << sc; | |
162 | double x = double(v - sc_shifted) / sc_shifted; //x is <0,1) in its scale grade | |
163 | // linear extrapolation in its scale grade | |
164 | double score = (sc_shifted ) * scales[sc] * (1-x) + | |
165 | (sc_shifted * 2) * scales[sc+1] * x; | |
166 | return score; | |
167 | }; | |
168 | ||
169 | auto iterated_allocation = [&](size_t off, size_t len) { | |
170 | ceph_assert(len > 0); | |
171 | score_sum += get_score(len); | |
172 | sum += len; | |
173 | }; | |
174 | dump(iterated_allocation); | |
175 | ||
176 | ||
177 | double ideal = get_score(sum); | |
178 | double terrible = sum * get_score(1); | |
179 | return (ideal - score_sum) / (ideal - terrible); | |
180 | } |