/* * Copyright(c) 2012-2020 Intel Corporation * SPDX-License-Identifier: BSD-3-Clause-Clear */ #include "ocf/ocf.h" #include "metadata.h" #include "metadata_collision.h" #include "metadata_segment_id.h" #include "metadata_internal.h" #include "metadata_io.h" #include "metadata_raw.h" #include "metadata_segment.h" #include "../concurrency/ocf_concurrency.h" #include "../ocf_def_priv.h" #include "../ocf_freelist.h" #include "../ocf_priv.h" #include "../utils/utils_cache_line.h" #include "../utils/utils_io.h" #include "../utils/utils_pipeline.h" #define OCF_METADATA_DEBUG 0 #if 1 == OCF_METADATA_DEBUG #define OCF_DEBUG_TRACE(cache) \ ocf_cache_log(cache, log_info, "[Metadata] %s\n", __func__) #define OCF_DEBUG_PARAM(cache, format, ...) \ ocf_cache_log(cache, log_info, "[Metadata] %s - "format"\n", \ __func__, ##__VA_ARGS__) #else #define OCF_DEBUG_TRACE(cache) #define OCF_DEBUG_PARAM(cache, format, ...) #endif #define OCF_METADATA_HASH_DIFF_MAX 1000 enum { ocf_metadata_status_type_valid = 0, ocf_metadata_status_type_dirty, ocf_metadata_status_type_max }; static inline size_t ocf_metadata_status_sizeof( const struct ocf_cache_line_settings *settings) { /* Number of bytes required to mark cache line status */ size_t size = settings->sector_count / 8; /* Number of types of status (valid, dirty, etc...) */ size *= ocf_metadata_status_type_max; /* At the end we have size */ return size; } /* * get entries for specified metadata hash type */ static ocf_cache_line_t ocf_metadata_get_entries( enum ocf_metadata_segment_id type, ocf_cache_line_t cache_lines) { ENV_BUG_ON(type >= metadata_segment_variable_size_start && cache_lines == 0); switch (type) { case metadata_segment_collision: case metadata_segment_cleaning: case metadata_segment_eviction: case metadata_segment_list_info: return cache_lines; case metadata_segment_hash: return OCF_DIV_ROUND_UP(cache_lines, 4); case metadata_segment_sb_config: return OCF_DIV_ROUND_UP(sizeof(struct ocf_superblock_config), PAGE_SIZE); case metadata_segment_sb_runtime: return OCF_DIV_ROUND_UP(sizeof(struct ocf_superblock_runtime), PAGE_SIZE); case metadata_segment_reserved: return 32; case metadata_segment_part_config: return OCF_IO_CLASS_MAX + 1; case metadata_segment_part_runtime: return OCF_IO_CLASS_MAX + 1; case metadata_segment_core_config: return OCF_CORE_MAX; case metadata_segment_core_runtime: return OCF_CORE_MAX; case metadata_segment_core_uuid: return OCF_CORE_MAX; default: break; } ENV_BUG(); return 0; } /* * Get size of particular hash metadata type element */ static int64_t ocf_metadata_get_element_size( enum ocf_metadata_segment_id type, const struct ocf_cache_line_settings *settings) { int64_t size = 0; ENV_BUG_ON(type >= metadata_segment_variable_size_start && !settings); switch (type) { case metadata_segment_eviction: size = sizeof(union eviction_policy_meta); break; case metadata_segment_cleaning: size = sizeof(struct cleaning_policy_meta); break; case metadata_segment_collision: size = sizeof(struct ocf_metadata_map) + ocf_metadata_status_sizeof(settings); break; case metadata_segment_list_info: size = sizeof(struct ocf_metadata_list_info); break; case metadata_segment_sb_config: size = PAGE_SIZE; break; case metadata_segment_sb_runtime: size = PAGE_SIZE; break; case metadata_segment_reserved: size = PAGE_SIZE; break; case metadata_segment_part_config: size = sizeof(struct ocf_user_part_config); break; case metadata_segment_part_runtime: size = sizeof(struct ocf_user_part_runtime); break; case metadata_segment_hash: size = sizeof(ocf_cache_line_t); break; case metadata_segment_core_config: size = sizeof(struct ocf_core_meta_config); break; case metadata_segment_core_runtime: size = sizeof(struct ocf_core_meta_runtime); break; case metadata_segment_core_uuid: size = sizeof(struct ocf_metadata_uuid); break; default: break; } ENV_BUG_ON(size > PAGE_SIZE); return size; } /* * Metadata calculation exception handling. * * @param unused_lines - Unused pages * @param device_lines - SSD Cache device pages amount * * @return true - Accept unused sapce * @return false - unused space is not acceptable */ static bool ocf_metadata_calculate_exception_hndl(ocf_cache_t cache, int64_t unused_lines, int64_t device_lines) { static bool warn; int64_t utilization = 0; if (!warn) { ocf_cache_log(cache, log_warn, "Metadata size calculation problem\n"); warn = true; } if (unused_lines < 0) return false; /* * Accepted disk utilization is 90 % off SSD space */ utilization = (device_lines - unused_lines) * 100 / device_lines; if (utilization < 90) return false; return true; } /* * Algorithm to calculate amount of cache lines taking into account required * space for metadata */ static int ocf_metadata_calculate_metadata_size( struct ocf_cache *cache, struct ocf_metadata_ctrl *ctrl, const struct ocf_cache_line_settings *settings) { int64_t i_diff = 0, diff_lines = 0, cache_lines = ctrl->device_lines; int64_t lowest_diff; ocf_cache_line_t count_pages; uint32_t i; OCF_DEBUG_PARAM(cache, "Cache lines = %lld", cache_lines); lowest_diff = cache_lines; do { count_pages = ctrl->count_pages; for (i = metadata_segment_variable_size_start; i < metadata_segment_max; i++) { struct ocf_metadata_raw *raw = &ctrl->raw_desc[i]; /* Setup number of entries */ raw->entries = ocf_metadata_get_entries(i, cache_lines); /* * Setup SSD location and size */ raw->ssd_pages_offset = count_pages; raw->ssd_pages = OCF_DIV_ROUND_UP(raw->entries, raw->entries_in_page); /* Update offset for next container */ count_pages += ocf_metadata_raw_size_on_ssd(raw); } /* * Check if max allowed iteration exceeded */ if (i_diff >= OCF_METADATA_HASH_DIFF_MAX) { /* * Never should be here but try handle this exception */ if (ocf_metadata_calculate_exception_hndl(cache, diff_lines, ctrl->device_lines)) { break; } if (i_diff > (2 * OCF_METADATA_HASH_DIFF_MAX)) { /* * We tried, but we fallen, have to return error */ ocf_cache_log(cache, log_err, "Metadata size calculation ERROR\n"); return -1; } } /* Calculate diff of cache lines */ /* Cache size in bytes */ diff_lines = ctrl->device_lines * settings->size; /* Sub metadata size which is in 4 kiB unit */ diff_lines -= count_pages * PAGE_SIZE; /* Convert back to cache lines */ diff_lines /= settings->size; /* Calculate difference */ diff_lines -= cache_lines; if (diff_lines > 0) { if (diff_lines < lowest_diff) lowest_diff = diff_lines; else if (diff_lines == lowest_diff) break; } /* Update new value of cache lines */ cache_lines += diff_lines; OCF_DEBUG_PARAM(cache, "Diff pages = %lld", diff_lines); OCF_DEBUG_PARAM(cache, "Cache lines = %lld", cache_lines); i_diff++; } while (diff_lines); ctrl->count_pages = count_pages; ctrl->cachelines = cache_lines; OCF_DEBUG_PARAM(cache, "Cache lines = %u", ctrl->cachelines); if (ctrl->device_lines < ctrl->cachelines) return -1; return 0; } const char * const ocf_metadata_segment_names[] = { [metadata_segment_sb_config] = "Super block config", [metadata_segment_sb_runtime] = "Super block runtime", [metadata_segment_reserved] = "Reserved", [metadata_segment_part_config] = "Part config", [metadata_segment_part_runtime] = "Part runtime", [metadata_segment_cleaning] = "Cleaning", [metadata_segment_eviction] = "Eviction", [metadata_segment_collision] = "Collision", [metadata_segment_list_info] = "List info", [metadata_segment_hash] = "Hash", [metadata_segment_core_config] = "Core config", [metadata_segment_core_runtime] = "Core runtime", [metadata_segment_core_uuid] = "Core UUID", }; #if 1 == OCF_METADATA_DEBUG /* * Debug info functions prints metadata and raw containers information */ static void ocf_metadata_raw_info(struct ocf_cache *cache, struct ocf_metadata_ctrl *ctrl) { uint64_t capacity = 0; uint64_t capacity_sum = 0; uint32_t i = 0; const char *unit; for (i = 0; i < metadata_segment_max; i++) { struct ocf_metadata_raw *raw = &(ctrl->raw_desc[i]); OCF_DEBUG_PARAM(cache, "Raw : name = %s", ocf_metadata_segment_names[i]); OCF_DEBUG_PARAM(cache, " : metadata type = %u", i); OCF_DEBUG_PARAM(cache, " : raw type = %u", raw->raw_type); OCF_DEBUG_PARAM(cache, " : entry size = %u", raw->entry_size); OCF_DEBUG_PARAM(cache, " : entries = %llu", raw->entries); OCF_DEBUG_PARAM(cache, " : entries in page = %u", raw->entries_in_page); OCF_DEBUG_PARAM(cache, " : page offset = %llu", raw->ssd_pages_offset); OCF_DEBUG_PARAM(cache, " : pages = %llu", raw->ssd_pages); } /* Provide capacity info */ for (i = 0; i < metadata_segment_max; i++) { capacity = ocf_metadata_raw_size_of(cache, &(ctrl->raw_desc[i])); capacity_sum += capacity; if (capacity / MiB) { capacity = capacity / MiB; unit = "MiB"; } else { unit = "KiB"; capacity = capacity / KiB; } OCF_DEBUG_PARAM(cache, "%s capacity %llu %s", ocf_metadata_segment_names[i], capacity, unit); } } #else #define ocf_metadata_raw_info(cache, ctrl) #endif /* * Deinitialize hash metadata interface */ void ocf_metadata_deinit_variable_size(struct ocf_cache *cache) { uint32_t i = 0; struct ocf_metadata_ctrl *ctrl = (struct ocf_metadata_ctrl *) cache->metadata.priv; OCF_DEBUG_TRACE(cache); ocf_metadata_concurrency_attached_deinit(&cache->metadata.lock); /* * De initialize RAW types */ for (i = metadata_segment_variable_size_start; i < metadata_segment_max; i++) { ocf_metadata_segment_destroy(cache, ctrl->segment[i]); } } static inline void ocf_metadata_config_init(struct ocf_cache *cache, struct ocf_cache_line_settings *settings, size_t size) { ENV_BUG_ON(!ocf_cache_line_size_is_valid(size)); ENV_BUG_ON(env_memset(settings, sizeof(*settings), 0)); settings->size = size; settings->sector_count = BYTES_TO_SECTORS(settings->size); settings->sector_start = 0; settings->sector_end = settings->sector_count - 1; OCF_DEBUG_PARAM(cache, "Cache line size = %lu, bits count = %llu, " "status size = %lu", settings->size, settings->sector_count, ocf_metadata_status_sizeof(settings)); } static void ocf_metadata_deinit_fixed_size(struct ocf_cache *cache) { int result = 0; uint32_t i; struct ocf_metadata_ctrl *ctrl = (struct ocf_metadata_ctrl *) cache->metadata.priv; struct ocf_metadata_segment *superblock = ctrl->segment[metadata_segment_sb_config]; for (i = 0; i < metadata_segment_fixed_size_max; i++) { if (i != metadata_segment_sb_config) ocf_metadata_segment_destroy(cache, ctrl->segment[i]); } ocf_metadata_superblock_destroy(cache, superblock); env_vfree(ctrl); cache->metadata.priv = NULL; if (result) ENV_BUG(); } static struct ocf_metadata_ctrl *ocf_metadata_ctrl_init( bool metadata_volatile) { struct ocf_metadata_ctrl *ctrl = NULL; uint32_t page = 0; uint32_t i = 0; ctrl = env_vzalloc(sizeof(*ctrl)); if (!ctrl) return NULL; /* Initial setup of RAW containers */ for (i = 0; i < metadata_segment_fixed_size_max; i++) { struct ocf_metadata_raw *raw = &ctrl->raw_desc[i]; raw->metadata_segment = i; /* Default type for metadata RAW container */ raw->raw_type = metadata_raw_type_ram; if (metadata_volatile) { raw->raw_type = metadata_raw_type_volatile; } else if (i == metadata_segment_core_uuid) { raw->raw_type = metadata_raw_type_dynamic; } /* Entry size configuration */ raw->entry_size = ocf_metadata_get_element_size(i, NULL); raw->entries_in_page = PAGE_SIZE / raw->entry_size; /* Setup number of entries */ raw->entries = ocf_metadata_get_entries(i, 0); /* * Setup SSD location and size */ raw->ssd_pages_offset = page; raw->ssd_pages = OCF_DIV_ROUND_UP(raw->entries, raw->entries_in_page); /* Update offset for next container */ page += ocf_metadata_raw_size_on_ssd(raw); } ctrl->count_pages = page; return ctrl; } static int ocf_metadata_init_fixed_size(struct ocf_cache *cache, ocf_cache_line_size_t cache_line_size) { struct ocf_metadata_ctrl *ctrl = NULL; struct ocf_metadata *metadata = &cache->metadata; struct ocf_cache_line_settings *settings = (struct ocf_cache_line_settings *)&metadata->settings; struct ocf_core_meta_config *core_meta_config; struct ocf_core_meta_runtime *core_meta_runtime; struct ocf_user_part_config *part_config; struct ocf_user_part_runtime *part_runtime; struct ocf_metadata_segment *superblock; ocf_core_t core; ocf_core_id_t core_id; uint32_t i = 0; int result = 0; OCF_DEBUG_TRACE(cache); ENV_WARN_ON(metadata->priv); ocf_metadata_config_init(cache, settings, cache_line_size); ctrl = ocf_metadata_ctrl_init(metadata->is_volatile); if (!ctrl) return -OCF_ERR_NO_MEM; metadata->priv = ctrl; result = ocf_metadata_superblock_init( &ctrl->segment[metadata_segment_sb_config], cache, &ctrl->raw_desc[metadata_segment_sb_config]); if (result) { ocf_metadata_deinit_fixed_size(cache); return result; } superblock = ctrl->segment[metadata_segment_sb_config]; for (i = 0; i < metadata_segment_fixed_size_max; i++) { if (i == metadata_segment_sb_config) continue; result |= ocf_metadata_segment_init( &ctrl->segment[i], cache, &ctrl->raw_desc[i], NULL, NULL, superblock); if (result) break; } if (result) { ocf_metadata_deinit_fixed_size(cache); return result; } cache->conf_meta = METADATA_MEM_POOL(ctrl, metadata_segment_sb_config); /* Set partition metadata */ part_config = METADATA_MEM_POOL(ctrl, metadata_segment_part_config); part_runtime = METADATA_MEM_POOL(ctrl, metadata_segment_part_runtime); for (i = 0; i < OCF_IO_CLASS_MAX + 1; i++) { cache->user_parts[i].config = &part_config[i]; cache->user_parts[i].runtime = &part_runtime[i]; cache->user_parts[i].id = i; } /* Set core metadata */ core_meta_config = METADATA_MEM_POOL(ctrl, metadata_segment_core_config); core_meta_runtime = METADATA_MEM_POOL(ctrl, metadata_segment_core_runtime); for_each_core_all(cache, core, core_id) { core->conf_meta = &core_meta_config[core_id]; core->runtime_meta = &core_meta_runtime[core_id]; } return 0; } static void ocf_metadata_flush_lock_collision_page(struct ocf_cache *cache, struct ocf_metadata_raw *raw, uint32_t page) { ocf_collision_start_exclusive_access(&cache->metadata.lock, page); } static void ocf_metadata_flush_unlock_collision_page( struct ocf_cache *cache, struct ocf_metadata_raw *raw, uint32_t page) { ocf_collision_end_exclusive_access(&cache->metadata.lock, page); } static void ocf_metadata_init_layout(struct ocf_cache *cache, ocf_metadata_layout_t layout) { ENV_BUG_ON(layout >= ocf_metadata_layout_max || layout < 0); /* Initialize metadata location interface*/ if (cache->device->init_mode == ocf_init_mode_metadata_volatile) layout = ocf_metadata_layout_seq; cache->metadata.layout = layout; } /* * Initialize hash metadata interface */ int ocf_metadata_init_variable_size(struct ocf_cache *cache, uint64_t device_size, ocf_cache_line_size_t cache_line_size, ocf_metadata_layout_t layout) { int result = 0; uint32_t i = 0; ocf_cache_line_t line; struct ocf_metadata_ctrl *ctrl = NULL; struct ocf_cache_line_settings *settings = (struct ocf_cache_line_settings *)&cache->metadata.settings; ocf_flush_page_synch_t lock_page, unlock_page; uint64_t device_lines; struct ocf_metadata_segment *superblock; OCF_DEBUG_TRACE(cache); ENV_WARN_ON(!cache->metadata.priv); ctrl = cache->metadata.priv; device_lines = device_size / cache_line_size; if (device_lines >= (ocf_cache_line_t)(-1)){ /* TODO: This is just a rough check. Most optimal one would be * located in calculate_metadata_size. */ ocf_cache_log(cache, log_err, "Device exceeds maximum suported size " "with this cache line size. Try bigger cache line size."); return -OCF_ERR_INVAL_CACHE_DEV; } ctrl->device_lines = device_lines; if (settings->size != cache_line_size) /* Re-initialize settings with different cache line size */ ocf_metadata_config_init(cache, settings, cache_line_size); ctrl->mapping_size = ocf_metadata_status_sizeof(settings) + sizeof(struct ocf_metadata_map); ocf_metadata_init_layout(cache, layout); /* Initial setup of dynamic size RAW containers */ for (i = metadata_segment_variable_size_start; i < metadata_segment_max; i++) { struct ocf_metadata_raw *raw = &ctrl->raw_desc[i]; raw->metadata_segment = i; /* Default type for metadata RAW container */ raw->raw_type = metadata_raw_type_ram; if (cache->device->init_mode == ocf_init_mode_metadata_volatile) { raw->raw_type = metadata_raw_type_volatile; } else if (i == metadata_segment_collision && ocf_volume_is_atomic(&cache->device->volume)) { raw->raw_type = metadata_raw_type_atomic; } /* Entry size configuration */ raw->entry_size = ocf_metadata_get_element_size(i, settings); raw->entries_in_page = PAGE_SIZE / raw->entry_size; } if (0 != ocf_metadata_calculate_metadata_size(cache, ctrl, settings)) { return -1; } OCF_DEBUG_PARAM(cache, "Metadata begin pages = %u", ctrl->start_page); OCF_DEBUG_PARAM(cache, "Metadata count pages = %u", ctrl->count_pages); OCF_DEBUG_PARAM(cache, "Metadata end pages = %u", ctrl->start_page + ctrl->count_pages); superblock = ctrl->segment[metadata_segment_sb_config]; /* * Initialize all dynamic size RAW types */ for (i = metadata_segment_variable_size_start; i < metadata_segment_max; i++) { if (i == metadata_segment_collision) { lock_page = ocf_metadata_flush_lock_collision_page; unlock_page = ocf_metadata_flush_unlock_collision_page; } else { lock_page = unlock_page = NULL; } result |= ocf_metadata_segment_init( &ctrl->segment[i], cache, &ctrl->raw_desc[i], lock_page, unlock_page, superblock); if (result) goto finalize; } for (i = 0; i < metadata_segment_max; i++) { ocf_cache_log(cache, log_info, "%s offset : %llu kiB\n", ocf_metadata_segment_names[i], ctrl->raw_desc[i].ssd_pages_offset * PAGE_SIZE / KiB); if (i == metadata_segment_sb_config) { ocf_cache_log(cache, log_info, "%s size : %lu B\n", ocf_metadata_segment_names[i], offsetof(struct ocf_superblock_config, checksum) + sizeof(((struct ocf_superblock_config *)0) ->checksum)); } else if (i == metadata_segment_sb_runtime) { ocf_cache_log(cache, log_info, "%s size : %lu B\n", ocf_metadata_segment_names[i], sizeof(struct ocf_superblock_runtime)); } else { ocf_cache_log(cache, log_info, "%s size : %llu kiB\n", ocf_metadata_segment_names[i], ctrl->raw_desc[i].ssd_pages * PAGE_SIZE / KiB); } } finalize: if (result) { /* * Hash De-Init also contains RAW deinitialization */ ocf_metadata_deinit_variable_size(cache); return result; } cache->device->runtime_meta = METADATA_MEM_POOL(ctrl, metadata_segment_sb_runtime); cache->device->collision_table_entries = ctrl->cachelines; cache->device->hash_table_entries = ctrl->raw_desc[metadata_segment_hash].entries; cache->device->metadata_offset = ctrl->count_pages * PAGE_SIZE; cache->conf_meta->cachelines = ctrl->cachelines; cache->conf_meta->line_size = cache_line_size; ocf_metadata_raw_info(cache, ctrl); ocf_cache_log(cache, log_info, "Cache line size: %llu kiB\n", settings->size / KiB); ocf_cache_log(cache, log_info, "Metadata capacity: %llu MiB\n", (uint64_t)ocf_metadata_size_of(cache) / MiB); /* * Self test of metadata */ for (line = 0; line < cache->device->collision_table_entries; line++) { ocf_cache_line_t phy, lg; phy = ocf_metadata_map_lg2phy(cache, line); lg = ocf_metadata_map_phy2lg(cache, phy); if (line != lg) { result = -OCF_ERR_INVAL; break; } env_cond_resched(); } if (result == 0) { ocf_cache_log(cache, log_info, "OCF metadata self-test PASSED\n"); } else { ocf_cache_log(cache, log_err, "OCF metadata self-test ERROR\n"); } result = ocf_metadata_concurrency_attached_init(&cache->metadata.lock, cache, ctrl->raw_desc[metadata_segment_hash].entries, (uint32_t)ctrl->raw_desc[metadata_segment_collision]. ssd_pages); if (result) { ocf_cache_log(cache, log_err, "Failed to initialize attached " "metadata concurrency\n"); ocf_metadata_deinit_variable_size(cache); return result; } return 0; } static inline void _ocf_init_collision_entry(struct ocf_cache *cache, ocf_cache_line_t idx) { ocf_cache_line_t invalid_idx = cache->device->collision_table_entries; ocf_metadata_set_collision_info(cache, idx, invalid_idx, invalid_idx); ocf_metadata_set_core_info(cache, idx, OCF_CORE_MAX, ULONG_MAX); metadata_init_status_bits(cache, idx); } /* * Initialize collision table */ void ocf_metadata_init_collision(struct ocf_cache *cache) { unsigned int i; unsigned int step = 0; for (i = 0; i < cache->device->collision_table_entries; i++) { _ocf_init_collision_entry(cache, i); OCF_COND_RESCHED_DEFAULT(step); } } /* * Initialize hash table */ void ocf_metadata_init_hash_table(struct ocf_cache *cache) { unsigned int i; unsigned int hash_table_entries = cache->device->hash_table_entries; ocf_cache_line_t invalid_idx = cache->device->collision_table_entries; /* Init hash table */ for (i = 0; i < hash_table_entries; i++) { /* hash_table contains indexes from collision_table * thus it shall be initialized in improper values * from collision_table **/ ocf_metadata_set_hash(cache, i, invalid_idx); } } /* * Get count of pages that is dedicated for metadata */ ocf_cache_line_t ocf_metadata_get_pages_count(struct ocf_cache *cache) { struct ocf_metadata_ctrl *ctrl = NULL; OCF_DEBUG_TRACE(cache); ctrl = (struct ocf_metadata_ctrl *) cache->metadata.priv; return ctrl->count_pages; } /* * Get amount of cache lines */ ocf_cache_line_t ocf_metadata_get_cachelines_count( struct ocf_cache *cache) { struct ocf_metadata_ctrl *ctrl = NULL; OCF_DEBUG_TRACE(cache); ctrl = (struct ocf_metadata_ctrl *) cache->metadata.priv; return ctrl->cachelines; } size_t ocf_metadata_size_of(struct ocf_cache *cache) { uint32_t i = 0; size_t size = 0; struct ocf_metadata_ctrl *ctrl = NULL; OCF_DEBUG_TRACE(cache); ctrl = (struct ocf_metadata_ctrl *) cache->metadata.priv; /* * Get size of all RAW metadata container */ for (i = 0; i < metadata_segment_max; i++) { size += ocf_metadata_raw_size_of(cache, &(ctrl->raw_desc[i])); } /* Get additional part of memory footprint */ /* Cache concurrency mechnism */ size += ocf_cache_line_concurrency_size_of(cache); return size; } /******************************************************************************* * RESERVED AREA ******************************************************************************/ uint64_t ocf_metadata_get_reserved_lba( struct ocf_cache *cache) { struct ocf_metadata_ctrl *ctrl; OCF_DEBUG_TRACE(cache); ctrl = (struct ocf_metadata_ctrl *) cache->metadata.priv; return ctrl->raw_desc[metadata_segment_reserved].ssd_pages_offset * PAGE_SIZE; } /******************************************************************************* * FLUSH AND LOAD ALL ******************************************************************************/ static void ocf_metadata_flush_all_set_status_complete( void *priv, int error) { struct ocf_metadata_context *context = priv; OCF_PL_NEXT_ON_SUCCESS_RET(context->pipeline, error); } static void ocf_metadata_flush_all_set_status(ocf_pipeline_t pipeline, void *priv, ocf_pipeline_arg_t arg) { struct ocf_metadata_context *context = priv; ocf_cache_t cache = context->cache; enum ocf_metadata_shutdown_status shutdown_status = ocf_pipeline_arg_get_int(arg); ocf_metadata_set_shutdown_status(cache, shutdown_status, ocf_metadata_flush_all_set_status_complete, context); } static void ocf_metadata_flush_all_finish(ocf_pipeline_t pipeline, void *priv, int error) { struct ocf_metadata_context *context = priv; ocf_cache_t cache = context->cache; if (error) { ocf_cache_log(cache, log_err, "Metadata Flush ERROR\n"); ocf_metadata_error(cache); goto out; } ocf_cache_log(cache, log_info, "Done saving cache state!\n"); out: context->cmpl(context->priv, error); ocf_pipeline_destroy(pipeline); } struct ocf_pipeline_arg ocf_metadata_flush_all_args[] = { OCF_PL_ARG_INT(metadata_segment_sb_runtime), OCF_PL_ARG_INT(metadata_segment_part_runtime), OCF_PL_ARG_INT(metadata_segment_core_runtime), OCF_PL_ARG_INT(metadata_segment_cleaning), OCF_PL_ARG_INT(metadata_segment_eviction), OCF_PL_ARG_INT(metadata_segment_collision), OCF_PL_ARG_INT(metadata_segment_list_info), OCF_PL_ARG_INT(metadata_segment_hash), OCF_PL_ARG_TERMINATOR(), }; struct ocf_pipeline_properties ocf_metadata_flush_all_pipeline_props = { .priv_size = sizeof(struct ocf_metadata_context), .finish = ocf_metadata_flush_all_finish, .steps = { OCF_PL_STEP_ARG_INT(ocf_metadata_flush_all_set_status, ocf_metadata_dirty_shutdown), OCF_PL_STEP_FOREACH(ocf_metadata_flush_segment, ocf_metadata_flush_all_args), OCF_PL_STEP_FOREACH(ocf_metadata_calculate_crc, ocf_metadata_flush_all_args), OCF_PL_STEP_ARG_INT(ocf_metadata_flush_all_set_status, ocf_metadata_clean_shutdown), OCF_PL_STEP_TERMINATOR(), }, }; /* * Flush all metadata */ void ocf_metadata_flush_all(ocf_cache_t cache, ocf_metadata_end_t cmpl, void *priv) { struct ocf_metadata_context *context; ocf_pipeline_t pipeline; int result; OCF_DEBUG_TRACE(cache); result = ocf_pipeline_create(&pipeline, cache, &ocf_metadata_flush_all_pipeline_props); if (result) OCF_CMPL_RET(priv, result); context = ocf_pipeline_get_priv(pipeline); context->cmpl = cmpl; context->priv = priv; context->pipeline = pipeline; context->cache = cache; context->ctrl = cache->metadata.priv; ocf_pipeline_next(pipeline); } /* * Flush specified cache line */ void ocf_metadata_flush_mark(struct ocf_cache *cache, struct ocf_request *req, uint32_t map_idx, int to_state, uint8_t start, uint8_t stop) { struct ocf_metadata_ctrl *ctrl = NULL; OCF_DEBUG_TRACE(cache); ctrl = (struct ocf_metadata_ctrl *) cache->metadata.priv; /* * Mark all required metadata elements to make given metadata cache * line persistent in case of recovery */ /* Collision table to get mapping cache line to HDD sector*/ ocf_metadata_raw_flush_mark(cache, &(ctrl->raw_desc[metadata_segment_collision]), req, map_idx, to_state, start, stop); } /* * Flush specified cache lines asynchronously */ void ocf_metadata_flush_do_asynch(struct ocf_cache *cache, struct ocf_request *req, ocf_req_end_t complete) { int result = 0; struct ocf_metadata_ctrl *ctrl = NULL; OCF_DEBUG_TRACE(cache); ctrl = (struct ocf_metadata_ctrl *) cache->metadata.priv; /* * Flush all required metadata elements to make given metadata cache * line persistent in case of recovery */ env_atomic_inc(&req->req_remaining); /* Core device IO */ result |= ocf_metadata_raw_flush_do_asynch(cache, req, &(ctrl->raw_desc[metadata_segment_collision]), complete); if (result) { ocf_metadata_error(cache); ocf_cache_log(cache, log_err, "Metadata Flush ERROR\n"); } } static void ocf_metadata_load_all_finish(ocf_pipeline_t pipeline, void *priv, int error) { struct ocf_metadata_context *context = priv; ocf_cache_t cache = context->cache; if (error) { ocf_cache_log(cache, log_err, "Metadata read FAILURE\n"); ocf_metadata_error(cache); goto out; } ocf_cache_log(cache, log_info, "Done loading cache state\n"); out: context->cmpl(context->priv, error); ocf_pipeline_destroy(pipeline); } struct ocf_pipeline_arg ocf_metadata_load_all_args[] = { OCF_PL_ARG_INT(metadata_segment_core_runtime), OCF_PL_ARG_INT(metadata_segment_cleaning), OCF_PL_ARG_INT(metadata_segment_eviction), OCF_PL_ARG_INT(metadata_segment_collision), OCF_PL_ARG_INT(metadata_segment_list_info), OCF_PL_ARG_INT(metadata_segment_hash), OCF_PL_ARG_TERMINATOR(), }; struct ocf_pipeline_properties ocf_metadata_load_all_pipeline_props = { .priv_size = sizeof(struct ocf_metadata_context), .finish = ocf_metadata_load_all_finish, .steps = { OCF_PL_STEP_FOREACH(ocf_metadata_load_segment, ocf_metadata_load_all_args), OCF_PL_STEP_FOREACH(ocf_metadata_check_crc, ocf_metadata_load_all_args), OCF_PL_STEP_TERMINATOR(), }, }; /* * Load all metadata */ void ocf_metadata_load_all(ocf_cache_t cache, ocf_metadata_end_t cmpl, void *priv) { struct ocf_metadata_context *context; ocf_pipeline_t pipeline; int result; OCF_DEBUG_TRACE(cache); result = ocf_pipeline_create(&pipeline, cache, &ocf_metadata_load_all_pipeline_props); if (result) OCF_CMPL_RET(priv, result); context = ocf_pipeline_get_priv(pipeline); context->cmpl = cmpl; context->priv = priv; context->pipeline = pipeline; context->cache = cache; context->ctrl = cache->metadata.priv; ocf_pipeline_next(pipeline); } static void _recovery_rebuild_cline_metadata(ocf_cache_t cache, ocf_core_id_t core_id, uint64_t core_line, ocf_cache_line_t cache_line) { ocf_core_t core = ocf_cache_get_core(cache, core_id); ocf_part_id_t part_id; ocf_cache_line_t hash_index; part_id = PARTITION_DEFAULT; ocf_metadata_add_to_partition(cache, part_id, cache_line); hash_index = ocf_metadata_hash_func(cache, core_line, core_id); ocf_metadata_add_to_collision(cache, core_id, core_line, hash_index, cache_line); ocf_eviction_init_cache_line(cache, cache_line); ocf_eviction_set_hot_cache_line(cache, cache_line); env_atomic_inc(&core->runtime_meta->cached_clines); env_atomic_inc(&core->runtime_meta-> part_counters[part_id].cached_clines); if (metadata_test_dirty(cache, cache_line)) { env_atomic_inc(&core->runtime_meta->dirty_clines); env_atomic_inc(&core->runtime_meta-> part_counters[part_id].dirty_clines); env_atomic64_cmpxchg(&core->runtime_meta->dirty_since, 0, env_get_tick_count()); } } static void _recovery_invalidate_clean_sec(struct ocf_cache *cache, ocf_cache_line_t cline) { uint8_t i; for (i = ocf_line_start_sector(cache); i <= ocf_line_end_sector(cache); i++) { if (!metadata_test_dirty_one(cache, cline, i)) { /* Invalidate clear sectors */ metadata_clear_valid_sec_one(cache, cline, i); } } } static void _recovery_reset_cline_metadata(struct ocf_cache *cache, ocf_cache_line_t cline) { ocf_cleaning_t clean_policy_type; ocf_metadata_set_core_info(cache, cline, OCF_CORE_MAX, ULLONG_MAX); metadata_clear_valid(cache, cline); clean_policy_type = cache->conf_meta->cleaning_policy_type; ENV_BUG_ON(clean_policy_type >= ocf_cleaning_max); if (cleaning_policy_ops[clean_policy_type].init_cache_block != NULL) cleaning_policy_ops[clean_policy_type]. init_cache_block(cache, cline); } static void _recovery_rebuild_metadata(ocf_pipeline_t pipeline, void *priv, ocf_pipeline_arg_t arg) { struct ocf_metadata_context *context = priv; bool dirty_only = ocf_pipeline_arg_get_int(arg); ocf_cache_t cache = context->cache; ocf_cache_line_t cline; ocf_core_id_t core_id; uint64_t core_line; unsigned char step = 0; const uint64_t collision_table_entries = ocf_metadata_collision_table_entries(cache); ocf_metadata_start_exclusive_access(&cache->metadata.lock); for (cline = 0; cline < collision_table_entries; cline++) { ocf_metadata_get_core_info(cache, cline, &core_id, &core_line); if (core_id != OCF_CORE_MAX && (!dirty_only || metadata_test_dirty(cache, cline))) { /* Rebuild metadata for mapped cache line */ _recovery_rebuild_cline_metadata(cache, core_id, core_line, cline); if (dirty_only) _recovery_invalidate_clean_sec(cache, cline); } else { /* Reset metadata for not mapped or clean cache line */ _recovery_reset_cline_metadata(cache, cline); } OCF_COND_RESCHED(step, 128); } ocf_metadata_end_exclusive_access(&cache->metadata.lock); ocf_pipeline_next(pipeline); } static void ocf_metadata_load_recovery_legacy_finish( ocf_pipeline_t pipeline, void *priv, int error) { struct ocf_metadata_context *context = priv; ocf_cache_t cache = context->cache; if (error) { ocf_cache_log(cache, log_err, "Metadata read for recovery FAILURE\n"); ocf_metadata_error(cache); goto out; } ocf_cache_log(cache, log_info, "Done loading cache state\n"); out: context->cmpl(context->priv, error); ocf_pipeline_destroy(pipeline); } struct ocf_pipeline_properties ocf_metadata_load_recovery_legacy_pl_props = { .priv_size = sizeof(struct ocf_metadata_context), .finish = ocf_metadata_load_recovery_legacy_finish, .steps = { OCF_PL_STEP_ARG_INT(ocf_metadata_load_segment, metadata_segment_collision), OCF_PL_STEP_ARG_INT(_recovery_rebuild_metadata, true), OCF_PL_STEP_TERMINATOR(), }, }; static void _ocf_metadata_load_recovery_legacy(ocf_cache_t cache, ocf_metadata_end_t cmpl, void *priv) { struct ocf_metadata_context *context; ocf_pipeline_t pipeline; int result; OCF_DEBUG_TRACE(cache); result = ocf_pipeline_create(&pipeline, cache, &ocf_metadata_load_recovery_legacy_pl_props); if (result) OCF_CMPL_RET(priv, result); context = ocf_pipeline_get_priv(pipeline); context->cmpl = cmpl; context->priv = priv; context->pipeline = pipeline; context->cache = cache; context->ctrl = cache->metadata.priv; ocf_pipeline_next(pipeline); } static ocf_core_id_t _ocf_metadata_find_core_by_seq( struct ocf_cache *cache, ocf_seq_no_t seq_no) { ocf_core_t core; ocf_core_id_t core_id; if (seq_no == OCF_SEQ_NO_INVALID) return OCF_CORE_ID_INVALID; for_each_core_all(cache, core, core_id) { if (core->conf_meta->seq_no == seq_no) break; } return core_id; } static void ocf_metadata_load_atomic_metadata_complete( ocf_cache_t cache, void *priv, int error) { struct ocf_metadata_context *context = priv; OCF_PL_NEXT_ON_SUCCESS_RET(context->pipeline, error); } static int ocf_metadata_load_atomic_metadata_drain(void *priv, uint64_t sector_addr, uint32_t sector_no, ctx_data_t *data) { struct ocf_metadata_context *context = priv; ocf_cache_t cache = context->cache; struct ocf_atomic_metadata meta; ocf_cache_line_t line = 0; uint8_t pos = 0; ocf_seq_no_t core_seq_no = OCF_SEQ_NO_INVALID; ocf_core_id_t core_id = OCF_CORE_ID_INVALID; uint64_t core_line = 0; bool core_line_ok = false; uint32_t i; for (i = 0; i < sector_no; i++) { ctx_data_rd_check(cache->owner, &meta, data, sizeof(meta)); line = (sector_addr + i) / ocf_line_sectors(cache); line = ocf_metadata_map_phy2lg(cache, line); pos = (sector_addr + i) % ocf_line_sectors(cache); core_seq_no = meta.core_seq_no; core_line = meta.core_line; /* Look for core with sequence number same as cache line */ core_id = _ocf_metadata_find_core_by_seq( cache, core_seq_no); if (pos == 0) core_line_ok = false; if (meta.valid && core_id != OCF_CORE_ID_INVALID) { if (!core_line_ok) { ocf_metadata_set_core_info(cache, line, core_id, core_line); core_line_ok = true; } metadata_set_valid_sec_one(cache, line, pos); meta.dirty ? metadata_set_dirty_sec_one(cache, line, pos) : metadata_clear_dirty_sec_one(cache, line, pos); } } return 0; } static void ocf_metadata_load_atomic_metadata( ocf_pipeline_t pipeline, void *priv, ocf_pipeline_arg_t arg) { struct ocf_metadata_context *context = priv; ocf_cache_t cache = context->cache; int result; result = metadata_io_read_i_atomic(cache, cache->mngt_queue, context, ocf_metadata_load_atomic_metadata_drain, ocf_metadata_load_atomic_metadata_complete); if (result) { ocf_metadata_error(cache); ocf_cache_log(cache, log_err, "Metadata read for recovery FAILURE\n"); OCF_PL_FINISH_RET(pipeline, result); } } static void ocf_metadata_load_recovery_atomic_finish( ocf_pipeline_t pipeline, void *priv, int error) { struct ocf_metadata_context *context = priv; ocf_cache_t cache = context->cache; if (error) { ocf_cache_log(cache, log_err, "Metadata read for recovery FAILURE\n"); ocf_metadata_error(cache); } context->cmpl(context->priv, error); ocf_pipeline_destroy(pipeline); } struct ocf_pipeline_properties ocf_metadata_load_recovery_atomic_pl_props = { .priv_size = sizeof(struct ocf_metadata_context), .finish = ocf_metadata_load_recovery_atomic_finish, .steps = { OCF_PL_STEP(ocf_metadata_load_atomic_metadata), OCF_PL_STEP_ARG_INT(_recovery_rebuild_metadata, false), OCF_PL_STEP_TERMINATOR(), }, }; /* * RAM Implementation - Load all metadata elements from SSD */ static void _ocf_metadata_load_recovery_atomic(ocf_cache_t cache, ocf_metadata_end_t cmpl, void *priv) { struct ocf_metadata_context *context; ocf_pipeline_t pipeline; int result; OCF_DEBUG_TRACE(cache); result = ocf_pipeline_create(&pipeline, cache, &ocf_metadata_load_recovery_atomic_pl_props); if (result) OCF_CMPL_RET(priv, result); context = ocf_pipeline_get_priv(pipeline); context->cmpl = cmpl; context->priv = priv; context->pipeline = pipeline; context->cache = cache; context->ctrl = cache->metadata.priv; ocf_pipeline_next(pipeline); } /* * Load for recovery - Load only data that is required for recovery procedure */ void ocf_metadata_load_recovery(ocf_cache_t cache, ocf_metadata_end_t cmpl, void *priv) { OCF_DEBUG_TRACE(cache); if (ocf_volume_is_atomic(&cache->device->volume)) _ocf_metadata_load_recovery_atomic(cache, cmpl, priv); else _ocf_metadata_load_recovery_legacy(cache, cmpl, priv); } /******************************************************************************* * Core and part id ******************************************************************************/ void ocf_metadata_get_core_and_part_id(struct ocf_cache *cache, ocf_cache_line_t line, ocf_core_id_t *core_id, ocf_part_id_t *part_id) { const struct ocf_metadata_map *collision; const struct ocf_metadata_list_info *info; struct ocf_metadata_ctrl *ctrl = (struct ocf_metadata_ctrl *) cache->metadata.priv; collision = ocf_metadata_raw_rd_access(cache, &(ctrl->raw_desc[metadata_segment_collision]), line); info = ocf_metadata_raw_rd_access(cache, &(ctrl->raw_desc[metadata_segment_list_info]), line); if (collision && info) { if (core_id) *core_id = collision->core_id; if (part_id) *part_id = info->partition_id; } else { ocf_metadata_error(cache); if (core_id) *core_id = OCF_CORE_MAX; if (part_id) *part_id = PARTITION_DEFAULT; } } /******************************************************************************* * Hash Table ******************************************************************************/ /* * Hash Table - Get */ ocf_cache_line_t ocf_metadata_get_hash(struct ocf_cache *cache, ocf_cache_line_t index) { struct ocf_metadata_ctrl *ctrl = (struct ocf_metadata_ctrl *) cache->metadata.priv; return *(ocf_cache_line_t *)ocf_metadata_raw_rd_access(cache, &(ctrl->raw_desc[metadata_segment_hash]), index); } /* * Hash Table - Set */ void ocf_metadata_set_hash(struct ocf_cache *cache, ocf_cache_line_t index, ocf_cache_line_t line) { struct ocf_metadata_ctrl *ctrl = (struct ocf_metadata_ctrl *) cache->metadata.priv; *(ocf_cache_line_t *)ocf_metadata_raw_wr_access(cache, &(ctrl->raw_desc[metadata_segment_hash]), index) = line; } /******************************************************************************* * Bitmap status ******************************************************************************/ #include "metadata_bit.h" #define _ocf_metadata_funcs_5arg(what) \ bool ocf_metadata_##what(struct ocf_cache *cache, \ ocf_cache_line_t line, uint8_t start, uint8_t stop, bool all) \ { \ switch (cache->metadata.settings.size) { \ case ocf_cache_line_size_4: \ return _ocf_metadata_##what##_u8(cache, line, start, stop, all); \ case ocf_cache_line_size_8: \ return _ocf_metadata_##what##_u16(cache, line, start, stop, all); \ case ocf_cache_line_size_16: \ return _ocf_metadata_##what##_u32(cache, line, start, stop, all); \ case ocf_cache_line_size_32: \ return _ocf_metadata_##what##_u64(cache, line, start, stop, all); \ case ocf_cache_line_size_64: \ return _ocf_metadata_##what##_u128(cache, line, start, stop, all); \ case ocf_cache_line_size_none: \ default: \ ENV_BUG_ON(1); \ return false; \ } \ } \ #define _ocf_metadata_funcs_4arg(what) \ bool ocf_metadata_##what(struct ocf_cache *cache, \ ocf_cache_line_t line, uint8_t start, uint8_t stop) \ { \ switch (cache->metadata.settings.size) { \ case ocf_cache_line_size_4: \ return _ocf_metadata_##what##_u8(cache, line, start, stop); \ case ocf_cache_line_size_8: \ return _ocf_metadata_##what##_u16(cache, line, start, stop); \ case ocf_cache_line_size_16: \ return _ocf_metadata_##what##_u32(cache, line, start, stop); \ case ocf_cache_line_size_32: \ return _ocf_metadata_##what##_u64(cache, line, start, stop); \ case ocf_cache_line_size_64: \ return _ocf_metadata_##what##_u128(cache, line, start, stop); \ case ocf_cache_line_size_none: \ default: \ ENV_BUG_ON(1); \ return false; \ } \ } \ #define _ocf_metadata_funcs(what) \ _ocf_metadata_funcs_5arg(test_##what) \ _ocf_metadata_funcs_4arg(test_out_##what) \ _ocf_metadata_funcs_4arg(clear_##what) \ _ocf_metadata_funcs_4arg(set_##what) \ _ocf_metadata_funcs_5arg(test_and_set_##what) \ _ocf_metadata_funcs_5arg(test_and_clear_##what) _ocf_metadata_funcs(dirty) _ocf_metadata_funcs(valid) int ocf_metadata_init(struct ocf_cache *cache, ocf_cache_line_size_t cache_line_size) { int ret; OCF_DEBUG_TRACE(cache); ret = ocf_metadata_init_fixed_size(cache, cache_line_size); if (ret) { ocf_metadata_io_deinit(cache); return ret; } ret = ocf_metadata_concurrency_init(&cache->metadata.lock); if (ret) { ocf_metadata_deinit_fixed_size(cache); ocf_metadata_io_deinit(cache); return ret; } return 0; } void ocf_metadata_deinit(struct ocf_cache *cache) { OCF_DEBUG_TRACE(cache); ocf_metadata_deinit_fixed_size(cache); ocf_metadata_concurrency_deinit(&cache->metadata.lock); ocf_metadata_io_deinit(cache); } void ocf_metadata_error(struct ocf_cache *cache) { if (cache->device->metadata_error == 0) ocf_cache_log(cache, log_err, "Metadata Error\n"); env_bit_clear(ocf_cache_state_running, &cache->cache_state); cache->device->metadata_error = -1; } struct ocf_metadata_read_sb_ctx; typedef void (*ocf_metadata_read_sb_end_t)( struct ocf_metadata_read_sb_ctx *context); struct ocf_metadata_read_sb_ctx { struct ocf_superblock_config superblock; ocf_metadata_read_sb_end_t cmpl; ocf_ctx_t ctx; void *priv1; void *priv2; int error; }; static void ocf_metadata_read_sb_complete(struct ocf_io *io, int error) { struct ocf_metadata_read_sb_ctx *context = io->priv1; ctx_data_t *data = ocf_io_get_data(io); if (!error) { /* Read data from data into super block buffer */ ctx_data_rd_check(context->ctx, &context->superblock, data, sizeof(context->superblock)); } ctx_data_free(context->ctx, data); ocf_io_put(io); context->error = error; context->cmpl(context); env_free(context); } static int ocf_metadata_read_sb(ocf_ctx_t ctx, ocf_volume_t volume, ocf_metadata_read_sb_end_t cmpl, void *priv1, void *priv2) { struct ocf_metadata_read_sb_ctx *context; size_t sb_pages = BYTES_TO_PAGES(sizeof(context->superblock)); ctx_data_t *data; struct ocf_io *io; int result = 0; /* Allocate memory for first page of super block */ context = env_zalloc(sizeof(*context), ENV_MEM_NORMAL); if (!context) { ocf_log(ctx, log_err, "Memory allocation error"); return -OCF_ERR_NO_MEM; } context->cmpl = cmpl; context->ctx = ctx; context->priv1 = priv1; context->priv2 = priv2; /* Allocate resources for IO */ io = ocf_volume_new_io(volume, NULL, 0, sb_pages * PAGE_SIZE, OCF_READ, 0, 0); if (!io) { ocf_log(ctx, log_err, "Memory allocation error"); result = -OCF_ERR_NO_MEM; goto err_io; } data = ctx_data_alloc(ctx, sb_pages); if (!data) { ocf_log(ctx, log_err, "Memory allocation error"); result = -OCF_ERR_NO_MEM; goto err_data; } /* * Read first page of cache device in order to recover metadata * properties */ result = ocf_io_set_data(io, data, 0); if (result) { ocf_log(ctx, log_err, "Metadata IO configuration error\n"); result = -OCF_ERR_IO; goto err_set_data; } ocf_io_set_cmpl(io, context, NULL, ocf_metadata_read_sb_complete); ocf_volume_submit_io(io); return 0; err_set_data: ctx_data_free(ctx, data); err_data: ocf_io_put(io); err_io: env_free(context); return result; } static void ocf_metadata_load_properties_cmpl( struct ocf_metadata_read_sb_ctx *context) { struct ocf_metadata_load_properties properties; struct ocf_superblock_config *superblock = &context->superblock; ocf_metadata_load_properties_end_t cmpl = context->priv1; void *priv = context->priv2; ocf_ctx_t ctx = context->ctx; if (superblock->magic_number != CACHE_MAGIC_NUMBER) { ocf_log(ctx, log_info, "Cannot detect pre-existing metadata\n"); OCF_CMPL_RET(priv, -OCF_ERR_NO_METADATA, NULL); } if (METADATA_VERSION() != superblock->metadata_version) { ocf_log(ctx, log_err, "Metadata version mismatch!\n"); OCF_CMPL_RET(priv, -OCF_ERR_METADATA_VER, NULL); } if (!ocf_cache_line_size_is_valid(superblock->line_size)) { ocf_log(ctx, log_err, "ERROR: Invalid cache line size!\n"); OCF_CMPL_RET(priv, -OCF_ERR_INVAL, NULL); } if ((unsigned)superblock->metadata_layout >= ocf_metadata_layout_max) { ocf_log(ctx, log_err, "ERROR: Invalid metadata layout!\n"); OCF_CMPL_RET(priv, -OCF_ERR_INVAL, NULL); } if (superblock->cache_mode >= ocf_cache_mode_max) { ocf_log(ctx, log_err, "ERROR: Invalid cache mode!\n"); OCF_CMPL_RET(priv, -OCF_ERR_INVAL, NULL); } if (superblock->clean_shutdown > ocf_metadata_clean_shutdown) { ocf_log(ctx, log_err, "ERROR: Invalid shutdown status!\n"); OCF_CMPL_RET(priv, -OCF_ERR_INVAL, NULL); } if (superblock->dirty_flushed > DIRTY_FLUSHED) { ocf_log(ctx, log_err, "ERROR: Invalid flush status!\n"); OCF_CMPL_RET(priv, -OCF_ERR_INVAL, NULL); } properties.line_size = superblock->line_size; properties.layout = superblock->metadata_layout; properties.cache_mode = superblock->cache_mode; properties.shutdown_status = superblock->clean_shutdown; properties.dirty_flushed = superblock->dirty_flushed; properties.cache_name = superblock->name; OCF_CMPL_RET(priv, 0, &properties); } void ocf_metadata_load_properties(ocf_volume_t volume, ocf_metadata_load_properties_end_t cmpl, void *priv) { int result; OCF_DEBUG_TRACE(cache); result = ocf_metadata_read_sb(volume->cache->owner, volume, ocf_metadata_load_properties_cmpl, cmpl, priv); if (result) OCF_CMPL_RET(priv, result, NULL); } /* metadata segment data + iterators */ struct query_cores_data { /* array of data */ ctx_data_t *data; /* current metadata entry counter */ uint32_t entry; /* number of entries per page */ uint32_t entries_in_page; }; /* query cores context */ struct query_cores_context { ocf_ctx_t ctx; struct ocf_superblock_config superblock; struct ocf_metadata_uuid muuid; struct { struct query_cores_data core_uuids; struct query_cores_data core_config; struct query_cores_data superblock; } data; env_atomic count; env_atomic error; /* OCF entry point parameters */ struct { struct ocf_volume_uuid *uuids; uint32_t uuids_count; void *priv; ocf_metadata_query_cores_end_t cmpl; } params; }; /* copy next metadata entry from data to memory buffer */ static void ocf_metadata_query_cores_data_read(ocf_ctx_t ctx, struct query_cores_data *data, void *buf, uint32_t size) { if (data->entry > 0 && data->entry % data->entries_in_page == 0) { ctx_data_seek_check(ctx, data->data, ctx_data_seek_current, PAGE_SIZE - data->entries_in_page * size); } ctx_data_rd_check(ctx, buf, data->data, size); ++data->entry; } static void ocf_metadata_query_cores_end(struct query_cores_context *context, int error) { ocf_ctx_t ctx = context->ctx; unsigned i, core_idx; struct ocf_metadata_uuid *muuid = &context->muuid; struct ocf_core_meta_config core_config; unsigned core_count = 0; unsigned long valid_core_bitmap[(OCF_CORE_MAX / (sizeof(unsigned long) * 8)) + 1]; unsigned out_cores; if (error) env_atomic_cmpxchg(&context->error, 0, error); if (env_atomic_dec_return(&context->count)) return; error = env_atomic_read(&context->error); if (error) goto exit; /* read superblock */ ctx_data_rd_check(ctx, &context->superblock, context->data.superblock.data, sizeof(context->superblock)); if (context->superblock.magic_number != CACHE_MAGIC_NUMBER) { error = -OCF_ERR_NO_METADATA; goto exit; } env_memset(&valid_core_bitmap, sizeof(valid_core_bitmap), 0); /* read valid cores from core config segment */ for (i = 0; i < OCF_CORE_MAX; i++) { ocf_metadata_query_cores_data_read(ctx, &context->data.core_config, &core_config, sizeof(core_config)); if (core_config.valid) { env_bit_set(i, valid_core_bitmap); ++core_count; } } /* read core uuids */ out_cores = OCF_MIN(core_count, context->params.uuids_count); for (i = 0, core_idx = 0; i < OCF_CORE_MAX && core_idx < out_cores; i++) { ocf_metadata_query_cores_data_read(ctx, &context->data.core_uuids, muuid, sizeof(*muuid)); if (!env_bit_test(i, valid_core_bitmap)) continue; if (muuid->size > OCF_VOLUME_UUID_MAX_SIZE) { error = -OCF_ERR_INVAL; goto exit; } if (muuid->size > context->params.uuids[core_idx].size) { error = -OCF_ERR_INVAL; goto exit; } error = env_memcpy(context->params.uuids[core_idx].data, context->params.uuids[core_idx].size, muuid->data, muuid->size); if (error) goto exit; context->params.uuids[core_idx].size = muuid->size; ++core_idx; } exit: /* provide actual core count to completion */ context->params.cmpl(context->params.priv, error, core_count); /* free data */ ctx_data_free(ctx, context->data.core_uuids.data); ctx_data_free(ctx, context->data.core_config.data); ctx_data_free(ctx, context->data.superblock.data); env_secure_free(context, sizeof(*context)); } static void ocf_metadata_query_cores_end_io(struct ocf_io *io, int error) { struct query_cores_context *context = io->priv1; ocf_io_put(io); ocf_metadata_query_cores_end(context, error); } static int ocf_metadata_query_cores_io(ocf_volume_t volume, struct query_cores_context *context, ctx_data_t *data, uint32_t offset, uint64_t page, uint32_t num_pages) { struct ocf_io *io; int err; env_atomic_inc(&context->count); /* Allocate new IO */ io = ocf_volume_new_io(volume, NULL, PAGES_TO_BYTES(page), PAGES_TO_BYTES(num_pages), OCF_READ, 0, 0); if (!io) { err = -OCF_ERR_NO_MEM; goto exit_error; } /* Setup IO */ ocf_io_set_cmpl(io, context, NULL, ocf_metadata_query_cores_end_io); err = ocf_io_set_data(io, data, PAGES_TO_BYTES(offset)); if (err) { ocf_io_put(io); goto exit_error; } ocf_volume_submit_io(io); return 0; exit_error: env_atomic_dec(&context->count); return err; } int ocf_metadata_query_cores_segment_io( struct query_cores_context *context, ocf_ctx_t owner, ocf_volume_t volume, enum ocf_metadata_segment_id segment, struct ocf_metadata_ctrl *ctrl, struct query_cores_data *segment_data) { uint32_t pages_left; uint32_t pages; uint32_t addr; uint32_t offset; uint32_t io_count; uint32_t i; uint32_t max_pages_per_io; int err = 0; unsigned int max_io_size = ocf_volume_get_max_io_size(volume); if (!max_io_size) { err = -OCF_ERR_INVAL; goto exit; } max_pages_per_io = max_io_size / PAGE_SIZE; /* Allocate data */ segment_data->data = ctx_data_alloc(owner, ctrl->raw_desc[segment].ssd_pages); if (!segment_data->data) { err = -OCF_ERR_NO_MEM; goto exit; } segment_data->entries_in_page = ctrl->raw_desc[segment].entries_in_page; io_count = OCF_DIV_ROUND_UP(ctrl->raw_desc[segment].ssd_pages, max_pages_per_io); /* submit segment data I/O */ pages_left = ctrl->raw_desc[segment].ssd_pages; addr = ctrl->raw_desc[segment].ssd_pages_offset; offset = 0; i = 0; while (pages_left) { ENV_BUG_ON(i >= io_count); pages = OCF_MIN(pages_left, max_pages_per_io); err = ocf_metadata_query_cores_io(volume, context, segment_data->data, offset, addr, pages); if (err) goto exit; addr += pages; offset += pages; pages_left -= pages; ++i; } exit: return err; } void ocf_metadata_query_cores(ocf_ctx_t owner, ocf_volume_t volume, struct ocf_volume_uuid *uuid, uint32_t count, ocf_metadata_query_cores_end_t cmpl, void *priv) { struct ocf_metadata_ctrl *ctrl = NULL; struct query_cores_context *context; int err; if (count > OCF_CORE_MAX) OCF_CMPL_RET(priv, -OCF_ERR_INVAL, 0); /* intialize query context */ context = env_secure_alloc(sizeof(*context)); if (!context) OCF_CMPL_RET(priv, -OCF_ERR_NO_MEM, 0); ENV_BUG_ON(env_memset(context, sizeof(*context), 0)); context->ctx = owner; context->params.cmpl = cmpl; context->params.priv = priv; context->params.uuids = uuid; context->params.uuids_count = count; env_atomic_set(&context->count, 1); ctrl = ocf_metadata_ctrl_init(false); if (!ctrl) { err = -OCF_ERR_NO_MEM; goto exit; } /* superblock I/O */ err = ocf_metadata_query_cores_segment_io(context, owner, volume, metadata_segment_sb_config, ctrl, &context->data.superblock); if (err) goto exit; /* core config I/O */ err = ocf_metadata_query_cores_segment_io(context, owner, volume, metadata_segment_core_uuid, ctrl, &context->data.core_uuids); if (err) goto exit; /* core uuid I/O */ err = ocf_metadata_query_cores_segment_io(context, owner, volume, metadata_segment_core_config, ctrl, &context->data.core_config); if (err) goto exit; exit: env_vfree(ctrl); ocf_metadata_query_cores_end(context, err); } static void ocf_metadata_probe_cmpl(struct ocf_metadata_read_sb_ctx *context) { struct ocf_metadata_probe_status status; struct ocf_superblock_config *superblock = &context->superblock; ocf_metadata_probe_end_t cmpl = context->priv1; void *priv = context->priv2; if (superblock->magic_number != CACHE_MAGIC_NUMBER) OCF_CMPL_RET(priv, -OCF_ERR_NO_METADATA, NULL); if (METADATA_VERSION() != superblock->metadata_version) OCF_CMPL_RET(priv, -OCF_ERR_METADATA_VER, NULL); if (superblock->clean_shutdown > ocf_metadata_clean_shutdown) OCF_CMPL_RET(priv, -OCF_ERR_INVAL, NULL); if (superblock->dirty_flushed > DIRTY_FLUSHED) OCF_CMPL_RET(priv, -OCF_ERR_INVAL, NULL); status.clean_shutdown = (superblock->clean_shutdown != ocf_metadata_dirty_shutdown); status.cache_dirty = (superblock->dirty_flushed == DIRTY_NOT_FLUSHED); env_strncpy(status.cache_name, OCF_CACHE_NAME_SIZE, superblock->name, OCF_CACHE_NAME_SIZE); OCF_CMPL_RET(priv, 0, &status); } void ocf_metadata_probe(ocf_ctx_t ctx, ocf_volume_t volume, ocf_metadata_probe_end_t cmpl, void *priv) { int result; OCF_CHECK_NULL(ctx); OCF_CHECK_NULL(volume); result = ocf_metadata_read_sb(ctx, volume, ocf_metadata_probe_cmpl, cmpl, priv); if (result) OCF_CMPL_RET(priv, result, NULL); } /* completion context for query_cores */ struct ocf_metadata_query_cores_context { ocf_metadata_probe_cores_end_t cmpl; void *priv; }; static void ocf_metadata_probe_cores_end(void *_context, int error, unsigned num_cores) { struct ocf_metadata_query_cores_context *context = _context; context->cmpl(context->priv, error, num_cores); env_vfree(context); } void ocf_metadata_probe_cores(ocf_ctx_t ctx, ocf_volume_t volume, struct ocf_volume_uuid *uuids, uint32_t uuids_count, ocf_metadata_probe_cores_end_t cmpl, void *priv) { struct ocf_metadata_query_cores_context *context; context = env_vzalloc(sizeof(*context)); if (!context) OCF_CMPL_RET(priv, -OCF_ERR_NO_MEM, 0); context->cmpl = cmpl; context->priv = priv; ocf_metadata_query_cores(ctx, volume, uuids, uuids_count, ocf_metadata_probe_cores_end, context); }