From ad6354a537b7b000a25fc42ccdb19ea973d2e362 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Sat, 19 Jul 2025 02:21:20 +0300 Subject: [PATCH] Add a test for "perfect_csum_update" mode --- src/blockstore/blockstore_flush.cpp | 197 ++++++++++++++-------------- src/blockstore/blockstore_flush.h | 6 +- src/blockstore/blockstore_heap.cpp | 49 +++++-- src/blockstore/blockstore_heap.h | 4 + src/blockstore/blockstore_impl.h | 2 +- src/blockstore/blockstore_open.cpp | 2 +- src/blockstore/blockstore_read.cpp | 32 +++-- src/blockstore/blockstore_write.cpp | 4 +- src/test/test_blockstore.cpp | 20 ++- src/test/test_heap.cpp | 4 +- 10 files changed, 182 insertions(+), 138 deletions(-) diff --git a/src/blockstore/blockstore_flush.cpp b/src/blockstore/blockstore_flush.cpp index 3120a7a4..49145855 100644 --- a/src/blockstore/blockstore_flush.cpp +++ b/src/blockstore/blockstore_flush.cpp @@ -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 objects @@ -375,13 +373,15 @@ void journal_flusher_co::iterate_partial_overwrites(std::function 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 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::functiondsk.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) diff --git a/src/blockstore/blockstore_flush.h b/src/blockstore/blockstore_flush.h index 2317f1a9..3464985c 100644 --- a/src/blockstore/blockstore_flush.h +++ b/src/blockstore/blockstore_flush.h @@ -50,13 +50,12 @@ class journal_flusher_co std::vector 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(); diff --git a/src/blockstore/blockstore_heap.cpp b/src/blockstore/blockstore_heap.cpp index 677b253c..e89890d3 100644 --- a/src/blockstore/blockstore_heap.cpp +++ b/src/blockstore/blockstore_heap.cpp @@ -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 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 & 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 next, + bool set, std::function 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; } diff --git a/src/blockstore/blockstore_heap.h b/src/blockstore/blockstore_heap.h index 073d8901..18dae413 100644 --- a/src/blockstore/blockstore_heap.h +++ b/src/blockstore/blockstore_heap.h @@ -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 bad_block_cb); + bool calc_block_checksums(uint32_t *block_csums, uint8_t *bitmap, + uint32_t start, uint32_t end, std::function next, + bool set, std::function 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(); diff --git a/src/blockstore/blockstore_impl.h b/src/blockstore/blockstore_impl.h index 7913f890..72d5a0f5 100644 --- a/src/blockstore/blockstore_impl.h +++ b/src/blockstore/blockstore_impl.h @@ -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; diff --git a/src/blockstore/blockstore_open.cpp b/src/blockstore/blockstore_open.cpp index f5d9c44d..95b0851a 100644 --- a/src/blockstore/blockstore_open.cpp +++ b/src/blockstore/blockstore_open.cpp @@ -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); diff --git a/src/blockstore/blockstore_read.cpp b/src/blockstore/blockstore_read.cpp index 2f9032a9..8bc812d7 100644 --- a/src/blockstore/blockstore_read.cpp +++ b/src/blockstore/blockstore_read.cpp @@ -111,8 +111,6 @@ int blockstore_impl_t::fulfill_read(blockstore_op_t *op) uint32_t blockstore_impl_t::prepare_read(std::vector & 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 & 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 & 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 ); })) { diff --git a/src/blockstore/blockstore_write.cpp b/src/blockstore/blockstore_write.cpp index c80b39fa..3791cdd8 100644 --- a/src/blockstore/blockstore_write.cpp +++ b/src/blockstore/blockstore_write.cpp @@ -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) || diff --git a/src/test/test_blockstore.cpp b/src/test/test_blockstore.cpp index 03d378d1..6eed7173 100644 --- a/src/test/test_blockstore.cpp +++ b/src/test/test_blockstore.cpp @@ -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; } diff --git a/src/test/test_heap.cpp b/src/test/test_heap.cpp index f88de36f..7bfcafe6 100644 --- a/src/test/test_heap.cpp +++ b/src/test/test_heap.cpp @@ -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);