Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Matthew Sakai | 5898 | 98.83% | 2 | 20.00% |
Mike Snitzer | 66 | 1.11% | 6 | 60.00% |
Bruce Johnston | 3 | 0.05% | 1 | 10.00% |
Jiapeng Chong | 1 | 0.02% | 1 | 10.00% |
Total | 5968 | 10 |
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2023 Red Hat */ #include "encodings.h" #include <linux/log2.h> #include "logger.h" #include "memory-alloc.h" #include "permassert.h" #include "constants.h" #include "status-codes.h" #include "types.h" /** The maximum logical space is 4 petabytes, which is 1 terablock. */ static const block_count_t MAXIMUM_VDO_LOGICAL_BLOCKS = 1024ULL * 1024 * 1024 * 1024; /** The maximum physical space is 256 terabytes, which is 64 gigablocks. */ static const block_count_t MAXIMUM_VDO_PHYSICAL_BLOCKS = 1024ULL * 1024 * 1024 * 64; struct geometry_block { char magic_number[VDO_GEOMETRY_MAGIC_NUMBER_SIZE]; struct packed_header header; u32 checksum; } __packed; static const struct header GEOMETRY_BLOCK_HEADER_5_0 = { .id = VDO_GEOMETRY_BLOCK, .version = { .major_version = 5, .minor_version = 0, }, /* * Note: this size isn't just the payload size following the header, like it is everywhere * else in VDO. */ .size = sizeof(struct geometry_block) + sizeof(struct volume_geometry), }; static const struct header GEOMETRY_BLOCK_HEADER_4_0 = { .id = VDO_GEOMETRY_BLOCK, .version = { .major_version = 4, .minor_version = 0, }, /* * Note: this size isn't just the payload size following the header, like it is everywhere * else in VDO. */ .size = sizeof(struct geometry_block) + sizeof(struct volume_geometry_4_0), }; const u8 VDO_GEOMETRY_MAGIC_NUMBER[VDO_GEOMETRY_MAGIC_NUMBER_SIZE + 1] = "dmvdo001"; #define PAGE_HEADER_4_1_SIZE (8 + 8 + 8 + 1 + 1 + 1 + 1) static const struct version_number BLOCK_MAP_4_1 = { .major_version = 4, .minor_version = 1, }; const struct header VDO_BLOCK_MAP_HEADER_2_0 = { .id = VDO_BLOCK_MAP, .version = { .major_version = 2, .minor_version = 0, }, .size = sizeof(struct block_map_state_2_0), }; const struct header VDO_RECOVERY_JOURNAL_HEADER_7_0 = { .id = VDO_RECOVERY_JOURNAL, .version = { .major_version = 7, .minor_version = 0, }, .size = sizeof(struct recovery_journal_state_7_0), }; const struct header VDO_SLAB_DEPOT_HEADER_2_0 = { .id = VDO_SLAB_DEPOT, .version = { .major_version = 2, .minor_version = 0, }, .size = sizeof(struct slab_depot_state_2_0), }; static const struct header VDO_LAYOUT_HEADER_3_0 = { .id = VDO_LAYOUT, .version = { .major_version = 3, .minor_version = 0, }, .size = sizeof(struct layout_3_0) + (sizeof(struct partition_3_0) * VDO_PARTITION_COUNT), }; static const enum partition_id REQUIRED_PARTITIONS[] = { VDO_BLOCK_MAP_PARTITION, VDO_SLAB_DEPOT_PARTITION, VDO_RECOVERY_JOURNAL_PARTITION, VDO_SLAB_SUMMARY_PARTITION, }; /* * The current version for the data encoded in the super block. This must be changed any time there * is a change to encoding of the component data of any VDO component. */ static const struct version_number VDO_COMPONENT_DATA_41_0 = { .major_version = 41, .minor_version = 0, }; const struct version_number VDO_VOLUME_VERSION_67_0 = { .major_version = 67, .minor_version = 0, }; static const struct header SUPER_BLOCK_HEADER_12_0 = { .id = VDO_SUPER_BLOCK, .version = { .major_version = 12, .minor_version = 0, }, /* This is the minimum size, if the super block contains no components. */ .size = VDO_SUPER_BLOCK_FIXED_SIZE - VDO_ENCODED_HEADER_SIZE, }; /** * validate_version() - Check whether a version matches an expected version. * @expected_version: The expected version. * @actual_version: The version being validated. * @component_name: The name of the component or the calling function (for error logging). * * Logs an error describing a mismatch. * * Return: VDO_SUCCESS if the versions are the same, * VDO_UNSUPPORTED_VERSION if the versions don't match. */ static int __must_check validate_version(struct version_number expected_version, struct version_number actual_version, const char *component_name) { if (!vdo_are_same_version(expected_version, actual_version)) { return vdo_log_error_strerror(VDO_UNSUPPORTED_VERSION, "%s version mismatch, expected %d.%d, got %d.%d", component_name, expected_version.major_version, expected_version.minor_version, actual_version.major_version, actual_version.minor_version); } return VDO_SUCCESS; } /** * vdo_validate_header() - Check whether a header matches expectations. * @expected_header: The expected header. * @actual_header: The header being validated. * @exact_size: If true, the size fields of the two headers must be the same, otherwise it is * required that actual_header.size >= expected_header.size. * @name: The name of the component or the calling function (for error logging). * * Logs an error describing the first mismatch found. * * Return: VDO_SUCCESS if the header meets expectations, * VDO_INCORRECT_COMPONENT if the component ids don't match, * VDO_UNSUPPORTED_VERSION if the versions or sizes don't match. */ int vdo_validate_header(const struct header *expected_header, const struct header *actual_header, bool exact_size, const char *name) { int result; if (expected_header->id != actual_header->id) { return vdo_log_error_strerror(VDO_INCORRECT_COMPONENT, "%s ID mismatch, expected %d, got %d", name, expected_header->id, actual_header->id); } result = validate_version(expected_header->version, actual_header->version, name); if (result != VDO_SUCCESS) return result; if ((expected_header->size > actual_header->size) || (exact_size && (expected_header->size < actual_header->size))) { return vdo_log_error_strerror(VDO_UNSUPPORTED_VERSION, "%s size mismatch, expected %zu, got %zu", name, expected_header->size, actual_header->size); } return VDO_SUCCESS; } static void encode_version_number(u8 *buffer, size_t *offset, struct version_number version) { struct packed_version_number packed = vdo_pack_version_number(version); memcpy(buffer + *offset, &packed, sizeof(packed)); *offset += sizeof(packed); } void vdo_encode_header(u8 *buffer, size_t *offset, const struct header *header) { struct packed_header packed = vdo_pack_header(header); memcpy(buffer + *offset, &packed, sizeof(packed)); *offset += sizeof(packed); } static void decode_version_number(u8 *buffer, size_t *offset, struct version_number *version) { struct packed_version_number packed; memcpy(&packed, buffer + *offset, sizeof(packed)); *offset += sizeof(packed); *version = vdo_unpack_version_number(packed); } void vdo_decode_header(u8 *buffer, size_t *offset, struct header *header) { struct packed_header packed; memcpy(&packed, buffer + *offset, sizeof(packed)); *offset += sizeof(packed); *header = vdo_unpack_header(&packed); } /** * decode_volume_geometry() - Decode the on-disk representation of a volume geometry from a buffer. * @buffer: A buffer to decode from. * @offset: The offset in the buffer at which to decode. * @geometry: The structure to receive the decoded fields. * @version: The geometry block version to decode. */ static void decode_volume_geometry(u8 *buffer, size_t *offset, struct volume_geometry *geometry, u32 version) { u32 unused, mem; enum volume_region_id id; nonce_t nonce; block_count_t bio_offset = 0; bool sparse; /* This is for backwards compatibility. */ decode_u32_le(buffer, offset, &unused); geometry->unused = unused; decode_u64_le(buffer, offset, &nonce); geometry->nonce = nonce; memcpy((unsigned char *) &geometry->uuid, buffer + *offset, sizeof(uuid_t)); *offset += sizeof(uuid_t); if (version > 4) decode_u64_le(buffer, offset, &bio_offset); geometry->bio_offset = bio_offset; for (id = 0; id < VDO_VOLUME_REGION_COUNT; id++) { physical_block_number_t start_block; enum volume_region_id saved_id; decode_u32_le(buffer, offset, &saved_id); decode_u64_le(buffer, offset, &start_block); geometry->regions[id] = (struct volume_region) { .id = saved_id, .start_block = start_block, }; } decode_u32_le(buffer, offset, &mem); *offset += sizeof(u32); sparse = buffer[(*offset)++]; geometry->index_config = (struct index_config) { .mem = mem, .sparse = sparse, }; } /** * vdo_parse_geometry_block() - Decode and validate an encoded geometry block. * @block: The encoded geometry block. * @geometry: The structure to receive the decoded fields. */ int __must_check vdo_parse_geometry_block(u8 *block, struct volume_geometry *geometry) { u32 checksum, saved_checksum; struct header header; size_t offset = 0; int result; if (memcmp(block, VDO_GEOMETRY_MAGIC_NUMBER, VDO_GEOMETRY_MAGIC_NUMBER_SIZE) != 0) return VDO_BAD_MAGIC; offset += VDO_GEOMETRY_MAGIC_NUMBER_SIZE; vdo_decode_header(block, &offset, &header); if (header.version.major_version <= 4) { result = vdo_validate_header(&GEOMETRY_BLOCK_HEADER_4_0, &header, true, __func__); } else { result = vdo_validate_header(&GEOMETRY_BLOCK_HEADER_5_0, &header, true, __func__); } if (result != VDO_SUCCESS) return result; decode_volume_geometry(block, &offset, geometry, header.version.major_version); result = VDO_ASSERT(header.size == offset + sizeof(u32), "should have decoded up to the geometry checksum"); if (result != VDO_SUCCESS) return result; /* Decode and verify the checksum. */ checksum = vdo_crc32(block, offset); decode_u32_le(block, &offset, &saved_checksum); return ((checksum == saved_checksum) ? VDO_SUCCESS : VDO_CHECKSUM_MISMATCH); } struct block_map_page *vdo_format_block_map_page(void *buffer, nonce_t nonce, physical_block_number_t pbn, bool initialized) { struct block_map_page *page = buffer; memset(buffer, 0, VDO_BLOCK_SIZE); page->version = vdo_pack_version_number(BLOCK_MAP_4_1); page->header.nonce = __cpu_to_le64(nonce); page->header.pbn = __cpu_to_le64(pbn); page->header.initialized = initialized; return page; } enum block_map_page_validity vdo_validate_block_map_page(struct block_map_page *page, nonce_t nonce, physical_block_number_t pbn) { BUILD_BUG_ON(sizeof(struct block_map_page_header) != PAGE_HEADER_4_1_SIZE); if (!vdo_are_same_version(BLOCK_MAP_4_1, vdo_unpack_version_number(page->version)) || !page->header.initialized || (nonce != __le64_to_cpu(page->header.nonce))) return VDO_BLOCK_MAP_PAGE_INVALID; if (pbn != vdo_get_block_map_page_pbn(page)) return VDO_BLOCK_MAP_PAGE_BAD; return VDO_BLOCK_MAP_PAGE_VALID; } static int decode_block_map_state_2_0(u8 *buffer, size_t *offset, struct block_map_state_2_0 *state) { size_t initial_offset; block_count_t flat_page_count, root_count; physical_block_number_t flat_page_origin, root_origin; struct header header; int result; vdo_decode_header(buffer, offset, &header); result = vdo_validate_header(&VDO_BLOCK_MAP_HEADER_2_0, &header, true, __func__); if (result != VDO_SUCCESS) return result; initial_offset = *offset; decode_u64_le(buffer, offset, &flat_page_origin); result = VDO_ASSERT(flat_page_origin == VDO_BLOCK_MAP_FLAT_PAGE_ORIGIN, "Flat page origin must be %u (recorded as %llu)", VDO_BLOCK_MAP_FLAT_PAGE_ORIGIN, (unsigned long long) state->flat_page_origin); if (result != VDO_SUCCESS) return result; decode_u64_le(buffer, offset, &flat_page_count); result = VDO_ASSERT(flat_page_count == 0, "Flat page count must be 0 (recorded as %llu)", (unsigned long long) state->flat_page_count); if (result != VDO_SUCCESS) return result; decode_u64_le(buffer, offset, &root_origin); decode_u64_le(buffer, offset, &root_count); result = VDO_ASSERT(VDO_BLOCK_MAP_HEADER_2_0.size == *offset - initial_offset, "decoded block map component size must match header size"); if (result != VDO_SUCCESS) return result; *state = (struct block_map_state_2_0) { .flat_page_origin = flat_page_origin, .flat_page_count = flat_page_count, .root_origin = root_origin, .root_count = root_count, }; return VDO_SUCCESS; } static void encode_block_map_state_2_0(u8 *buffer, size_t *offset, struct block_map_state_2_0 state) { size_t initial_offset; vdo_encode_header(buffer, offset, &VDO_BLOCK_MAP_HEADER_2_0); initial_offset = *offset; encode_u64_le(buffer, offset, state.flat_page_origin); encode_u64_le(buffer, offset, state.flat_page_count); encode_u64_le(buffer, offset, state.root_origin); encode_u64_le(buffer, offset, state.root_count); VDO_ASSERT_LOG_ONLY(VDO_BLOCK_MAP_HEADER_2_0.size == *offset - initial_offset, "encoded block map component size must match header size"); } /** * vdo_compute_new_forest_pages() - Compute the number of pages which must be allocated at each * level in order to grow the forest to a new number of entries. * @entries: The new number of entries the block map must address. * * Return: The total number of non-leaf pages required. */ block_count_t vdo_compute_new_forest_pages(root_count_t root_count, struct boundary *old_sizes, block_count_t entries, struct boundary *new_sizes) { page_count_t leaf_pages = max(vdo_compute_block_map_page_count(entries), 1U); page_count_t level_size = DIV_ROUND_UP(leaf_pages, root_count); block_count_t total_pages = 0; height_t height; for (height = 0; height < VDO_BLOCK_MAP_TREE_HEIGHT; height++) { block_count_t new_pages; level_size = DIV_ROUND_UP(level_size, VDO_BLOCK_MAP_ENTRIES_PER_PAGE); new_sizes->levels[height] = level_size; new_pages = level_size; if (old_sizes != NULL) new_pages -= old_sizes->levels[height]; total_pages += (new_pages * root_count); } return total_pages; } /** * encode_recovery_journal_state_7_0() - Encode the state of a recovery journal. * * Return: VDO_SUCCESS or an error code. */ static void encode_recovery_journal_state_7_0(u8 *buffer, size_t *offset, struct recovery_journal_state_7_0 state) { size_t initial_offset; vdo_encode_header(buffer, offset, &VDO_RECOVERY_JOURNAL_HEADER_7_0); initial_offset = *offset; encode_u64_le(buffer, offset, state.journal_start); encode_u64_le(buffer, offset, state.logical_blocks_used); encode_u64_le(buffer, offset, state.block_map_data_blocks); VDO_ASSERT_LOG_ONLY(VDO_RECOVERY_JOURNAL_HEADER_7_0.size == *offset - initial_offset, "encoded recovery journal component size must match header size"); } /** * decode_recovery_journal_state_7_0() - Decode the state of a recovery journal saved in a buffer. * @buffer: The buffer containing the saved state. * @state: A pointer to a recovery journal state to hold the result of a successful decode. * * Return: VDO_SUCCESS or an error code. */ static int __must_check decode_recovery_journal_state_7_0(u8 *buffer, size_t *offset, struct recovery_journal_state_7_0 *state) { struct header header; int result; size_t initial_offset; sequence_number_t journal_start; block_count_t logical_blocks_used, block_map_data_blocks; vdo_decode_header(buffer, offset, &header); result = vdo_validate_header(&VDO_RECOVERY_JOURNAL_HEADER_7_0, &header, true, __func__); if (result != VDO_SUCCESS) return result; initial_offset = *offset; decode_u64_le(buffer, offset, &journal_start); decode_u64_le(buffer, offset, &logical_blocks_used); decode_u64_le(buffer, offset, &block_map_data_blocks); result = VDO_ASSERT(VDO_RECOVERY_JOURNAL_HEADER_7_0.size == *offset - initial_offset, "decoded recovery journal component size must match header size"); if (result != VDO_SUCCESS) return result; *state = (struct recovery_journal_state_7_0) { .journal_start = journal_start, .logical_blocks_used = logical_blocks_used, .block_map_data_blocks = block_map_data_blocks, }; return VDO_SUCCESS; } /** * vdo_get_journal_operation_name() - Get the name of a journal operation. * @operation: The operation to name. * * Return: The name of the operation. */ const char *vdo_get_journal_operation_name(enum journal_operation operation) { switch (operation) { case VDO_JOURNAL_DATA_REMAPPING: return "data remapping"; case VDO_JOURNAL_BLOCK_MAP_REMAPPING: return "block map remapping"; default: return "unknown journal operation"; } } /** * encode_slab_depot_state_2_0() - Encode the state of a slab depot into a buffer. */ static void encode_slab_depot_state_2_0(u8 *buffer, size_t *offset, struct slab_depot_state_2_0 state) { size_t initial_offset; vdo_encode_header(buffer, offset, &VDO_SLAB_DEPOT_HEADER_2_0); initial_offset = *offset; encode_u64_le(buffer, offset, state.slab_config.slab_blocks); encode_u64_le(buffer, offset, state.slab_config.data_blocks); encode_u64_le(buffer, offset, state.slab_config.reference_count_blocks); encode_u64_le(buffer, offset, state.slab_config.slab_journal_blocks); encode_u64_le(buffer, offset, state.slab_config.slab_journal_flushing_threshold); encode_u64_le(buffer, offset, state.slab_config.slab_journal_blocking_threshold); encode_u64_le(buffer, offset, state.slab_config.slab_journal_scrubbing_threshold); encode_u64_le(buffer, offset, state.first_block); encode_u64_le(buffer, offset, state.last_block); buffer[(*offset)++] = state.zone_count; VDO_ASSERT_LOG_ONLY(VDO_SLAB_DEPOT_HEADER_2_0.size == *offset - initial_offset, "encoded block map component size must match header size"); } /** * decode_slab_depot_state_2_0() - Decode slab depot component state version 2.0 from a buffer. * * Return: VDO_SUCCESS or an error code. */ static int decode_slab_depot_state_2_0(u8 *buffer, size_t *offset, struct slab_depot_state_2_0 *state) { struct header header; int result; size_t initial_offset; struct slab_config slab_config; block_count_t count; physical_block_number_t first_block, last_block; zone_count_t zone_count; vdo_decode_header(buffer, offset, &header); result = vdo_validate_header(&VDO_SLAB_DEPOT_HEADER_2_0, &header, true, __func__); if (result != VDO_SUCCESS) return result; initial_offset = *offset; decode_u64_le(buffer, offset, &count); slab_config.slab_blocks = count; decode_u64_le(buffer, offset, &count); slab_config.data_blocks = count; decode_u64_le(buffer, offset, &count); slab_config.reference_count_blocks = count; decode_u64_le(buffer, offset, &count); slab_config.slab_journal_blocks = count; decode_u64_le(buffer, offset, &count); slab_config.slab_journal_flushing_threshold = count; decode_u64_le(buffer, offset, &count); slab_config.slab_journal_blocking_threshold = count; decode_u64_le(buffer, offset, &count); slab_config.slab_journal_scrubbing_threshold = count; decode_u64_le(buffer, offset, &first_block); decode_u64_le(buffer, offset, &last_block); zone_count = buffer[(*offset)++]; result = VDO_ASSERT(VDO_SLAB_DEPOT_HEADER_2_0.size == *offset - initial_offset, "decoded slab depot component size must match header size"); if (result != VDO_SUCCESS) return result; *state = (struct slab_depot_state_2_0) { .slab_config = slab_config, .first_block = first_block, .last_block = last_block, .zone_count = zone_count, }; return VDO_SUCCESS; } /** * vdo_configure_slab_depot() - Configure the slab depot. * @partition: The slab depot partition * @slab_config: The configuration of a single slab. * @zone_count: The number of zones the depot will use. * @state: The state structure to be configured. * * Configures the slab_depot for the specified storage capacity, finding the number of data blocks * that will fit and still leave room for the depot metadata, then return the saved state for that * configuration. * * Return: VDO_SUCCESS or an error code. */ int vdo_configure_slab_depot(const struct partition *partition, struct slab_config slab_config, zone_count_t zone_count, struct slab_depot_state_2_0 *state) { block_count_t total_slab_blocks, total_data_blocks; size_t slab_count; physical_block_number_t last_block; block_count_t slab_size = slab_config.slab_blocks; vdo_log_debug("slabDepot %s(block_count=%llu, first_block=%llu, slab_size=%llu, zone_count=%u)", __func__, (unsigned long long) partition->count, (unsigned long long) partition->offset, (unsigned long long) slab_size, zone_count); /* We do not allow runt slabs, so we waste up to a slab's worth. */ slab_count = (partition->count / slab_size); if (slab_count == 0) return VDO_NO_SPACE; if (slab_count > MAX_VDO_SLABS) return VDO_TOO_MANY_SLABS; total_slab_blocks = slab_count * slab_config.slab_blocks; total_data_blocks = slab_count * slab_config.data_blocks; last_block = partition->offset + total_slab_blocks; *state = (struct slab_depot_state_2_0) { .slab_config = slab_config, .first_block = partition->offset, .last_block = last_block, .zone_count = zone_count, }; vdo_log_debug("slab_depot last_block=%llu, total_data_blocks=%llu, slab_count=%zu, left_over=%llu", (unsigned long long) last_block, (unsigned long long) total_data_blocks, slab_count, (unsigned long long) (partition->count - (last_block - partition->offset))); return VDO_SUCCESS; } /** * vdo_configure_slab() - Measure and initialize the configuration to use for each slab. * @slab_size: The number of blocks per slab. * @slab_journal_blocks: The number of blocks for the slab journal. * @slab_config: The slab configuration to initialize. * * Return: VDO_SUCCESS or an error code. */ int vdo_configure_slab(block_count_t slab_size, block_count_t slab_journal_blocks, struct slab_config *slab_config) { block_count_t ref_blocks, meta_blocks, data_blocks; block_count_t flushing_threshold, remaining, blocking_threshold; block_count_t minimal_extra_space, scrubbing_threshold; if (slab_journal_blocks >= slab_size) return VDO_BAD_CONFIGURATION; /* * This calculation should technically be a recurrence, but the total number of metadata * blocks is currently less than a single block of ref_counts, so we'd gain at most one * data block in each slab with more iteration. */ ref_blocks = vdo_get_saved_reference_count_size(slab_size - slab_journal_blocks); meta_blocks = (ref_blocks + slab_journal_blocks); /* Make sure test code hasn't configured slabs to be too small. */ if (meta_blocks >= slab_size) return VDO_BAD_CONFIGURATION; /* * If the slab size is very small, assume this must be a unit test and override the number * of data blocks to be a power of two (wasting blocks in the slab). Many tests need their * data_blocks fields to be the exact capacity of the configured volume, and that used to * fall out since they use a power of two for the number of data blocks, the slab size was * a power of two, and every block in a slab was a data block. * * TODO: Try to figure out some way of structuring testParameters and unit tests so this * hack isn't needed without having to edit several unit tests every time the metadata size * changes by one block. */ data_blocks = slab_size - meta_blocks; if ((slab_size < 1024) && !is_power_of_2(data_blocks)) data_blocks = ((block_count_t) 1 << ilog2(data_blocks)); /* * Configure the slab journal thresholds. The flush threshold is 168 of 224 blocks in * production, or 3/4ths, so we use this ratio for all sizes. */ flushing_threshold = ((slab_journal_blocks * 3) + 3) / 4; /* * The blocking threshold should be far enough from the flushing threshold to not produce * delays, but far enough from the end of the journal to allow multiple successive recovery * failures. */ remaining = slab_journal_blocks - flushing_threshold; blocking_threshold = flushing_threshold + ((remaining * 5) / 7); /* The scrubbing threshold should be at least 2048 entries before the end of the journal. */ minimal_extra_space = 1 + (MAXIMUM_VDO_USER_VIOS / VDO_SLAB_JOURNAL_FULL_ENTRIES_PER_BLOCK); scrubbing_threshold = blocking_threshold; if (slab_journal_blocks > minimal_extra_space) scrubbing_threshold = slab_journal_blocks - minimal_extra_space; if (blocking_threshold > scrubbing_threshold) blocking_threshold = scrubbing_threshold; *slab_config = (struct slab_config) { .slab_blocks = slab_size, .data_blocks = data_blocks, .reference_count_blocks = ref_blocks, .slab_journal_blocks = slab_journal_blocks, .slab_journal_flushing_threshold = flushing_threshold, .slab_journal_blocking_threshold = blocking_threshold, .slab_journal_scrubbing_threshold = scrubbing_threshold}; return VDO_SUCCESS; } /** * vdo_decode_slab_journal_entry() - Decode a slab journal entry. * @block: The journal block holding the entry. * @entry_count: The number of the entry. * * Return: The decoded entry. */ struct slab_journal_entry vdo_decode_slab_journal_entry(struct packed_slab_journal_block *block, journal_entry_count_t entry_count) { struct slab_journal_entry entry = vdo_unpack_slab_journal_entry(&block->payload.entries[entry_count]); if (block->header.has_block_map_increments && ((block->payload.full_entries.entry_types[entry_count / 8] & ((u8) 1 << (entry_count % 8))) != 0)) entry.operation = VDO_JOURNAL_BLOCK_MAP_REMAPPING; return entry; } /** * allocate_partition() - Allocate a partition and add it to a layout. * @layout: The layout containing the partition. * @id: The id of the partition. * @offset: The offset into the layout at which the partition begins. * @size: The size of the partition in blocks. * * Return: VDO_SUCCESS or an error. */ static int allocate_partition(struct layout *layout, u8 id, physical_block_number_t offset, block_count_t size) { struct partition *partition; int result; result = vdo_allocate(1, struct partition, __func__, &partition); if (result != VDO_SUCCESS) return result; partition->id = id; partition->offset = offset; partition->count = size; partition->next = layout->head; layout->head = partition; return VDO_SUCCESS; } /** * make_partition() - Create a new partition from the beginning or end of the unused space in a * layout. * @layout: The layout. * @id: The id of the partition to make. * @size: The number of blocks to carve out; if 0, all remaining space will be used. * @beginning: True if the partition should start at the beginning of the unused space. * * Return: A success or error code, particularly VDO_NO_SPACE if there are fewer than size blocks * remaining. */ static int __must_check make_partition(struct layout *layout, enum partition_id id, block_count_t size, bool beginning) { int result; physical_block_number_t offset; block_count_t free_blocks = layout->last_free - layout->first_free; if (size == 0) { if (free_blocks == 0) return VDO_NO_SPACE; size = free_blocks; } else if (size > free_blocks) { return VDO_NO_SPACE; } result = vdo_get_partition(layout, id, NULL); if (result != VDO_UNKNOWN_PARTITION) return VDO_PARTITION_EXISTS; offset = beginning ? layout->first_free : (layout->last_free - size); result = allocate_partition(layout, id, offset, size); if (result != VDO_SUCCESS) return result; layout->num_partitions++; if (beginning) layout->first_free += size; else layout->last_free = layout->last_free - size; return VDO_SUCCESS; } /** * vdo_initialize_layout() - Lay out the partitions of a vdo. * @size: The entire size of the vdo. * @origin: The start of the layout on the underlying storage in blocks. * @block_map_blocks: The size of the block map partition. * @journal_blocks: The size of the journal partition. * @summary_blocks: The size of the slab summary partition. * @layout: The layout to initialize. * * Return: VDO_SUCCESS or an error. */ int vdo_initialize_layout(block_count_t size, physical_block_number_t offset, block_count_t block_map_blocks, block_count_t journal_blocks, block_count_t summary_blocks, struct layout *layout) { int result; block_count_t necessary_size = (offset + block_map_blocks + journal_blocks + summary_blocks); if (necessary_size > size) return vdo_log_error_strerror(VDO_NO_SPACE, "Not enough space to make a VDO"); *layout = (struct layout) { .start = offset, .size = size, .first_free = offset, .last_free = size, .num_partitions = 0, .head = NULL, }; result = make_partition(layout, VDO_BLOCK_MAP_PARTITION, block_map_blocks, true); if (result != VDO_SUCCESS) { vdo_uninitialize_layout(layout); return result; } result = make_partition(layout, VDO_SLAB_SUMMARY_PARTITION, summary_blocks, false); if (result != VDO_SUCCESS) { vdo_uninitialize_layout(layout); return result; } result = make_partition(layout, VDO_RECOVERY_JOURNAL_PARTITION, journal_blocks, false); if (result != VDO_SUCCESS) { vdo_uninitialize_layout(layout); return result; } result = make_partition(layout, VDO_SLAB_DEPOT_PARTITION, 0, true); if (result != VDO_SUCCESS) vdo_uninitialize_layout(layout); return result; } /** * vdo_uninitialize_layout() - Clean up a layout. * @layout: The layout to clean up. * * All partitions created by this layout become invalid pointers. */ void vdo_uninitialize_layout(struct layout *layout) { while (layout->head != NULL) { struct partition *part = layout->head; layout->head = part->next; vdo_free(part); } memset(layout, 0, sizeof(struct layout)); } /** * vdo_get_partition() - Get a partition by id. * @layout: The layout from which to get a partition. * @id: The id of the partition. * @partition_ptr: A pointer to hold the partition. * * Return: VDO_SUCCESS or an error. */ int vdo_get_partition(struct layout *layout, enum partition_id id, struct partition **partition_ptr) { struct partition *partition; for (partition = layout->head; partition != NULL; partition = partition->next) { if (partition->id == id) { if (partition_ptr != NULL) *partition_ptr = partition; return VDO_SUCCESS; } } return VDO_UNKNOWN_PARTITION; } /** * vdo_get_known_partition() - Get a partition by id from a validated layout. * @layout: The layout from which to get a partition. * @id: The id of the partition. * * Return: the partition */ struct partition *vdo_get_known_partition(struct layout *layout, enum partition_id id) { struct partition *partition; int result = vdo_get_partition(layout, id, &partition); VDO_ASSERT_LOG_ONLY(result == VDO_SUCCESS, "layout has expected partition: %u", id); return partition; } static void encode_layout(u8 *buffer, size_t *offset, const struct layout *layout) { const struct partition *partition; size_t initial_offset; struct header header = VDO_LAYOUT_HEADER_3_0; BUILD_BUG_ON(sizeof(enum partition_id) != sizeof(u8)); VDO_ASSERT_LOG_ONLY(layout->num_partitions <= U8_MAX, "layout partition count must fit in a byte"); vdo_encode_header(buffer, offset, &header); initial_offset = *offset; encode_u64_le(buffer, offset, layout->first_free); encode_u64_le(buffer, offset, layout->last_free); buffer[(*offset)++] = layout->num_partitions; VDO_ASSERT_LOG_ONLY(sizeof(struct layout_3_0) == *offset - initial_offset, "encoded size of a layout header must match structure"); for (partition = layout->head; partition != NULL; partition = partition->next) { buffer[(*offset)++] = partition->id; encode_u64_le(buffer, offset, partition->offset); /* This field only exists for backwards compatibility */ encode_u64_le(buffer, offset, 0); encode_u64_le(buffer, offset, partition->count); } VDO_ASSERT_LOG_ONLY(header.size == *offset - initial_offset, "encoded size of a layout must match header size"); } static int decode_layout(u8 *buffer, size_t *offset, physical_block_number_t start, block_count_t size, struct layout *layout) { struct header header; struct layout_3_0 layout_header; struct partition *partition; size_t initial_offset; physical_block_number_t first_free, last_free; u8 partition_count; u8 i; int result; vdo_decode_header(buffer, offset, &header); /* Layout is variable size, so only do a minimum size check here. */ result = vdo_validate_header(&VDO_LAYOUT_HEADER_3_0, &header, false, __func__); if (result != VDO_SUCCESS) return result; initial_offset = *offset; decode_u64_le(buffer, offset, &first_free); decode_u64_le(buffer, offset, &last_free); partition_count = buffer[(*offset)++]; layout_header = (struct layout_3_0) { .first_free = first_free, .last_free = last_free, .partition_count = partition_count, }; result = VDO_ASSERT(sizeof(struct layout_3_0) == *offset - initial_offset, "decoded size of a layout header must match structure"); if (result != VDO_SUCCESS) return result; layout->start = start; layout->size = size; layout->first_free = layout_header.first_free; layout->last_free = layout_header.last_free; layout->num_partitions = layout_header.partition_count; if (layout->num_partitions > VDO_PARTITION_COUNT) { return vdo_log_error_strerror(VDO_UNKNOWN_PARTITION, "layout has extra partitions"); } for (i = 0; i < layout->num_partitions; i++) { u8 id; u64 partition_offset, count; id = buffer[(*offset)++]; decode_u64_le(buffer, offset, &partition_offset); *offset += sizeof(u64); decode_u64_le(buffer, offset, &count); result = allocate_partition(layout, id, partition_offset, count); if (result != VDO_SUCCESS) { vdo_uninitialize_layout(layout); return result; } } /* Validate that the layout has all (and only) the required partitions */ for (i = 0; i < VDO_PARTITION_COUNT; i++) { result = vdo_get_partition(layout, REQUIRED_PARTITIONS[i], &partition); if (result != VDO_SUCCESS) { vdo_uninitialize_layout(layout); return vdo_log_error_strerror(result, "layout is missing required partition %u", REQUIRED_PARTITIONS[i]); } start += partition->count; } if (start != size) { vdo_uninitialize_layout(layout); return vdo_log_error_strerror(UDS_BAD_STATE, "partitions do not cover the layout"); } return VDO_SUCCESS; } /** * pack_vdo_config() - Convert a vdo_config to its packed on-disk representation. * @config: The vdo config to convert. * * Return: The platform-independent representation of the config. */ static struct packed_vdo_config pack_vdo_config(struct vdo_config config) { return (struct packed_vdo_config) { .logical_blocks = __cpu_to_le64(config.logical_blocks), .physical_blocks = __cpu_to_le64(config.physical_blocks), .slab_size = __cpu_to_le64(config.slab_size), .recovery_journal_size = __cpu_to_le64(config.recovery_journal_size), .slab_journal_blocks = __cpu_to_le64(config.slab_journal_blocks), }; } /** * pack_vdo_component() - Convert a vdo_component to its packed on-disk representation. * @component: The VDO component data to convert. * * Return: The platform-independent representation of the component. */ static struct packed_vdo_component_41_0 pack_vdo_component(const struct vdo_component component) { return (struct packed_vdo_component_41_0) { .state = __cpu_to_le32(component.state), .complete_recoveries = __cpu_to_le64(component.complete_recoveries), .read_only_recoveries = __cpu_to_le64(component.read_only_recoveries), .config = pack_vdo_config(component.config), .nonce = __cpu_to_le64(component.nonce), }; } static void encode_vdo_component(u8 *buffer, size_t *offset, struct vdo_component component) { struct packed_vdo_component_41_0 packed; encode_version_number(buffer, offset, VDO_COMPONENT_DATA_41_0); packed = pack_vdo_component(component); memcpy(buffer + *offset, &packed, sizeof(packed)); *offset += sizeof(packed); } /** * unpack_vdo_config() - Convert a packed_vdo_config to its native in-memory representation. * @config: The packed vdo config to convert. * * Return: The native in-memory representation of the vdo config. */ static struct vdo_config unpack_vdo_config(struct packed_vdo_config config) { return (struct vdo_config) { .logical_blocks = __le64_to_cpu(config.logical_blocks), .physical_blocks = __le64_to_cpu(config.physical_blocks), .slab_size = __le64_to_cpu(config.slab_size), .recovery_journal_size = __le64_to_cpu(config.recovery_journal_size), .slab_journal_blocks = __le64_to_cpu(config.slab_journal_blocks), }; } /** * unpack_vdo_component_41_0() - Convert a packed_vdo_component_41_0 to its native in-memory * representation. * @component: The packed vdo component data to convert. * * Return: The native in-memory representation of the component. */ static struct vdo_component unpack_vdo_component_41_0(struct packed_vdo_component_41_0 component) { return (struct vdo_component) { .state = __le32_to_cpu(component.state), .complete_recoveries = __le64_to_cpu(component.complete_recoveries), .read_only_recoveries = __le64_to_cpu(component.read_only_recoveries), .config = unpack_vdo_config(component.config), .nonce = __le64_to_cpu(component.nonce), }; } /** * decode_vdo_component() - Decode the component data for the vdo itself out of the super block. * * Return: VDO_SUCCESS or an error. */ static int decode_vdo_component(u8 *buffer, size_t *offset, struct vdo_component *component) { struct version_number version; struct packed_vdo_component_41_0 packed; int result; decode_version_number(buffer, offset, &version); result = validate_version(version, VDO_COMPONENT_DATA_41_0, "VDO component data"); if (result != VDO_SUCCESS) return result; memcpy(&packed, buffer + *offset, sizeof(packed)); *offset += sizeof(packed); *component = unpack_vdo_component_41_0(packed); return VDO_SUCCESS; } /** * vdo_validate_config() - Validate constraints on a VDO config. * @config: The VDO config. * @physical_block_count: The minimum block count of the underlying storage. * @logical_block_count: The expected logical size of the VDO, or 0 if the logical size may be * unspecified. * * Return: A success or error code. */ int vdo_validate_config(const struct vdo_config *config, block_count_t physical_block_count, block_count_t logical_block_count) { struct slab_config slab_config; int result; result = VDO_ASSERT(config->slab_size > 0, "slab size unspecified"); if (result != VDO_SUCCESS) return result; result = VDO_ASSERT(is_power_of_2(config->slab_size), "slab size must be a power of two"); if (result != VDO_SUCCESS) return result; result = VDO_ASSERT(config->slab_size <= (1 << MAX_VDO_SLAB_BITS), "slab size must be less than or equal to 2^%d", MAX_VDO_SLAB_BITS); if (result != VDO_SUCCESS) return result; result = VDO_ASSERT(config->slab_journal_blocks >= MINIMUM_VDO_SLAB_JOURNAL_BLOCKS, "slab journal size meets minimum size"); if (result != VDO_SUCCESS) return result; result = VDO_ASSERT(config->slab_journal_blocks <= config->slab_size, "slab journal size is within expected bound"); if (result != VDO_SUCCESS) return result; result = vdo_configure_slab(config->slab_size, config->slab_journal_blocks, &slab_config); if (result != VDO_SUCCESS) return result; result = VDO_ASSERT((slab_config.data_blocks >= 1), "slab must be able to hold at least one block"); if (result != VDO_SUCCESS) return result; result = VDO_ASSERT(config->physical_blocks > 0, "physical blocks unspecified"); if (result != VDO_SUCCESS) return result; result = VDO_ASSERT(config->physical_blocks <= MAXIMUM_VDO_PHYSICAL_BLOCKS, "physical block count %llu exceeds maximum %llu", (unsigned long long) config->physical_blocks, (unsigned long long) MAXIMUM_VDO_PHYSICAL_BLOCKS); if (result != VDO_SUCCESS) return VDO_OUT_OF_RANGE; if (physical_block_count != config->physical_blocks) { vdo_log_error("A physical size of %llu blocks was specified, not the %llu blocks configured in the vdo super block", (unsigned long long) physical_block_count, (unsigned long long) config->physical_blocks); return VDO_PARAMETER_MISMATCH; } if (logical_block_count > 0) { result = VDO_ASSERT((config->logical_blocks > 0), "logical blocks unspecified"); if (result != VDO_SUCCESS) return result; if (logical_block_count != config->logical_blocks) { vdo_log_error("A logical size of %llu blocks was specified, but that differs from the %llu blocks configured in the vdo super block", (unsigned long long) logical_block_count, (unsigned long long) config->logical_blocks); return VDO_PARAMETER_MISMATCH; } } result = VDO_ASSERT(config->logical_blocks <= MAXIMUM_VDO_LOGICAL_BLOCKS, "logical blocks too large"); if (result != VDO_SUCCESS) return result; result = VDO_ASSERT(config->recovery_journal_size > 0, "recovery journal size unspecified"); if (result != VDO_SUCCESS) return result; result = VDO_ASSERT(is_power_of_2(config->recovery_journal_size), "recovery journal size must be a power of two"); if (result != VDO_SUCCESS) return result; return result; } /** * vdo_destroy_component_states() - Clean up any allocations in a vdo_component_states. * @states: The component states to destroy. */ void vdo_destroy_component_states(struct vdo_component_states *states) { if (states == NULL) return; vdo_uninitialize_layout(&states->layout); } /** * decode_components() - Decode the components now that we know the component data is a version we * understand. * @buffer: The buffer being decoded. * @offset: The offset to start decoding from. * @geometry: The vdo geometry * @states: An object to hold the successfully decoded state. * * Return: VDO_SUCCESS or an error. */ static int __must_check decode_components(u8 *buffer, size_t *offset, struct volume_geometry *geometry, struct vdo_component_states *states) { int result; decode_vdo_component(buffer, offset, &states->vdo); result = decode_layout(buffer, offset, vdo_get_data_region_start(*geometry) + 1, states->vdo.config.physical_blocks, &states->layout); if (result != VDO_SUCCESS) return result; result = decode_recovery_journal_state_7_0(buffer, offset, &states->recovery_journal); if (result != VDO_SUCCESS) return result; result = decode_slab_depot_state_2_0(buffer, offset, &states->slab_depot); if (result != VDO_SUCCESS) return result; result = decode_block_map_state_2_0(buffer, offset, &states->block_map); if (result != VDO_SUCCESS) return result; VDO_ASSERT_LOG_ONLY(*offset == VDO_COMPONENT_DATA_OFFSET + VDO_COMPONENT_DATA_SIZE, "All decoded component data was used"); return VDO_SUCCESS; } /** * vdo_decode_component_states() - Decode the payload of a super block. * @buffer: The buffer containing the encoded super block contents. * @geometry: The vdo geometry * @states: A pointer to hold the decoded states. * * Return: VDO_SUCCESS or an error. */ int vdo_decode_component_states(u8 *buffer, struct volume_geometry *geometry, struct vdo_component_states *states) { int result; size_t offset = VDO_COMPONENT_DATA_OFFSET; /* This is for backwards compatibility. */ decode_u32_le(buffer, &offset, &states->unused); /* Check the VDO volume version */ decode_version_number(buffer, &offset, &states->volume_version); result = validate_version(VDO_VOLUME_VERSION_67_0, states->volume_version, "volume"); if (result != VDO_SUCCESS) return result; result = decode_components(buffer, &offset, geometry, states); if (result != VDO_SUCCESS) vdo_uninitialize_layout(&states->layout); return result; } /** * vdo_validate_component_states() - Validate the decoded super block configuration. * @states: The state decoded from the super block. * @geometry_nonce: The nonce from the geometry block. * @physical_size: The minimum block count of the underlying storage. * @logical_size: The expected logical size of the VDO, or 0 if the logical size may be * unspecified. * * Return: VDO_SUCCESS or an error if the configuration is invalid. */ int vdo_validate_component_states(struct vdo_component_states *states, nonce_t geometry_nonce, block_count_t physical_size, block_count_t logical_size) { if (geometry_nonce != states->vdo.nonce) { return vdo_log_error_strerror(VDO_BAD_NONCE, "Geometry nonce %llu does not match superblock nonce %llu", (unsigned long long) geometry_nonce, (unsigned long long) states->vdo.nonce); } return vdo_validate_config(&states->vdo.config, physical_size, logical_size); } /** * vdo_encode_component_states() - Encode the state of all vdo components in the super block. */ static void vdo_encode_component_states(u8 *buffer, size_t *offset, const struct vdo_component_states *states) { /* This is for backwards compatibility. */ encode_u32_le(buffer, offset, states->unused); encode_version_number(buffer, offset, states->volume_version); encode_vdo_component(buffer, offset, states->vdo); encode_layout(buffer, offset, &states->layout); encode_recovery_journal_state_7_0(buffer, offset, states->recovery_journal); encode_slab_depot_state_2_0(buffer, offset, states->slab_depot); encode_block_map_state_2_0(buffer, offset, states->block_map); VDO_ASSERT_LOG_ONLY(*offset == VDO_COMPONENT_DATA_OFFSET + VDO_COMPONENT_DATA_SIZE, "All super block component data was encoded"); } /** * vdo_encode_super_block() - Encode a super block into its on-disk representation. */ void vdo_encode_super_block(u8 *buffer, struct vdo_component_states *states) { u32 checksum; struct header header = SUPER_BLOCK_HEADER_12_0; size_t offset = 0; header.size += VDO_COMPONENT_DATA_SIZE; vdo_encode_header(buffer, &offset, &header); vdo_encode_component_states(buffer, &offset, states); checksum = vdo_crc32(buffer, offset); encode_u32_le(buffer, &offset, checksum); /* * Even though the buffer is a full block, to avoid the potential corruption from a torn * write, the entire encoding must fit in the first sector. */ VDO_ASSERT_LOG_ONLY(offset <= VDO_SECTOR_SIZE, "entire superblock must fit in one sector"); } /** * vdo_decode_super_block() - Decode a super block from its on-disk representation. */ int vdo_decode_super_block(u8 *buffer) { struct header header; int result; u32 checksum, saved_checksum; size_t offset = 0; /* Decode and validate the header. */ vdo_decode_header(buffer, &offset, &header); result = vdo_validate_header(&SUPER_BLOCK_HEADER_12_0, &header, false, __func__); if (result != VDO_SUCCESS) return result; if (header.size > VDO_COMPONENT_DATA_SIZE + sizeof(u32)) { /* * We can't check release version or checksum until we know the content size, so we * have to assume a version mismatch on unexpected values. */ return vdo_log_error_strerror(VDO_UNSUPPORTED_VERSION, "super block contents too large: %zu", header.size); } /* Skip past the component data for now, to verify the checksum. */ offset += VDO_COMPONENT_DATA_SIZE; checksum = vdo_crc32(buffer, offset); decode_u32_le(buffer, &offset, &saved_checksum); result = VDO_ASSERT(offset == VDO_SUPER_BLOCK_FIXED_SIZE + VDO_COMPONENT_DATA_SIZE, "must have decoded entire superblock payload"); if (result != VDO_SUCCESS) return result; return ((checksum != saved_checksum) ? VDO_CHECKSUM_MISMATCH : VDO_SUCCESS); }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1