
This simplifies cases when we want to call completion callback and immediately return from void-returning function, by allowing to explicitly express programmers intent. That way we can avoid cases when return statement is missing by mistake (this patch fixes at least one bug related to this). Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
2984 lines
80 KiB
C
2984 lines
80 KiB
C
/*
|
|
* Copyright(c) 2012-2018 Intel Corporation
|
|
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
*/
|
|
|
|
#include "metadata.h"
|
|
#include "metadata_hash.h"
|
|
#include "metadata_raw.h"
|
|
#include "metadata_io.h"
|
|
#include "metadata_status.h"
|
|
#include "../concurrency/ocf_concurrency.h"
|
|
#include "../utils/utils_cache_line.h"
|
|
#include "../utils/utils_pipeline.h"
|
|
#include "../ocf_def_priv.h"
|
|
#include "../ocf_priv.h"
|
|
|
|
#define OCF_METADATA_HASH_DEBUG 0
|
|
|
|
#if 1 == OCF_METADATA_HASH_DEBUG
|
|
#define OCF_DEBUG_TRACE(cache) \
|
|
ocf_cache_log(cache, log_info, "[Metadata][Hash] %s\n", __func__)
|
|
|
|
#define OCF_DEBUG_PARAM(cache, format, ...) \
|
|
ocf_cache_log(cache, log_info, "[Metadata][Hash] %s - "format"\n", \
|
|
__func__, ##__VA_ARGS__)
|
|
#else
|
|
#define OCF_DEBUG_TRACE(cache)
|
|
#define OCF_DEBUG_PARAM(cache, format, ...)
|
|
#endif
|
|
|
|
#define METADATA_MEM_POOL(ctrl, section) ctrl->raw_desc[section].mem_pool
|
|
|
|
static void ocf_metadata_hash_init_iface(struct ocf_cache *cache,
|
|
ocf_metadata_layout_t layout);
|
|
|
|
#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;
|
|
}
|
|
|
|
/*
|
|
* Hash metadata control structure
|
|
*/
|
|
struct ocf_metadata_hash_ctrl {
|
|
ocf_cache_line_t cachelines;
|
|
ocf_cache_line_t start_page;
|
|
ocf_cache_line_t count_pages;
|
|
uint32_t device_lines;
|
|
size_t mapping_size;
|
|
struct ocf_metadata_raw raw_desc[metadata_segment_max];
|
|
};
|
|
|
|
/*
|
|
* get entries for specified metadata hash type
|
|
*/
|
|
static ocf_cache_line_t ocf_metadata_hash_get_entires(
|
|
enum ocf_metadata_segment 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, OCF_HASH_PRIME) *
|
|
OCF_HASH_PRIME - 1;
|
|
|
|
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_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_hash_get_element_size(
|
|
enum ocf_metadata_segment 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_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_hash_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_hash_calculate_metadata_size(
|
|
struct ocf_cache *cache,
|
|
struct ocf_metadata_hash_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_hash_get_entires(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_hash_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;
|
|
}
|
|
|
|
static const char * const ocf_metadata_hash_raw_names[] = {
|
|
[metadata_segment_sb_config] = "Super block config",
|
|
[metadata_segment_sb_runtime] = "Super block runtime",
|
|
[metadata_segment_reserved] = "Reserved",
|
|
[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_HASH_DEBUG
|
|
/*
|
|
* Debug info functions prints metadata and raw containers information
|
|
*/
|
|
static void ocf_metadata_hash_raw_info(struct ocf_cache *cache,
|
|
struct ocf_metadata_hash_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_hash_raw_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_hash_raw_names[i], capacity, unit);
|
|
}
|
|
}
|
|
#else
|
|
#define ocf_metadata_hash_raw_info(cache, ctrl)
|
|
#endif
|
|
|
|
/*
|
|
* Deinitialize hash metadata interface
|
|
*/
|
|
static void ocf_metadata_hash_deinit_variable_size(struct ocf_cache *cache)
|
|
{
|
|
|
|
int result = 0;
|
|
uint32_t i = 0;
|
|
|
|
struct ocf_metadata_hash_ctrl *ctrl = (struct ocf_metadata_hash_ctrl *)
|
|
cache->metadata.iface_priv;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
/*
|
|
* De initialize RAW types
|
|
*/
|
|
for (i = metadata_segment_variable_size_start;
|
|
i < metadata_segment_max; i++) {
|
|
result |= ocf_metadata_raw_deinit(cache,
|
|
&(ctrl->raw_desc[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_hash_deinit(struct ocf_cache *cache)
|
|
{
|
|
int result = 0;
|
|
uint32_t i;
|
|
|
|
struct ocf_metadata_hash_ctrl *ctrl = (struct ocf_metadata_hash_ctrl *)
|
|
cache->metadata.iface_priv;
|
|
|
|
for (i = 0; i < metadata_segment_fixed_size_max; i++) {
|
|
result |= ocf_metadata_raw_deinit(cache,
|
|
&(ctrl->raw_desc[i]));
|
|
}
|
|
|
|
env_vfree(ctrl);
|
|
cache->metadata.iface_priv = NULL;
|
|
|
|
if (result)
|
|
ENV_BUG();
|
|
}
|
|
|
|
static struct ocf_metadata_hash_ctrl *ocf_metadata_hash_ctrl_init(
|
|
bool metadata_volatile)
|
|
{
|
|
struct ocf_metadata_hash_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_hash_get_element_size(i, NULL);
|
|
raw->entries_in_page = PAGE_SIZE / raw->entry_size;
|
|
|
|
/* Setup number of entries */
|
|
raw->entries = ocf_metadata_hash_get_entires(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;
|
|
}
|
|
|
|
int ocf_metadata_hash_init(struct ocf_cache *cache,
|
|
ocf_cache_line_size_t cache_line_size)
|
|
{
|
|
struct ocf_metadata_hash_ctrl *ctrl = NULL;
|
|
struct ocf_metadata *metadata = &cache->metadata;
|
|
struct ocf_cache_line_settings *settings =
|
|
(struct ocf_cache_line_settings *)&metadata->settings;
|
|
uint32_t i = 0;
|
|
int result = 0;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
ENV_WARN_ON(metadata->iface_priv);
|
|
|
|
ocf_metadata_config_init(cache, settings, cache_line_size);
|
|
|
|
ctrl = ocf_metadata_hash_ctrl_init(metadata->is_volatile);
|
|
if (!ctrl)
|
|
return -ENOMEM;
|
|
metadata->iface_priv = ctrl;
|
|
|
|
for (i = 0; i < metadata_segment_fixed_size_max; i++) {
|
|
result |= ocf_metadata_raw_init(cache, &(ctrl->raw_desc[i]));
|
|
if (result)
|
|
break;
|
|
}
|
|
|
|
if (result) {
|
|
ocf_metadata_hash_deinit(cache);
|
|
} else {
|
|
cache->conf_meta = METADATA_MEM_POOL(ctrl,
|
|
metadata_segment_sb_config);
|
|
|
|
/* Set core metadata */
|
|
cache->core_conf_meta = METADATA_MEM_POOL(ctrl,
|
|
metadata_segment_core_config);
|
|
|
|
cache->core_runtime_meta = METADATA_MEM_POOL(ctrl,
|
|
metadata_segment_core_runtime);
|
|
|
|
env_spinlock_init(&cache->metadata.lock.eviction);
|
|
env_rwlock_init(&cache->metadata.lock.status);
|
|
env_rwsem_init(&cache->metadata.lock.collision);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* 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_hash_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 = -ENODATA;
|
|
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_hash_query_cores_data_read(ctx,
|
|
&context->data.core_config,
|
|
&core_config, sizeof(core_config));
|
|
if (core_config.added) {
|
|
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_hash_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 = -EINVAL;
|
|
goto exit;
|
|
}
|
|
if (muuid->size > context->params.uuids[core_idx].size) {
|
|
error = -ENOSPC;
|
|
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);
|
|
if (!io) {
|
|
err = -OCF_ERR_NO_MEM;
|
|
goto exit_error;
|
|
}
|
|
|
|
/* Setup IO */
|
|
ocf_io_configure(io,
|
|
PAGES_TO_BYTES(page),
|
|
PAGES_TO_BYTES(num_pages),
|
|
OCF_READ, 0, 0);
|
|
|
|
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 segment,
|
|
struct ocf_metadata_hash_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 = ocf_volume_get_max_io_size(volume) /
|
|
PAGE_SIZE;
|
|
int err = 0;
|
|
|
|
/* Allocate data */
|
|
segment_data->data = ctx_data_alloc(owner,
|
|
ctrl->raw_desc[segment].ssd_pages);
|
|
if (!segment_data->data) {
|
|
err = -ENOMEM;
|
|
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_hash_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_hash_ctrl *ctrl = NULL;
|
|
struct query_cores_context *context;
|
|
int err;
|
|
|
|
if (count > OCF_CORE_MAX)
|
|
OCF_CMPL_RET(priv, -EINVAL, 0);
|
|
|
|
/* intialize query context */
|
|
context = env_secure_alloc(sizeof(*context));
|
|
if (!context)
|
|
OCF_CMPL_RET(priv, -ENOMEM, 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_hash_ctrl_init(false);
|
|
if (!ctrl) {
|
|
err = -ENOMEM;
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Initialize hash metadata interface
|
|
*/
|
|
static int ocf_metadata_hash_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_hash_ctrl *ctrl = NULL;
|
|
struct ocf_cache_line_settings *settings =
|
|
(struct ocf_cache_line_settings *)&cache->metadata.settings;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
ENV_WARN_ON(!cache->metadata.iface_priv);
|
|
|
|
ctrl = cache->metadata.iface_priv;
|
|
|
|
ctrl->device_lines = device_size / cache_line_size;
|
|
|
|
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_hash_init_iface(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_hash_get_element_size(i, settings);
|
|
raw->entries_in_page = PAGE_SIZE / raw->entry_size;
|
|
}
|
|
|
|
if (0 != ocf_metadata_hash_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);
|
|
|
|
/*
|
|
* Initialize all dynamic size RAW types
|
|
*/
|
|
for (i = metadata_segment_variable_size_start;
|
|
i < metadata_segment_max; i++) {
|
|
result |= ocf_metadata_raw_init(cache, &(ctrl->raw_desc[i]));
|
|
|
|
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_hash_raw_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_hash_raw_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_hash_raw_names[i],
|
|
sizeof(struct ocf_superblock_runtime));
|
|
} else {
|
|
ocf_cache_log(cache, log_info, "%s size : %llu kiB\n",
|
|
ocf_metadata_hash_raw_names[i],
|
|
ctrl->raw_desc[i].ssd_pages
|
|
* PAGE_SIZE / KiB);
|
|
}
|
|
}
|
|
|
|
finalize:
|
|
if (result) {
|
|
/*
|
|
* Hash De-Init also contains RAW deinitialization
|
|
*/
|
|
ocf_metadata_hash_deinit_variable_size(cache);
|
|
} else {
|
|
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->device->metadata_offset_line = ctrl->count_pages;
|
|
|
|
cache->conf_meta->cachelines = ctrl->cachelines;
|
|
cache->conf_meta->line_size = cache_line_size;
|
|
|
|
ocf_metadata_hash_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 = -EINVAL;
|
|
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");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static inline void _ocf_init_collision_entry(struct ocf_cache *cache,
|
|
ocf_cache_line_t idx, ocf_cache_line_t next,
|
|
ocf_cache_line_t prev)
|
|
{
|
|
ocf_cache_line_t invalid_idx = cache->device->collision_table_entries;
|
|
ocf_part_id_t invalid_part_id = PARTITION_INVALID;
|
|
|
|
ocf_metadata_set_partition_info(cache, idx,
|
|
invalid_part_id, next, prev);
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Default initialization of freelist partition
|
|
*/
|
|
static void ocf_metadata_hash_init_freelist_seq(struct ocf_cache *cache)
|
|
{
|
|
uint32_t step = 0;
|
|
unsigned int i = 0;
|
|
ocf_cache_line_t collision_table_entries =
|
|
cache->device->collision_table_entries;
|
|
|
|
cache->device->freelist_part->head = 0;
|
|
cache->device->freelist_part->curr_size = cache->device->collision_table_entries;
|
|
|
|
/* hash_father is an index in hash_table and it's limited
|
|
* to the hash_table_entries
|
|
* hash_table_entries is invalid index here.
|
|
*/
|
|
_ocf_init_collision_entry(cache, i, 1, collision_table_entries);
|
|
|
|
for (i = 1; i < collision_table_entries - 1; i++) {
|
|
_ocf_init_collision_entry(cache, i, i + 1, i - 1);
|
|
OCF_COND_RESCHED_DEFAULT(step);
|
|
}
|
|
|
|
cache->device->freelist_part->tail = i;
|
|
_ocf_init_collision_entry(cache, i, collision_table_entries, i - 1);
|
|
}
|
|
|
|
/*
|
|
* Modified initialization of freelist partition
|
|
*/
|
|
static void ocf_metadata_hash_init_freelist_striping(
|
|
struct ocf_cache *cache)
|
|
{
|
|
uint32_t step = 0;
|
|
unsigned int i, j;
|
|
ocf_cache_line_t prev, next;
|
|
ocf_cache_line_t idx, last_page;
|
|
ocf_cache_line_t collision_table_entries =
|
|
cache->device->collision_table_entries;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
unsigned int entries_in_page =
|
|
ctrl->raw_desc[metadata_segment_collision].entries_in_page;
|
|
unsigned int pages =
|
|
ctrl->raw_desc[metadata_segment_collision].ssd_pages;
|
|
|
|
cache->device->freelist_part->head = 0;
|
|
cache->device->freelist_part->curr_size = cache->device->collision_table_entries;
|
|
|
|
/* Modified initialization procedure */
|
|
prev = next = collision_table_entries;
|
|
last_page = pages;
|
|
|
|
for (i = 0; i < pages; i++) {
|
|
idx = i * entries_in_page;
|
|
for (j = 0; j < entries_in_page &&
|
|
idx < collision_table_entries; j++) {
|
|
next = idx + entries_in_page;
|
|
|
|
if (next >= collision_table_entries)
|
|
next = j + 1;
|
|
|
|
_ocf_init_collision_entry(cache, idx, next, prev);
|
|
|
|
if (idx >= entries_in_page - 1) {
|
|
prev = idx - entries_in_page + 1;
|
|
} else {
|
|
prev = last_page * entries_in_page + j;
|
|
if (prev >= collision_table_entries) {
|
|
prev -= entries_in_page;
|
|
last_page = pages - 1;
|
|
}
|
|
}
|
|
|
|
OCF_COND_RESCHED_DEFAULT(step);
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
if (collision_table_entries < entries_in_page) {
|
|
idx = collision_table_entries - 1;
|
|
} else {
|
|
idx = pages * entries_in_page - 1;
|
|
if (idx >= collision_table_entries)
|
|
idx -= entries_in_page;
|
|
|
|
}
|
|
|
|
/* Terminate free list */
|
|
cache->device->freelist_part->tail = idx;
|
|
ocf_metadata_set_partition_next(cache, idx, collision_table_entries);
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialize hash table
|
|
*/
|
|
static void ocf_metadata_hash_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
|
|
*/
|
|
static ocf_cache_line_t ocf_metadata_hash_pages(struct ocf_cache *cache)
|
|
{
|
|
struct ocf_metadata_hash_ctrl *ctrl = NULL;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
return ctrl->count_pages;
|
|
}
|
|
|
|
/*
|
|
* Get amount of cache lines
|
|
*/
|
|
static ocf_cache_line_t ocf_metadata_hash_cachelines(
|
|
struct ocf_cache *cache)
|
|
{
|
|
struct ocf_metadata_hash_ctrl *ctrl = NULL;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
return ctrl->cachelines;
|
|
}
|
|
|
|
static size_t ocf_metadata_hash_size_of(struct ocf_cache *cache)
|
|
{
|
|
uint32_t i = 0;
|
|
size_t size = 0;
|
|
struct ocf_metadata_hash_ctrl *ctrl = NULL;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_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_concurrency_size_of(cache);
|
|
|
|
return size;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Super Block
|
|
******************************************************************************/
|
|
|
|
struct ocf_metadata_hash_context {
|
|
ocf_metadata_end_t cmpl;
|
|
void *priv;
|
|
ocf_pipeline_t pipeline;
|
|
ocf_cache_t cache;
|
|
};
|
|
|
|
static void ocf_metadata_hash_generic_complete(void *priv, int error)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
|
|
if (error)
|
|
ocf_pipeline_finish(context->pipeline, error);
|
|
else
|
|
ocf_pipeline_next(context->pipeline);
|
|
}
|
|
|
|
static void ocf_medatata_hash_load_segment(ocf_pipeline_t pipeline,
|
|
void *priv, ocf_pipeline_arg_t arg)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
int segment = ocf_pipeline_arg_get_int(arg);
|
|
struct ocf_metadata_hash_ctrl *ctrl;
|
|
ocf_cache_t cache = context->cache;
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *)cache->metadata.iface_priv;
|
|
|
|
ocf_metadata_raw_load_all(cache, &ctrl->raw_desc[segment],
|
|
ocf_metadata_hash_generic_complete, context);
|
|
}
|
|
|
|
static void ocf_medatata_hash_check_crc_sb_config(ocf_pipeline_t pipeline,
|
|
void *priv, ocf_pipeline_arg_t arg)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
struct ocf_metadata_hash_ctrl *ctrl;
|
|
struct ocf_superblock_config *sb_config;
|
|
ocf_cache_t cache = context->cache;
|
|
int segment = metadata_segment_sb_config;
|
|
uint32_t crc;
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *)cache->metadata.iface_priv;
|
|
sb_config = METADATA_MEM_POOL(ctrl, metadata_segment_sb_config);
|
|
|
|
crc = env_crc32(0, (void *)sb_config,
|
|
offsetof(struct ocf_superblock_config, checksum));
|
|
|
|
if (crc != sb_config->checksum[segment]) {
|
|
/* Checksum does not match */
|
|
ocf_cache_log(cache, log_err,
|
|
"Loading %s ERROR, invalid checksum",
|
|
ocf_metadata_hash_raw_names[segment]);
|
|
ocf_pipeline_finish(pipeline, -OCF_ERR_INVAL);
|
|
return;
|
|
}
|
|
|
|
ocf_pipeline_next(pipeline);
|
|
}
|
|
|
|
static void ocf_medatata_hash_check_crc(ocf_pipeline_t pipeline,
|
|
void *priv, ocf_pipeline_arg_t arg)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
int segment = ocf_pipeline_arg_get_int(arg);
|
|
struct ocf_metadata_hash_ctrl *ctrl;
|
|
struct ocf_superblock_config *sb_config;
|
|
ocf_cache_t cache = context->cache;
|
|
uint32_t crc;
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *)cache->metadata.iface_priv;
|
|
sb_config = METADATA_MEM_POOL(ctrl, metadata_segment_sb_config);
|
|
|
|
crc = ocf_metadata_raw_checksum(cache, &(ctrl->raw_desc[segment]));
|
|
|
|
if (crc != sb_config->checksum[segment]) {
|
|
/* Checksum does not match */
|
|
ocf_cache_log(cache, log_err,
|
|
"Loading %s ERROR, invalid checksum",
|
|
ocf_metadata_hash_raw_names[segment]);
|
|
ocf_pipeline_finish(pipeline, -OCF_ERR_INVAL);
|
|
return;
|
|
}
|
|
|
|
ocf_pipeline_next(pipeline);
|
|
}
|
|
|
|
static void ocf_medatata_hash_load_superblock_post(ocf_pipeline_t pipeline,
|
|
void *priv, ocf_pipeline_arg_t arg)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
struct ocf_metadata_hash_ctrl *ctrl;
|
|
struct ocf_superblock_config *sb_config;
|
|
ocf_cache_t cache = context->cache;
|
|
struct ocf_metadata_uuid *muuid;
|
|
struct ocf_volume_uuid uuid;
|
|
uint32_t i;
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *)cache->metadata.iface_priv;
|
|
sb_config = METADATA_MEM_POOL(ctrl, metadata_segment_sb_config);
|
|
|
|
for (i = 0; i < OCF_CORE_MAX; i++) {
|
|
if (!cache->core_conf_meta[i].added)
|
|
continue;
|
|
|
|
muuid = ocf_metadata_get_core_uuid(cache, i);
|
|
uuid.data = muuid->data;
|
|
uuid.size = muuid->size;
|
|
|
|
/* Initialize core volume */
|
|
ocf_volume_init(&cache->core[i].volume,
|
|
ocf_ctx_get_volume_type(cache->owner,
|
|
cache->core_conf_meta[i].type),
|
|
&uuid, false);
|
|
}
|
|
|
|
/* Restore all dynamics items */
|
|
|
|
if (sb_config->core_count > OCF_CORE_MAX) {
|
|
ocf_cache_log(cache, log_err,
|
|
"Loading cache state ERROR, invalid cores count\n");
|
|
ocf_pipeline_finish(pipeline, -OCF_ERR_INVAL);
|
|
return;
|
|
}
|
|
|
|
if (sb_config->valid_parts_no > OCF_IO_CLASS_MAX) {
|
|
ocf_cache_log(cache, log_err,
|
|
"Loading cache state ERROR, invalid partition count\n");
|
|
ocf_pipeline_finish(pipeline, -OCF_ERR_INVAL);
|
|
return;
|
|
}
|
|
|
|
ocf_pipeline_next(pipeline);
|
|
}
|
|
|
|
static void ocf_metadata_hash_load_superblock_finish(ocf_pipeline_t pipeline,
|
|
void *priv, int error)
|
|
{
|
|
struct ocf_metadata_hash_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);
|
|
}
|
|
|
|
context->cmpl(context->priv, error);
|
|
ocf_pipeline_destroy(pipeline);
|
|
}
|
|
|
|
struct ocf_pipeline_arg ocf_metadata_hash_load_sb_load_segment_args[] = {
|
|
OCF_PL_ARG_INT(metadata_segment_sb_config),
|
|
OCF_PL_ARG_INT(metadata_segment_sb_runtime),
|
|
OCF_PL_ARG_INT(metadata_segment_core_config),
|
|
OCF_PL_ARG_INT(metadata_segment_core_uuid),
|
|
OCF_PL_ARG_TERMINATOR(),
|
|
};
|
|
|
|
struct ocf_pipeline_arg ocf_metadata_hash_load_sb_check_crc_args[] = {
|
|
OCF_PL_ARG_INT(metadata_segment_sb_runtime),
|
|
OCF_PL_ARG_INT(metadata_segment_core_config),
|
|
OCF_PL_ARG_INT(metadata_segment_core_uuid),
|
|
OCF_PL_ARG_TERMINATOR(),
|
|
};
|
|
|
|
struct ocf_pipeline_properties ocf_metadata_hash_load_sb_pipeline_props = {
|
|
.priv_size = sizeof(struct ocf_metadata_hash_context),
|
|
.finish = ocf_metadata_hash_load_superblock_finish,
|
|
.steps = {
|
|
OCF_PL_STEP_FOREACH(ocf_medatata_hash_load_segment,
|
|
ocf_metadata_hash_load_sb_load_segment_args),
|
|
OCF_PL_STEP(ocf_medatata_hash_check_crc_sb_config),
|
|
OCF_PL_STEP_FOREACH(ocf_medatata_hash_check_crc,
|
|
ocf_metadata_hash_load_sb_check_crc_args),
|
|
OCF_PL_STEP(ocf_medatata_hash_load_superblock_post),
|
|
OCF_PL_STEP_TERMINATOR(),
|
|
},
|
|
};
|
|
|
|
/*
|
|
* Super Block - Load, This function has to prevent to pointers overwrite
|
|
*/
|
|
static void ocf_metadata_hash_load_superblock(ocf_cache_t cache,
|
|
ocf_metadata_end_t cmpl, void *priv)
|
|
{
|
|
struct ocf_metadata_hash_context *context;
|
|
ocf_pipeline_t pipeline;
|
|
struct ocf_metadata_hash_ctrl *ctrl;
|
|
struct ocf_superblock_config *sb_config;
|
|
struct ocf_superblock_runtime *sb_runtime;
|
|
int result;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
ENV_BUG_ON(!ctrl);
|
|
|
|
sb_config = METADATA_MEM_POOL(ctrl, metadata_segment_sb_config);
|
|
ENV_BUG_ON(!sb_config);
|
|
|
|
sb_runtime = METADATA_MEM_POOL(ctrl, metadata_segment_sb_runtime);
|
|
ENV_BUG_ON(!sb_runtime);
|
|
|
|
result = ocf_pipeline_create(&pipeline, cache,
|
|
&ocf_metadata_hash_load_sb_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;
|
|
|
|
ocf_pipeline_next(pipeline);
|
|
}
|
|
|
|
static void ocf_medatata_hash_flush_superblock_prepare(ocf_pipeline_t pipeline,
|
|
void *priv, ocf_pipeline_arg_t arg)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
ocf_cache_t cache = context->cache;
|
|
uint32_t i;
|
|
|
|
/* Synchronize core objects types */
|
|
for (i = 0; i < OCF_CORE_MAX; i++) {
|
|
cache->core_conf_meta[i].type = ocf_ctx_get_volume_type_id(
|
|
cache->owner, cache->core[i].volume.type);
|
|
}
|
|
|
|
ocf_pipeline_next(pipeline);
|
|
}
|
|
|
|
static void ocf_medatata_hash_calculate_crc_sb_config(ocf_pipeline_t pipeline,
|
|
void *priv, ocf_pipeline_arg_t arg)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
struct ocf_metadata_hash_ctrl *ctrl;
|
|
struct ocf_superblock_config *sb_config;
|
|
ocf_cache_t cache = context->cache;
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *)cache->metadata.iface_priv;
|
|
sb_config = METADATA_MEM_POOL(ctrl, metadata_segment_sb_config);
|
|
|
|
sb_config->checksum[metadata_segment_sb_config] = env_crc32(0,
|
|
(void *)sb_config,
|
|
offsetof(struct ocf_superblock_config, checksum));
|
|
|
|
ocf_pipeline_next(pipeline);
|
|
}
|
|
|
|
static void ocf_medatata_hash_calculate_crc(ocf_pipeline_t pipeline,
|
|
void *priv, ocf_pipeline_arg_t arg)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
int segment = ocf_pipeline_arg_get_int(arg);
|
|
struct ocf_metadata_hash_ctrl *ctrl;
|
|
struct ocf_superblock_config *sb_config;
|
|
ocf_cache_t cache = context->cache;
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *)cache->metadata.iface_priv;
|
|
sb_config = METADATA_MEM_POOL(ctrl, metadata_segment_sb_config);
|
|
|
|
sb_config->checksum[segment] = ocf_metadata_raw_checksum(cache,
|
|
&(ctrl->raw_desc[segment]));
|
|
|
|
ocf_pipeline_next(pipeline);
|
|
}
|
|
|
|
static void ocf_medatata_hash_flush_segment(ocf_pipeline_t pipeline,
|
|
void *priv, ocf_pipeline_arg_t arg)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
int segment = ocf_pipeline_arg_get_int(arg);
|
|
struct ocf_metadata_hash_ctrl *ctrl;
|
|
ocf_cache_t cache = context->cache;
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *)cache->metadata.iface_priv;
|
|
|
|
ocf_metadata_raw_flush_all(cache, &ctrl->raw_desc[segment],
|
|
ocf_metadata_hash_generic_complete, context);
|
|
}
|
|
|
|
static void ocf_metadata_hash_flush_superblock_finish(ocf_pipeline_t pipeline,
|
|
void *priv, int error)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
ocf_cache_t cache = context->cache;
|
|
|
|
if (error)
|
|
ocf_metadata_error(cache);
|
|
|
|
context->cmpl(context->priv, error);
|
|
ocf_pipeline_destroy(pipeline);
|
|
}
|
|
|
|
struct ocf_pipeline_arg ocf_metadata_hash_flush_sb_calculate_crc_args[] = {
|
|
OCF_PL_ARG_INT(metadata_segment_core_config),
|
|
OCF_PL_ARG_INT(metadata_segment_core_uuid),
|
|
OCF_PL_ARG_TERMINATOR(),
|
|
};
|
|
|
|
struct ocf_pipeline_arg ocf_metadata_hash_flush_sb_flush_segment_args[] = {
|
|
OCF_PL_ARG_INT(metadata_segment_sb_config),
|
|
OCF_PL_ARG_INT(metadata_segment_core_config),
|
|
OCF_PL_ARG_INT(metadata_segment_core_uuid),
|
|
OCF_PL_ARG_TERMINATOR(),
|
|
};
|
|
|
|
struct ocf_pipeline_properties ocf_metadata_hash_flush_sb_pipeline_props = {
|
|
.priv_size = sizeof(struct ocf_metadata_hash_context),
|
|
.finish = ocf_metadata_hash_flush_superblock_finish,
|
|
.steps = {
|
|
OCF_PL_STEP(ocf_medatata_hash_flush_superblock_prepare),
|
|
OCF_PL_STEP(ocf_medatata_hash_calculate_crc_sb_config),
|
|
OCF_PL_STEP_FOREACH(ocf_medatata_hash_calculate_crc,
|
|
ocf_metadata_hash_flush_sb_calculate_crc_args),
|
|
OCF_PL_STEP_FOREACH(ocf_medatata_hash_flush_segment,
|
|
ocf_metadata_hash_flush_sb_flush_segment_args),
|
|
OCF_PL_STEP_TERMINATOR(),
|
|
},
|
|
};
|
|
|
|
/*
|
|
* Super Block - FLUSH
|
|
*/
|
|
static void ocf_metadata_hash_flush_superblock(ocf_cache_t cache,
|
|
ocf_metadata_end_t cmpl, void *priv)
|
|
{
|
|
struct ocf_metadata_hash_context *context;
|
|
ocf_pipeline_t pipeline;
|
|
int result;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
result = ocf_pipeline_create(&pipeline, cache,
|
|
&ocf_metadata_hash_flush_sb_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;
|
|
|
|
ocf_pipeline_next(pipeline);
|
|
}
|
|
|
|
/**
|
|
* @brief Super Block - Set Shutdown Status
|
|
*
|
|
* to get shutdown status, one needs to call ocf_metadata_load_properties.
|
|
* @param shutdown_status - status to be assigned to cache.
|
|
*
|
|
* @return Operation status (0 success, otherwise error)
|
|
*/
|
|
static void ocf_metadata_hash_set_shutdown_status(ocf_cache_t cache,
|
|
enum ocf_metadata_shutdown_status shutdown_status,
|
|
ocf_metadata_end_t cmpl, void *priv)
|
|
{
|
|
struct ocf_metadata_hash_ctrl *ctrl;
|
|
struct ocf_superblock_config *superblock;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
/*
|
|
* Get metadata hash service control structure
|
|
*/
|
|
ctrl = (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
/*
|
|
* Get super block
|
|
*/
|
|
superblock = METADATA_MEM_POOL(ctrl, metadata_segment_sb_config);
|
|
|
|
/* Set shutdown status */
|
|
superblock->clean_shutdown = shutdown_status;
|
|
superblock->magic_number = CACHE_MAGIC_NUMBER;
|
|
|
|
/* Flush superblock */
|
|
ocf_metadata_hash_flush_superblock(cache, cmpl, priv);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* RESERVED AREA
|
|
******************************************************************************/
|
|
|
|
static uint64_t ocf_metadata_hash_get_reserved_lba(
|
|
struct ocf_cache *cache)
|
|
{
|
|
struct ocf_metadata_hash_ctrl *ctrl;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
return ctrl->raw_desc[metadata_segment_reserved].ssd_pages_offset *
|
|
PAGE_SIZE;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* FLUSH AND LOAD ALL
|
|
******************************************************************************/
|
|
|
|
static void ocf_medatata_hash_flush_all_set_status_complete(
|
|
void *priv, int error)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
|
|
if (error) {
|
|
ocf_pipeline_finish(context->pipeline, error);
|
|
return;
|
|
}
|
|
|
|
ocf_pipeline_next(context->pipeline);
|
|
}
|
|
|
|
static void ocf_medatata_hash_flush_all_set_status(ocf_pipeline_t pipeline,
|
|
void *priv, ocf_pipeline_arg_t arg)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
ocf_cache_t cache = context->cache;
|
|
enum ocf_metadata_shutdown_status shutdown_status =
|
|
ocf_pipeline_arg_get_int(arg);
|
|
|
|
ocf_metadata_hash_set_shutdown_status(cache, shutdown_status,
|
|
ocf_medatata_hash_flush_all_set_status_complete,
|
|
context);
|
|
}
|
|
|
|
static void ocf_metadata_hash_flush_all_finish(ocf_pipeline_t pipeline,
|
|
void *priv, int error)
|
|
{
|
|
struct ocf_metadata_hash_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_hash_flush_all_args[] = {
|
|
OCF_PL_ARG_INT(metadata_segment_sb_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_hash_flush_all_pipeline_props = {
|
|
.priv_size = sizeof(struct ocf_metadata_hash_context),
|
|
.finish = ocf_metadata_hash_flush_all_finish,
|
|
.steps = {
|
|
OCF_PL_STEP_ARG_INT(ocf_medatata_hash_flush_all_set_status,
|
|
ocf_metadata_dirty_shutdown),
|
|
OCF_PL_STEP_FOREACH(ocf_medatata_hash_flush_segment,
|
|
ocf_metadata_hash_flush_all_args),
|
|
OCF_PL_STEP_FOREACH(ocf_medatata_hash_calculate_crc,
|
|
ocf_metadata_hash_flush_all_args),
|
|
OCF_PL_STEP_ARG_INT(ocf_medatata_hash_flush_all_set_status,
|
|
ocf_metadata_clean_shutdown),
|
|
OCF_PL_STEP_TERMINATOR(),
|
|
},
|
|
};
|
|
|
|
/*
|
|
* Flush all metadata
|
|
*/
|
|
static void ocf_metadata_hash_flush_all(ocf_cache_t cache,
|
|
ocf_metadata_end_t cmpl, void *priv)
|
|
{
|
|
struct ocf_metadata_hash_context *context;
|
|
ocf_pipeline_t pipeline;
|
|
int result;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
result = ocf_pipeline_create(&pipeline, cache,
|
|
&ocf_metadata_hash_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;
|
|
|
|
ocf_pipeline_next(pipeline);
|
|
}
|
|
|
|
/*
|
|
* Flush specified cache line
|
|
*/
|
|
static void ocf_metadata_hash_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_hash_ctrl *ctrl = NULL;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_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
|
|
*/
|
|
static void ocf_metadata_hash_flush_do_asynch(struct ocf_cache *cache,
|
|
struct ocf_request *req, ocf_req_end_t complete)
|
|
{
|
|
int result = 0;
|
|
struct ocf_metadata_hash_ctrl *ctrl = NULL;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
ctrl = (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_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_hash_load_all_finish(ocf_pipeline_t pipeline,
|
|
void *priv, int error)
|
|
{
|
|
struct ocf_metadata_hash_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_hash_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_hash_load_all_pipeline_props = {
|
|
.priv_size = sizeof(struct ocf_metadata_hash_context),
|
|
.finish = ocf_metadata_hash_load_all_finish,
|
|
.steps = {
|
|
OCF_PL_STEP_FOREACH(ocf_medatata_hash_load_segment,
|
|
ocf_metadata_hash_load_all_args),
|
|
OCF_PL_STEP_FOREACH(ocf_medatata_hash_check_crc,
|
|
ocf_metadata_hash_load_all_args),
|
|
OCF_PL_STEP_TERMINATOR(),
|
|
},
|
|
};
|
|
|
|
/*
|
|
* Load all metadata
|
|
*/
|
|
static void ocf_metadata_hash_load_all(ocf_cache_t cache,
|
|
ocf_metadata_end_t cmpl, void *priv)
|
|
{
|
|
struct ocf_metadata_hash_context *context;
|
|
ocf_pipeline_t pipeline;
|
|
int result;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
result = ocf_pipeline_create(&pipeline, cache,
|
|
&ocf_metadata_hash_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;
|
|
|
|
ocf_pipeline_next(pipeline);
|
|
}
|
|
|
|
static void _recovery_rebuild_cline_metadata(struct ocf_cache *cache,
|
|
ocf_core_id_t core_id, uint64_t core_line,
|
|
ocf_cache_line_t cache_line)
|
|
{
|
|
ocf_part_id_t part_id;
|
|
ocf_cache_line_t hash_index;
|
|
|
|
part_id = PARTITION_DEFAULT;
|
|
|
|
ocf_metadata_remove_from_free_list(cache, cache_line);
|
|
|
|
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, part_id);
|
|
|
|
ocf_eviction_set_hot_cache_line(cache, cache_line);
|
|
|
|
env_atomic_inc(&cache->core_runtime_meta[core_id].cached_clines);
|
|
env_atomic_inc(&cache->core_runtime_meta[core_id].
|
|
part_counters[part_id].cached_clines);
|
|
|
|
if (metadata_test_dirty(cache, cache_line)) {
|
|
env_atomic_inc(&cache->core_runtime_meta[core_id].
|
|
dirty_clines);
|
|
env_atomic_inc(&cache->core_runtime_meta[core_id].
|
|
part_counters[part_id].dirty_clines);
|
|
env_atomic64_cmpxchg(&cache->core_runtime_meta[core_id].
|
|
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_hash_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;
|
|
|
|
OCF_METADATA_LOCK_WR();
|
|
|
|
for (cline = 0; cline < cache->device->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_UNLOCK_WR();
|
|
|
|
ocf_pipeline_next(pipeline);
|
|
}
|
|
|
|
static void ocf_metadata_hash_load_recovery_legacy_finish(
|
|
ocf_pipeline_t pipeline, void *priv, int error)
|
|
{
|
|
struct ocf_metadata_hash_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_hash_load_recovery_legacy_pl_props = {
|
|
.priv_size = sizeof(struct ocf_metadata_hash_context),
|
|
.finish = ocf_metadata_hash_load_recovery_legacy_finish,
|
|
.steps = {
|
|
OCF_PL_STEP_ARG_INT(ocf_medatata_hash_load_segment,
|
|
metadata_segment_collision),
|
|
OCF_PL_STEP_ARG_INT(_recovery_rebuild_metadata, true),
|
|
OCF_PL_STEP_TERMINATOR(),
|
|
},
|
|
};
|
|
|
|
static void _ocf_metadata_hash_load_recovery_legacy(ocf_cache_t cache,
|
|
ocf_metadata_end_t cmpl, void *priv)
|
|
{
|
|
struct ocf_metadata_hash_context *context;
|
|
ocf_pipeline_t pipeline;
|
|
int result;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
result = ocf_pipeline_create(&pipeline, cache,
|
|
&ocf_metadata_hash_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;
|
|
|
|
ocf_pipeline_next(pipeline);
|
|
}
|
|
|
|
static ocf_core_id_t _ocf_metadata_hash_find_core_by_seq(
|
|
struct ocf_cache *cache, ocf_seq_no_t seq_no)
|
|
{
|
|
ocf_core_id_t i;
|
|
|
|
if (seq_no == OCF_SEQ_NO_INVALID)
|
|
return OCF_CORE_ID_INVALID;
|
|
|
|
for (i = OCF_CORE_ID_MIN; i <= OCF_CORE_ID_MAX; i++)
|
|
if (cache->core_conf_meta[i].seq_no == seq_no)
|
|
break;
|
|
|
|
return i;
|
|
}
|
|
|
|
static void ocf_metadata_hash_load_atomic_metadata_complete(
|
|
ocf_cache_t cache, void *priv, int error)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
|
|
if (error) {
|
|
ocf_pipeline_finish(context->pipeline, error);
|
|
return;
|
|
}
|
|
|
|
ocf_pipeline_next(context->pipeline);
|
|
}
|
|
|
|
static int ocf_metadata_hash_load_atomic_metadata_drain(void *priv,
|
|
uint64_t sector_addr, uint32_t sector_no, ctx_data_t *data)
|
|
{
|
|
struct ocf_metadata_hash_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_hash_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_medatata_hash_load_atomic_metadata(
|
|
ocf_pipeline_t pipeline, void *priv, ocf_pipeline_arg_t arg)
|
|
{
|
|
struct ocf_metadata_hash_context *context = priv;
|
|
ocf_cache_t cache = context->cache;
|
|
int result;
|
|
|
|
result = metadata_io_read_i_atomic(cache, cache->mngt_queue,
|
|
context, ocf_metadata_hash_load_atomic_metadata_drain,
|
|
ocf_metadata_hash_load_atomic_metadata_complete);
|
|
if (result) {
|
|
ocf_metadata_error(cache);
|
|
ocf_cache_log(cache, log_err,
|
|
"Metadata read for recovery FAILURE\n");
|
|
ocf_pipeline_finish(pipeline, result);
|
|
}
|
|
}
|
|
|
|
static void ocf_metadata_hash_load_recovery_atomic_finish(
|
|
ocf_pipeline_t pipeline, void *priv, int error)
|
|
{
|
|
struct ocf_metadata_hash_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_hash_load_recovery_atomic_pl_props = {
|
|
.priv_size = sizeof(struct ocf_metadata_hash_context),
|
|
.finish = ocf_metadata_hash_load_recovery_atomic_finish,
|
|
.steps = {
|
|
OCF_PL_STEP(ocf_medatata_hash_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_hash_load_recovery_atomic(ocf_cache_t cache,
|
|
ocf_metadata_end_t cmpl, void *priv)
|
|
{
|
|
struct ocf_metadata_hash_context *context;
|
|
ocf_pipeline_t pipeline;
|
|
int result;
|
|
|
|
OCF_DEBUG_TRACE(cache);
|
|
|
|
result = ocf_pipeline_create(&pipeline, cache,
|
|
&ocf_metadata_hash_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;
|
|
|
|
ocf_pipeline_next(pipeline);
|
|
}
|
|
|
|
/*
|
|
* Load for recovery - Load only data that is required for recovery procedure
|
|
*/
|
|
static void ocf_metadata_hash_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_hash_load_recovery_atomic(cache, cmpl, priv);
|
|
else
|
|
_ocf_metadata_hash_load_recovery_legacy(cache, cmpl, priv);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Core Info
|
|
******************************************************************************/
|
|
static void ocf_metadata_hash_get_core_info(struct ocf_cache *cache,
|
|
ocf_cache_line_t line, ocf_core_id_t *core_id,
|
|
uint64_t *core_sector)
|
|
{
|
|
const struct ocf_metadata_map *collision;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
collision = ocf_metadata_raw_rd_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_collision]), line,
|
|
ctrl->mapping_size);
|
|
if (collision) {
|
|
if (core_id)
|
|
*core_id = collision->core_id;
|
|
if (core_sector)
|
|
*core_sector = collision->core_line;
|
|
} else {
|
|
ocf_metadata_error(cache);
|
|
|
|
if (core_id)
|
|
*core_id = OCF_CORE_MAX;
|
|
if (core_sector)
|
|
*core_sector = ULLONG_MAX;
|
|
}
|
|
}
|
|
|
|
static void ocf_metadata_hash_set_core_info(struct ocf_cache *cache,
|
|
ocf_cache_line_t line, ocf_core_id_t core_id,
|
|
uint64_t core_sector)
|
|
{
|
|
struct ocf_metadata_map *collisioin;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
collisioin = ocf_metadata_raw_wr_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_collision]), line,
|
|
ctrl->mapping_size);
|
|
|
|
if (collisioin) {
|
|
collisioin->core_id = core_id;
|
|
collisioin->core_line = core_sector;
|
|
} else {
|
|
ocf_metadata_error(cache);
|
|
}
|
|
}
|
|
|
|
static ocf_core_id_t ocf_metadata_hash_get_core_id(
|
|
struct ocf_cache *cache, ocf_cache_line_t line)
|
|
{
|
|
const struct ocf_metadata_map *collision;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
collision = ocf_metadata_raw_rd_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_collision]), line,
|
|
ctrl->mapping_size);
|
|
|
|
if (collision)
|
|
return collision->core_id;
|
|
|
|
ocf_metadata_error(cache);
|
|
return OCF_CORE_MAX;
|
|
}
|
|
|
|
static uint64_t ocf_metadata_hash_get_core_sector(
|
|
struct ocf_cache *cache, ocf_cache_line_t line)
|
|
{
|
|
const struct ocf_metadata_map *collision;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
collision = ocf_metadata_raw_rd_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_collision]), line,
|
|
ctrl->mapping_size);
|
|
|
|
if (collision)
|
|
return collision->core_line;
|
|
|
|
ocf_metadata_error(cache);
|
|
return ULLONG_MAX;
|
|
}
|
|
|
|
static struct ocf_metadata_uuid *ocf_metadata_hash_get_core_uuid(
|
|
struct ocf_cache *cache, ocf_core_id_t core_id)
|
|
{
|
|
struct ocf_metadata_uuid *muuid;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
muuid = ocf_metadata_raw_wr_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_core_uuid]),
|
|
core_id, sizeof(struct ocf_metadata_uuid));
|
|
|
|
if (!muuid)
|
|
ocf_metadata_error(cache);
|
|
|
|
return muuid;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Core and part id
|
|
******************************************************************************/
|
|
|
|
static void ocf_metadata_hash_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_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
collision = ocf_metadata_raw_rd_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_collision]), line,
|
|
ctrl->mapping_size);
|
|
|
|
info = ocf_metadata_raw_rd_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
|
|
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
|
|
*/
|
|
static ocf_cache_line_t ocf_metadata_hash_get_hash(
|
|
struct ocf_cache *cache, ocf_cache_line_t index)
|
|
{
|
|
ocf_cache_line_t line = cache->device->collision_table_entries;
|
|
int result = 0;
|
|
struct ocf_metadata_hash_ctrl *ctrl
|
|
= (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
result = ocf_metadata_raw_get(cache,
|
|
&(ctrl->raw_desc[metadata_segment_hash]), index,
|
|
&line, sizeof(line));
|
|
|
|
if (result)
|
|
ocf_metadata_error(cache);
|
|
|
|
return line;
|
|
}
|
|
|
|
/*
|
|
* Hash Table - Set
|
|
*/
|
|
static void ocf_metadata_hash_set_hash(struct ocf_cache *cache,
|
|
ocf_cache_line_t index, ocf_cache_line_t line)
|
|
{
|
|
int result = 0;
|
|
struct ocf_metadata_hash_ctrl *ctrl
|
|
= (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
result = ocf_metadata_raw_set(cache,
|
|
&(ctrl->raw_desc[metadata_segment_hash]), index,
|
|
&line, sizeof(line));
|
|
|
|
if (result)
|
|
ocf_metadata_error(cache);
|
|
}
|
|
|
|
/*
|
|
* Hash Table - Get Entries
|
|
*/
|
|
static ocf_cache_line_t ocf_metadata_hash_entries_hash(
|
|
struct ocf_cache *cache)
|
|
{
|
|
struct ocf_metadata_hash_ctrl *ctrl
|
|
= (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
return ctrl->raw_desc[metadata_segment_hash].entries;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Cleaning Policy
|
|
******************************************************************************/
|
|
|
|
/*
|
|
* Cleaning policy - Get
|
|
*/
|
|
static void ocf_metadata_hash_get_cleaning_policy(
|
|
struct ocf_cache *cache, ocf_cache_line_t line,
|
|
struct cleaning_policy_meta *cleaning_policy)
|
|
{
|
|
int result = 0;
|
|
struct ocf_metadata_hash_ctrl *ctrl
|
|
= (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
result = ocf_metadata_raw_get(cache,
|
|
&(ctrl->raw_desc[metadata_segment_cleaning]), line,
|
|
cleaning_policy, sizeof(*cleaning_policy));
|
|
|
|
if (result)
|
|
ocf_metadata_error(cache);
|
|
}
|
|
|
|
/*
|
|
* Cleaning policy - Set
|
|
*/
|
|
static void ocf_metadata_hash_set_cleaning_policy(
|
|
struct ocf_cache *cache, ocf_cache_line_t line,
|
|
struct cleaning_policy_meta *cleaning_policy)
|
|
{
|
|
int result = 0;
|
|
struct ocf_metadata_hash_ctrl *ctrl
|
|
= (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
result = ocf_metadata_raw_set(cache,
|
|
&(ctrl->raw_desc[metadata_segment_cleaning]), line,
|
|
cleaning_policy, sizeof(*cleaning_policy));
|
|
|
|
if (result)
|
|
ocf_metadata_error(cache);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Eviction policy
|
|
******************************************************************************/
|
|
|
|
/*
|
|
* Eviction policy - Get
|
|
*/
|
|
static void ocf_metadata_hash_get_eviction_policy(
|
|
struct ocf_cache *cache, ocf_cache_line_t line,
|
|
union eviction_policy_meta *eviction_policy)
|
|
{
|
|
int result = 0;
|
|
struct ocf_metadata_hash_ctrl *ctrl
|
|
= (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
result = ocf_metadata_raw_get(cache,
|
|
&(ctrl->raw_desc[metadata_segment_eviction]), line,
|
|
eviction_policy, sizeof(*eviction_policy));
|
|
|
|
if (result)
|
|
ocf_metadata_error(cache);
|
|
}
|
|
|
|
/*
|
|
* Cleaning policy - Set
|
|
*/
|
|
static void ocf_metadata_hash_set_eviction_policy(
|
|
struct ocf_cache *cache, ocf_cache_line_t line,
|
|
union eviction_policy_meta *eviction_policy)
|
|
{
|
|
int result = 0;
|
|
struct ocf_metadata_hash_ctrl *ctrl
|
|
= (struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
result = ocf_metadata_raw_set(cache,
|
|
&(ctrl->raw_desc[metadata_segment_eviction]), line,
|
|
eviction_policy, sizeof(*eviction_policy));
|
|
|
|
if (result)
|
|
ocf_metadata_error(cache);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Collision
|
|
******************************************************************************/
|
|
static ocf_cache_line_t ocf_metadata_hash_map_lg2phy_seq(
|
|
struct ocf_cache *cache, ocf_cache_line_t coll_idx)
|
|
{
|
|
return coll_idx;
|
|
}
|
|
|
|
static ocf_cache_line_t ocf_metadata_hash_map_phy2lg_seq(
|
|
struct ocf_cache *cache, ocf_cache_line_t cache_line)
|
|
{
|
|
return cache_line;
|
|
}
|
|
|
|
static ocf_cache_line_t ocf_metadata_hash_map_lg2phy_striping(
|
|
struct ocf_cache *cache, ocf_cache_line_t coll_idx)
|
|
{
|
|
ocf_cache_line_t cache_line = 0, offset = 0;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
unsigned int entries_in_page =
|
|
ctrl->raw_desc[metadata_segment_collision].entries_in_page;
|
|
unsigned int pages =
|
|
ctrl->raw_desc[metadata_segment_collision].ssd_pages;
|
|
ocf_cache_line_t collision_table_entries =
|
|
cache->device->collision_table_entries;
|
|
ocf_cache_line_t delta =
|
|
(entries_in_page * pages) - collision_table_entries;
|
|
unsigned int row = coll_idx % entries_in_page;
|
|
|
|
if (row > entries_in_page - delta)
|
|
offset = row - (entries_in_page - delta);
|
|
else
|
|
offset = 0;
|
|
|
|
cache_line = (row * pages) + (coll_idx / entries_in_page) - offset;
|
|
return cache_line;
|
|
}
|
|
|
|
static ocf_cache_line_t ocf_metadata_hash_map_phy2lg_striping(
|
|
struct ocf_cache *cache, ocf_cache_line_t cache_line)
|
|
{
|
|
ocf_cache_line_t coll_idx = 0;
|
|
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
struct ocf_metadata_raw *raw =
|
|
&ctrl->raw_desc[metadata_segment_collision];
|
|
|
|
unsigned int pages = raw->ssd_pages;
|
|
unsigned int entries_in_page = raw->entries_in_page;
|
|
unsigned int entries_in_last_page = raw->entries % entries_in_page ?:
|
|
entries_in_page;
|
|
|
|
unsigned int row = 0, coll = 0;
|
|
|
|
unsigned int last = entries_in_last_page * pages;
|
|
|
|
if (cache_line < last) {
|
|
row = cache_line % pages;
|
|
coll = cache_line / pages;
|
|
} else {
|
|
cache_line -= last;
|
|
row = cache_line % (pages - 1);
|
|
coll = cache_line / (pages - 1) + entries_in_last_page;
|
|
}
|
|
|
|
coll_idx = (row * entries_in_page) + coll;
|
|
|
|
return coll_idx;
|
|
}
|
|
|
|
static void ocf_metadata_hash_set_collision_info(
|
|
struct ocf_cache *cache, ocf_cache_line_t line,
|
|
ocf_cache_line_t next, ocf_cache_line_t prev)
|
|
{
|
|
struct ocf_metadata_list_info *info;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
info = ocf_metadata_raw_wr_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
|
|
if (info) {
|
|
info->next_col = next;
|
|
info->prev_col = prev;
|
|
} else {
|
|
ocf_metadata_error(cache);
|
|
}
|
|
}
|
|
|
|
static void ocf_metadata_hash_set_collision_next(
|
|
struct ocf_cache *cache, ocf_cache_line_t line,
|
|
ocf_cache_line_t next)
|
|
{
|
|
struct ocf_metadata_list_info *info;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
info = ocf_metadata_raw_wr_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
|
|
if (info)
|
|
info->next_col = next;
|
|
else
|
|
ocf_metadata_error(cache);
|
|
}
|
|
|
|
static void ocf_metadata_hash_set_collision_prev(
|
|
struct ocf_cache *cache, ocf_cache_line_t line,
|
|
ocf_cache_line_t prev)
|
|
{
|
|
struct ocf_metadata_list_info *info;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
info = ocf_metadata_raw_wr_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
|
|
if (info)
|
|
info->prev_col = prev;
|
|
else
|
|
ocf_metadata_error(cache);
|
|
}
|
|
|
|
static void ocf_metadata_hash_get_collision_info(
|
|
struct ocf_cache *cache, ocf_cache_line_t line,
|
|
ocf_cache_line_t *next, ocf_cache_line_t *prev)
|
|
{
|
|
const struct ocf_metadata_list_info *info;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
ENV_BUG_ON(NULL == next && NULL == prev);
|
|
|
|
info = ocf_metadata_raw_rd_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
if (info) {
|
|
if (next)
|
|
*next = info->next_col;
|
|
if (prev)
|
|
*prev = info->prev_col;
|
|
} else {
|
|
ocf_metadata_error(cache);
|
|
|
|
if (next)
|
|
*next = cache->device->collision_table_entries;
|
|
if (prev)
|
|
*prev = cache->device->collision_table_entries;
|
|
}
|
|
}
|
|
|
|
static ocf_cache_line_t ocf_metadata_hash_get_collision_next(
|
|
struct ocf_cache *cache, ocf_cache_line_t line)
|
|
{
|
|
const struct ocf_metadata_list_info *info;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
info = ocf_metadata_raw_rd_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
if (info)
|
|
return info->next_col;
|
|
|
|
ocf_metadata_error(cache);
|
|
return cache->device->collision_table_entries;
|
|
}
|
|
|
|
static ocf_cache_line_t ocf_metadata_hash_get_collision_prev(
|
|
struct ocf_cache *cache, ocf_cache_line_t line)
|
|
{
|
|
const struct ocf_metadata_list_info *info;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
info = ocf_metadata_raw_rd_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
if (info)
|
|
return info->prev_col;
|
|
|
|
ocf_metadata_error(cache);
|
|
return cache->device->collision_table_entries;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Partition
|
|
******************************************************************************/
|
|
|
|
static ocf_part_id_t ocf_metadata_hash_get_partition_id(
|
|
struct ocf_cache *cache, ocf_cache_line_t line)
|
|
{
|
|
const struct ocf_metadata_list_info *info;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
info = ocf_metadata_raw_rd_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
|
|
if (info)
|
|
return info->partition_id;
|
|
|
|
ocf_metadata_error(cache);
|
|
return PARTITION_DEFAULT;
|
|
}
|
|
|
|
static ocf_cache_line_t ocf_metadata_hash_get_partition_next(
|
|
struct ocf_cache *cache, ocf_cache_line_t line)
|
|
{
|
|
const struct ocf_metadata_list_info *info;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
info = ocf_metadata_raw_rd_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
|
|
if (info)
|
|
return info->partition_next;
|
|
|
|
ocf_metadata_error(cache);
|
|
return PARTITION_DEFAULT;
|
|
}
|
|
|
|
static ocf_cache_line_t ocf_metadata_hash_get_partition_prev(
|
|
struct ocf_cache *cache, ocf_cache_line_t line)
|
|
{
|
|
const struct ocf_metadata_list_info *info;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
info = ocf_metadata_raw_rd_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
|
|
if (info)
|
|
return info->partition_prev;
|
|
|
|
ocf_metadata_error(cache);
|
|
return PARTITION_DEFAULT;
|
|
}
|
|
|
|
static void ocf_metadata_hash_get_partition_info(
|
|
struct ocf_cache *cache, ocf_cache_line_t line,
|
|
ocf_part_id_t *part_id, ocf_cache_line_t *next_line,
|
|
ocf_cache_line_t *prev_line)
|
|
{
|
|
const struct ocf_metadata_list_info *info;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
info = ocf_metadata_raw_rd_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
|
|
if (info) {
|
|
if (part_id)
|
|
*part_id = info->partition_id;
|
|
if (next_line)
|
|
*next_line = info->partition_next;
|
|
if (prev_line)
|
|
*prev_line = info->partition_prev;
|
|
} else {
|
|
ocf_metadata_error(cache);
|
|
if (part_id)
|
|
*part_id = PARTITION_DEFAULT;
|
|
if (next_line)
|
|
*next_line = cache->device->collision_table_entries;
|
|
if (prev_line)
|
|
*prev_line = cache->device->collision_table_entries;
|
|
}
|
|
}
|
|
|
|
static void ocf_metadata_hash_set_partition_next(
|
|
struct ocf_cache *cache, ocf_cache_line_t line,
|
|
ocf_cache_line_t next_line)
|
|
{
|
|
struct ocf_metadata_list_info *info;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
info = ocf_metadata_raw_wr_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
|
|
if (info)
|
|
info->partition_next = next_line;
|
|
else
|
|
ocf_metadata_error(cache);
|
|
}
|
|
|
|
static void ocf_metadata_hash_set_partition_prev(
|
|
struct ocf_cache *cache, ocf_cache_line_t line,
|
|
ocf_cache_line_t prev_line)
|
|
{
|
|
struct ocf_metadata_list_info *info;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
info = ocf_metadata_raw_wr_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
|
|
if (info)
|
|
info->partition_prev = prev_line;
|
|
else
|
|
ocf_metadata_error(cache);
|
|
}
|
|
|
|
static void ocf_metadata_hash_set_partition_info(
|
|
struct ocf_cache *cache, ocf_cache_line_t line,
|
|
ocf_part_id_t part_id, ocf_cache_line_t next_line,
|
|
ocf_cache_line_t prev_line)
|
|
{
|
|
struct ocf_metadata_list_info *info;
|
|
struct ocf_metadata_hash_ctrl *ctrl =
|
|
(struct ocf_metadata_hash_ctrl *) cache->metadata.iface_priv;
|
|
|
|
info = ocf_metadata_raw_wr_access(cache,
|
|
&(ctrl->raw_desc[metadata_segment_list_info]), line,
|
|
sizeof(*info));
|
|
|
|
if (info) {
|
|
info->partition_id = part_id;
|
|
info->partition_next = next_line;
|
|
info->partition_prev = prev_line;
|
|
} else {
|
|
ocf_metadata_error(cache);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Hash Metadata interface definition
|
|
******************************************************************************/
|
|
|
|
static const struct ocf_metadata_iface metadata_hash_iface = {
|
|
.init = ocf_metadata_hash_init,
|
|
.deinit = ocf_metadata_hash_deinit,
|
|
.query_cores = ocf_metadata_hash_query_cores,
|
|
.init_variable_size = ocf_metadata_hash_init_variable_size,
|
|
.deinit_variable_size = ocf_metadata_hash_deinit_variable_size,
|
|
.init_hash_table = ocf_metadata_hash_init_hash_table,
|
|
|
|
.layout_iface = NULL,
|
|
.pages = ocf_metadata_hash_pages,
|
|
.cachelines = ocf_metadata_hash_cachelines,
|
|
.size_of = ocf_metadata_hash_size_of,
|
|
|
|
/*
|
|
* Load all, flushing all, etc...
|
|
*/
|
|
.flush_all = ocf_metadata_hash_flush_all,
|
|
.flush_mark = ocf_metadata_hash_flush_mark,
|
|
.flush_do_asynch = ocf_metadata_hash_flush_do_asynch,
|
|
.load_all = ocf_metadata_hash_load_all,
|
|
.load_recovery = ocf_metadata_hash_load_recovery,
|
|
|
|
/*
|
|
* Super Block
|
|
*/
|
|
.set_shutdown_status = ocf_metadata_hash_set_shutdown_status,
|
|
.flush_superblock = ocf_metadata_hash_flush_superblock,
|
|
.load_superblock = ocf_metadata_hash_load_superblock,
|
|
|
|
/*
|
|
* Reserved area
|
|
*/
|
|
.get_reserved_lba = ocf_metadata_hash_get_reserved_lba,
|
|
|
|
/*
|
|
* Core Info
|
|
*/
|
|
.set_core_info = ocf_metadata_hash_set_core_info,
|
|
.get_core_info = ocf_metadata_hash_get_core_info,
|
|
.get_core_id = ocf_metadata_hash_get_core_id,
|
|
.get_core_sector = ocf_metadata_hash_get_core_sector,
|
|
.get_core_uuid = ocf_metadata_hash_get_core_uuid,
|
|
|
|
/*
|
|
* Core and part id
|
|
*/
|
|
|
|
.get_core_and_part_id = ocf_metadata_hash_get_core_and_part_id,
|
|
|
|
/*
|
|
* Collision Info
|
|
*/
|
|
.get_collision_info = ocf_metadata_hash_get_collision_info,
|
|
.set_collision_info = ocf_metadata_hash_set_collision_info,
|
|
.set_collision_next = ocf_metadata_hash_set_collision_next,
|
|
.set_collision_prev = ocf_metadata_hash_set_collision_prev,
|
|
.get_collision_next = ocf_metadata_hash_get_collision_next,
|
|
.get_collision_prev = ocf_metadata_hash_get_collision_prev,
|
|
|
|
/*
|
|
* Partition Info
|
|
*/
|
|
.get_partition_id = ocf_metadata_hash_get_partition_id,
|
|
.get_partition_next = ocf_metadata_hash_get_partition_next,
|
|
.get_partition_prev = ocf_metadata_hash_get_partition_prev,
|
|
.get_partition_info = ocf_metadata_hash_get_partition_info,
|
|
.set_partition_next = ocf_metadata_hash_set_partition_next,
|
|
.set_partition_prev = ocf_metadata_hash_set_partition_prev,
|
|
.set_partition_info = ocf_metadata_hash_set_partition_info,
|
|
|
|
/*
|
|
* Hash Table
|
|
*/
|
|
.get_hash = ocf_metadata_hash_get_hash,
|
|
.set_hash = ocf_metadata_hash_set_hash,
|
|
.entries_hash = ocf_metadata_hash_entries_hash,
|
|
|
|
/*
|
|
* Cleaning Policy
|
|
*/
|
|
.get_cleaning_policy = ocf_metadata_hash_get_cleaning_policy,
|
|
.set_cleaning_policy = ocf_metadata_hash_set_cleaning_policy,
|
|
|
|
/*
|
|
* Eviction Policy
|
|
*/
|
|
.get_eviction_policy = ocf_metadata_hash_get_eviction_policy,
|
|
.set_eviction_policy = ocf_metadata_hash_set_eviction_policy,
|
|
};
|
|
|
|
/*******************************************************************************
|
|
* Bitmap status
|
|
******************************************************************************/
|
|
|
|
#include "metadata_bit.h"
|
|
|
|
static const struct ocf_metadata_layout_iface layout_ifaces[ocf_metadata_layout_max] = {
|
|
[ocf_metadata_layout_striping] = {
|
|
.init_freelist = ocf_metadata_hash_init_freelist_striping,
|
|
.lg2phy = ocf_metadata_hash_map_lg2phy_striping,
|
|
.phy2lg = ocf_metadata_hash_map_phy2lg_striping
|
|
},
|
|
[ocf_metadata_layout_seq] = {
|
|
.init_freelist = ocf_metadata_hash_init_freelist_seq,
|
|
.lg2phy = ocf_metadata_hash_map_lg2phy_seq,
|
|
.phy2lg = ocf_metadata_hash_map_phy2lg_seq
|
|
}
|
|
};
|
|
|
|
|
|
static void ocf_metadata_hash_init_iface(struct ocf_cache *cache,
|
|
ocf_metadata_layout_t layout)
|
|
{
|
|
struct ocf_metadata_iface *iface = (struct ocf_metadata_iface *)
|
|
&cache->metadata.iface;
|
|
|
|
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;
|
|
iface->layout_iface = &layout_ifaces[layout];
|
|
|
|
/* Initialize bit status function */
|
|
|
|
switch (cache->metadata.settings.size) {
|
|
case ocf_cache_line_size_4:
|
|
iface->test_dirty = _ocf_metadata_test_dirty_u8;
|
|
iface->test_out_dirty = _ocf_metadata_test_out_dirty_u8;
|
|
iface->clear_dirty = _ocf_metadata_clear_dirty_u8;
|
|
iface->set_dirty = _ocf_metadata_set_dirty_u8;
|
|
iface->test_and_set_dirty = _ocf_metadata_test_and_set_dirty_u8;
|
|
iface->test_and_clear_dirty =
|
|
_ocf_metadata_test_and_clear_dirty_u8;
|
|
iface->test_valid = _ocf_metadata_test_valid_u8;
|
|
iface->test_out_valid = _ocf_metadata_test_out_valid_u8;
|
|
iface->clear_valid = _ocf_metadata_clear_valid_u8;
|
|
iface->set_valid = _ocf_metadata_set_valid_u8;
|
|
iface->test_and_set_valid = _ocf_metadata_test_and_set_valid_u8;
|
|
iface->test_and_clear_valid =
|
|
_ocf_metadata_test_and_clear_valid_u8;
|
|
break;
|
|
|
|
case ocf_cache_line_size_8:
|
|
iface->test_dirty = _ocf_metadata_test_dirty_u16;
|
|
iface->test_out_dirty = _ocf_metadata_test_out_dirty_u16;
|
|
iface->clear_dirty = _ocf_metadata_clear_dirty_u16;
|
|
iface->set_dirty = _ocf_metadata_set_dirty_u16;
|
|
iface->test_and_set_dirty =
|
|
_ocf_metadata_test_and_set_dirty_u16;
|
|
iface->test_and_clear_dirty =
|
|
_ocf_metadata_test_and_clear_dirty_u16;
|
|
iface->test_valid = _ocf_metadata_test_valid_u16;
|
|
iface->test_out_valid = _ocf_metadata_test_out_valid_u16;
|
|
iface->clear_valid = _ocf_metadata_clear_valid_u16;
|
|
iface->set_valid = _ocf_metadata_set_valid_u16;
|
|
iface->test_and_set_valid =
|
|
_ocf_metadata_test_and_set_valid_u16;
|
|
iface->test_and_clear_valid =
|
|
_ocf_metadata_test_and_clear_valid_u16;
|
|
break;
|
|
|
|
case ocf_cache_line_size_16:
|
|
iface->test_dirty = _ocf_metadata_test_dirty_u32;
|
|
iface->test_out_dirty = _ocf_metadata_test_out_dirty_u32;
|
|
iface->clear_dirty = _ocf_metadata_clear_dirty_u32;
|
|
iface->set_dirty = _ocf_metadata_set_dirty_u32;
|
|
iface->test_and_set_dirty =
|
|
_ocf_metadata_test_and_set_dirty_u32;
|
|
iface->test_and_clear_dirty =
|
|
_ocf_metadata_test_and_clear_dirty_u32;
|
|
iface->test_valid = _ocf_metadata_test_valid_u32;
|
|
iface->test_out_valid = _ocf_metadata_test_out_valid_u32;
|
|
iface->clear_valid = _ocf_metadata_clear_valid_u32;
|
|
iface->set_valid = _ocf_metadata_set_valid_u32;
|
|
iface->test_and_set_valid =
|
|
_ocf_metadata_test_and_set_valid_u32;
|
|
iface->test_and_clear_valid =
|
|
_ocf_metadata_test_and_clear_valid_u32;
|
|
break;
|
|
case ocf_cache_line_size_32:
|
|
iface->test_dirty = _ocf_metadata_test_dirty_u64;
|
|
iface->test_out_dirty = _ocf_metadata_test_out_dirty_u64;
|
|
iface->clear_dirty = _ocf_metadata_clear_dirty_u64;
|
|
iface->set_dirty = _ocf_metadata_set_dirty_u64;
|
|
iface->test_and_set_dirty =
|
|
_ocf_metadata_test_and_set_dirty_u64;
|
|
iface->test_and_clear_dirty =
|
|
_ocf_metadata_test_and_clear_dirty_u64;
|
|
iface->test_valid = _ocf_metadata_test_valid_u64;
|
|
iface->test_out_valid = _ocf_metadata_test_out_valid_u64;
|
|
iface->clear_valid = _ocf_metadata_clear_valid_u64;
|
|
iface->set_valid = _ocf_metadata_set_valid_u64;
|
|
iface->test_and_set_valid =
|
|
_ocf_metadata_test_and_set_valid_u64;
|
|
iface->test_and_clear_valid =
|
|
_ocf_metadata_test_and_clear_valid_u64;
|
|
break;
|
|
|
|
case ocf_cache_line_size_64:
|
|
iface->test_dirty = _ocf_metadata_test_dirty_u128;
|
|
iface->test_out_dirty = _ocf_metadata_test_out_dirty_u128;
|
|
iface->clear_dirty = _ocf_metadata_clear_dirty_u128;
|
|
iface->set_dirty = _ocf_metadata_set_dirty_u128;
|
|
iface->test_and_set_dirty =
|
|
_ocf_metadata_test_and_set_dirty_u128;
|
|
iface->test_and_clear_dirty =
|
|
_ocf_metadata_test_and_clear_dirty_u128;
|
|
iface->test_valid = _ocf_metadata_test_valid_u128;
|
|
iface->test_out_valid = _ocf_metadata_test_out_valid_u128;
|
|
iface->clear_valid = _ocf_metadata_clear_valid_u128;
|
|
iface->set_valid = _ocf_metadata_set_valid_u128;
|
|
iface->test_and_set_valid =
|
|
_ocf_metadata_test_and_set_valid_u128;
|
|
iface->test_and_clear_valid =
|
|
_ocf_metadata_test_and_clear_valid_u128;
|
|
break;
|
|
|
|
default:
|
|
ENV_BUG();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get metadata hash interface
|
|
*/
|
|
const struct ocf_metadata_iface *metadata_hash_get_iface(void)
|
|
{
|
|
return &metadata_hash_iface;
|
|
}
|