The function was used only by writer v2, but it was returning 0 always.
Now it properly returns the mask of used and unused blob regions.
Changed returned type unused_t (16 bits) -> uint64_t.
Made write_v2 path now properly mark unused.
+ new unittest bluestore_blob_t.get_unused_mask
+ fixed unittest ExtentMapFixture.rain
Signed-off-by: Adam Kupczyk <akupczyk@ibm.com>
_get_disk_space(blob_length - alloc_offset, blob_allocs);
bblob.allocated(alloc_offset, blob_length - alloc_offset, blob_allocs);
//^sets also logical_length = blob_length
+ bblob.add_unused_all();
dout(25) << __func__ << " @0x" << std::hex << in_blob_offset
<< "~" << disk_data.length()
<< " alloc_offset=" << alloc_offset
_crop_allocs_to_io(disk_extents, in_blob_offset - alloc_offset,
blob_length - in_blob_offset - disk_data.length());
_schedule_io(disk_extents, disk_data);
+ bblob.mark_used(in_blob_offset, data_length);
return blob;
}
_get_disk_space(blob_length, blob_allocs);
_schedule_io(blob_allocs, disk_data); //have to do before move()
bblob.allocated_full(blob_length, std::move(blob_allocs));
- bblob.mark_used(0, blob_length); //todo - optimize; this obviously clears it
+ bblob.mark_used_all();
return blob;
}
inline void BlueStore::Writer::_schedule_io_masked(
uint64_t disk_position,
bufferlist data,
- bluestore_blob_t::unused_t mask,
+ uint64_t mask,
uint32_t chunk_size)
{
if (test_write_divertor == nullptr) {
uint32_t data_size = want_subau_end - want_subau_begin;
bufferlist data_at_left = split_left(data, data_size);
bd.real_length -= data_size;
- uint32_t mask = bb.get_unused_mask(in_blob_offset, data_size, chunk_size);
+ uint64_t mask = bb.get_unused_mask(in_blob_offset, data_size, chunk_size);
_blob_put_data_subau(b, in_blob_offset, data_at_left);
// transfer do disk
_schedule_io_masked(subau_disk_offset, data_at_left, mask, chunk_size);
uint32_t data_size = want_subau_end - want_subau_begin;
bufferlist data_at_right = split_right(data, data.length() - data_size);
bd.real_length -= data_size;
- uint32_t mask = bb.get_unused_mask(in_blob_offset, data_size, chunk_size);
+ uint64_t mask = bb.get_unused_mask(in_blob_offset, data_size, chunk_size);
_blob_put_data_subau(b, in_blob_offset, data_at_right);
- //transfer to disk
+ // transfer to disk
_schedule_io_masked(subau_disk_offset, data_at_right, mask, chunk_size);
uint32_t ref_end = std::min(ref_end_offset, want_subau_end);
inline void _schedule_io_masked(
uint64_t disk_offset,
bufferlist data,
- bluestore_blob_t::unused_t mask,
+ uint64_t mask,
uint32_t chunk_size);
inline void _schedule_io(
}
}
+ /// mark everything as unused
+ void add_unused_all() {
+ set_flag(FLAG_HAS_UNUSED);
+ unused = ~0;
+ }
+
/// indicate that a range has (now) been used.
void mark_used(uint64_t offset, uint64_t length) {
if (has_unused()) {
}
}
}
- /// todo implement me!
- unused_t get_unused_mask(uint32_t offset, uint32_t length, uint32_t chunk_size) {
+
+ ///mark everything as used
+ void mark_used_all() {
+ clear_flag(FLAG_HAS_UNUSED);
+ }
+
+ /// create bitmap mask, io_chunk_size per bit
+ /// bit 0 is offset, bit 1 is offset + io_chunk_size, ....
+ uint64_t get_unused_mask(uint32_t offset, uint32_t length, uint32_t io_chunk_size) {
if (has_unused()) {
- return 0;
+ uint32_t blob_len = get_logical_length();
+ ceph_assert((blob_len % (sizeof(unused)*8)) == 0);
+ ceph_assert(offset + length <= blob_len);
+ ceph_assert((offset % io_chunk_size) == 0);
+ ceph_assert((length % io_chunk_size) == 0);
+ if (length / io_chunk_size > 64) {
+ // the result cannot fit 64 bits, pretend all is used
+ return 0;
+ }
+ uint32_t chunk_size = blob_len / (sizeof(unused)*8);
+ uint16_t i = offset / chunk_size;
+ uint16_t j = 0;
+ uint64_t io_used = 0;
+ uint64_t next_u = round_down_to(offset + chunk_size, chunk_size);
+ uint64_t next_io = round_down_to(offset + io_chunk_size, io_chunk_size);
+ // The algorithm here is iterating 2 sequences that have different "speeds":
+ // unused bit speed (chunk_size) and output disk region speed (io_chunk_size)
+ // unused_bits : aaaaabbbbbcccccdddddeeeeefffffggggghhhhh
+ // disk_io_chnk: AAABBBCCCDDDEEEFFFGGGHHHIIIJJJ
+ // But we operate on "used" logic, as it allows for easier summation, and return the inverse.
+ // We apply restriction from i-th unused bit to j-th io_chunk.
+ // The relative sizes of chunk_size and io_chunk_size determine
+ // how fast we increase i and j respectively.
+ for (; next_io < offset + length + io_chunk_size; ) {
+ //produce io_mask bit, by copying state from unused bit
+ (!(unused & (1 << i))) ? io_used |= uint64_t(1) << j : 0;
+ auto le = next_u <= next_io;
+ if (next_u >= next_io) {
+ j++;
+ next_io += io_chunk_size;
+ }
+ if (le) {
+ i++;
+ next_u += chunk_size;
+ }
+ }
+ return ~io_used;
} else {
return 0;
}
}
+
// map_f_invoke templates intended to mask parameters which are not expected
// by the provided callback
template<class F, typename std::enable_if<std::is_invocable_r_v<
X.push_back(create());
}
for (size_t i = 0; i < H - 1; i++) {
- write(X[i], (rand() % W - 1) * au_size, au_size);
+ write(X[i], (rand() % (W - 1)) * au_size, au_size);
dup(X[i], X[i + 1], 0, W * au_size);
}
for (size_t i = 0; i < H; i++) {
ASSERT_EQ(expected_pos, num_expected_entries);
}
}
+
+TEST(bluestore_blob_t, get_unused_mask) {
+ uint32_t disk_block = 4 * 1024;
+ for (uint32_t alloc = 4 * 1024; alloc <= 256 * 1024; alloc *= 2) {
+ for (uint32_t t = 0; t < 10000; t++) {
+ bluestore_blob_t b;
+ uint32_t size = (rand() % 10 + 1) * alloc;
+ b.allocated_test({uint64_t(rand() * 0x1000), size});
+ b.add_unused(0, size);
+ // sprinkle used
+ uint32_t regions = (rand() % 4) + 1;
+ for (uint32_t i = 0; i < regions; i++) {
+ uint32_t left = (rand() % 4) ?
+ rand() % (size / disk_block + 1) * disk_block : //aligned to disk block
+ (rand() * 1024 + rand()) % size; // completely free
+ uint32_t right = (rand() % 4) ?
+ rand() % (size / disk_block + 1) * disk_block : //aligned to disk block
+ (rand() * 1024 + rand()) % size; // completely free
+ if (left == right) continue;
+ if (left > right) swap(left, right);
+ b.mark_used(left, right - left);
+ }
+
+ for (uint32_t io_chunk_size = 1024; io_chunk_size <= 32 * 1024; io_chunk_size *= 2) {
+ if (size < io_chunk_size || (size % io_chunk_size) != 0) {
+ continue;
+ }
+ if (size / io_chunk_size > 64) continue;
+ uint32_t io_begin = rand() % (size / io_chunk_size + 1) * io_chunk_size;
+ uint32_t io_end = rand() % (size / io_chunk_size + 1) * io_chunk_size;
+ if (io_begin == io_end) continue;
+ if (io_begin > io_end) swap(io_begin, io_end);
+
+ uint64_t mask = 0;
+ uint64_t bit = 1;
+ for (uint32_t i = io_begin; i < io_end; i += io_chunk_size) {
+ mask = mask | (b.is_unused(i, io_chunk_size) ? bit : 0);
+ bit = bit << 1;
+ }
+ uint64_t result = b.get_unused_mask(io_begin, io_end - io_begin, io_chunk_size);
+ auto ref = std::bitset<64>(mask).to_string().substr(64 - (io_end - io_begin) / io_chunk_size);
+ auto uuu = std::bitset<64>(result).to_string().substr(64 - (io_end - io_begin) / io_chunk_size);
+ EXPECT_EQ(ref, uuu);
+ }
+ }
+ }
+}
+
class bluestore_blob_t_test :
public ::testing::Test,
public ::testing::WithParamInterface<std::vector<int>>