diff --git a/src/blockstore/blockstore_disk.cpp b/src/blockstore/blockstore_disk.cpp index 02a65f98..2afa29a3 100644 --- a/src/blockstore/blockstore_disk.cpp +++ b/src/blockstore/blockstore_disk.cpp @@ -228,11 +228,11 @@ void blockstore_disk_t::calc_lengths() { // data data_len = data_device_size - data_offset; - if (data_fd == meta_fd && data_offset < meta_offset) + if (data_device == meta_device && data_offset < meta_offset) { data_len = meta_offset - data_offset; } - if (data_fd == journal_fd && data_offset < journal_offset) + if (data_device == journal_device && data_offset < journal_offset) { data_len = data_len < journal_offset-data_offset ? data_len : journal_offset-data_offset; @@ -247,23 +247,23 @@ void blockstore_disk_t::calc_lengths() data_len = cfg_data_size; } // meta - meta_area_size = (meta_fd == data_fd ? data_device_size : meta_device_size) - meta_offset; - if (meta_fd == data_fd && meta_offset <= data_offset) + meta_area_size = (meta_device == data_device ? data_device_size : meta_device_size) - meta_offset; + if (meta_device == data_device && meta_offset <= data_offset) { meta_area_size = data_offset - meta_offset; } - if (meta_fd == journal_fd && meta_offset <= journal_offset) + if (meta_device == journal_device && meta_offset <= journal_offset) { meta_area_size = meta_area_size < journal_offset-meta_offset ? meta_area_size : journal_offset-meta_offset; } // journal - journal_len = (journal_fd == data_fd ? data_device_size : (journal_fd == meta_fd ? meta_device_size : journal_device_size)) - journal_offset; - if (journal_fd == data_fd && journal_offset <= data_offset) + journal_len = (journal_device == data_device ? data_device_size : (journal_device == meta_device ? meta_device_size : journal_device_size)) - journal_offset; + if (journal_device == data_device && journal_offset <= data_offset) { journal_len = data_offset - journal_offset; } - if (journal_fd == meta_fd && journal_offset <= meta_offset) + if (journal_device == meta_device && journal_offset <= meta_offset) { journal_len = journal_len < meta_offset-journal_offset ? journal_len : meta_offset-journal_offset; diff --git a/src/blockstore/blockstore_heap.cpp b/src/blockstore/blockstore_heap.cpp index ee4fcee7..1978d332 100644 --- a/src/blockstore/blockstore_heap.cpp +++ b/src/blockstore/blockstore_heap.cpp @@ -1285,18 +1285,11 @@ void blockstore_heap_t::allocate_block(heap_block_info_t & inf) } } -int blockstore_heap_t::add_object(object_id oid, heap_write_t *wr, uint32_t *modified_block) +int blockstore_heap_t::allocate_new_object(object_id oid, uint32_t full_object_size, uint32_t *modified_block, heap_object_t **new_obj) { - // By now, initial small_writes are not allowed - if (wr->type() != BS_HEAP_BIG_WRITE && - wr->type() != BS_HEAP_TOMBSTONE) - { - return EINVAL; - } - const uint32_t wr_size = wr->get_size(this); - // Allocate block (always leave at least free_space in the block) uint32_t block_num = 0; - int res = get_block_for_new_object(block_num, sizeof(heap_object_t)+wr_size+max_write_entry_size); + // Allocate block (always leave at least free_space in the block) + int res = get_block_for_new_object(block_num, full_object_size+max_write_entry_size); if (res != 0) { return res; @@ -1307,27 +1300,66 @@ int blockstore_heap_t::add_object(object_id oid, heap_write_t *wr, uint32_t *mod { *modified_block = block_num; } - const uint32_t offset = find_block_space(block_num, sizeof(heap_object_t)+wr_size); + const uint32_t offset = find_block_space(block_num, full_object_size); if (offset == UINT32_MAX) { return ENOSPC; } + add_used_space(block_num, full_object_size); block_index[get_pg_id(oid.inode, oid.stripe)][oid.inode][oid.stripe] = (uint64_t)block_num*dsk->meta_block_size + offset; - // and just append the object entry - heap_object_t *new_entry = (heap_object_t *)(inf.data + offset); - new_entry->write_pos = sizeof(heap_object_t); - new_entry->inode = oid.inode; - new_entry->stripe = oid.stripe; - heap_write_t *new_wr = new_entry->get_writes(); + *new_obj = (heap_object_t *)(inf.data + offset); + return 0; +} + +int blockstore_heap_t::add_object(object_id oid, heap_write_t *wr, uint32_t *modified_block) +{ + // By now, initial small_writes are not allowed + if (wr->type() != BS_HEAP_BIG_WRITE && + wr->type() != BS_HEAP_TOMBSTONE) + { + return EINVAL; + } + const uint32_t wr_size = wr->get_size(this); + heap_object_t *new_obj = NULL; + int res = allocate_new_object(oid, sizeof(heap_object_t)+wr_size, modified_block, &new_obj); + if (res != 0) + { + return res; + } + // Fill the object entry + new_obj->size = sizeof(heap_object_t); + new_obj->write_pos = sizeof(heap_object_t); + new_obj->inode = oid.inode; + new_obj->stripe = oid.stripe; + heap_write_t *new_wr = new_obj->get_writes(); memcpy(new_wr, wr, wr_size); new_wr->next_pos = 0; new_wr->size = wr_size; new_wr->lsn = ++next_lsn; wr->lsn = new_wr->lsn; push_inflight_lsn(oid, new_wr->lsn, new_wr->needs_compact(this) ? HEAP_INFLIGHT_COMPACTABLE : 0); - new_entry->size = sizeof(heap_object_t); - new_entry->crc32c = new_entry->calc_crc32c(); - add_used_space(block_num, sizeof(heap_object_t) + wr_size); + new_obj->crc32c = new_obj->calc_crc32c(); + return 0; +} + +int blockstore_heap_t::copy_object(heap_object_t *obj, uint32_t *modified_block) +{ + // Allocate block (always leave at least free_space in the block) + auto oid = (object_id){ .inode = obj->inode, .stripe = obj->stripe }; + assert(!read_entry(oid, NULL)); + uint32_t full_object_size = obj->size; + for (auto wr = obj->get_writes(); wr; wr = wr->next()) + { + full_object_size += wr->size; + } + heap_object_t *new_obj = NULL; + int res = allocate_new_object(oid, full_object_size, modified_block, &new_obj); + if (res != 0) + { + return res; + } + copy_full_object((uint8_t*)new_obj, obj); + new_obj->crc32c = new_obj->calc_crc32c(); return 0; } diff --git a/src/blockstore/blockstore_heap.h b/src/blockstore/blockstore_heap.h index b3b67367..fc156051 100644 --- a/src/blockstore/blockstore_heap.h +++ b/src/blockstore/blockstore_heap.h @@ -215,6 +215,7 @@ class blockstore_heap_t bool mvcc_check_tracking(object_id oid); void free_mvcc(heap_mvcc_map_t::iterator mvcc_it); void allocate_block(heap_block_info_t & inf); + int allocate_new_object(object_id oid, uint32_t full_object_size, uint32_t *modified_block, heap_object_t **new_obj); int add_object(object_id oid, heap_write_t *wr, uint32_t *modified_block); void mark_overwritten(uint64_t over_lsn, uint64_t inode, heap_write_t *wr, heap_write_t *end_wr, bool tracking_active); int update_object(uint32_t block_num, heap_object_t *obj, heap_write_t *wr, uint32_t *modified_block, uint32_t *moved_from_block); @@ -268,6 +269,8 @@ public: 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); + // copy an object as is + int copy_object(heap_object_t *obj, uint32_t *modified_block); // 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, uint32_t *moved_from_block); diff --git a/src/blockstore/blockstore_impl.cpp b/src/blockstore/blockstore_impl.cpp index 2eb4ff5e..58bdb2fc 100644 --- a/src/blockstore/blockstore_impl.cpp +++ b/src/blockstore/blockstore_impl.cpp @@ -355,7 +355,8 @@ void blockstore_impl_t::dump_diagnostics() void blockstore_meta_header_v3_t::set_crc32c() { header_csum = 0; - uint32_t calc = crc32c(0, this, sizeof(*this)); + uint32_t calc = crc32c(0, this, version == BLOCKSTORE_META_FORMAT_HEAP + ? sizeof(blockstore_meta_header_v3_t) : sizeof(blockstore_meta_header_v2_t)); header_csum = calc; } diff --git a/src/disk_tool/CMakeLists.txt b/src/disk_tool/CMakeLists.txt index 6aa55d02..9d641786 100644 --- a/src/disk_tool/CMakeLists.txt +++ b/src/disk_tool/CMakeLists.txt @@ -7,7 +7,8 @@ add_executable(vitastor-disk disk_tool.cpp disk_simple_offsets.cpp disk_tool_discard.cpp disk_tool_journal.cpp disk_tool_meta.cpp disk_tool_prepare.cpp disk_tool_resize.cpp disk_tool_resize_auto.cpp disk_tool_udev.cpp disk_tool_utils.cpp disk_tool_upgrade.cpp - ../util/crc32c.c ../util/str_util.cpp ../util/json_util.cpp ../../json11/json11.cpp ../util/rw_blocking.cpp ../util/allocator.cpp ../util/ringloop.cpp ../blockstore/blockstore_disk.cpp + ../util/crc32c.c ../util/str_util.cpp ../util/json_util.cpp ../../json11/json11.cpp ../util/rw_blocking.cpp ../util/allocator.cpp ../util/ringloop.cpp + ../blockstore/blockstore_disk.cpp ../blockstore/blockstore_heap.cpp ../blockstore/multilist.cpp ) target_link_libraries(vitastor-disk tcmalloc_minimal diff --git a/src/disk_tool/disk_simple_offsets.cpp b/src/disk_tool/disk_simple_offsets.cpp index 28e7cc29..f174c9dd 100644 --- a/src/disk_tool/disk_simple_offsets.cpp +++ b/src/disk_tool/disk_simple_offsets.cpp @@ -12,6 +12,7 @@ #include "blockstore.h" #include "blockstore_disk.h" #include "blockstore_heap.h" +#include "ondisk_formats.h" // Calculate offsets for a block device and print OSD command line parameters void disk_tool_simple_offsets(json11::Json cfg, bool json_output) diff --git a/src/disk_tool/disk_tool.cpp b/src/disk_tool/disk_tool.cpp index ffb70275..c5c854ae 100644 --- a/src/disk_tool/disk_tool.cpp +++ b/src/disk_tool/disk_tool.cpp @@ -192,12 +192,12 @@ static const char *help_text = " You can specify any OSD device (data, metadata or journal), or the layout manually.\n" "\n" "vitastor-disk dump-meta \n" - "vitastor-disk dump-meta \n" + "vitastor-disk dump-meta [osd_options...]\n" " Dump metadata in JSON format.\n" " You can specify any OSD device (data, metadata or journal), or the layout manually.\n" "\n" "vitastor-disk write-meta \n" - "vitastor-disk write-meta \n" + "vitastor-disk write-meta [osd_options...]\n" " Write metadata from JSON taken from standard input in the same format as produced by `dump-meta`.\n" " You can specify any OSD device (data, metadata or journal), or the layout manually.\n" "\n" @@ -364,48 +364,64 @@ int main(int argc, char *argv[]) } else if (!strcmp(cmd[0], "dump-meta")) { - if (cmd.size() != 2 && cmd.size() < 5) - { - print_help(help_text, "vitastor-disk", cmd[0], false); - return 1; - } - self.dsk.meta_device = cmd[1]; - if (cmd.size() > 2) + if (cmd.size() == 5) { + // Old format + self.dsk.meta_device = cmd[1]; self.dsk.meta_block_size = strtoul(cmd[2], NULL, 10); self.dsk.meta_offset = strtoull(cmd[3], NULL, 10); self.dsk.meta_area_size = strtoull(cmd[4], NULL, 10); } - else + else if (cmd.size() == 2) { // First argument is an OSD device - take metadata layout parameters from it + self.dsk.meta_device = cmd[1]; if (self.dump_load_check_superblock(self.dsk.meta_device)) return 1; } + else + { + // Parse all OSD options from cmdline + self.dsk.parse_config(self.options); + if (self.options["io"] != "") + self.dsk.data_io = self.dsk.meta_io = self.dsk.journal_io = self.options["io"]; + // FIXME: This is a really repeated pattern, move it somewhere + self.dsk.open_data(); + self.dsk.open_meta(); + self.dsk.open_journal(); + self.dsk.calc_lengths(); + self.dsk.close_all(); + } return self.dump_meta(); } else if (!strcmp(cmd[0], "write-meta")) { - if (cmd.size() != 2 && cmd.size() < 4) - { - print_help(help_text, "vitastor-disk", cmd[0], false); - return 1; - } - self.new_meta_device = cmd[1]; - if (cmd.size() > 2) + if (cmd.size() == 4) { + self.new_meta_device = cmd[1]; self.new_meta_offset = strtoull(cmd[2], NULL, 10); self.new_meta_len = strtoull(cmd[3], NULL, 10); } - else + else if (cmd.size() == 2) { // First argument is an OSD device - take metadata layout parameters from it + self.new_meta_device = cmd[1]; if (self.dump_load_check_superblock(self.new_meta_device)) return 1; self.new_meta_device = self.dsk.meta_device; self.new_meta_offset = self.dsk.meta_offset; self.new_meta_len = self.dsk.meta_area_size; } + else + { + // Parse all OSD options from cmdline + self.dsk.parse_config(self.options); + self.dsk.open_data(); + self.dsk.open_meta(); + self.dsk.open_journal(); + self.dsk.calc_lengths(); + self.dsk.close_all(); + } std::string json_err; json11::Json meta = json11::Json::parse(read_all_fd(0), json_err); if (json_err != "") diff --git a/src/disk_tool/disk_tool.h b/src/disk_tool/disk_tool.h index 9c4733ca..c62b55ed 100644 --- a/src/disk_tool/disk_tool.h +++ b/src/disk_tool/disk_tool.h @@ -16,6 +16,7 @@ #include "json11/json11.hpp" #include "blockstore_disk.h" #include "blockstore.h" +#include "blockstore_heap.h" #include "ondisk_formats.h" #include "crc32c.h" #include "allocator.h" @@ -46,7 +47,9 @@ struct disk_tool_t std::map options; bool test_mode = false; bool all = false, json = false, now = false; - bool dump_with_blocks, dump_with_data; + bool dump_with_blocks = false, dump_with_data = false; + bool dump_as_old = false; + int log_level = 1; blockstore_disk_t dsk; // resize data and/or move metadata and journal @@ -61,25 +64,30 @@ struct disk_tool_t uint64_t meta_pos; uint64_t journal_pos, journal_calc_data_pos; + uint8_t *buffer_area = NULL; bool first_block, first_entry; - allocator_t *data_alloc; + allocator_t *data_alloc = NULL; std::map data_remap; std::map::iterator remap_it; - ring_loop_t *ringloop; + ring_loop_t *ringloop = NULL; ring_consumer_t ring_consumer; int remap_active; journal_entry_start je_start; - uint8_t *new_journal_buf, *new_meta_buf, *new_journal_ptr, *new_journal_data; + uint8_t *new_journal_buf = NULL, *new_meta_buf = NULL, *new_journal_ptr = NULL, *new_journal_data = NULL; + blockstore_meta_header_v3_t *new_meta_hdr = NULL; + blockstore_disk_t new_dsk; + blockstore_heap_t *new_heap = NULL; uint64_t new_journal_in_pos; int64_t data_idx_diff; uint64_t total_blocks, free_first, free_last; uint64_t new_clean_entry_bitmap_size, new_data_csum_size, new_clean_entry_size, new_entries_per_block; - int new_journal_fd, new_meta_fd; - resizer_data_moving_t *moving_blocks; + uint32_t new_meta_format = 0; + int new_journal_fd = -1, new_meta_fd = -1; + resizer_data_moving_t *moving_blocks = NULL; bool started; - void *small_write_data; + void *small_write_data = NULL; uint32_t data_crc32; bool data_csum_valid; uint32_t crc32_last; @@ -91,17 +99,24 @@ struct disk_tool_t void dump_journal_entry(int num, journal_entry *je, bool json); int process_journal(std::function block_fn, bool do_open = true); int process_journal_block(void *buf, std::function iter_fn); - int process_meta(std::function hdr_fn, - std::function record_fn, bool do_open = true); + int process_meta(std::function hdr_fn, + std::function obj_fn, + std::function record_fn, + bool with_data, bool do_open); int dump_meta(); - void dump_meta_header(blockstore_meta_header_v2_t *hdr); + void dump_meta_header(blockstore_meta_header_v3_t *hdr); void dump_meta_entry(uint64_t block_num, clean_disk_entry *entry, uint8_t *bitmap); + void dump_heap_entry_as_old(blockstore_heap_t *heap, heap_object_t *obj); + void dump_heap_entry(blockstore_heap_t *heap, heap_object_t *obj); int dump_load_check_superblock(const std::string & device); int write_json_journal(json11::Json entries); int write_json_meta(json11::Json meta); + int write_json_heap(json11::Json meta, json11::Json journal); + int index_journal_by_object(json11::Json journal, + std::map> & journal_by_object); int resize_data(std::string device); int resize_parse_move_journal(std::map & move_options, bool dry_run); @@ -109,13 +124,17 @@ struct disk_tool_t int raw_resize(); int resize_parse_params(); - void resize_init(blockstore_meta_header_v2_t *hdr); + void resize_init(blockstore_meta_header_v3_t *hdr); int resize_remap_blocks(); int resize_copy_data(); - int resize_rewrite_journal(); + void resize_alloc_journal(); + void build_journal_start(); + void choose_journal_block(uint32_t je_size); + int resize_rebuild_journal(); int resize_write_new_journal(); - int resize_rewrite_meta(); + int resize_rebuild_meta(); int resize_write_new_meta(); + void free_new_meta(); int udev_import(std::string device); int read_sb(std::string device); diff --git a/src/disk_tool/disk_tool_discard.cpp b/src/disk_tool/disk_tool_discard.cpp index 29dd9dcf..a38abf0a 100644 --- a/src/disk_tool/disk_tool_discard.cpp +++ b/src/disk_tool/disk_tool_discard.cpp @@ -54,12 +54,22 @@ int disk_tool_t::trim_data(std::string device) fprintf(stderr, "Reading metadata\n"); data_alloc = new allocator_t(dsk.block_count); r = process_meta( - [this](blockstore_meta_header_v2_t *hdr) {}, + [this](blockstore_meta_header_v3_t *hdr) {}, + [this](blockstore_heap_t *heap, heap_object_t *obj, uint32_t meta_block_num) + { + for (auto wr = obj->get_writes(); wr; wr = wr->next()) + { + if ((wr->flags & BS_HEAP_TYPE) == BS_HEAP_BIG_WRITE) + { + data_alloc->set(wr->location / dsk.data_block_size, true); + } + } + }, [this](uint64_t block_num, clean_disk_entry *entry, uint8_t *bitmap) { data_alloc->set(block_num, true); }, - false + false, false ); if (r != 0) { diff --git a/src/disk_tool/disk_tool_meta.cpp b/src/disk_tool/disk_tool_meta.cpp index 11bb8e5c..580aa16a 100644 --- a/src/disk_tool/disk_tool_meta.cpp +++ b/src/disk_tool/disk_tool_meta.cpp @@ -7,37 +7,116 @@ #include "json_util.h" #include "malloc_or_die.h" -int disk_tool_t::process_meta(std::function hdr_fn, - std::function record_fn, bool do_open) +#define FREE_SPACE_BIT 0x8000 + +int disk_tool_t::process_meta(std::function hdr_fn, + std::function obj_fn, + std::function record_fn, + bool with_data, bool do_open) { + int r = 0; if (dsk.meta_block_size % DIRECT_IO_ALIGNMENT) { fprintf(stderr, "Invalid metadata block size: is not a multiple of %d\n", DIRECT_IO_ALIGNMENT); return 1; } + int buf_size = 1024*1024; + if (buf_size % dsk.meta_block_size) + buf_size = 8*dsk.meta_block_size; + uint8_t *data = NULL; + data = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, buf_size); + blockstore_meta_header_v3_t *hdr = (blockstore_meta_header_v3_t *)data; if (do_open) { if (dsk.meta_fd >= 0) { fprintf(stderr, "Bug: Metadata device is already opened\n"); - return 1; +close_error: + r = 1; + goto close_free; } dsk.meta_fd = open(dsk.meta_device.c_str(), (options["io"] == "cached" ? 0 : O_DIRECT) | O_RDONLY); if (dsk.meta_fd < 0) { fprintf(stderr, "Failed to open metadata device %s: %s\n", dsk.meta_device.c_str(), strerror(errno)); - return 1; + goto close_error; } } - int buf_size = 1024*1024; - if (buf_size % dsk.meta_block_size) - buf_size = 8*dsk.meta_block_size; - void *data = memalign_or_die(MEM_ALIGNMENT, buf_size); + else if (dsk.meta_fd < 0) + { + fprintf(stderr, "Bug: Metadata device is not opened\n"); + goto close_error; + } // Check superblock - blockstore_meta_header_v2_t *hdr = (blockstore_meta_header_v2_t *)data; lseek64(dsk.meta_fd, dsk.meta_offset, 0); read_blocking(dsk.meta_fd, hdr, dsk.meta_block_size); - if (hdr->zero == 0 && hdr->magic == BLOCKSTORE_META_MAGIC_V1) + if (hdr->zero == 0 && hdr->magic == BLOCKSTORE_META_MAGIC_V1 && hdr->version == BLOCKSTORE_META_FORMAT_HEAP) + { + if (hdr->data_csum_type != 0 && + hdr->data_csum_type != BLOCKSTORE_CSUM_CRC32C) + { + goto csum_unknown; + } + if (!dsk.journal_len && !with_data) + { + fprintf(stderr, "Buffer area (former journal) location must be specified to dump \"heap\" with data\n"); + goto close_error; + } + // Load buffer_area + if (with_data) + { + buffer_area = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, dsk.journal_len); + if (dsk.journal_device == dsk.meta_device || dsk.journal_device == "") + { + dsk.journal_fd = dsk.meta_fd; + } + else if (do_open) + { + if (dsk.journal_fd >= 0) + { + fprintf(stderr, "Bug: Metadata device is already opened\n"); + goto close_error; + } + dsk.journal_fd = open(dsk.journal_device.c_str(), (options["io"] == "cached" ? 0 : O_DIRECT) | O_RDONLY); + if (dsk.journal_fd < 0) + { + fprintf(stderr, "Failed to open journal device %s: %s\n", dsk.journal_device.c_str(), strerror(errno)); + goto close_error; + } + } + else if (dsk.journal_fd < 0) + { + fprintf(stderr, "Bug: journal device is not opened\n"); + goto close_error; + } + uint64_t journal_pos = 0; + lseek64(dsk.journal_fd, dsk.journal_offset+journal_pos, 0); + while (journal_pos < dsk.journal_len) + { + uint64_t read_len = buf_size < dsk.journal_len-journal_pos ? buf_size : dsk.journal_len-journal_pos; + read_blocking(dsk.journal_fd, buffer_area+journal_pos, read_len); + journal_pos += read_len; + } + } + blockstore_heap_t *heap = new blockstore_heap_t(&dsk, buffer_area, log_level); + // Load heap and just iterate it in memory + hdr_fn(hdr); + hdr = NULL; + meta_pos = dsk.meta_block_size; + lseek64(dsk.meta_fd, dsk.meta_offset+meta_pos, 0); + while (meta_pos < dsk.meta_area_size) + { + uint64_t read_len = buf_size < dsk.meta_area_size-meta_pos ? buf_size : dsk.meta_area_size-meta_pos; + read_blocking(dsk.meta_fd, data, read_len); + heap->read_blocks(meta_pos-dsk.meta_block_size, read_len, data, [&](heap_object_t *obj) + { + obj_fn(heap, obj, ((uint8_t*)obj-data+meta_pos)/dsk.meta_block_size); + }, [](uint32_t, uint32_t, uint8_t*){}); + meta_pos += read_len; + } + delete heap; + } + else if (hdr->zero == 0 && hdr->magic == BLOCKSTORE_META_MAGIC_V1) { dsk.meta_format = hdr->version; dsk.calc_lengths(); @@ -55,27 +134,16 @@ int disk_tool_t::process_meta(std::function if (hdr->data_csum_type != 0 && hdr->data_csum_type != BLOCKSTORE_CSUM_CRC32C) { +csum_unknown: fprintf(stderr, "I don't know checksum format %u, the only supported format is crc32c = %u.\n", hdr->data_csum_type, BLOCKSTORE_CSUM_CRC32C); - free(data); - if (do_open) - { - close(dsk.meta_fd); - dsk.meta_fd = -1; - } - return 1; + goto close_error; } } else { // Unsupported version fprintf(stderr, "Metadata format is too new for me (stored version is %ju, max supported %u).\n", hdr->version, BLOCKSTORE_META_FORMAT_V2); - free(data); - if (do_open) - { - close(dsk.meta_fd); - dsk.meta_fd = -1; - } - return 1; + goto close_error; } if (hdr->meta_block_size != dsk.meta_block_size) { @@ -100,9 +168,9 @@ int disk_tool_t::process_meta(std::function hdr = NULL; meta_pos = dsk.meta_block_size; lseek64(dsk.meta_fd, dsk.meta_offset+meta_pos, 0); - while (meta_pos < dsk.meta_area_size) + while (meta_pos < dsk.min_meta_len) { - uint64_t read_len = buf_size < dsk.meta_area_size-meta_pos ? buf_size : dsk.meta_area_size-meta_pos; + uint64_t read_len = buf_size < dsk.min_meta_len-meta_pos ? buf_size : dsk.min_meta_len-meta_pos; read_blocking(dsk.meta_fd, data, read_len); meta_pos += read_len; for (uint64_t blk = 0; blk < read_len; blk += dsk.meta_block_size) @@ -117,7 +185,7 @@ int disk_tool_t::process_meta(std::function uint32_t *entry_csum = (uint32_t*)((uint8_t*)entry + dsk.clean_entry_size - 4); if (*entry_csum != crc32c(0, entry, dsk.clean_entry_size - 4)) { - fprintf(stderr, "Metadata entry %ju is corrupt (checksum mismatch), skipping\n", block_num); + fprintf(stderr, "Metadata entry %lu is corrupt (checksum mismatch), skipping\n", block_num); continue; } } @@ -153,13 +221,25 @@ int disk_tool_t::process_meta(std::function } } } +close_free: free(data); + if (buffer_area) + { + free(buffer_area); + buffer_area = NULL; + } if (do_open) { close(dsk.meta_fd); dsk.meta_fd = -1; + if (dsk.journal_fd >= 0) + { + if (dsk.journal_fd != dsk.meta_fd) + close(dsk.journal_fd); + dsk.journal_fd = -1; + } } - return 0; + return r; } int disk_tool_t::dump_load_check_superblock(const std::string & device) @@ -190,15 +270,33 @@ int disk_tool_t::dump_load_check_superblock(const std::string & device) int disk_tool_t::dump_meta() { int r = process_meta( - [this](blockstore_meta_header_v2_t *hdr) { dump_meta_header(hdr); }, - [this](uint64_t block_num, clean_disk_entry *entry, uint8_t *bitmap) { dump_meta_entry(block_num, entry, bitmap); } + [this](blockstore_meta_header_v3_t *hdr) + { + if (dump_as_old) + { + hdr->version = BLOCKSTORE_META_FORMAT_V2; + hdr->compacted_lsn = 0; + hdr->header_csum = 0; + hdr->header_csum = crc32c(0, hdr, sizeof(blockstore_meta_header_v2_t)); + } + dump_meta_header(hdr); + }, + [this](blockstore_heap_t *heap, heap_object_t *obj, uint32_t meta_block_num) + { + if (dump_as_old) + dump_heap_entry_as_old(heap, obj); + else + dump_heap_entry(heap, obj); + }, + [this](uint64_t block_num, clean_disk_entry *entry, uint8_t *bitmap) { dump_meta_entry(block_num, entry, bitmap); }, + true, true ); if (r == 0) printf("\n]}\n"); return r; } -void disk_tool_t::dump_meta_header(blockstore_meta_header_v2_t *hdr) +void disk_tool_t::dump_meta_header(blockstore_meta_header_v3_t *hdr) { if (hdr) { @@ -219,6 +317,15 @@ void disk_tool_t::dump_meta_header(blockstore_meta_header_v2_t *hdr) csum_type_str(hdr->data_csum_type).c_str(), hdr->csum_block_size ); } + else if (hdr->version == BLOCKSTORE_META_FORMAT_HEAP) + { + printf( + "{\"version\":\"3.0\",\"meta_block_size\":%u,\"data_block_size\":%u,\"bitmap_granularity\":%u," + "\"data_csum_type\":\"%s\",\"csum_block_size\":%u,\"entries\":[\n", + hdr->meta_block_size, hdr->data_block_size, hdr->bitmap_granularity, + csum_type_str(hdr->data_csum_type).c_str(), hdr->csum_block_size + ); + } } else { @@ -227,6 +334,125 @@ void disk_tool_t::dump_meta_header(blockstore_meta_header_v2_t *hdr) first_entry = true; } +void disk_tool_t::dump_heap_entry_as_old(blockstore_heap_t *heap, heap_object_t *obj) +{ + heap_write_t *wr = NULL; + for (wr = obj->get_writes(); wr && wr->flags != (BS_HEAP_BIG_WRITE|BS_HEAP_STABLE) && + wr->flags != (BS_HEAP_TOMBSTONE|BS_HEAP_STABLE); wr = wr->next()) + { + } + if (!wr || wr->flags != (BS_HEAP_BIG_WRITE|BS_HEAP_STABLE)) + { + return; + } + printf( +#define ENTRY_FMT "{\"block\":%ju,\"pool\":%u,\"inode\":\"0x%jx\",\"stripe\":\"0x%jx\",\"version\":%ju" + (first_entry ? ENTRY_FMT : (",\n" ENTRY_FMT)), +#undef ENTRY_FMT + wr->location/dsk.data_block_size, INODE_POOL(obj->inode), INODE_NO_POOL(obj->inode), + obj->stripe, wr->version + ); + printf(",\"bitmap\":\""); + uint8_t* bitmap = wr->get_int_bitmap(heap); + for (uint64_t i = 0; i < dsk.clean_entry_bitmap_size; i++) + { + printf("%02x", bitmap[i]); + } + bitmap = wr->get_ext_bitmap(heap); + printf("\",\"ext_bitmap\":\""); + for (uint64_t i = 0; i < dsk.clean_entry_bitmap_size; i++) + { + printf("%02x", bitmap[i]); + } + uint8_t *csums = wr->get_checksums(heap); + uint32_t csum_size = wr->get_csum_size(heap); + if (csums) + { + printf("\",\"block_csums\":\""); + for (uint32_t i = 0; i < csum_size; i++) + { + printf("%02x", csums[i]); + } + } + if (wr->get_checksum(heap)) + { + printf("\",\"crc32c\":\"%08x", *wr->get_checksum(heap)); + } + printf("\"}"); + first_entry = false; +} + +void disk_tool_t::dump_heap_entry(blockstore_heap_t *heap, heap_object_t *obj) +{ + printf( +#define ENTRY_FMT "{\"pool\":%u,\"inode\":\"0x%jx\",\"stripe\":\"0x%jx\",\"writes\":[" + (first_entry ? ENTRY_FMT : (",\n" ENTRY_FMT)), +#undef ENTRY_FMT + INODE_POOL(obj->inode), INODE_NO_POOL(obj->inode), obj->stripe + ); + heap_write_t *wr = NULL; + bool first_wr = true; + for (wr = obj->get_writes(); wr; wr = wr->next()) + { + printf( +#define ENTRY_FMT "{\"lsn\":%ju,\"version\":%ju,\"type\":\"%s\",\"stable\":%s,\"offset\":%u,\"len\":%u" + (first_wr ? ENTRY_FMT : ("," ENTRY_FMT)), +#undef ENTRY_FMT + wr->lsn, wr->version, (wr->flags & BS_HEAP_TYPE) == BS_HEAP_SMALL_WRITE ? "small" : ( + (wr->flags & BS_HEAP_TYPE) == BS_HEAP_BIG_WRITE ? "big" : ( + (wr->flags & BS_HEAP_TYPE) == BS_HEAP_INTENT_WRITE ? "intent" : ( + (wr->flags & BS_HEAP_TYPE) == BS_HEAP_TOMBSTONE ? "tombstone" : "unknown"))), + (wr->flags & BS_HEAP_STABLE) ? "true" : "false", + wr->offset, wr->len + ); + if ((wr->flags & BS_HEAP_TYPE) == BS_HEAP_BIG_WRITE || + (wr->flags & BS_HEAP_TYPE) == BS_HEAP_SMALL_WRITE && !dump_with_data) + { + printf(",\"location\":%ju", wr->location); + } + else if ((wr->flags & BS_HEAP_TYPE) == BS_HEAP_SMALL_WRITE && dump_with_data) + { + printf(",\"data\":\""); + for (uint32_t i = 0; i < wr->len; i++) + printf("%02x", buffer_area[wr->location + i]); + printf("\""); + } + uint8_t* bitmap = wr->get_int_bitmap(heap); + if (bitmap) + { + printf(",\"bitmap\":\""); + for (uint64_t i = 0; i < dsk.clean_entry_bitmap_size; i++) + printf("%02x", bitmap[i]); + printf("\""); + } + bitmap = wr->get_ext_bitmap(heap); + if (bitmap) + { + printf(",\"ext_bitmap\":\""); + for (uint64_t i = 0; i < dsk.clean_entry_bitmap_size; i++) + printf("%02x", bitmap[i]); + printf("\""); + } + uint8_t *csums = wr->get_checksums(heap); + if (csums) + { + printf(",\"block_csums\":\""); + uint32_t csum_size = wr->get_csum_size(heap); + for (uint32_t i = 0; i < csum_size; i++) + printf("%02x", csums[i]); + printf("\""); + } + if (wr->get_checksum(heap)) + { + printf(",\"data_crc32c\":\"%08x\"", *wr->get_checksum(heap)); + } + printf("}"); + first_wr = false; + } + printf("]}"); + first_entry = false; +} + void disk_tool_t::dump_meta_entry(uint64_t block_num, clean_disk_entry *entry, uint8_t *bitmap) { printf( @@ -289,7 +515,7 @@ int disk_tool_t::write_json_meta(json11::Json meta) ? BLOCKSTORE_CSUM_CRC32C : BLOCKSTORE_CSUM_NONE); new_hdr->csum_block_size = meta["csum_block_size"].uint64_value(); - new_hdr->header_csum = crc32c(0, new_hdr, sizeof(*new_hdr)); + new_hdr->header_csum = crc32c(0, new_hdr, sizeof(blockstore_meta_header_v2_t)); } uint32_t new_clean_entry_header_size = (new_hdr->version == BLOCKSTORE_META_FORMAT_V1 ? sizeof(clean_disk_entry) : sizeof(clean_disk_entry) + 4 /*entry_csum*/); @@ -299,6 +525,7 @@ int disk_tool_t::write_json_meta(json11::Json meta) : 0); new_clean_entry_size = new_clean_entry_header_size + 2*new_clean_entry_bitmap_size + new_data_csum_size; new_entries_per_block = new_hdr->meta_block_size / new_clean_entry_size; + // FIXME: Use a streaming json parser for (const auto & e: meta["entries"].array_items()) { uint64_t data_block = e["block"].uint64_value(); @@ -332,7 +559,366 @@ int disk_tool_t::write_json_meta(json11::Json meta) } } int r = resize_write_new_meta(); - free(new_meta_buf); - new_meta_buf = NULL; + free_new_meta(); return r; } + +int disk_tool_t::write_json_heap(json11::Json meta, json11::Json journal) +{ + new_meta_hdr->zero = 0; + new_meta_hdr->magic = BLOCKSTORE_META_MAGIC_V1; + new_meta_hdr->version = BLOCKSTORE_META_FORMAT_HEAP; + new_meta_hdr->meta_block_size = meta["meta_block_size"].uint64_value() + ? meta["meta_block_size"].uint64_value() : 4096; + new_meta_hdr->data_block_size = meta["data_block_size"].uint64_value() + ? meta["data_block_size"].uint64_value() : 131072; + new_meta_hdr->bitmap_granularity = meta["bitmap_granularity"].uint64_value() + ? meta["bitmap_granularity"].uint64_value() : 4096; + new_meta_hdr->data_csum_type = meta["data_csum_type"].is_number() + ? meta["data_csum_type"].uint64_value() + : (meta["data_csum_type"].string_value() == "crc32c" + ? BLOCKSTORE_CSUM_CRC32C + : BLOCKSTORE_CSUM_NONE); + new_meta_hdr->csum_block_size = meta["csum_block_size"].uint64_value(); + new_meta_hdr->header_csum = crc32c(0, new_meta_hdr, sizeof(blockstore_meta_header_v3_t)); + new_clean_entry_bitmap_size = (new_meta_hdr->data_block_size / new_meta_hdr->bitmap_granularity + 7) / 8; + new_clean_entry_size = 0; + new_entries_per_block = 0; + new_data_csum_size = (new_meta_hdr->data_csum_type + ? ((new_meta_hdr->data_block_size+new_meta_hdr->csum_block_size-1)/new_meta_hdr->csum_block_size*(new_meta_hdr->data_csum_type & 0xFF)) + : 0); + new_journal_buf = new_journal_len ? (uint8_t*)memalign(MEM_ALIGNMENT, new_journal_len) : NULL; + if (new_journal_len) + { + memset(new_journal_buf, 0, new_journal_len); + } + uint64_t total_used_space = 0; + uint32_t used_space = 0; + // FIXME: Use a streaming json parser + if (meta["version"] == "3.0") + { + // New format + std::vector object_buf; + new_heap = new blockstore_heap_t(&dsk, new_journal_buf, 0); + for (const auto & meta_entry: meta["entries"].array_items()) + { + bool invalid = false; + object_id oid = { + .inode = (sscanf_json(NULL, meta_entry["pool"]) << (64-POOL_ID_BITS)) | sscanf_json(NULL, meta_entry["inode"]), + .stripe = sscanf_json(NULL, meta_entry["stripe"]), + }; + object_buf.clear(); + object_buf.resize(sizeof(heap_object_t)); + heap_object_t *obj = (heap_object_t*)object_buf.data(); + obj->size = sizeof(heap_object_t); + obj->write_pos = meta_entry["writes"].array_items().size() ? sizeof(heap_object_t) : 0; + obj->inode = oid.inode; + obj->stripe = oid.stripe; + size_t pos = sizeof(heap_object_t); + heap_write_t *last_wr = NULL; + for (auto & write_entry: meta_entry["writes"].array_items()) + { + object_buf.resize(object_buf.size() + new_heap->get_max_write_entry_size()); + heap_write_t *wr = (heap_write_t*)(object_buf.data() + pos); + last_wr = wr; + uint8_t wr_type = 0; + if (write_entry["type"] == "small") + wr_type = BS_HEAP_SMALL_WRITE; + else if (write_entry["type"] == "intent") + wr_type = BS_HEAP_INTENT_WRITE; + else if (write_entry["type"] == "big") + wr_type = BS_HEAP_BIG_WRITE; + else if (write_entry["type"] == "tombstone") + wr_type = BS_HEAP_TOMBSTONE; + else + { + fprintf(stderr, "Write entry in %s has invalid type: %s, skipping object\n", meta_entry.dump().c_str(), write_entry["type"].dump().c_str()); + invalid = true; + break; + } + wr->flags = wr_type | (write_entry["stable"].bool_value() ? BS_HEAP_STABLE : 0); + wr->lsn = write_entry["lsn"].uint64_value(); + wr->version = write_entry["version"].uint64_value(); + wr->offset = write_entry["offset"].uint64_value(); + wr->len = write_entry["len"].uint64_value(); + wr->location = write_entry["location"].uint64_value(); + wr->size = wr->get_size(new_heap); + wr->next_pos = wr->size; + if (wr_type == BS_HEAP_SMALL_WRITE && write_entry["data"].is_string() && wr->len > 0) + { + if (!new_journal_buf) + { + fprintf(stderr, "Loading small write data requires overwriting buffer area\n"); + free_new_meta(); + return 1; + } + wr->location = new_heap->find_free_buffer_area(wr->len); + fromhexstr(write_entry["data"].string_value(), wr->len, new_journal_buf + wr->location); + } + if (write_entry["bitmap"].is_string() && wr->get_int_bitmap(new_heap)) + { + fromhexstr(write_entry["bitmap"].string_value(), new_clean_entry_bitmap_size, wr->get_int_bitmap(new_heap)); + } + if (write_entry["ext_bitmap"].is_string() && wr->get_ext_bitmap(new_heap)) + { + fromhexstr(write_entry["ext_bitmap"].string_value(), new_clean_entry_bitmap_size, wr->get_ext_bitmap(new_heap)); + } + if (write_entry["block_csums"].is_string() && wr->get_checksums(new_heap)) + { + fromhexstr(write_entry["block_csums"].string_value(), wr->get_csum_size(new_heap), wr->get_ext_bitmap(new_heap)); + } + if (write_entry["data_crc32c"].is_string() && wr->get_checksum(new_heap)) + { + *wr->get_checksum(new_heap) = sscanf_json("%jx", write_entry["data_crc32c"]); + } + } + if (invalid) + { + continue; + } + last_wr->next_pos = 0; + new_heap->copy_object(obj, NULL); + } + } + else + { + if (!journal.is_array()) + { + fprintf(stderr, "Metadata should include journal in you want to convert it to the \"heap\" format\n"); +close_err: + free(new_meta_buf); + new_meta_buf = NULL; + return 1; + } + std::map> journal_by_object; + if (index_journal_by_object(journal, journal_by_object) != 0) + { + goto close_err; + } + journal = json11::Json(); + // Convert old format to the new format + uint64_t next_lsn = 0; + uint64_t meta_offset = 0; + const uint32_t space_per_object = sizeof(heap_object_t) + sizeof(heap_write_t) + + new_clean_entry_bitmap_size*2 + new_data_csum_size; + uint64_t buffer_pos = 0; + // FIXME: Rather ugly. Remove the dependency on dsk from heap? + blockstore_disk_t dsk; + dsk.bitmap_granularity = new_meta_hdr->bitmap_granularity; + dsk.block_count = 16; + dsk.data_block_size = new_meta_hdr->data_block_size; + dsk.clean_entry_bitmap_size = new_clean_entry_bitmap_size; + dsk.csum_block_size = new_meta_hdr->csum_block_size; + dsk.data_csum_type = new_meta_hdr->data_csum_type; + dsk.journal_len = 4096; + dsk.meta_area_size = new_meta_len; + dsk.meta_block_size = new_meta_hdr->meta_block_size; + dsk.meta_block_target_free_space = 800; + blockstore_heap_t heap(&dsk, NULL, 0); + for (const auto & meta_entry: meta["entries"].array_items()) + { + object_id oid = { + .inode = (sscanf_json(NULL, meta_entry["pool"]) << (64-POOL_ID_BITS)) | sscanf_json(NULL, meta_entry["inode"]), + .stripe = sscanf_json(NULL, meta_entry["stripe"]), + }; + uint32_t space_for_this = space_per_object; + auto j_it = journal_by_object.find(oid); + if (j_it != journal_by_object.end()) + { + for (auto & rec: j_it->second) + { + if (rec["type"] == "small_write" || rec["type"] == "small_write_instant") + { + uint64_t off = rec["offset"].uint64_value(); + uint64_t len = rec["len"].uint64_value(); + if (off+len > new_meta_hdr->data_block_size) + { + fprintf(stderr, "Journal entry has too large offset or length: %s\n", json11::Json(rec).dump().c_str()); + goto close_err; + } + space_for_this += sizeof(heap_write_t) + new_clean_entry_bitmap_size + + ((off+len+new_meta_hdr->csum_block_size-1)/new_meta_hdr->csum_block_size - off/new_meta_hdr->csum_block_size) * (new_meta_hdr->data_csum_type & 0xFF); + } + else /*if (rec["type"] == "big_write" || rec["type"] == "big_write_instant")*/ + { + space_for_this += sizeof(heap_write_t) + 2*new_clean_entry_bitmap_size + new_data_csum_size; + } + } + } + if (space_for_this > new_meta_hdr->meta_block_size) + { + fprintf(stderr, "Object doesn't fit in a single metadata block. Object meta: %s, object journal: %s\n", + meta_entry.dump().c_str(), json11::Json(j_it->second).dump().c_str()); + goto close_err; + } + if (used_space + space_for_this > new_meta_hdr->meta_block_size-dsk.meta_block_target_free_space) + { + if (used_space < new_meta_hdr->meta_block_size-2) + { + *((uint16_t*)(new_meta_buf + meta_offset + used_space)) = FREE_SPACE_BIT | (uint16_t)(new_meta_hdr->meta_block_size-used_space); + } + meta_offset += new_meta_hdr->meta_block_size; + used_space = 0; + if (meta_offset >= new_meta_len) + { + fprintf(stderr, "Metadata doesn't fit into the new area (total used space: %ju, minimum free space in block: %u/%u)\n", + total_used_space, dsk.meta_block_target_free_space, new_meta_hdr->meta_block_size); + goto close_err; + } + } + heap_object_t *obj = (heap_object_t*)(new_meta_buf + meta_offset + used_space); + obj->size = sizeof(heap_object_t); + obj->write_pos = sizeof(heap_object_t); + obj->inode = oid.inode; + obj->stripe = oid.stripe; + heap_write_t *wr = obj->get_writes(); + wr->next_pos = 0; + wr->flags = BS_HEAP_BIG_WRITE|BS_HEAP_STABLE; + wr->lsn = ++next_lsn; + wr->version = sscanf_json(NULL, meta_entry["version"]); + wr->offset = 0; + wr->len = new_meta_hdr->data_block_size; + wr->location = meta_entry["block"].uint64_value() * new_meta_hdr->data_block_size; + wr->size = wr->get_size(&heap); + fromhexstr(meta_entry["bitmap"].string_value(), new_clean_entry_bitmap_size, wr->get_int_bitmap(&heap)); + fromhexstr(meta_entry["ext_bitmap"].string_value(), new_clean_entry_bitmap_size, wr->get_ext_bitmap(&heap)); + if (new_meta_hdr->data_csum_type != 0) + fromhexstr(meta_entry["data_csum"].string_value(), new_data_csum_size, wr->get_checksums(&heap)); + if (j_it != journal_by_object.end()) + { + for (auto & rec: j_it->second) + { + wr->next_pos = wr->get_size(&heap); + wr = wr->next(); + wr->next_pos = 0; + wr->lsn = ++next_lsn; + wr->version = rec["ver"].uint64_value(); + wr->offset = rec["offset"].uint64_value(); + wr->len = rec["len"].uint64_value(); + if (rec["type"] == "small_write" || rec["type"] == "small_write_instant") + { + if (wr->len > 0 && !rec["data"].is_string()) + { + fprintf(stderr, "Error: entry data is missing, please generate the dump with --json --format data\n"); + goto close_err; + } + wr->flags = BS_HEAP_SMALL_WRITE | (rec["type"] == "small_write_instant" ? BS_HEAP_STABLE : 0); + fromhexstr(rec["bitmap"].string_value(), new_clean_entry_bitmap_size, wr->get_ext_bitmap(&heap)); + fromhexstr(rec["data"].string_value(), wr->len, new_journal_buf+buffer_pos); + if (wr->len > 0) + { + if (!new_meta_hdr->data_csum_type) + *wr->get_checksum(&heap) = crc32c(0, new_journal_buf+buffer_pos, wr->len); + else + heap.calc_block_checksums((uint32_t*)wr->get_checksums(&heap), new_journal_buf+buffer_pos, NULL, wr->offset, wr->offset+wr->len, true, NULL); + } + buffer_pos += wr->len; + } + else if (rec["type"] == "big_write" || rec["type"] == "big_write_instant") + { + wr->flags = BS_HEAP_BIG_WRITE | (rec["type"] == "big_write_instant" ? BS_HEAP_STABLE : 0); + wr->location = sscanf_json(NULL, rec["loc"]); + bitmap_set(wr->get_int_bitmap(&heap), wr->offset, wr->len, new_meta_hdr->bitmap_granularity); + fromhexstr(rec["bitmap"].string_value(), new_clean_entry_bitmap_size, wr->get_ext_bitmap(&heap)); + if (new_meta_hdr->data_csum_type != 0) + { + if ((wr->offset % new_meta_hdr->csum_block_size) || (wr->len % new_meta_hdr->csum_block_size)) + { + fprintf(stderr, + "Error: big_write journal entries not aligned to csum_block_size can't be converted between v0.9 and v3.0 metadata\n" + "Stop writes and flush the journal or convert OSDs one by one without the journal if you still want to do it.\n"); + goto close_err; + } + fromhexstr(rec["block_csums"].string_value(), + ((wr->offset+wr->len+new_meta_hdr->csum_block_size-1)/new_meta_hdr->csum_block_size + - wr->offset/new_meta_hdr->csum_block_size) * (new_meta_hdr->data_csum_type & 0xFF), + wr->get_checksums(&heap)); + } + } + else + { + assert(0); + } + wr->size = wr->get_size(&heap); + } + } + obj->crc32c = obj->calc_crc32c(); + assert(((uint8_t*)wr + wr->size - (uint8_t*)obj) == space_for_this); + used_space += space_for_this; + total_used_space += space_for_this; + } + if (used_space > 0 && used_space < new_meta_hdr->meta_block_size-2) + { + *((uint16_t*)(new_meta_buf + meta_offset + used_space)) = FREE_SPACE_BIT | (uint16_t)(new_meta_hdr->meta_block_size-used_space); + } + } + int r = resize_write_new_meta(); + if (r == 0) + { + r = resize_write_new_journal(); + } + free_new_meta(); + return r; +} + +int disk_tool_t::index_journal_by_object(json11::Json journal, + std::map> & journal_by_object) +{ + for (const auto & rec: journal.array_items()) + { + object_id oid = { + .inode = sscanf_json(NULL, rec["inode"]), + .stripe = sscanf_json(NULL, rec["stripe"]), + }; + auto & jbo = journal_by_object[oid]; + if (rec["type"] == "small_write" || rec["type"] == "small_write_instant") + { + jbo.push_back(rec.object_items()); + } + else if (rec["type"] == "big_write" || rec["type"] == "big_write_instant") + { + if (rec["type"] == "big_write_instant") + jbo.clear(); + jbo.push_back(rec.object_items()); + } + else if (rec["type"] == "delete") + { + jbo.clear(); + } + else if (rec["type"] == "stable") + { + uint64_t commit_to = rec["version"].uint64_value(); + for (size_t i = 0; i < jbo.size(); i++) + { + if (jbo[i]["version"].uint64_value() <= commit_to) + { + if (jbo[i]["type"] == "big_write") + { + jbo.erase(jbo.begin(), jbo.begin()+i); + i = 0; + jbo[i]["type"] = "big_write_instant"; + } + else if (jbo[i]["type"] == "small_write") + { + jbo[i]["type"] = "small_write_instant"; + } + } + } + } + else if (rec["type"] == "rollback") + { + uint64_t rollback_to = rec["version"].uint64_value(); + for (size_t i = jbo.size()-1; i >= 0; i--) + { + if (jbo[i]["version"].uint64_value() > rollback_to) + jbo.erase(jbo.begin()+i, jbo.begin()+i+1); + } + } + else + { + fprintf(stderr, "Unknown journal entry type: %s\n", rec.dump().c_str()); + return -1; + } + } + return 0; +} diff --git a/src/disk_tool/disk_tool_prepare.cpp b/src/disk_tool/disk_tool_prepare.cpp index f27e65a6..8def1f74 100644 --- a/src/disk_tool/disk_tool_prepare.cpp +++ b/src/disk_tool/disk_tool_prepare.cpp @@ -9,6 +9,7 @@ int disk_tool_t::prepare_one(std::map options, int is_hdd, json11::Json::object & result) { static const char *allow_additional_params[] = { + "meta_format", "data_csum_type", "csum_block_size", "autosync_writes", @@ -128,7 +129,8 @@ int disk_tool_t::prepare_one(std::map options, int is_ dsk.open_meta(); dsk.open_journal(); dsk.calc_lengths(); - dsk.data_offset += (new_meta_len ? 0 : (dsk.meta_format == BLOCKSTORE_META_FORMAT_HEAP ? dsk.min_meta_len*2 : dsk.min_meta_len)); + if (dsk.data_device == dsk.meta_device && !new_meta_len) + dsk.data_offset += (dsk.meta_format == BLOCKSTORE_META_FORMAT_HEAP ? dsk.min_meta_len*2 : dsk.min_meta_len); dsk.meta_area_size = (dsk.data_device == dsk.meta_device ? dsk.data_offset : dsk.meta_device_size) - dsk.meta_offset; sb = json11::Json::object { { "meta_format", options["meta_format"] }, diff --git a/src/disk_tool/disk_tool_resize.cpp b/src/disk_tool/disk_tool_resize.cpp index 8b0b6be2..a6e72f4d 100644 --- a/src/disk_tool/disk_tool_resize.cpp +++ b/src/disk_tool/disk_tool_resize.cpp @@ -1,6 +1,9 @@ // Copyright (c) Vitaliy Filippov, 2019+ // License: VNPL-1.1 (see README.md for details) +#define _XOPEN_SOURCE +#include + #include "disk_tool.h" #include "rw_blocking.h" #include "str_util.h" @@ -25,65 +28,87 @@ int disk_tool_t::raw_resize() // Parse parameters r = resize_parse_params(); if (r != 0) - return r; + goto ret; // Fill allocator fprintf(stderr, "Reading metadata\n"); data_alloc = new allocator_t((new_data_len < dsk.data_len ? dsk.data_len : new_data_len) / dsk.data_block_size); r = process_meta( - [this](blockstore_meta_header_v2_t *hdr) + [this](blockstore_meta_header_v3_t *hdr) { resize_init(hdr); }, + [this](blockstore_heap_t *heap, heap_object_t *obj, uint32_t meta_block_num) + { + for (auto wr = obj->get_writes(); wr; wr = wr->next()) + { + if ((wr->flags & BS_HEAP_TYPE) == BS_HEAP_BIG_WRITE) + { + data_alloc->set(wr->location / dsk.data_block_size, true); + } + } + }, [this](uint64_t block_num, clean_disk_entry *entry, uint8_t *bitmap) { data_alloc->set(block_num, true); - } + }, + true, true ); if (r != 0) - return r; - fprintf(stderr, "Reading journal\n"); - r = process_journal([this](void *buf) + goto ret; + if (dsk.meta_format != BLOCKSTORE_META_FORMAT_HEAP) { - return process_journal_block(buf, [this](int num, journal_entry *je) + fprintf(stderr, "Reading journal\n"); + r = process_journal([this](void *buf) { - if (je->type == JE_BIG_WRITE || je->type == JE_BIG_WRITE_INSTANT) + return process_journal_block(buf, [this](int num, journal_entry *je) { - data_alloc->set(je->big_write.location / dsk.data_block_size, true); - } + if (je->type == JE_BIG_WRITE || je->type == JE_BIG_WRITE_INSTANT) + { + data_alloc->set(je->big_write.location / dsk.data_block_size, true); + } + }); }); - }); - if (r != 0) - return r; + if (r != 0) + goto ret; + } // Remap blocks r = resize_remap_blocks(); if (r != 0) - return r; + goto ret; // Copy data blocks into new places fprintf(stderr, "Moving data blocks\n"); r = resize_copy_data(); if (r != 0) - return r; - // Rewrite journal - fprintf(stderr, "Rebuilding journal\n"); - r = resize_rewrite_journal(); - if (r != 0) - return r; + goto ret; // Rewrite metadata + resize_alloc_journal(); fprintf(stderr, "Rebuilding metadata\n"); - r = resize_rewrite_meta(); + r = resize_rebuild_meta(); if (r != 0) - return r; + goto ret; + if (new_meta_format != BLOCKSTORE_META_FORMAT_HEAP) + { + // Rewrite journal + fprintf(stderr, "Rebuilding journal\n"); + r = resize_rebuild_journal(); + if (r != 0) + goto ret; + fprintf(stderr, "Writing new journal\n"); + } + else + fprintf(stderr, "Writing new buffer area\n"); // Write new journal - fprintf(stderr, "Writing new journal\n"); r = resize_write_new_journal(); if (r != 0) - return r; + goto ret; // Write new metadata fprintf(stderr, "Writing new metadata\n"); r = resize_write_new_meta(); if (r != 0) - return r; + goto ret; fprintf(stderr, "Done\n"); +ret: + free_new_meta(); return 0; } @@ -127,6 +152,8 @@ int disk_tool_t::resize_parse_params() ? parse_size(options["new_journal_offset"]) : dsk.journal_offset; new_journal_len = options.find("new_journal_len") != options.end() ? parse_size(options["new_journal_len"]) : dsk.journal_len; + new_meta_format = options.find("new_meta_format") != options.end() + ? stoull_full(options["new_meta_format"]) : 0; if (new_data_len+new_data_offset > dsk.data_device_size) new_data_len = dsk.data_device_size-new_data_offset; if (new_meta_device == dsk.data_device && new_data_offset < new_meta_offset && @@ -152,7 +179,7 @@ int disk_tool_t::resize_parse_params() return 0; } -void disk_tool_t::resize_init(blockstore_meta_header_v2_t *hdr) +void disk_tool_t::resize_init(blockstore_meta_header_v3_t *hdr) { if (hdr && dsk.data_block_size != hdr->data_block_size) { @@ -171,6 +198,15 @@ void disk_tool_t::resize_init(blockstore_meta_header_v2_t *hdr) dsk.data_csum_type = hdr->data_csum_type; dsk.csum_block_size = hdr->csum_block_size; } + if (hdr && dsk.meta_format != hdr->version) + { + dsk.meta_format = hdr->version; + } + if (new_meta_format == 0) + { + new_meta_format = hdr && hdr->version == BLOCKSTORE_META_FORMAT_HEAP ? BLOCKSTORE_META_FORMAT_HEAP : BLOCKSTORE_META_FORMAT_V2; + } + dsk.calc_lengths(); if (((new_data_offset-dsk.data_offset) % dsk.data_block_size)) { fprintf(stderr, "Data alignment mismatch: old data offset is 0x%jx, new is 0x%jx, but alignment on %x should be equal\n", @@ -360,15 +396,57 @@ int disk_tool_t::resize_copy_data() return 0; } -int disk_tool_t::resize_rewrite_journal() +void disk_tool_t::resize_alloc_journal() { - // Simply overwriting on the fly may be impossible because old and new areas may overlap - // For now, just build new journal data in memory new_journal_buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, new_journal_len); + memset(new_journal_buf, 0, new_journal_len); new_journal_ptr = new_journal_buf; new_journal_data = new_journal_ptr + dsk.journal_block_size; new_journal_in_pos = 0; - memset(new_journal_buf, 0, new_journal_len); +} + +void disk_tool_t::build_journal_start() +{ + journal_entry *ne = (journal_entry*)(new_journal_ptr + new_journal_in_pos); + *((journal_entry_start*)ne) = (journal_entry_start){ + .magic = JOURNAL_MAGIC, + .type = JE_START, + .size = sizeof(journal_entry_start), + .journal_start = dsk.journal_block_size, + .version = JOURNAL_VERSION_V2, + .data_csum_type = dsk.data_csum_type, + .csum_block_size = dsk.csum_block_size, + }; + ne->crc32 = je_crc32(ne); + new_journal_ptr += dsk.journal_block_size; + new_journal_data = new_journal_ptr+dsk.journal_block_size; + new_journal_in_pos = 0; +} + +void disk_tool_t::choose_journal_block(uint32_t je_size) +{ + if (dsk.journal_block_size < new_journal_in_pos+je_size) + { + new_journal_ptr = new_journal_data; + if (new_journal_ptr-new_journal_buf >= new_journal_len) + { + fprintf(stderr, "Error: live entries don't fit to the new journal\n"); + exit(1); + } + new_journal_data = new_journal_ptr+dsk.journal_block_size; + new_journal_in_pos = 0; + if (dsk.journal_block_size < je_size) + { + fprintf(stderr, "Error: journal entry too large (%u bytes)\n", je_size); + exit(1); + } + } +} + +int disk_tool_t::resize_rebuild_journal() +{ + // Simply overwriting on the fly may be impossible because old and new areas may overlap + // For now, just build new journal data in memory process_journal([this](void *buf) { return process_journal_block(buf, [this](int num, journal_entry *je) @@ -385,39 +463,11 @@ int disk_tool_t::resize_rewrite_journal() ); exit(1); } - journal_entry *ne = (journal_entry*)(new_journal_ptr + new_journal_in_pos); - *((journal_entry_start*)ne) = (journal_entry_start){ - .magic = JOURNAL_MAGIC, - .type = JE_START, - .size = sizeof(journal_entry_start), - .journal_start = dsk.journal_block_size, - .version = JOURNAL_VERSION_V2, - .data_csum_type = dsk.data_csum_type, - .csum_block_size = dsk.csum_block_size, - }; - ne->crc32 = je_crc32(ne); - new_journal_ptr += dsk.journal_block_size; - new_journal_data = new_journal_ptr+dsk.journal_block_size; - new_journal_in_pos = 0; + build_journal_start(); } else { - if (dsk.journal_block_size < new_journal_in_pos+je->size) - { - new_journal_ptr = new_journal_data; - if (new_journal_ptr-new_journal_buf >= new_journal_len) - { - fprintf(stderr, "Error: live entries don't fit to the new journal\n"); - exit(1); - } - new_journal_data = new_journal_ptr+dsk.journal_block_size; - new_journal_in_pos = 0; - if (dsk.journal_block_size < je->size) - { - fprintf(stderr, "Error: journal entry too large (%u bytes)\n", je->size); - exit(1); - } - } + choose_journal_block(je->size); journal_entry *ne = (journal_entry*)(new_journal_ptr + new_journal_in_pos); memcpy(ne, je, je->size); ne->crc32_prev = new_crc32_prev; @@ -464,30 +514,173 @@ int disk_tool_t::resize_write_new_journal() fsync(new_journal_fd); close(new_journal_fd); new_journal_fd = -1; - free(new_journal_buf); - new_journal_buf = NULL; return 0; } -int disk_tool_t::resize_rewrite_meta() +int disk_tool_t::resize_rebuild_meta() { - new_meta_buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, new_meta_len); - memset(new_meta_buf, 0, new_meta_len); + if (new_meta_format == BLOCKSTORE_META_FORMAT_HEAP) + { + new_dsk = dsk; + new_dsk.data_offset = new_data_offset; + new_dsk.data_len = new_data_len; + new_dsk.block_count = new_data_len / dsk.data_block_size; + new_dsk.journal_device = new_journal_device; + new_dsk.journal_offset = new_journal_offset; + new_dsk.journal_len = new_journal_len; + new_dsk.meta_device = new_meta_device; + new_dsk.meta_offset = new_meta_offset; + new_dsk.meta_area_size = new_meta_len; + new_dsk.meta_format = new_meta_format; + new_heap = new blockstore_heap_t(&new_dsk, NULL, 0); + new_meta_hdr = (blockstore_meta_header_v3_t *)memalign_or_die(MEM_ALIGNMENT, dsk.meta_block_size); + memset(new_meta_hdr, 0, dsk.meta_block_size); + } + else + { + new_meta_buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, new_meta_len); + memset(new_meta_buf, 0, new_meta_len); + new_meta_hdr = (blockstore_meta_header_v3_t *)new_meta_buf; + } + std::vector writes; int r = process_meta( - [this](blockstore_meta_header_v2_t *hdr) + [&](blockstore_meta_header_v3_t *hdr) { - blockstore_meta_header_v2_t *new_hdr = (blockstore_meta_header_v2_t *)new_meta_buf; - new_hdr->zero = 0; - new_hdr->magic = BLOCKSTORE_META_MAGIC_V1; - new_hdr->version = BLOCKSTORE_META_FORMAT_V2; - new_hdr->meta_block_size = dsk.meta_block_size; - new_hdr->data_block_size = dsk.data_block_size; - new_hdr->bitmap_granularity = dsk.bitmap_granularity ? dsk.bitmap_granularity : 4096; - new_hdr->data_csum_type = dsk.data_csum_type; - new_hdr->csum_block_size = dsk.csum_block_size; - new_hdr->header_csum = crc32c(0, new_hdr, sizeof(*new_hdr)); + new_meta_hdr->zero = 0; + new_meta_hdr->magic = BLOCKSTORE_META_MAGIC_V1; + new_meta_hdr->version = new_meta_format == 0 ? BLOCKSTORE_META_FORMAT_HEAP : new_meta_format; + new_meta_hdr->meta_block_size = dsk.meta_block_size; + new_meta_hdr->data_block_size = dsk.data_block_size; + new_meta_hdr->bitmap_granularity = dsk.bitmap_granularity ? dsk.bitmap_granularity : 4096; + new_meta_hdr->data_csum_type = dsk.data_csum_type; + new_meta_hdr->csum_block_size = dsk.csum_block_size; + new_meta_hdr->compacted_lsn = hdr->compacted_lsn; + new_meta_hdr->header_csum = 0; + new_meta_hdr->header_csum = crc32c(0, new_meta_hdr, new_meta_hdr->version == BLOCKSTORE_META_FORMAT_HEAP + ? sizeof(blockstore_meta_header_v3_t) : sizeof(blockstore_meta_header_v2_t)); + if (hdr->version == BLOCKSTORE_META_FORMAT_HEAP && new_meta_format != BLOCKSTORE_META_FORMAT_HEAP) + { + build_journal_start(); + } }, - [this](uint64_t block_num, clean_disk_entry *entry, uint8_t *bitmap) + [&](blockstore_heap_t *heap, heap_object_t *obj, uint32_t meta_block_num) + { + for (auto wr = obj->get_writes(); wr; wr = wr->next()) + { + if ((wr->flags & BS_HEAP_TYPE) == BS_HEAP_BIG_WRITE) + { + if (wr->next() && new_meta_hdr->data_csum_type != 0 && + ((wr->offset % new_meta_hdr->csum_block_size) || (wr->len % new_meta_hdr->csum_block_size))) + { + fprintf(stderr, "Error: big_write journal entries not aligned to csum_block_size can't be converted between v0.9 and v3.0 metadata\n"); + exit(1); + } + auto block_num = wr->location / dsk.data_block_size; + auto remap_it = data_remap.find(block_num); + if (remap_it != data_remap.end()) + block_num = remap_it->second; + if (block_num < free_first || block_num >= total_blocks-free_last) + { + fprintf(stderr, "BUG: remapped block %ju not in range %ju..%ju\n", block_num, free_first, total_blocks-free_last); + exit(1); + } + block_num += data_idx_diff; + wr->location = block_num * dsk.data_block_size; + } + else if ((wr->flags & BS_HEAP_TYPE) == BS_HEAP_SMALL_WRITE) + { + if (new_heap && wr->len > 0) + { + if (new_journal_ptr-new_journal_buf+wr->len > new_journal_len) + { + fprintf(stderr, "Small write data doesn't fit into the new buffer area\n"); + exit(1); + } + memcpy(new_journal_ptr, buffer_area+wr->location, wr->len); + wr->location = new_journal_ptr-new_journal_buf; + new_journal_ptr += wr->len; + } + } + else if (!new_heap) + { + fprintf(stderr, "Object %jx:%jx can't be converted to the old format because it contains %s\n", + obj->inode, obj->stripe, (wr->flags & BS_HEAP_TYPE) == BS_HEAP_TOMBSTONE + ? "a tombstone" : ((wr->flags & BS_HEAP_TYPE) == BS_HEAP_INTENT_WRITE ? "an intent_write entry" : "an unknown entry")); + exit(1); + } + } + if (new_heap) + { + // New -> New + new_heap->copy_object(obj, NULL); + } + else + { + // Fill journal + writes.clear(); + for (auto wr = obj->get_writes(); wr; wr = wr->next()) + { + writes.push_back(wr); + } + for (ssize_t i = writes.size()-2; i >= 0; i--) + { + auto wr = writes[i]; + assert((wr->flags & BS_HEAP_TYPE) == BS_HEAP_SMALL_WRITE || wr->flags == BS_HEAP_BIG_WRITE); + uint32_t je_size = dsk.dirty_dyn_size(wr->offset, wr->len) + + ((wr->flags & BS_HEAP_TYPE) == BS_HEAP_SMALL_WRITE ? sizeof(journal_entry_small_write) : sizeof(journal_entry_big_write)); + choose_journal_block(je_size); + journal_entry *je = (journal_entry*)(new_journal_ptr + new_journal_in_pos); + je->magic = JOURNAL_MAGIC; + je->type = (wr->flags & BS_HEAP_STABLE) ? JE_SMALL_WRITE_INSTANT : JE_SMALL_WRITE; + je->size = je_size; + je->crc32_prev = new_crc32_prev; + je->small_write.oid = (object_id){ .inode = obj->inode, .stripe = obj->stripe }; + je->small_write.version = wr->version; + je->small_write.offset = wr->offset; + je->small_write.len = wr->len; + if ((wr->flags & BS_HEAP_TYPE) == BS_HEAP_SMALL_WRITE) + { + je->small_write.data_offset = new_journal_data-new_journal_buf; + if (je->small_write.data_offset + je->small_write.len > new_journal_len) + { + fprintf(stderr, "Error: live entries don't fit to the new journal\n"); + exit(1); + } + memcpy(new_journal_data, buffer_area+wr->location, je->small_write.len); + new_journal_data += je->small_write.len; + if (dsk.data_csum_type == 0 && wr->get_checksum(heap)) + je->small_write.crc32_data = *wr->get_checksum(heap); + } + else + { + je->big_write.location = wr->location; + } + memcpy((uint8_t*)je + je->size, wr->get_ext_bitmap(heap), new_clean_entry_bitmap_size); + if (dsk.data_csum_type != 0 && wr->get_checksums(heap)) + memcpy((uint8_t*)je + je->size + new_clean_entry_bitmap_size, wr->get_checksums(heap), new_data_csum_size); + je->crc32 = je_crc32(je); + new_journal_in_pos += je->size; + new_crc32_prev = je->crc32; + } + // New -> Old + if (writes[writes.size()-1]->flags == BS_HEAP_BIG_WRITE|BS_HEAP_STABLE) + { + auto big_wr = writes[writes.size()-1]; + uint64_t block_num = big_wr->location / dsk.data_block_size; + clean_disk_entry *new_entry = (clean_disk_entry*)(new_meta_buf + dsk.meta_block_size + + dsk.meta_block_size*(block_num / new_entries_per_block) + + new_clean_entry_size*(block_num % new_entries_per_block)); + new_entry->oid = (object_id){ .inode = obj->inode, .stripe = obj->stripe }; + new_entry->version = big_wr->version; + memcpy(new_entry->bitmap, big_wr->get_ext_bitmap(heap), new_clean_entry_bitmap_size); + memcpy(new_entry->bitmap + new_clean_entry_bitmap_size, big_wr->get_int_bitmap(heap), new_clean_entry_bitmap_size); + memcpy(new_entry->bitmap + 2*new_clean_entry_bitmap_size, big_wr->get_checksums(heap), new_data_csum_size); + uint32_t *new_entry_csum = (uint32_t*)(((uint8_t*)new_entry) + new_clean_entry_size - 4); + *new_entry_csum = crc32c(0, new_entry, new_clean_entry_size - 4); + } + } + }, + [&](uint64_t block_num, clean_disk_entry *entry, uint8_t *bitmap) { auto remap_it = data_remap.find(block_num); if (remap_it != data_remap.end()) @@ -498,26 +691,44 @@ int disk_tool_t::resize_rewrite_meta() exit(1); } block_num += data_idx_diff; - clean_disk_entry *new_entry = (clean_disk_entry*)(new_meta_buf + dsk.meta_block_size + - dsk.meta_block_size*(block_num / new_entries_per_block) + - new_clean_entry_size*(block_num % new_entries_per_block)); - new_entry->oid = entry->oid; - new_entry->version = entry->version; - if (bitmap) - memcpy(new_entry->bitmap, bitmap, 2*new_clean_entry_bitmap_size + new_data_csum_size); + if (new_heap) + { + // Old -> New + uint8_t wr_buf[new_heap->get_max_write_entry_size()]; + heap_write_t *wr = (heap_write_t*)wr_buf; + wr->flags = BS_HEAP_BIG_WRITE|BS_HEAP_STABLE; + wr->location = block_num * dsk.data_block_size; + wr->next_pos = 0; + wr->offset = 0; + wr->len = 0; + wr->size = wr->get_size(new_heap); + if (bitmap) + { + memcpy(wr->get_ext_bitmap(new_heap), bitmap, new_clean_entry_bitmap_size); + memcpy(wr->get_int_bitmap(new_heap), bitmap+new_clean_entry_bitmap_size, new_clean_entry_bitmap_size); + memcpy(wr->get_checksums(new_heap), bitmap+2*new_clean_entry_bitmap_size, new_data_csum_size); + } + new_heap->post_write(entry->oid, wr, NULL, NULL); + } else - memset(new_entry->bitmap, 0xff, 2*new_clean_entry_bitmap_size); - uint32_t *new_entry_csum = (uint32_t*)(((uint8_t*)new_entry) + new_clean_entry_size - 4); - *new_entry_csum = crc32c(0, new_entry, new_clean_entry_size - 4); - } + { + // Old -> Old + clean_disk_entry *new_entry = (clean_disk_entry*)(new_meta_buf + dsk.meta_block_size + + dsk.meta_block_size*(block_num / new_entries_per_block) + + new_clean_entry_size*(block_num % new_entries_per_block)); + new_entry->oid = entry->oid; + new_entry->version = entry->version; + if (bitmap) + memcpy(new_entry->bitmap, bitmap, 2*new_clean_entry_bitmap_size + new_data_csum_size); + else + memset(new_entry->bitmap, 0xff, 2*new_clean_entry_bitmap_size); + uint32_t *new_entry_csum = (uint32_t*)(((uint8_t*)new_entry) + new_clean_entry_size - 4); + *new_entry_csum = crc32c(0, new_entry, new_clean_entry_size - 4); + } + }, + true, true ); - if (r != 0) - { - free(new_meta_buf); - new_meta_buf = NULL; - return r; - } - return 0; + return r; } int disk_tool_t::resize_write_new_meta() @@ -529,11 +740,60 @@ int disk_tool_t::resize_write_new_meta() return 1; } lseek64(new_meta_fd, new_meta_offset, 0); - write_blocking(new_meta_fd, new_meta_buf, new_meta_len); + if (new_meta_buf) + { + write_blocking(new_meta_fd, new_meta_buf, new_meta_len); + } + else + { + assert(new_heap); + uint32_t new_meta_blocks = new_meta_len / dsk.meta_block_size - 1; + uint8_t *zero_block = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, dsk.meta_block_size); + memset(zero_block, 0, dsk.meta_block_size); + std::vector iov; + iov.reserve(IOV_MAX); + iov.push_back((iovec){ .iov_base = new_meta_hdr, .iov_len = dsk.meta_block_size }); + for (uint32_t i = 0; i < new_meta_blocks; i++) + { + uint8_t *data = new_heap->get_meta_block(i); + iov.push_back((iovec){ .iov_base = data ? data : zero_block, .iov_len = dsk.meta_block_size }); + if (iov.size() >= IOV_MAX) + { + writev_blocking(new_meta_fd, iov.data(), iov.size()); + iov.clear(); + } + } + if (iov.size() > 0) + writev_blocking(new_meta_fd, iov.data(), iov.size()); + free(zero_block); + zero_block = NULL; + } fsync(new_meta_fd); close(new_meta_fd); new_meta_fd = -1; - free(new_meta_buf); - new_meta_buf = NULL; return 0; } + +void disk_tool_t::free_new_meta() +{ + if (new_heap) + { + delete new_heap; + new_heap = NULL; + } + if ((uint8_t*)new_meta_hdr != new_meta_buf) + { + free(new_meta_hdr); + new_meta_hdr = NULL; + } + if (new_meta_buf) + { + free(new_meta_buf); + new_meta_buf = NULL; + } + if (new_journal_buf) + { + free(new_journal_buf); + new_journal_buf = NULL; + } +} diff --git a/src/disk_tool/disk_tool_resize_auto.cpp b/src/disk_tool/disk_tool_resize_auto.cpp index 07a2e84d..e7c7dc63 100644 --- a/src/disk_tool/disk_tool_resize_auto.cpp +++ b/src/disk_tool/disk_tool_resize_auto.cpp @@ -82,8 +82,10 @@ int disk_tool_t::resize_data(std::string device) auto new_meta_device = move_options.find("new_meta_device") != move_options.end() ? move_options["new_meta_device"] : dsk.meta_device; // Calculate new data & meta offsets + if (!new_meta_len) + new_meta_len = (dsk.meta_format == BLOCKSTORE_META_FORMAT_HEAP ? dsk.min_meta_len*2 : dsk.min_meta_len); new_data_offset = 4096 + (new_journal_device == dsk.data_device ? new_journal_len : 0) + - (new_meta_device == dsk.data_device ? dsk.meta_area_size : 0); + (new_meta_device == dsk.data_device ? new_meta_len : 0); new_data_offset += ((dsk.data_offset-new_data_offset) % dsk.data_block_size); if (new_data_offset != dsk.data_offset) move_options["new_data_offset"] = std::to_string(new_data_offset); @@ -236,7 +238,7 @@ int disk_tool_t::resize_parse_move_meta(std::map & mov auto new_journal_device = move_options.find("new_journal_device") != move_options.end() ? move_options["new_journal_device"] : dsk.journal_device; move_options["new_meta_device"] = dsk.data_device; - move_options["new_meta_len"] = std::to_string(dsk.meta_area_size); + move_options["new_meta_len"] = std::to_string(new_meta_len); } else { @@ -246,7 +248,6 @@ int disk_tool_t::resize_parse_move_meta(std::map & mov std::string parent_dev = get_parent_device(real_dev); if (parent_dev == "") return 1; - uint64_t new_meta_len = 0; if (parent_dev == real_dev) { // whole disk - create partition diff --git a/src/test/test_heap.cpp b/src/test/test_heap.cpp index c011c9ce..4852334b 100644 --- a/src/test/test_heap.cpp +++ b/src/test/test_heap.cpp @@ -145,6 +145,9 @@ void _test_init(blockstore_disk_t & dsk, bool csum) if (csum) config["data_csum_type"] = "crc32c"; dsk.parse_config(config); + dsk.data_device = "data"; + dsk.meta_device = "meta"; + dsk.journal_device = "journal"; dsk.data_device_size = 1*1024*1024*1024; dsk.meta_device_size = 4*1024*1024; dsk.journal_device_size = 4*1024*1024; @@ -1236,6 +1239,7 @@ void test_alloc_buffer() heap.use_buffer_area(1, pos, 64*1024); assert(heap.get_buffer_area_used_space() == (i+1)*64*1024); assert(!heap.is_buffer_area_free(i*64*1024+4096, 4096)); + assert(heap.is_buffer_area_free(i*64*1024+4096, 0)); // zero length is always free if (i < 4096/64-1) assert(heap.is_buffer_area_free((i+1)*64*1024, 64*1024)); } @@ -1280,9 +1284,9 @@ void test_full_alloc() dsk.data_device_size = 8*1024*1024; dsk.meta_device_size = 5*4096; dsk.journal_device_size = 4*1024*1024; - dsk.data_fd = 0; - dsk.meta_fd = 1; - dsk.journal_fd = 2; + dsk.data_device = "data"; + dsk.meta_device = "meta"; + dsk.journal_device = "journal"; dsk.calc_lengths(); std::vector buffer_area(dsk.journal_device_size); @@ -1581,9 +1585,9 @@ void test_move() dsk.data_device_size = 8*1024*1024; dsk.meta_device_size = 5*4096; dsk.journal_device_size = 4*1024*1024; - dsk.data_fd = 0; - dsk.meta_fd = 1; - dsk.journal_fd = 2; + dsk.data_device = "data"; + dsk.meta_device = "meta"; + dsk.journal_device = "journal"; dsk.calc_lengths(); std::vector buffer_area(dsk.journal_device_size); diff --git a/tests/test_resize.sh b/tests/test_resize.sh index 8b75d742..d3b2c99e 100755 --- a/tests/test_resize.sh +++ b/tests/test_resize.sh @@ -25,27 +25,34 @@ done for i in $(seq 1 $OSD_COUNT); do offsets=$(build/src/disk_tool/vitastor-disk simple-offsets --format json ./testdata/bin/test_osd$i.bin) + opts=$(build/src/disk_tool/vitastor-disk simple-offsets --format options ./testdata/bin/test_osd$i.bin) meta_format=$(echo $offsets | jq -r .meta_format) meta_offset=$(echo $offsets | jq -r .meta_offset) data_offset=$(echo $offsets | jq -r .data_offset) - build/src/disk_tool/vitastor-disk dump-journal --io cached --json ./testdata/bin/test_osd$i.bin 4096 0 $meta_offset >./testdata/journal_before_resize.json - build/src/disk_tool/vitastor-disk dump-meta --io cached ./testdata/bin/test_osd$i.bin 4096 $meta_offset $((data_offset-meta_offset)) >./testdata/meta_before_resize.json + #build/src/disk_tool/vitastor-disk dump-journal --io cached --json ./testdata/bin/test_osd$i.bin 4096 0 $meta_offset >./testdata/journal_before_resize.json + #build/src/disk_tool/vitastor-disk dump-meta --io cached ./testdata/bin/test_osd$i.bin 4096 $meta_offset $((data_offset-meta_offset)) >./testdata/meta_before_resize.json + build/src/disk_tool/vitastor-disk dump-meta --io cached $opts >./testdata/meta_before_resize.json + new_data_offset=$((128*1024*1024+data_offset%131072)) build/src/disk_tool/vitastor-disk raw-resize --io cached \ - $(build/src/disk_tool/vitastor-disk simple-offsets --format options ./testdata/bin/test_osd$i.bin 2>/dev/null) \ + $opts \ --new_meta_offset 0 \ --new_meta_len $((1024*1024)) \ --new_journal_offset $((1024*1024)) \ - --new_data_offset $((128*1024*1024+32768)) - build/src/disk_tool/vitastor-disk dump-journal --io cached --json ./testdata/bin/test_osd$i.bin 4096 $((1024*1024)) $((127*1024*1024)) >./testdata/journal_after_resize.json - build/src/disk_tool/vitastor-disk dump-meta --io cached ./testdata/bin/test_osd$i.bin 4096 0 $((1024*1024)) >./testdata/meta_after_resize.json + --new_data_offset $new_data_offset + #build/src/disk_tool/vitastor-disk dump-journal --io cached --json ./testdata/bin/test_osd$i.bin 4096 $((1024*1024)) $((127*1024*1024)) >./testdata/journal_after_resize.json + build/src/disk_tool/vitastor-disk dump-meta --io cached $opts \ + --meta_offset 0 \ + --meta_len $((1024*1024)) \ + --journal_offset $((1024*1024)) \ + --data_offset $new_data_offset >./testdata/meta_after_resize.json if ! (cat ./testdata/meta_before_resize.json ./testdata/meta_after_resize.json | \ - jq -e -s 'map([ .entries[] | del(.block) ] | sort_by(.pool, .inode, .stripe)) | .[0] == .[1] and (.[0] | length) > 1000'); then + jq -e -s 'map([ .entries[] | del(.block, .writes[].location) ] | sort_by(.pool, .inode, .stripe)) | .[0] == .[1] and (.[0] | length) > 1000'); then format_error "OSD $i metadata corrupted after resizing" fi - if ! (cat ./testdata/journal_before_resize.json ./testdata/journal_after_resize.json | \ - jq -e -s 'map([ .[] | del(.crc32, .crc32_prev, .valid, .loc, .start) ]) | .[0] == .[1] and (.[0] | length) > 1'); then - format_error "OSD $i journal corrupted after resizing" - fi + #if ! (cat ./testdata/journal_before_resize.json ./testdata/journal_after_resize.json | \ + # jq -e -s 'map([ .[] | del(.crc32, .crc32_prev, .valid, .loc, .start) ]) | .[0] == .[1] and (.[0] | length) > 1'); then + # format_error "OSD $i journal corrupted after resizing" + #fi done $ETCDCTL del --prefix /vitastor/osd/state/ @@ -56,7 +63,7 @@ for i in $(seq 1 $OSD_COUNT); do --data_device ./testdata/bin/test_osd$i.bin \ --meta_offset 0 \ --journal_offset $((1024*1024)) \ - --data_offset $((128*1024*1024+32768)) >>./testdata/osd$i.log 2>&1 & + --data_offset $new_data_offset >>./testdata/osd$i.log 2>&1 & eval OSD${i}_PID=$! done diff --git a/tests/test_resize_auto.sh b/tests/test_resize_auto.sh index 55afd46e..f905e271 100755 --- a/tests/test_resize_auto.sh +++ b/tests/test_resize_auto.sh @@ -15,7 +15,7 @@ trap "kill -9 $(jobs -p) || true; sudo losetup -d $LOOP1 $LOOP2"' || true' EXIT # also test prepare --hybrid :) # non-vitastor random type UUID to prevent udev activation mount | grep '/dev type devtmpfs' || sudo mount udev /dev/ -t devtmpfs -sudo build/src/disk_tool/vitastor-disk-test prepare --no_init 1 --meta_reserve 1x,1M \ +sudo build/src/disk_tool/vitastor-disk-test prepare --meta_format 2 --no_init 1 --meta_reserve 1x,1M \ --block_size 131072 --osd_num 987654 --part_type_uuid 0df42ae0-3695-4395-a957-7d5ff3645c56 \ --hybrid --fast-devices $LOOP2 $LOOP1