]> git.proxmox.com Git - ceph.git/blob - ceph/src/os/bluestore/StupidAllocator.cc
import ceph 12.2.12
[ceph.git] / ceph / src / os / bluestore / StupidAllocator.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "StupidAllocator.h"
5 #include "bluestore_types.h"
6 #include "common/debug.h"
7
8 #define dout_context cct
9 #define dout_subsys ceph_subsys_bluestore
10 #undef dout_prefix
11 #define dout_prefix *_dout << "stupidalloc 0x" << this << " "
12
13 StupidAllocator::StupidAllocator(CephContext* cct)
14 : cct(cct), num_free(0),
15 free(10),
16 last_alloc(0)
17 {
18 }
19
20 StupidAllocator::~StupidAllocator()
21 {
22 }
23
24 unsigned StupidAllocator::_choose_bin(uint64_t orig_len)
25 {
26 uint64_t len = orig_len / cct->_conf->bdev_block_size;
27 int bin = std::min((int)cbits(len), (int)free.size() - 1);
28 dout(30) << __func__ << " len 0x" << std::hex << orig_len << std::dec
29 << " -> " << bin << dendl;
30 return bin;
31 }
32
33 void StupidAllocator::_insert_free(uint64_t off, uint64_t len)
34 {
35 unsigned bin = _choose_bin(len);
36 dout(30) << __func__ << " 0x" << std::hex << off << "~" << len << std::dec
37 << " in bin " << bin << dendl;
38 while (true) {
39 free[bin].insert(off, len, &off, &len);
40 unsigned newbin = _choose_bin(len);
41 if (newbin == bin)
42 break;
43 dout(30) << __func__ << " promoting 0x" << std::hex << off << "~" << len
44 << std::dec << " to bin " << newbin << dendl;
45 free[bin].erase(off, len);
46 bin = newbin;
47 }
48 }
49
50 /// return the effective length of the extent if we align to alloc_unit
51 uint64_t StupidAllocator::_aligned_len(
52 StupidAllocator::interval_set_t::iterator p,
53 uint64_t alloc_unit)
54 {
55 uint64_t skew = p.get_start() % alloc_unit;
56 if (skew)
57 skew = alloc_unit - skew;
58 if (skew > p.get_len())
59 return 0;
60 else
61 return p.get_len() - skew;
62 }
63
64 int64_t StupidAllocator::allocate_int(
65 uint64_t want_size, uint64_t alloc_unit, int64_t hint,
66 uint64_t *offset, uint32_t *length)
67 {
68 std::lock_guard<std::mutex> l(lock);
69 dout(10) << __func__ << " want_size 0x" << std::hex << want_size
70 << " alloc_unit 0x" << alloc_unit
71 << " hint 0x" << hint << std::dec
72 << dendl;
73 uint64_t want = MAX(alloc_unit, want_size);
74 int bin = _choose_bin(want);
75 int orig_bin = bin;
76
77 auto p = free[0].begin();
78
79 if (!hint)
80 hint = last_alloc;
81
82 // search up (from hint)
83 if (hint) {
84 for (bin = orig_bin; bin < (int)free.size(); ++bin) {
85 p = free[bin].lower_bound(hint);
86 while (p != free[bin].end()) {
87 if (_aligned_len(p, alloc_unit) >= want_size) {
88 goto found;
89 }
90 ++p;
91 }
92 }
93 }
94
95 // search up (from origin, and skip searched extents by hint)
96 for (bin = orig_bin; bin < (int)free.size(); ++bin) {
97 p = free[bin].begin();
98 auto end = hint ? free[bin].lower_bound(hint) : free[bin].end();
99 while (p != end) {
100 if (_aligned_len(p, alloc_unit) >= want_size) {
101 goto found;
102 }
103 ++p;
104 }
105 }
106
107 // search down (hint)
108 if (hint) {
109 for (bin = orig_bin; bin >= 0; --bin) {
110 p = free[bin].lower_bound(hint);
111 while (p != free[bin].end()) {
112 if (_aligned_len(p, alloc_unit) >= alloc_unit) {
113 goto found;
114 }
115 ++p;
116 }
117 }
118 }
119
120 // search down (from origin, and skip searched extents by hint)
121 for (bin = orig_bin; bin >= 0; --bin) {
122 p = free[bin].begin();
123 auto end = hint ? free[bin].lower_bound(hint) : free[bin].end();
124 while (p != end) {
125 if (_aligned_len(p, alloc_unit) >= alloc_unit) {
126 goto found;
127 }
128 ++p;
129 }
130 }
131
132 return -ENOSPC;
133
134 found:
135 uint64_t skew = p.get_start() % alloc_unit;
136 if (skew)
137 skew = alloc_unit - skew;
138 *offset = p.get_start() + skew;
139 *length = MIN(MAX(alloc_unit, want_size), P2ALIGN((p.get_len() - skew), alloc_unit));
140 if (cct->_conf->bluestore_debug_small_allocations) {
141 uint64_t max =
142 alloc_unit * (rand() % cct->_conf->bluestore_debug_small_allocations);
143 if (max && *length > max) {
144 dout(10) << __func__ << " shortening allocation of 0x" << std::hex
145 << *length << " -> 0x"
146 << max << " due to debug_small_allocations" << std::dec << dendl;
147 *length = max;
148 }
149 }
150 dout(30) << __func__ << " got 0x" << std::hex << *offset << "~" << *length
151 << " from bin " << std::dec << bin << dendl;
152
153 free[bin].erase(*offset, *length);
154 uint64_t off, len;
155 if (*offset && free[bin].contains(*offset - skew - 1, &off, &len)) {
156 int newbin = _choose_bin(len);
157 if (newbin != bin) {
158 dout(30) << __func__ << " demoting 0x" << std::hex << off << "~" << len
159 << std::dec << " to bin " << newbin << dendl;
160 free[bin].erase(off, len);
161 _insert_free(off, len);
162 }
163 }
164 if (free[bin].contains(*offset + *length, &off, &len)) {
165 int newbin = _choose_bin(len);
166 if (newbin != bin) {
167 dout(30) << __func__ << " demoting 0x" << std::hex << off << "~" << len
168 << std::dec << " to bin " << newbin << dendl;
169 free[bin].erase(off, len);
170 _insert_free(off, len);
171 }
172 }
173
174 num_free -= *length;
175 assert(num_free >= 0);
176 last_alloc = *offset + *length;
177 return 0;
178 }
179
180 int64_t StupidAllocator::allocate(
181 uint64_t want_size,
182 uint64_t alloc_unit,
183 uint64_t max_alloc_size,
184 int64_t hint,
185 PExtentVector *extents)
186 {
187 uint64_t allocated_size = 0;
188 uint64_t offset = 0;
189 uint32_t length = 0;
190 int res = 0;
191
192 if (max_alloc_size == 0) {
193 max_alloc_size = want_size;
194 }
195
196 while (allocated_size < want_size) {
197 res = allocate_int(MIN(max_alloc_size, (want_size - allocated_size)),
198 alloc_unit, hint, &offset, &length);
199 if (res != 0) {
200 /*
201 * Allocation failed.
202 */
203 break;
204 }
205 bool can_append = true;
206 if (!extents->empty()) {
207 bluestore_pextent_t &last_extent = extents->back();
208 if ((last_extent.end() == offset) &&
209 ((last_extent.length + length) <= max_alloc_size)) {
210 can_append = false;
211 last_extent.length += length;
212 }
213 }
214 if (can_append) {
215 extents->emplace_back(bluestore_pextent_t(offset, length));
216 }
217
218 allocated_size += length;
219 hint = offset + length;
220 }
221
222 if (allocated_size == 0) {
223 return -ENOSPC;
224 }
225 return allocated_size;
226 }
227
228 void StupidAllocator::release(
229 const interval_set<uint64_t>& release_set)
230 {
231 std::lock_guard<std::mutex> l(lock);
232 for (interval_set<uint64_t>::const_iterator p = release_set.begin();
233 p != release_set.end();
234 ++p) {
235 const auto offset = p.get_start();
236 const auto length = p.get_len();
237 ldout(cct, 10) << __func__ << " 0x" << std::hex << offset << "~" << length
238 << std::dec << dendl;
239 _insert_free(offset, length);
240 num_free += length;
241 }
242 }
243
244 uint64_t StupidAllocator::get_free()
245 {
246 std::lock_guard<std::mutex> l(lock);
247 return num_free;
248 }
249
250 double StupidAllocator::get_fragmentation(uint64_t alloc_unit)
251 {
252 assert(alloc_unit);
253 double res;
254 uint64_t max_intervals = 0;
255 uint64_t intervals = 0;
256 {
257 std::lock_guard<std::mutex> l(lock);
258 max_intervals = num_free / alloc_unit;
259 for (unsigned bin = 0; bin < free.size(); ++bin) {
260 intervals += free[bin].num_intervals();
261 }
262 }
263 ldout(cct, 30) << __func__ << " " << intervals << "/" << max_intervals
264 << dendl;
265 assert(intervals <= max_intervals);
266 if (!intervals || max_intervals <= 1) {
267 return 0.0;
268 }
269 intervals--;
270 max_intervals--;
271 res = (double)intervals / max_intervals;
272 return res;
273 }
274
275 void StupidAllocator::dump()
276 {
277 std::lock_guard<std::mutex> l(lock);
278 for (unsigned bin = 0; bin < free.size(); ++bin) {
279 dout(0) << __func__ << " free bin " << bin << ": "
280 << free[bin].num_intervals() << " extents" << dendl;
281 for (auto p = free[bin].begin();
282 p != free[bin].end();
283 ++p) {
284 dout(0) << __func__ << " 0x" << std::hex << p.get_start() << "~"
285 << p.get_len() << std::dec << dendl;
286 }
287 }
288 }
289
290 void StupidAllocator::init_add_free(uint64_t offset, uint64_t length)
291 {
292 std::lock_guard<std::mutex> l(lock);
293 dout(10) << __func__ << " 0x" << std::hex << offset << "~" << length
294 << std::dec << dendl;
295 _insert_free(offset, length);
296 num_free += length;
297 }
298
299 void StupidAllocator::init_rm_free(uint64_t offset, uint64_t length)
300 {
301 std::lock_guard<std::mutex> l(lock);
302 dout(10) << __func__ << " 0x" << std::hex << offset << "~" << length
303 << std::dec << dendl;
304 interval_set_t rm;
305 rm.insert(offset, length);
306 for (unsigned i = 0; i < free.size() && !rm.empty(); ++i) {
307 interval_set_t overlap;
308 overlap.intersection_of(rm, free[i]);
309 if (!overlap.empty()) {
310 dout(20) << __func__ << " bin " << i << " rm 0x" << std::hex << overlap
311 << std::dec << dendl;
312 auto it = overlap.begin();
313 auto it_end = overlap.end();
314 while (it != it_end) {
315 auto o = it.get_start();
316 auto l = it.get_len();
317
318 free[i].erase(o, l,
319 [&](uint64_t off, uint64_t len) {
320 unsigned newbin = _choose_bin(len);
321 if (newbin != i) {
322 ldout(cct, 30) << __func__ << " demoting1 0x" << std::hex << off << "~" << len
323 << std::dec << " to bin " << newbin << dendl;
324 _insert_free(off, len);
325 return true;
326 }
327 return false;
328 });
329 ++it;
330 }
331
332 rm.subtract(overlap);
333 }
334 }
335 assert(rm.empty());
336 num_free -= length;
337 assert(num_free >= 0);
338 }
339
340
341 void StupidAllocator::shutdown()
342 {
343 dout(1) << __func__ << dendl;
344 }
345