Add a test for "perfect_csum_update" mode
This commit is contained in:
@@ -25,8 +25,6 @@ journal_flusher_t::journal_flusher_t(blockstore_impl_t *bs)
|
||||
co[i].co_id = i;
|
||||
co[i].bs = bs;
|
||||
co[i].flusher = this;
|
||||
if (bs->dsk.csum_block_size)
|
||||
co[i].csum_buf = (uint8_t*)malloc_or_die(bs->dsk.data_block_size/bs->dsk.csum_block_size * (bs->dsk.data_csum_type & 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,15 +55,6 @@ journal_flusher_t::~journal_flusher_t()
|
||||
journal_flusher_co::~journal_flusher_co()
|
||||
{
|
||||
free_buffers();
|
||||
if (csum_buf)
|
||||
{
|
||||
free(csum_buf);
|
||||
}
|
||||
}
|
||||
|
||||
int journal_flusher_t::get_active()
|
||||
{
|
||||
return active_flushers;
|
||||
}
|
||||
|
||||
int journal_flusher_t::get_syncing_buffer()
|
||||
@@ -98,7 +87,7 @@ void journal_flusher_t::dump_diagnostics()
|
||||
{
|
||||
printf(
|
||||
"Compaction queue: %u items, data: %ju/%ju blocks used, meta: %ju/%ju bytes used, %u/%ju blocks nearfull\n",
|
||||
bs->heap->get_compact_queue_size(),
|
||||
bs->heap->get_to_compact_count(),
|
||||
bs->heap->get_data_used_space()/bs->dsk.data_block_size, bs->dsk.block_count,
|
||||
bs->heap->get_meta_used_space(), bs->heap->get_meta_total_space(),
|
||||
bs->heap->get_meta_nearfull_blocks(), bs->dsk.meta_area_size/bs->dsk.meta_block_size-1
|
||||
@@ -124,7 +113,7 @@ void journal_flusher_t::loop()
|
||||
}
|
||||
}
|
||||
int prev_active = active_flushers;
|
||||
for (int i = 0; (active_flushers > 0 || force_start > 0 || bs->heap->get_compact_queue_size() > bs->flusher_start_threshold) && i < cur_flusher_count; i++)
|
||||
for (int i = 0; (active_flushers > 0 || force_start > 0 || bs->heap->get_to_compact_count() > bs->flusher_start_threshold) && i < cur_flusher_count; i++)
|
||||
co[i].loop();
|
||||
if (prev_active && !active_flushers && force_start > 0)
|
||||
bs->ringloop->wakeup();
|
||||
@@ -177,6 +166,7 @@ resume_0:
|
||||
if (res == ENOENT && flusher->force_start > 0 && co_id == 0 &&
|
||||
(!bs->dsk.disable_journal_fsync || !bs->dsk.disable_meta_fsync))
|
||||
{
|
||||
flusher->active_flushers++;
|
||||
resume_21:
|
||||
resume_22:
|
||||
res = fsync_buffer(21);
|
||||
@@ -184,12 +174,14 @@ resume_22:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
flusher->active_flushers--;
|
||||
res = (res == 2 ? bs->heap->get_next_compact(cur_oid) : ENOENT);
|
||||
}
|
||||
if (res == ENOENT)
|
||||
{
|
||||
if (co_id == 0 && flusher->force_start > 0)
|
||||
{
|
||||
flusher->active_flushers++;
|
||||
resume_16:
|
||||
resume_17:
|
||||
resume_18:
|
||||
@@ -197,6 +189,7 @@ resume_19:
|
||||
resume_20:
|
||||
if (!trim_lsn(16))
|
||||
return false;
|
||||
flusher->active_flushers--;
|
||||
}
|
||||
cur_oid = {};
|
||||
wait_state = 0;
|
||||
@@ -231,28 +224,31 @@ resume_1:
|
||||
}
|
||||
assert(!end_wr->next() && end_wr->flags == (BS_HEAP_BIG_WRITE|BS_HEAP_STABLE));
|
||||
clean_loc = end_wr->location;
|
||||
#ifdef BLOCKSTORE_DEBUG
|
||||
printf("Compacting %jx:%jx l%ju .. l%ju (last l%ju)\n", cur_oid.inode, cur_oid.stripe, end_wr->lsn, begin_wr->lsn, compact_lsn);
|
||||
#endif
|
||||
if (bs->log_level > 9)
|
||||
printf("Compacting %jx:%jx l%ju .. l%ju (last l%ju)\n", cur_oid.inode, cur_oid.stripe, end_wr->lsn, begin_wr->lsn, compact_lsn);
|
||||
flusher->active_flushers++;
|
||||
// Scan versions to flush
|
||||
free_buffers();
|
||||
copy_count = 0;
|
||||
for (auto wr = begin_wr; wr != end_wr; wr = wr->next())
|
||||
{
|
||||
bs->prepare_read(read_vec, cur_obj, wr, 0, bs->dsk.data_block_size);
|
||||
copy_count++;
|
||||
}
|
||||
overwrite_start = overwrite_end = 0;
|
||||
if (read_vec.size() > 0)
|
||||
{
|
||||
overwrite_start = read_vec[0].offset;
|
||||
overwrite_end = read_vec[read_vec.size()-1].offset + read_vec[read_vec.size()-1].len;
|
||||
big_start = overwrite_start < end_wr->offset ? overwrite_start : end_wr->offset;
|
||||
big_end = overwrite_end > end_wr->offset+end_wr->len ? overwrite_end : end_wr->offset+end_wr->len;
|
||||
}
|
||||
read_to_fill_incomplete = false;
|
||||
if (bs->dsk.csum_block_size > bs->dsk.bitmap_granularity)
|
||||
{
|
||||
// Read original checksum blocks to calculate padded checksums if required
|
||||
fill_partial_checksum_blocks();
|
||||
if (read_to_fill_incomplete && bs->padded_csum_update)
|
||||
if (read_to_fill_incomplete && bs->perfect_csum_update)
|
||||
{
|
||||
flusher->wanting_meta_fsync++;
|
||||
}
|
||||
@@ -272,7 +268,7 @@ resume_3:
|
||||
// we'll have a correct checksum because it won't include overwritten parts!
|
||||
// The same thing actually happens even when csum_block_size == bitmap_granularity, but in that case
|
||||
// we never need to read (and thus verify) overwritten parts from the data device.
|
||||
if (read_to_fill_incomplete && bs->padded_csum_update)
|
||||
if (read_to_fill_incomplete && bs->perfect_csum_update)
|
||||
{
|
||||
flusher->wanting_meta_fsync--;
|
||||
}
|
||||
@@ -292,6 +288,7 @@ resume_8:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
res = 0;
|
||||
}
|
||||
else if (res == ENOENT || res == EDOM)
|
||||
{
|
||||
@@ -301,7 +298,6 @@ resume_8:
|
||||
}
|
||||
assert(res == 0);
|
||||
// Submit data writes
|
||||
copy_count = 0;
|
||||
for (i = 0; i < read_vec.size(); i++)
|
||||
{
|
||||
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) &&
|
||||
@@ -313,7 +309,6 @@ resume_8:
|
||||
data->callback = simple_callback_w;
|
||||
io_uring_prep_writev(sqe, bs->dsk.data_fd, &data->iov, 1, bs->dsk.data_offset + clean_loc + read_vec[i].offset);
|
||||
wait_count++;
|
||||
copy_count++;
|
||||
}
|
||||
}
|
||||
resume_10:
|
||||
@@ -333,7 +328,11 @@ resume_10:
|
||||
// Abort compaction
|
||||
goto resume_0;
|
||||
}
|
||||
calc_block_checksums();
|
||||
if (!calc_block_checksums())
|
||||
{
|
||||
// Abort compaction
|
||||
goto resume_0;
|
||||
}
|
||||
if (read_to_fill_incomplete)
|
||||
{
|
||||
resume_23:
|
||||
@@ -345,9 +344,8 @@ resume_24:
|
||||
}
|
||||
bs->heap->mark_object_compacted(cur_obj, compact_lsn);
|
||||
// Done
|
||||
#ifdef BLOCKSTORE_DEBUG
|
||||
printf("Compacted %jx:%jx l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe, compact_lsn, copy_count);
|
||||
#endif
|
||||
if (bs->log_level > 9)
|
||||
printf("Compacted %jx:%jx l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe, compact_lsn, copy_count);
|
||||
flusher->compact_counter++;
|
||||
flusher->active_flushers--;
|
||||
// Advance compacted_lsn every <journal_trim_interval> objects
|
||||
@@ -375,13 +373,15 @@ void journal_flusher_co::iterate_partial_overwrites(std::function<int(int, uint3
|
||||
{
|
||||
int prev = 0;
|
||||
uint32_t prev_begin = 0, prev_end = 0;
|
||||
for (int i = 0; i < read_vec.size(); i++)
|
||||
for (int i = 0; i < read_vec.size() && !(read_vec[i].copy_flags & COPY_BUF_CSUM_FILL); i++)
|
||||
{
|
||||
if (!(read_vec[i].copy_flags & COPY_BUF_COALESCED))
|
||||
{
|
||||
if (read_vec[i].offset > prev_end)
|
||||
{
|
||||
if (prev_end > prev_begin && ((prev_begin % bs->dsk.csum_block_size) || (prev_end % bs->dsk.csum_block_size)))
|
||||
if (prev_end > prev_begin &&
|
||||
((prev_begin % bs->dsk.csum_block_size) && prev_begin > big_start ||
|
||||
(prev_end % bs->dsk.csum_block_size) && prev_end < big_end))
|
||||
{
|
||||
i += cb(prev, prev_begin, prev_end);
|
||||
}
|
||||
@@ -391,7 +391,9 @@ void journal_flusher_co::iterate_partial_overwrites(std::function<int(int, uint3
|
||||
prev_end = read_vec[i].offset + read_vec[i].len;
|
||||
}
|
||||
}
|
||||
if (prev_end > prev_begin && ((prev_begin % bs->dsk.csum_block_size) || (prev_end % bs->dsk.csum_block_size)))
|
||||
if (prev_end > prev_begin &&
|
||||
((prev_begin % bs->dsk.csum_block_size) && prev_begin > big_start ||
|
||||
(prev_end % bs->dsk.csum_block_size) && prev_end < big_end))
|
||||
{
|
||||
cb(prev, prev_begin, prev_end);
|
||||
}
|
||||
@@ -402,15 +404,21 @@ void journal_flusher_co::iterate_checksum_holes(std::function<void(int, uint32_t
|
||||
iterate_partial_overwrites([&](int pos, uint32_t prev_begin, uint32_t prev_end)
|
||||
{
|
||||
int r = 0;
|
||||
if ((prev_begin % bs->dsk.csum_block_size) &&
|
||||
if ((prev_begin % bs->dsk.csum_block_size) && prev_begin > big_start &&
|
||||
(prev_begin / bs->dsk.csum_block_size) != (prev_end / bs->dsk.csum_block_size))
|
||||
{
|
||||
cb(pos, prev_begin - prev_begin%bs->dsk.csum_block_size, prev_begin);
|
||||
uint32_t blk_begin = (prev_begin - prev_begin%bs->dsk.csum_block_size);
|
||||
if (blk_begin < big_start)
|
||||
blk_begin = big_start;
|
||||
cb(pos, blk_begin, prev_begin);
|
||||
r++;
|
||||
}
|
||||
if (prev_end % bs->dsk.csum_block_size)
|
||||
if ((prev_end % bs->dsk.csum_block_size) && prev_end < big_end)
|
||||
{
|
||||
cb(i, prev_end, prev_end - (prev_end % bs->dsk.csum_block_size) + bs->dsk.csum_block_size);
|
||||
uint32_t blk_end = prev_end - (prev_end % bs->dsk.csum_block_size) + bs->dsk.csum_block_size;
|
||||
if (blk_end > big_end)
|
||||
blk_end = big_end;
|
||||
cb(i, prev_end, blk_end);
|
||||
r++;
|
||||
}
|
||||
return r;
|
||||
@@ -422,10 +430,11 @@ void journal_flusher_co::fill_partial_checksum_blocks()
|
||||
iterate_checksum_holes([&](int vec_pos, uint32_t hole_start, uint32_t hole_end)
|
||||
{
|
||||
read_to_fill_incomplete = true;
|
||||
// bs->prepare_read(read_vec, cur_obj, end_wr, hole_start, hole_end);
|
||||
bs->prepare_disk_read(read_vec, read_vec.size(), cur_obj, end_wr,
|
||||
hole_start - hole_start % bs->dsk.csum_block_size, hole_start - hole_start % bs->dsk.csum_block_size + bs->dsk.csum_block_size,
|
||||
hole_start - hole_start % bs->dsk.csum_block_size, hole_start - hole_start % bs->dsk.csum_block_size + bs->dsk.csum_block_size,
|
||||
COPY_BUF_CSUM_FILL | (bs->padded_csum_update ? 0 : COPY_BUF_SKIP_CSUM));
|
||||
COPY_BUF_CSUM_FILL | (bs->perfect_csum_update ? 0 : COPY_BUF_SKIP_CSUM));
|
||||
auto & vec = read_vec[read_vec.size()-1];
|
||||
if (!vec.buf)
|
||||
vec.buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, vec.disk_len);
|
||||
@@ -452,7 +461,6 @@ void journal_flusher_co::free_buffers()
|
||||
read_vec.clear();
|
||||
}
|
||||
|
||||
// FIXME: Write tests for it
|
||||
int journal_flusher_co::check_and_punch_checksums()
|
||||
{
|
||||
if (!bs->dsk.csum_block_size)
|
||||
@@ -479,8 +487,8 @@ int journal_flusher_co::check_and_punch_checksums()
|
||||
csums, vec.buf, wr->get_int_bitmap(bs->heap), vec.offset, vec.offset+vec.len, false,
|
||||
[&](uint32_t mismatch_pos, uint32_t expected_csum, uint32_t real_csum)
|
||||
{
|
||||
printf("Checksum mismatch in object %jx:%jx v%ju in %s area at offset 0x%jx: got %08x, expected %08x\n",
|
||||
cur_oid.inode, cur_oid.stripe, wr->version,
|
||||
printf("Checksum mismatch during compaction in object %jx:%jx v%ju, offset 0x%x in %s area at offset 0x%jx: got %08x, expected %08x\n",
|
||||
cur_oid.inode, cur_oid.stripe, wr->version, mismatch_pos,
|
||||
(vec.copy_flags & COPY_BUF_JOURNAL ? "buffer" : "data"),
|
||||
vec.disk_offset, real_csum, expected_csum);
|
||||
csum_ok = false;
|
||||
@@ -494,11 +502,12 @@ int journal_flusher_co::check_and_punch_checksums()
|
||||
// FIXME: Report the corrupted object to the upper layer
|
||||
return EDOM;
|
||||
}
|
||||
if (!read_to_fill_incomplete || !bs->padded_csum_update)
|
||||
if (!read_to_fill_incomplete || !bs->perfect_csum_update)
|
||||
{
|
||||
// Nothing to do
|
||||
return 0;
|
||||
}
|
||||
// FIXME: Do it before read_buffered?
|
||||
cur_obj = bs->heap->read_entry(cur_oid, &modified_block, true);
|
||||
if (!cur_obj)
|
||||
{
|
||||
@@ -506,7 +515,7 @@ int journal_flusher_co::check_and_punch_checksums()
|
||||
return ENOENT;
|
||||
}
|
||||
bs->heap->get_compact_range(cur_obj, compact_lsn, &begin_wr, &end_wr);
|
||||
if (!begin_wr || begin_wr->lsn != compact_lsn)
|
||||
if (!begin_wr)
|
||||
{
|
||||
// Object is overwritten, abort compaction
|
||||
return ENOENT;
|
||||
@@ -539,68 +548,64 @@ int journal_flusher_co::check_and_punch_checksums()
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
void journal_flusher_co::calc_block_checksums()
|
||||
bool journal_flusher_co::calc_block_checksums()
|
||||
{
|
||||
new_data_csums = NULL;
|
||||
if (bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity)
|
||||
return;
|
||||
new_data_csums = csum_buf + overwrite_start/bs->dsk.csum_block_size * (bs->dsk.data_csum_type & 0xFF);
|
||||
uint64_t block_offset = 0;
|
||||
uint32_t block_done = 0;
|
||||
uint32_t block_csum = 0;
|
||||
for (auto it = read_vec.begin(); it != read_vec.end(); it++)
|
||||
if (bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity || !read_vec.size())
|
||||
{
|
||||
if (it->copy_flags & COPY_BUF_CSUM_FILL)
|
||||
continue;
|
||||
if (block_done == 0)
|
||||
{
|
||||
// `read_vec` should contain aligned items, possibly split into pieces
|
||||
assert(!(it->offset % bs->dsk.csum_block_size));
|
||||
block_offset = it->offset;
|
||||
}
|
||||
bool zero = (it->copy_flags & COPY_BUF_ZERO);
|
||||
auto len = it->len;
|
||||
while ((block_done+len) >= bs->dsk.csum_block_size)
|
||||
{
|
||||
if (!block_done && it->wr_lsn)
|
||||
{
|
||||
// We may take existing checksums if an overwrite contains a full block
|
||||
heap_write_t *wr = cur_obj->get_writes();
|
||||
while (wr && wr->lsn != it->wr_lsn)
|
||||
wr = wr->next();
|
||||
assert(wr);
|
||||
assert(!(it->offset % bs->dsk.csum_block_size));
|
||||
assert(!(wr->offset % bs->dsk.csum_block_size));
|
||||
auto full_csum_offset = (it->offset - wr->offset) / bs->dsk.csum_block_size;
|
||||
auto full_csum_count = len/bs->dsk.csum_block_size;
|
||||
memcpy(new_data_csums + block_offset/bs->dsk.csum_block_size,
|
||||
wr->get_checksums(bs->heap) + full_csum_offset*4, full_csum_count*4);
|
||||
len -= full_csum_count*bs->dsk.csum_block_size;
|
||||
block_offset += full_csum_count*bs->dsk.csum_block_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto cur_len = bs->dsk.csum_block_size-block_done;
|
||||
block_csum = zero
|
||||
? crc32c_pad(block_csum, NULL, 0, cur_len, 0)
|
||||
: crc32c(block_csum, (uint8_t*)it->buf+(it->len-len), cur_len);
|
||||
new_data_csums[block_offset / bs->dsk.csum_block_size] = block_csum;
|
||||
block_csum = 0;
|
||||
block_done = 0;
|
||||
block_offset += bs->dsk.csum_block_size;
|
||||
len -= cur_len;
|
||||
}
|
||||
}
|
||||
if (len > 0)
|
||||
{
|
||||
block_csum = zero
|
||||
? crc32c_pad(block_csum, NULL, 0, len, 0)
|
||||
: crc32c(block_csum, (uint8_t*)it->buf+(it->len-len), len);
|
||||
block_done += len;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// `read_vec` should contain aligned items, possibly split into pieces
|
||||
assert(!block_done);
|
||||
bs->heap->get_compact_range(cur_obj, compact_lsn, &begin_wr, &end_wr);
|
||||
if (!begin_wr)
|
||||
{
|
||||
// Object is overwritten, abort compaction
|
||||
return false;
|
||||
}
|
||||
uint8_t *bmp = end_wr->get_int_bitmap(bs->heap);
|
||||
uint8_t *csums = end_wr->get_checksums(bs->heap);
|
||||
// Set bits
|
||||
for (auto & vec: read_vec)
|
||||
{
|
||||
if (!(vec.copy_flags & COPY_BUF_COALESCED))
|
||||
bitmap_set(bmp, vec.offset, vec.len, bs->dsk.bitmap_granularity);
|
||||
}
|
||||
end_wr->offset = big_start;
|
||||
end_wr->len = big_end-big_start;
|
||||
// Update block checksums
|
||||
size_t i = 0;
|
||||
while (i < read_vec.size() && !(read_vec[i].copy_flags & COPY_BUF_CSUM_FILL))
|
||||
{
|
||||
uint32_t start = read_vec[i].offset;
|
||||
uint32_t end = read_vec[i].offset+read_vec[i].len;
|
||||
i++;
|
||||
while (i < read_vec.size() && !(read_vec[i].copy_flags & COPY_BUF_CSUM_FILL) &&
|
||||
read_vec[i].offset == end)
|
||||
{
|
||||
end = read_vec[i].offset+read_vec[i].len;
|
||||
i++;
|
||||
}
|
||||
// `read_vec` should contain aligned items (with respect to big_start/big_end), possibly split into pieces
|
||||
assert(!(start % bs->dsk.csum_block_size) || start == big_start);
|
||||
assert(!(end % bs->dsk.csum_block_size) || end == big_end);
|
||||
uint32_t csum_off = (start/bs->dsk.csum_block_size - big_start/bs->dsk.csum_block_size) * (bs->dsk.data_csum_type & 0xFF);
|
||||
bs->heap->calc_block_checksums(
|
||||
(uint32_t*)(csums+csum_off), bmp, start, end,
|
||||
[&](uint32_t start, uint32_t & len)
|
||||
{
|
||||
// O(n^2) search, may be fixed later :-p
|
||||
for (size_t i = 0; i < read_vec.size(); i++)
|
||||
{
|
||||
assert(read_vec[i].offset <= start);
|
||||
if (read_vec[i].offset+read_vec[i].len > start)
|
||||
{
|
||||
len = read_vec[i].offset+read_vec[i].len-start;
|
||||
return read_vec[i].buf + start-read_vec[i].offset;
|
||||
}
|
||||
}
|
||||
return (uint8_t*)NULL;
|
||||
}, true, NULL
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool journal_flusher_co::write_meta_block(int wait_base)
|
||||
|
||||
@@ -50,13 +50,12 @@ class journal_flusher_co
|
||||
|
||||
std::vector<copy_buffer_t> read_vec;
|
||||
uint32_t overwrite_start, overwrite_end;
|
||||
uint32_t big_start, big_end;
|
||||
int i, res;
|
||||
bool read_to_fill_incomplete;
|
||||
int copy_count;
|
||||
uint64_t clean_loc;
|
||||
flusher_meta_write_t meta_old, meta_new;
|
||||
uint8_t *csum_buf = NULL;
|
||||
uint8_t *new_data_csums = NULL;
|
||||
bool do_repeat = false;
|
||||
|
||||
friend class journal_flusher_t;
|
||||
@@ -66,7 +65,7 @@ class journal_flusher_co
|
||||
void fill_partial_checksum_blocks();
|
||||
void free_buffers();
|
||||
int check_and_punch_checksums();
|
||||
void calc_block_checksums();
|
||||
bool calc_block_checksums();
|
||||
bool write_meta_block(int wait_base);
|
||||
bool read_buffered(int wait_base);
|
||||
bool fsync_meta(int wait_base);
|
||||
@@ -99,7 +98,6 @@ public:
|
||||
journal_flusher_t(blockstore_impl_t *bs);
|
||||
~journal_flusher_t();
|
||||
void loop();
|
||||
int get_active();
|
||||
int get_syncing_buffer();
|
||||
uint64_t get_compact_counter();
|
||||
bool is_active();
|
||||
|
||||
@@ -567,6 +567,32 @@ bool blockstore_heap_t::calc_checksums(heap_write_t *wr, uint8_t *data, bool set
|
||||
|
||||
bool blockstore_heap_t::calc_block_checksums(uint32_t *block_csums, uint8_t *data, uint8_t *bitmap, uint32_t start, uint32_t end,
|
||||
bool set, std::function<void(uint32_t, uint32_t, uint32_t)> bad_block_cb)
|
||||
{
|
||||
return calc_block_checksums(block_csums, bitmap, start, end, [&](uint32_t pos, uint32_t & len)
|
||||
{
|
||||
len = UINT32_MAX;
|
||||
return data+pos-start;
|
||||
}, set, bad_block_cb);
|
||||
}
|
||||
|
||||
static uint32_t crc32c_iter(uint32_t prev_crc, const std::function<uint8_t*(uint32_t start, uint32_t & len)> & next, uint32_t pos, uint32_t size)
|
||||
{
|
||||
uint32_t cur_len = 0;
|
||||
while (size > 0)
|
||||
{
|
||||
uint8_t *data = next(pos, cur_len);
|
||||
assert(data);
|
||||
cur_len = (cur_len < size ? cur_len : size);
|
||||
prev_crc = crc32c(prev_crc, data, cur_len);
|
||||
pos += cur_len;
|
||||
size -= cur_len;
|
||||
}
|
||||
return prev_crc;
|
||||
}
|
||||
|
||||
bool blockstore_heap_t::calc_block_checksums(uint32_t *block_csums, uint8_t *bitmap,
|
||||
uint32_t start, uint32_t end, std::function<uint8_t*(uint32_t start, uint32_t & len)> next,
|
||||
bool set, std::function<void(uint32_t, uint32_t, uint32_t)> bad_block_cb)
|
||||
{
|
||||
bool res = true;
|
||||
uint32_t pos = start;
|
||||
@@ -575,28 +601,30 @@ bool blockstore_heap_t::calc_block_checksums(uint32_t *block_csums, uint8_t *dat
|
||||
bool isset = false;
|
||||
while (pos < end)
|
||||
{
|
||||
uint32_t prev = pos;
|
||||
if (bitmap)
|
||||
{
|
||||
uint32_t prev = pos;
|
||||
while (pos < end && pos < block_end)
|
||||
{
|
||||
while (pos < end && pos < block_end && !(bitmap[pos/dsk->bitmap_granularity/8] & (1 << ((pos/dsk->bitmap_granularity) % 8))))
|
||||
pos += dsk->bitmap_granularity;
|
||||
if (pos > prev && (isset || pos < block_end))
|
||||
// zero padding at the beginning or at the end of the block is not counted
|
||||
if (pos > prev && prev > 0 && pos < block_end)
|
||||
block_crc = crc32c_pad(block_crc, NULL, 0, pos-prev, 0);
|
||||
prev = pos;
|
||||
while (pos < end && pos < block_end && (bitmap[pos/dsk->bitmap_granularity/8] & (1 << ((pos/dsk->bitmap_granularity) % 8))))
|
||||
pos += dsk->bitmap_granularity;
|
||||
if (pos > prev)
|
||||
{
|
||||
block_crc = crc32c(block_crc, data+prev-start, pos-prev);
|
||||
isset = true;
|
||||
block_crc = crc32c_iter(block_crc, next, prev, pos-prev);
|
||||
}
|
||||
prev = pos;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
block_crc = crc32c(block_crc, data+pos-start, (end > block_end ? block_end : end) - pos);
|
||||
block_crc = crc32c_iter(block_crc, next, pos, (end > block_end ? block_end : end)-pos);
|
||||
pos = (end > block_end ? block_end : end);
|
||||
isset = true;
|
||||
}
|
||||
@@ -604,11 +632,11 @@ bool blockstore_heap_t::calc_block_checksums(uint32_t *block_csums, uint8_t *dat
|
||||
{
|
||||
*block_csums = block_crc;
|
||||
}
|
||||
else if (block_crc != *block_csums)
|
||||
else if (isset && block_crc != *block_csums)
|
||||
{
|
||||
if (bad_block_cb)
|
||||
{
|
||||
bad_block_cb(prev-start, *block_csums, block_crc);
|
||||
bad_block_cb(block_end, *block_csums, block_crc);
|
||||
res = false;
|
||||
}
|
||||
else
|
||||
@@ -616,7 +644,6 @@ bool blockstore_heap_t::calc_block_checksums(uint32_t *block_csums, uint8_t *dat
|
||||
}
|
||||
block_end += dsk->csum_block_size;
|
||||
block_crc = 0;
|
||||
isset = false;
|
||||
block_csums++;
|
||||
}
|
||||
return res;
|
||||
@@ -888,7 +915,8 @@ void blockstore_heap_t::get_compact_range(heap_object_t *obj, uint64_t max_lsn,
|
||||
{
|
||||
if (wr->is_compacted(max_lsn))
|
||||
{
|
||||
*begin_wr = wr;
|
||||
if (!*begin_wr)
|
||||
*begin_wr = wr;
|
||||
*end_wr = wr;
|
||||
}
|
||||
else if (*begin_wr)
|
||||
@@ -1949,6 +1977,11 @@ uint32_t blockstore_heap_t::get_meta_nearfull_blocks()
|
||||
}
|
||||
|
||||
uint32_t blockstore_heap_t::get_compact_queue_size()
|
||||
{
|
||||
return inflight_lsn.size() - (next_compact_lsn < first_inflight_lsn ? 0 : next_compact_lsn-first_inflight_lsn);
|
||||
}
|
||||
|
||||
uint32_t blockstore_heap_t::get_to_compact_count()
|
||||
{
|
||||
return to_compact_count;
|
||||
}
|
||||
|
||||
@@ -224,6 +224,9 @@ public:
|
||||
// set or verify raw block checksums
|
||||
bool calc_block_checksums(uint32_t *block_csums, uint8_t *data, uint8_t *bitmap, uint32_t start, uint32_t end,
|
||||
bool set, std::function<void(uint32_t, uint32_t, uint32_t)> bad_block_cb);
|
||||
bool calc_block_checksums(uint32_t *block_csums, uint8_t *bitmap,
|
||||
uint32_t start, uint32_t end, std::function<uint8_t*(uint32_t start, uint32_t & len)> next,
|
||||
bool set, std::function<void(uint32_t, uint32_t, uint32_t)> bad_block_cb);
|
||||
// auto-compacts the object, then adds a write entry to it and to the compaction queue
|
||||
// return 0 if OK, or maybe ENOSPC
|
||||
int post_write(object_id oid, heap_write_t *wr, uint32_t *modified_block);
|
||||
@@ -285,6 +288,7 @@ public:
|
||||
uint32_t get_meta_nearfull_blocks();
|
||||
uint32_t get_inflight_queue_size();
|
||||
uint32_t get_compact_queue_size();
|
||||
uint32_t get_to_compact_count();
|
||||
|
||||
// get maximum size for a temporary heap_write_t buffer
|
||||
uint32_t get_max_write_entry_size();
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
int log_level = 0;
|
||||
// Enable correct block checksum validation on objects updated with small writes when checksum block
|
||||
// is larger than bitmap_granularity, at the expense of extra metadata fsyncs during compaction
|
||||
bool padded_csum_update = false;
|
||||
bool perfect_csum_update = false;
|
||||
/******* END OF OPTIONS *******/
|
||||
|
||||
struct ring_consumer_t ring_consumer;
|
||||
|
||||
@@ -26,7 +26,7 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config, bool init)
|
||||
throttle_target_mbs = strtoull(config["throttle_target_mbs"].c_str(), NULL, 10);
|
||||
throttle_target_parallelism = strtoull(config["throttle_target_parallelism"].c_str(), NULL, 10);
|
||||
throttle_threshold_us = strtoull(config["throttle_threshold_us"].c_str(), NULL, 10);
|
||||
padded_csum_update = config["padded_csum_update"] == "true" || config["padded_csum_update"] == "1" || config["padded_csum_update"] == "yes";
|
||||
perfect_csum_update = config["perfect_csum_update"] == "true" || config["perfect_csum_update"] == "1" || config["perfect_csum_update"] == "yes";
|
||||
if (config["autosync_writes"] != "")
|
||||
{
|
||||
autosync_writes = strtoull(config["autosync_writes"].c_str(), NULL, 10);
|
||||
|
||||
@@ -111,8 +111,6 @@ int blockstore_impl_t::fulfill_read(blockstore_op_t *op)
|
||||
|
||||
uint32_t blockstore_impl_t::prepare_read(std::vector<copy_buffer_t> & read_vec, heap_object_t *obj, heap_write_t *wr, uint32_t start, uint32_t end)
|
||||
{
|
||||
start = start < wr->offset ? wr->offset : start;
|
||||
end = end > wr->offset+wr->len ? wr->offset+wr->len : end;
|
||||
if ((wr->flags & BS_HEAP_TYPE) == BS_HEAP_BIG_WRITE)
|
||||
{
|
||||
return prepare_read_with_bitmaps(read_vec, obj, wr, start, end);
|
||||
@@ -176,6 +174,8 @@ uint32_t blockstore_impl_t::prepare_read_simple(std::vector<copy_buffer_t> & rea
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
start = start < wr->offset ? wr->offset : start;
|
||||
end = end > wr->offset+wr->len ? wr->offset+wr->len : end;
|
||||
find_holes(read_vec, start, end, [&](int & pos, uint32_t start, uint32_t end)
|
||||
{
|
||||
res += end-start;
|
||||
@@ -206,33 +206,31 @@ uint32_t blockstore_impl_t::prepare_read_simple(std::vector<copy_buffer_t> & rea
|
||||
blk_end = ((end-1) / dsk.csum_block_size + 1) * dsk.csum_block_size;
|
||||
blk_end = blk_end > wr->offset+wr->len ? wr->offset+wr->len : blk_end;
|
||||
uint32_t skip_csum = 0;
|
||||
if (!padded_csum_update)
|
||||
if (!perfect_csum_update && (wr->flags & BS_HEAP_TYPE) == BS_HEAP_BIG_WRITE)
|
||||
{
|
||||
for (auto owr = obj->get_writes(); owr && owr != wr; owr = owr->next())
|
||||
{
|
||||
if (owr->offset < blk_end && owr->offset+owr->len > blk_start)
|
||||
{
|
||||
skip_csum = COPY_BUF_SKIP_CSUM;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (blk_end == blk_start+dsk.csum_block_size ||
|
||||
blk_end == blk_start+2*dsk.csum_block_size && blk_end != end && blk_start != start ||
|
||||
if ((blk_end-1)/dsk.csum_block_size == blk_start/dsk.csum_block_size ||
|
||||
blk_end/dsk.csum_block_size == blk_start/dsk.csum_block_size+1 && blk_end != end && blk_start != start ||
|
||||
blk_end == end && blk_start == start)
|
||||
{
|
||||
// single block, two partial blocks, or any number of full blocks
|
||||
// single block, exactly two partial blocks, or any number of full blocks
|
||||
// i.e. [..X.] or [..XX][XX..] or [XXXX]..[XXXX]
|
||||
prepare_disk_read(read_vec, pos++, obj, wr, blk_start, blk_end, start, end, skip_csum);
|
||||
}
|
||||
else
|
||||
{
|
||||
// one or two partial blocks
|
||||
// one or two partial blocks plus any number of full blocks
|
||||
// i.e. [..XX][XXXX][X...]
|
||||
uint32_t full_start = (blk_start != start ? blk_start+dsk.csum_block_size : blk_start);
|
||||
uint32_t full_end = (blk_end != end ? blk_end-dsk.csum_block_size : blk_end);
|
||||
if (blk_start != start)
|
||||
if (blk_start != start) // starting padded block
|
||||
prepare_disk_read(read_vec, pos++, obj, wr, blk_start, full_start, start, full_start, skip_csum);
|
||||
if (full_start > full_end)
|
||||
if (full_end > full_start) // full non-padded blocks
|
||||
prepare_disk_read(read_vec, pos++, obj, wr, full_start, full_end, full_start, full_end, skip_csum);
|
||||
if (blk_end != end)
|
||||
if (blk_end != end) // ending padded block
|
||||
prepare_disk_read(read_vec, pos++, obj, wr, full_end, blk_end, full_end, end, skip_csum);
|
||||
}
|
||||
}
|
||||
@@ -359,10 +357,10 @@ bool blockstore_impl_t::verify_read_checksums(blockstore_op_t *op)
|
||||
blk_start, blk_end, false, [&](uint32_t mismatch_pos, uint32_t expected_csum, uint32_t real_csum)
|
||||
{
|
||||
printf(
|
||||
"Checksum mismatch in object %jx:%jx v%ju in %s area at offset 0x%jx+%x: %08x expected vs %08x actual\n",
|
||||
op->oid.inode, op->oid.stripe, op->version,
|
||||
"Checksum mismatch in object %jx:%jx v%ju, offset 0x%x in %s area at offset 0x%jx: %08x expected vs %08x actual\n",
|
||||
op->oid.inode, op->oid.stripe, op->version, mismatch_pos,
|
||||
(vec.copy_flags & COPY_BUF_JOURNAL) ? "buffer" : "data", vec.disk_offset,
|
||||
mismatch_pos, expected_csum, real_csum
|
||||
expected_csum, real_csum
|
||||
);
|
||||
}))
|
||||
{
|
||||
|
||||
@@ -162,8 +162,8 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
||||
// parallel writes to the same object are forbidden anyway
|
||||
else if (op->len > 0 && op->len <= dsk.atomic_write_size &&
|
||||
// Intent-writes are disabled if "absolutely correct during compaction" checksum validation algorithm is enabled
|
||||
// We could also do RMW here when padded_csum_update is enabled, but it's unclear if we need it
|
||||
(!padded_csum_update || dsk.csum_block_size <= dsk.bitmap_granularity ||
|
||||
// We could also do RMW here when perfect_csum_update is enabled, but it's unclear if we need it
|
||||
(!perfect_csum_update || dsk.csum_block_size <= dsk.bitmap_granularity ||
|
||||
!(op->offset % dsk.csum_block_size) &&
|
||||
!(op->len % dsk.csum_block_size) &&
|
||||
(obj->get_writes()->flags != (BS_HEAP_INTENT_WRITE|BS_HEAP_STABLE) ||
|
||||
|
||||
@@ -313,13 +313,15 @@ static void test_intent_over_unstable()
|
||||
free(op.buf);
|
||||
}
|
||||
|
||||
static void test_padded_csum_intent()
|
||||
static void test_padded_csum_intent(bool perfect)
|
||||
{
|
||||
printf("\n-- test_padded_csum_intent\n");
|
||||
printf("\n-- test_padded_csum_intent%s\n", perfect ? " perfect_csum_update" : "");
|
||||
|
||||
bs_test_t test;
|
||||
test.default_cfg();
|
||||
test.config["csum_block_size"] = "16384";
|
||||
if (perfect)
|
||||
test.config["perfect_csum_update"] = "1";
|
||||
test.init();
|
||||
|
||||
// Write
|
||||
@@ -350,8 +352,8 @@ static void test_padded_csum_intent()
|
||||
assert(memcmp(op2.buf+8*1024, op.buf, 4*1024) == 0);
|
||||
assert(is_zero(op2.buf+12*1024, 116*1024));
|
||||
|
||||
// Write again (intent)
|
||||
printf("writing (intent)\n");
|
||||
// Write again (intent if not "perfect")
|
||||
printf("writing (%s)\n", perfect ? "small" : "intent");
|
||||
op.version = 2;
|
||||
op.offset = 28*1024;
|
||||
memset(op.buf, 0xbb, 4096);
|
||||
@@ -372,7 +374,7 @@ static void test_padded_csum_intent()
|
||||
assert(obj);
|
||||
assert(!obj->get_writes()->next()->next()->next());
|
||||
assert(obj->get_writes()->flags == BS_HEAP_SMALL_WRITE);
|
||||
assert(obj->get_writes()->next()->flags == BS_HEAP_INTENT_WRITE);
|
||||
assert(obj->get_writes()->next()->flags == (perfect ? BS_HEAP_SMALL_WRITE : BS_HEAP_INTENT_WRITE));
|
||||
assert(obj->get_writes()->next()->next()->flags == BS_HEAP_BIG_WRITE);
|
||||
|
||||
// Commit
|
||||
@@ -389,10 +391,13 @@ static void test_padded_csum_intent()
|
||||
|
||||
// Trigger & wait compaction
|
||||
test.bs->flusher->request_trim();
|
||||
// FIXME: Не зацикливаться при обломе
|
||||
while (test.bs->heap->get_compact_queue_size())
|
||||
test.ringloop->loop();
|
||||
while (test.bs->flusher->is_active())
|
||||
test.ringloop->loop();
|
||||
test.bs->flusher->release_trim();
|
||||
// Check that compaction succeeded
|
||||
assert(!test.bs->heap->get_to_compact_count());
|
||||
|
||||
// Read again and check
|
||||
printf("reading compacted\n");
|
||||
@@ -420,6 +425,7 @@ int main(int narg, char *args[])
|
||||
test_fsync(false);
|
||||
test_fsync(true);
|
||||
test_intent_over_unstable();
|
||||
test_padded_csum_intent();
|
||||
test_padded_csum_intent(false);
|
||||
test_padded_csum_intent(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -393,7 +393,7 @@ void test_compact(bool csum, bool stable)
|
||||
heap.mark_lsn_completed(4);
|
||||
}
|
||||
|
||||
assert(heap.get_compact_queue_size() == 1);
|
||||
assert(heap.get_to_compact_count() == 1);
|
||||
res = heap.get_next_compact(compact_oid);
|
||||
assert(res == 0);
|
||||
assert(oid == compact_oid);
|
||||
@@ -408,7 +408,7 @@ void test_compact(bool csum, bool stable)
|
||||
|
||||
heap.mark_object_compacted(obj, 4);
|
||||
assert(heap.get_compacted_lsn() == (stable ? 3 : 4));
|
||||
assert(heap.get_compact_queue_size() == 0);
|
||||
assert(heap.get_to_compact_count() == 0);
|
||||
|
||||
res = heap.compact_object((object_id){ .inode = INODE_WITH_POOL(1, 3), .stripe = 0 }, compact_begin->lsn, NULL);
|
||||
assert(res == ENOENT);
|
||||
|
||||
Reference in New Issue
Block a user