diff --git a/env/posix/ocf_env_headers.h b/env/posix/ocf_env_headers.h index b9841610..b90c4160 100644 --- a/env/posix/ocf_env_headers.h +++ b/env/posix/ocf_env_headers.h @@ -1,5 +1,6 @@ /* * Copyright(c) 2019-2021 Intel Corporation + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -15,8 +16,9 @@ #define OCF_PREFIX_SHORT "[" OCF_LOGO "] " #define OCF_PREFIX_LONG "Open CAS Framework" -#define OCF_VERSION_MAIN 20 -#define OCF_VERSION_MAJOR 3 -#define OCF_VERSION_MINOR 0 +#define ENV_ADAPTER_NAME "OCF Posix" +#define ENV_ADAPTER_VERSION_MAIN 20 +#define ENV_ADAPTER_VERSION_MAJOR 3 +#define ENV_ADAPTER_VERSION_MINOR 0 #endif /* __OCF_ENV_HEADERS_H__ */ diff --git a/inc/ocf_def.h b/inc/ocf_def.h index 219c5965..8a354752 100644 --- a/inc/ocf_def.h +++ b/inc/ocf_def.h @@ -21,6 +21,10 @@ * @brief OCF definitions */ +#define OCF_VERSION_MAIN 26 +#define OCF_VERSION_MAJOR 9 +#define OCF_VERSION_MINOR 0 + /** * Enabling debug statistics */ @@ -64,6 +68,10 @@ * Size of cache name */ #define OCF_CACHE_NAME_SIZE 32 +/** + * Size of adapter name + */ +#define OCF_ADAPTER_NAME_SIZE 32 /** * Value to turn off fallback pass through */ diff --git a/inc/ocf_err.h b/inc/ocf_err.h index eec29e87..de350d0a 100644 --- a/inc/ocf_err.h +++ b/inc/ocf_err.h @@ -187,7 +187,16 @@ typedef enum { /** Operation not allowed when cleaner is disabled **/ OCF_ERR_CLEANER_DISABLED, - OCF_ERR_MAX = OCF_ERR_CLEANER_DISABLED, + /** Metadata was created by a different adapter */ + OCF_ERR_ADAPTER_MISMATCH, + + /** Adapter version mismatch */ + OCF_ERR_ADAPTER_VER, + + /** Metadata was created in different block mode */ + OCF_ERR_BLOCK_MODE_MISMATCH, + + OCF_ERR_MAX = OCF_ERR_BLOCK_MODE_MISMATCH, } ocf_error_t; diff --git a/src/metadata/metadata.c b/src/metadata/metadata.c index ca9cdd29..60865a5c 100644 --- a/src/metadata/metadata.c +++ b/src/metadata/metadata.c @@ -1954,8 +1954,23 @@ static void ocf_metadata_probe_cmpl(struct ocf_metadata_read_sb_ctx *context) if (METADATA_VERSION() != superblock->metadata_version) OCF_CMPL_RET(priv, -OCF_ERR_METADATA_VER, &status); + /* + * Following fields presence and offset is dependent on metadata + * version, so they can be safely accessed only after positive + * metadata version verification. + */ + + if (env_strncmp(superblock->adapter_name, OCF_ADAPTER_NAME_SIZE, + ENV_ADAPTER_NAME, OCF_ADAPTER_NAME_SIZE)) { + OCF_CMPL_RET(priv, -OCF_ERR_ADAPTER_MISMATCH, &status); + } + + if (superblock->adapter_version != ADAPTER_VERSION()) + OCF_CMPL_RET(priv, -OCF_ERR_ADAPTER_VER, &status); + env_strncpy(status.cache_name, OCF_CACHE_NAME_SIZE, superblock->name, OCF_CACHE_NAME_SIZE); + status.cache_mode = superblock->cache_mode; status.cache_line_size = superblock->line_size; diff --git a/src/metadata/metadata_superblock.c b/src/metadata/metadata_superblock.c index 1e28f02a..0c3eaad2 100644 --- a/src/metadata/metadata_superblock.c +++ b/src/metadata/metadata_superblock.c @@ -116,7 +116,15 @@ int ocf_metadata_validate_superblock(ocf_ctx_t ctx, } if (METADATA_VERSION() != superblock->metadata_version) { - ocf_log(ctx, log_err, "Metadata version mismatch!\n"); + ocf_log(ctx, log_err, "Loading %s: metadata version mismatch! " + "Metadata was created by OCF %u.%u.%u, but " + "current version is %u.%u.%u\n", segment_name, + (superblock->metadata_version >> 16) & 0xff, + (superblock->metadata_version >> 8) & 0xff, + superblock->adapter_version & 0xff, + OCF_VERSION_MAIN, + OCF_VERSION_MAJOR, + OCF_VERSION_MINOR); return -OCF_ERR_METADATA_VER; } @@ -128,6 +136,42 @@ int ocf_metadata_validate_superblock(ocf_ctx_t ctx, return -OCF_ERR_CRC_MISMATCH; } + if (env_strncmp(superblock->adapter_name, OCF_ADAPTER_NAME_SIZE, + ENV_ADAPTER_NAME, OCF_ADAPTER_NAME_SIZE)) { + /* Field comes off disk and need not be terminated - bound + * the print to its size instead of running past it. + */ + ocf_log(ctx, log_err, "Loading %s: metadata was created by " + "adapter '%.*s', but OCF is running as '%s'!\n", + segment_name, OCF_ADAPTER_NAME_SIZE, + superblock->adapter_name, ENV_ADAPTER_NAME); + return -OCF_ERR_ADAPTER_MISMATCH; + } + + if (superblock->adapter_version != ADAPTER_VERSION()) { + ocf_log(ctx, log_err, "Loading %s: adapter version mismatch! " + "Metadata was created by %s %u.%u.%u, but OCF " + "is running under %s %u.%u.%u\n", segment_name, + ENV_ADAPTER_NAME, + (superblock->adapter_version >> 16) & 0xff, + (superblock->adapter_version >> 8) & 0xff, + superblock->adapter_version & 0xff, + ENV_ADAPTER_NAME, + ENV_ADAPTER_VERSION_MAIN, + ENV_ADAPTER_VERSION_MAJOR, + ENV_ADAPTER_VERSION_MINOR); + return -OCF_ERR_ADAPTER_VER; + } + + if (superblock->metadata_4k_mode != METADATA_4K_MODE()) { + ocf_log(ctx, log_err, "Loading %s: metadata was created in %s " + "block mode, but OCF is running in %s block " + "mode!\n", segment_name, + superblock->metadata_4k_mode ? "4KiB" : "512B", + METADATA_4K_MODE() ? "4KiB" : "512B"); + return -OCF_ERR_BLOCK_MODE_MISMATCH; + } + if (superblock->clean_shutdown > ocf_metadata_clean_shutdown) { ocf_log_invalid_superblock("shutdown status"); return -OCF_ERR_INVAL; diff --git a/src/metadata/metadata_superblock.h b/src/metadata/metadata_superblock.h index f0eab0aa..a01f23fc 100644 --- a/src/metadata/metadata_superblock.h +++ b/src/metadata/metadata_superblock.h @@ -18,20 +18,35 @@ /** * @brief OCF cache metadata configuration superblock + * + * The first 12 bytes of superblock do not change between verions. + * These fields are used by metadata probe to recognize OCF metadata + * and identify metadata version and status. + * The layout is following: + * 0:7 | clean shutdown status + * 8:15 | dirty metadata present + * 16:31 | padding; kept for historic reasons; zeroed + * 32:63 | OCF magic number; must be 0x187E1CA6 + * 64:95 | OCF metadata version + * + * The remaining fields can should be accessed only after confirming + * correct magic and metadata version. */ struct ocf_superblock_config { - /** WARNING: Metadata probe disregards metadata version when - * checking if the cache is dirty - position of next two fields - * shouldn't change!! */ - uint8_t clean_shutdown; - uint8_t dirty_flushed; + /* Superblock header */ + struct { + uint8_t clean_shutdown; + uint8_t dirty_flushed; + uint16_t _padding; + uint32_t magic_number; + uint32_t metadata_version; + }; - /* Current core sequence number */ - ocf_core_id_t curr_core_seq_no; - - uint32_t magic_number; + /* Identity of the adapter */ + char adapter_name[OCF_ADAPTER_NAME_SIZE]; + uint32_t adapter_version; - uint32_t metadata_version; + bool metadata_4k_mode; unsigned flapping_idx; @@ -46,6 +61,9 @@ struct ocf_superblock_config { ocf_cache_line_size_t line_size; uint32_t core_count; + /* Current core sequence number */ + ocf_core_id_t curr_core_seq_no; + unsigned long valid_core_bitmap[OCF_DIV_ROUND_UP_STATIC(OCF_CORE_NUM, sizeof(unsigned long) * 8)]; diff --git a/src/mngt/ocf_mngt_cache.c b/src/mngt/ocf_mngt_cache.c index 89a10119..ffdc396c 100644 --- a/src/mngt/ocf_mngt_cache.c +++ b/src/mngt/ocf_mngt_cache.c @@ -297,9 +297,21 @@ static void __init_cores(ocf_cache_t cache) sizeof(cache->conf_meta->valid_core_bitmap), 0)); } -static void __init_metadata_version(ocf_cache_t cache) +static void __init_superblock_header(ocf_cache_t cache) { cache->conf_meta->metadata_version = METADATA_VERSION(); + cache->conf_meta->adapter_version = ADAPTER_VERSION(); + cache->conf_meta->metadata_4k_mode = METADATA_4K_MODE(); + + /* + * Zero the whole field first - the name is covered by the superblock + * checksum, so the bytes past the terminator have to be deterministic. + */ + ENV_BUG_ON(env_memset(cache->conf_meta->adapter_name, + OCF_ADAPTER_NAME_SIZE, 0)); + ENV_BUG_ON(env_strncpy(cache->conf_meta->adapter_name, + OCF_ADAPTER_NAME_SIZE, ENV_ADAPTER_NAME, + OCF_ADAPTER_NAME_SIZE - 1)); } static void _reset_stats(ocf_pipeline_t pipeline, void *priv, @@ -327,13 +339,13 @@ static void _reset_stats(ocf_pipeline_t pipeline, void *priv, ocf_pipeline_next(pipeline); } -static void _init_metadata_version(ocf_pipeline_t pipeline, void *priv, +static void _init_superblock_header(ocf_pipeline_t pipeline, void *priv, ocf_pipeline_arg_t arg) { struct ocf_init_metadata_context *context = priv; ocf_cache_t cache = context->cache; - __init_metadata_version(cache); + __init_superblock_header(cache); ocf_pipeline_next(pipeline); } @@ -356,7 +368,7 @@ struct ocf_pipeline_properties ocf_init_attached_recovery_props = { OCF_PL_STEP(ocf_metadata_init_collision), OCF_PL_STEP(_init_parts_attached), OCF_PL_STEP(_reset_stats), - OCF_PL_STEP(_init_metadata_version), + OCF_PL_STEP(_init_superblock_header), OCF_PL_STEP_TERMINATOR(), }, }; @@ -1598,7 +1610,7 @@ static void _ocf_mngt_cache_init(ocf_cache_t cache, __init_free(cache); __init_cores(cache); - __init_metadata_version(cache); + __init_superblock_header(cache); __init_partitions(cache); } diff --git a/src/ocf_def_priv.h b/src/ocf_def_priv.h index 9b361b61..d07ce7e3 100644 --- a/src/ocf_def_priv.h +++ b/src/ocf_def_priv.h @@ -56,6 +56,15 @@ #define METADATA_VERSION() ((OCF_VERSION_MAIN << 16) + \ (OCF_VERSION_MAJOR << 8) + OCF_VERSION_MINOR) +#define ADAPTER_VERSION() ((ENV_ADAPTER_VERSION_MAIN << 16) + \ + (ENV_ADAPTER_VERSION_MAJOR << 8) + ENV_ADAPTER_VERSION_MINOR) + +#ifdef OCF_BLOCK_SIZE_4K +#define METADATA_4K_MODE() true +#else +#define METADATA_4K_MODE() false +#endif + /* call conditional reschedule every 'iterations' calls */ #define OCF_COND_RESCHED(cnt, iterations) \ if (unlikely(++(cnt) == (iterations))) { \ diff --git a/tests/functional/pyocf/types/shared.py b/tests/functional/pyocf/types/shared.py index 6203156c..82a8cdda 100644 --- a/tests/functional/pyocf/types/shared.py +++ b/tests/functional/pyocf/types/shared.py @@ -71,6 +71,9 @@ class OcfErrorCode(IntEnum): OCF_ERR_CORE_NOT_REMOVED = auto() OCF_ERR_CACHE_NOT_STANDBY = auto() OCF_ERR_CLEANER_DISABLED = auto() + OCF_ERR_ADAPTER_MISMATCH = auto() + OCF_ERR_ADAPTER_VER = auto() + OCF_ERR_BLOCK_MODE_MISMATCH = auto() class OcfCompletion: