ocf/src/metadata/metadata.c
Robert Baldyga db92083432 Unify req naming convention (rq -> req)
Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
2018-12-12 13:36:34 +01:00

389 lines
9.5 KiB
C

/*
* Copyright(c) 2012-2018 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include "ocf/ocf.h"
#include "metadata.h"
#include "metadata_hash.h"
#include "metadata_io.h"
#include "../ocf_priv.h"
#include "../utils/utils_io.h"
#include "../utils/utils_cache_line.h"
#define OCF_METADATA_DEBUG 0
#if 1 == OCF_METADATA_DEBUG
#define OCF_DEBUG_TRACE(cache) \
ocf_cache_log(cache, log_info, "[Metadata][Hash] %s\n", __func__)
#else
#define OCF_DEBUG_TRACE(cache)
#endif
int ocf_metadata_init(struct ocf_cache *cache,
ocf_cache_line_size_t cache_line_size)
{
struct ocf_metadata_iface *iface = (struct ocf_metadata_iface *)
&cache->metadata.iface;
int ret;
OCF_DEBUG_TRACE(cache);
ENV_BUG_ON(cache->metadata.iface_priv);
ret = ocf_metadata_io_init(cache);
if (ret)
return ret;
*iface = *metadata_hash_get_iface();
ret = cache->metadata.iface.init(cache, cache_line_size);
if (ret)
ocf_metadata_io_deinit(cache);
return ret;
}
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)
{
OCF_DEBUG_TRACE(cache);
return cache->metadata.iface.init_variable_size(cache, device_size,
cache_line_size, layout);
}
void ocf_metadata_init_freelist_partition(struct ocf_cache *cache)
{
OCF_DEBUG_TRACE(cache);
cache->metadata.iface.layout_iface->init_freelist(cache);
}
void ocf_metadata_init_hash_table(struct ocf_cache *cache)
{
OCF_DEBUG_TRACE(cache);
cache->metadata.iface.init_hash_table(cache);
}
void ocf_metadata_deinit(struct ocf_cache *cache)
{
OCF_DEBUG_TRACE(cache);
if (cache->metadata.iface.deinit) {
cache->metadata.iface.deinit(cache);
}
ocf_metadata_io_deinit(cache);
}
void ocf_metadata_deinit_variable_size(struct ocf_cache *cache)
{
OCF_DEBUG_TRACE(cache);
if (cache->metadata.iface.deinit_variable_size)
cache->metadata.iface.deinit_variable_size(cache);
}
size_t ocf_metadata_size_of(struct ocf_cache *cache)
{
return cache->metadata.iface.size_of(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;
}
ocf_cache_line_t ocf_metadata_get_pages_count(struct ocf_cache *cache)
{
return cache->metadata.iface.pages(cache);
}
ocf_cache_line_t
ocf_metadata_get_cachelines_count(struct ocf_cache *cache)
{
return cache->metadata.iface.cachelines(cache);
}
int ocf_metadata_flush_all(struct ocf_cache *cache)
{
int result;
OCF_METADATA_LOCK_WR();
result = cache->metadata.iface.flush_all(cache);
OCF_METADATA_UNLOCK_WR();
return result;
}
void ocf_metadata_flush(struct ocf_cache *cache, ocf_cache_line_t line)
{
cache->metadata.iface.flush(cache, line);
}
int ocf_metadata_load_all(struct ocf_cache *cache)
{
int result;
OCF_METADATA_LOCK_WR();
result = cache->metadata.iface.load_all(cache);
OCF_METADATA_UNLOCK_WR();
return result;
}
int ocf_metadata_load_recovery(struct ocf_cache *cache)
{
return cache->metadata.iface.load_recovery(cache);
}
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)
{
cache->metadata.iface.flush_mark(cache, req, map_idx, to_state,
start, stop);
}
void ocf_metadata_flush_do_asynch(struct ocf_cache *cache,
struct ocf_request *req, ocf_req_end_t complete)
{
cache->metadata.iface.flush_do_asynch(cache, req, complete);
}
static inline int ocf_metadata_check_properties(void)
{
uint32_t field_offset;
/* Because metadata basic properties are on the beginning of super block
* read/write only first page of supper block.
*
* For safety reason check if offset of metadata properties are in first
* page of super block.
*
* Maybe in future super block fields order may be changed and metadata
* variant may go out first page of super block
*/
field_offset = offsetof(struct ocf_superblock_config, line_size);
ENV_BUG_ON(field_offset >= PAGE_SIZE);
/* The same checking for magic number */
field_offset = offsetof(struct ocf_superblock_config, magic_number);
ENV_BUG_ON(field_offset >= PAGE_SIZE);
/* The same checking for IO interface type */
field_offset = offsetof(struct ocf_superblock_config, cache_mode);
ENV_BUG_ON(field_offset >= PAGE_SIZE);
/* And the same for version location within superblock structure */
field_offset = offsetof(struct ocf_superblock_config, metadata_version);
ENV_BUG_ON(field_offset >= PAGE_SIZE);
return 0;
}
static int ocf_metadata_read_properties(ocf_ctx_t ctx, ocf_data_obj_t cache_obj,
struct ocf_superblock_config *superblock)
{
ctx_data_t *data;
struct ocf_io *io;
int result = 0;
if (ocf_metadata_check_properties())
return -EINVAL;
/* Allocate resources for IO */
io = ocf_dobj_new_io(cache_obj);
data = ctx_data_alloc(ctx, 1);
/* Check allocation result */
if (!io || !data) {
ocf_log(ctx, log_err, "Memory allocation error");
result = -ENOMEM;
goto out;
}
/*
* 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 = -EIO;
goto out;
}
ocf_io_configure(io, 0, PAGE_SIZE, OCF_READ, 0, 0);
result = ocf_submit_io_wait(io);
if (result) {
ocf_log(ctx, log_err, "Metadata IO request submit error\n");
result = -EIO;
goto out;
}
/* Read data from data into super block buffer */
ctx_data_rd_check(ctx, superblock, data,
PAGE_SIZE);
out:
if (io)
ocf_io_put(io);
ctx_data_free(ctx, data);
return result;
}
/**
* @brief function loads individual properties from metadata set
* @param cache_obj object from which to load metadata
* @param variant - field to which save metadata variant; if NULL,
* metadata variant won't be read.
* @param cache mode; if NULL is passed it won't be read
* @param shutdown_status - dirty shutdown or clean shutdown
* @param dirty_flushed - if all dirty data was flushed prior to closing
* the cache
* @return 0 upon successful completion
*/
int ocf_metadata_load_properties(ocf_data_obj_t cache_obj,
ocf_cache_line_size_t *line_size,
ocf_metadata_layout_t *layout,
ocf_cache_mode_t *cache_mode,
enum ocf_metadata_shutdown_status *shutdown_status,
uint8_t *dirty_flushed)
{
struct ocf_superblock_config *superblock;
int err_value = 0;
/* Allocate first page of super block */
superblock = env_zalloc(PAGE_SIZE, ENV_MEM_NORMAL);
if (!superblock) {
ocf_cache_log(cache_obj->cache, log_err,
"Allocation memory error");
return -ENOMEM;
}
OCF_DEBUG_TRACE(cache);
err_value = ocf_metadata_read_properties(cache_obj->cache->owner,
cache_obj, superblock);
if (err_value)
goto ocf_metadata_load_variant_ERROR;
if (superblock->magic_number != CACHE_MAGIC_NUMBER) {
err_value = -ENODATA;
ocf_cache_log(cache_obj->cache, log_info,
"Can not detect pre-existing metadata\n");
goto ocf_metadata_load_variant_ERROR;
}
if (METADATA_VERSION() != superblock->metadata_version) {
err_value = -EBADF;
ocf_cache_log(cache_obj->cache, log_err,
"Metadata version mismatch!\n");
goto ocf_metadata_load_variant_ERROR;
}
if (line_size) {
if (ocf_cache_line_size_is_valid(superblock->line_size)) {
*line_size = superblock->line_size;
} else {
err_value = -EINVAL;
ocf_cache_log(cache_obj->cache, log_err,
"ERROR: Invalid cache line size!\n");
}
}
if (layout) {
if (superblock->metadata_layout >= ocf_metadata_layout_max ||
superblock->metadata_layout < 0) {
err_value = -EINVAL;
ocf_cache_log(cache_obj->cache, log_err,
"ERROR: Invalid metadata layout!\n");
} else {
*layout = superblock->metadata_layout;
}
}
if (cache_mode) {
if (superblock->cache_mode < ocf_cache_mode_max) {
*cache_mode = superblock->cache_mode;
} else {
ocf_cache_log(cache_obj->cache, log_err,
"ERROR: Invalid cache mode!\n");
err_value = -EINVAL;
}
}
if (shutdown_status != NULL) {
if (superblock->clean_shutdown <= ocf_metadata_clean_shutdown) {
*shutdown_status = superblock->clean_shutdown;
} else {
ocf_cache_log(cache_obj->cache, log_err,
"ERROR: Invalid shutdown status!\n");
err_value = -EINVAL;
}
}
if (dirty_flushed != NULL) {
if (superblock->dirty_flushed <= DIRTY_FLUSHED) {
*dirty_flushed = superblock->dirty_flushed;
} else {
ocf_cache_log(cache_obj->cache, log_err,
"ERROR: Invalid flush status!\n");
err_value = -EINVAL;
}
}
ocf_metadata_load_variant_ERROR:
env_free(superblock);
return err_value;
}
int ocf_metadata_probe(ocf_ctx_t ctx, ocf_data_obj_t cache_obj,
bool *clean_shutdown, bool *cache_dirty)
{
struct ocf_superblock_config *superblock;
int result = 0;
OCF_CHECK_NULL(ctx);
OCF_CHECK_NULL(cache_obj);
/* Allocate first page of super block */
superblock = env_zalloc(PAGE_SIZE, ENV_MEM_NORMAL);
if (!superblock) {
ocf_log(ctx, log_err, "Allocation memory error");
return -ENOMEM;
}
OCF_DEBUG_TRACE(cache);
result = ocf_metadata_read_properties(ctx, cache_obj, superblock);
if (result)
goto ocf_metadata_probe_END;
if (superblock->magic_number != CACHE_MAGIC_NUMBER) {
result = -ENODATA;
goto ocf_metadata_probe_END;
}
if (clean_shutdown != NULL) {
*clean_shutdown = (superblock->clean_shutdown !=
ocf_metadata_dirty_shutdown);
}
if (cache_dirty != NULL)
*cache_dirty = (superblock->dirty_flushed == DIRTY_NOT_FLUSHED);
if (METADATA_VERSION() != superblock->metadata_version)
result = -EBADF;
ocf_metadata_probe_END:
env_free(superblock);
return result;
}