"Read EIO errors propagated to high level callers");
b.add_u64_counter(l_bluestore_reads_with_retries, "bluestore_reads_with_retries",
"Read operations that required at least one retry due to failed checksum validation");
+ 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);
}
dout(10) << __func__ << " reaping empty zombie osr " << osr << dendl;
osr->_unregister();
}
+ logger->set(l_bluestore_fragmentation,
+ (uint64_t)(alloc->get_fragmentation(min_alloc_size) * 1000));
}
void BlueStore::_txc_release_alloc(TransContext *txc)
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);
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,