Initial commit
Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
This commit is contained in:
2121
src/mngt/ocf_mngt_cache.c
Normal file
2121
src/mngt/ocf_mngt_cache.c
Normal file
File diff suppressed because it is too large
Load Diff
448
src/mngt/ocf_mngt_common.c
Normal file
448
src/mngt/ocf_mngt_common.c
Normal file
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2018 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "ocf/ocf.h"
|
||||
#include "ocf_mngt_common.h"
|
||||
#include "../ocf_priv.h"
|
||||
#include "../ocf_ctx_priv.h"
|
||||
#include "../metadata/metadata.h"
|
||||
#include "../engine/cache_engine.h"
|
||||
#include "../utils/utils_part.h"
|
||||
#include "../utils/utils_rq.h"
|
||||
#include "../utils/utils_device.h"
|
||||
#include "../eviction/ops.h"
|
||||
#include "../ocf_logger_priv.h"
|
||||
#include "../ocf_queue_priv.h"
|
||||
|
||||
/* Close if opened */
|
||||
int cache_mng_core_close(ocf_cache_t cache, ocf_core_id_t core_id)
|
||||
{
|
||||
if (!cache->core_obj[core_id].opened)
|
||||
return -OCF_ERR_CORE_IN_INACTIVE_STATE;
|
||||
|
||||
ocf_data_obj_close(&cache->core_obj[core_id].obj);
|
||||
cache->core_obj[core_id].opened = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove core from cleaning policy */
|
||||
void cache_mng_core_remove_from_cleaning_pol(struct ocf_cache *cache,
|
||||
int core_id)
|
||||
{
|
||||
ocf_cleaning_t clean_pol_type;
|
||||
|
||||
OCF_METADATA_LOCK_WR();
|
||||
|
||||
clean_pol_type = cache->conf_meta->cleaning_policy_type;
|
||||
if (cache->core_obj[core_id].opened) {
|
||||
if (cleaning_policy_ops[clean_pol_type].remove_core) {
|
||||
cleaning_policy_ops[clean_pol_type].
|
||||
remove_core(cache, core_id);
|
||||
}
|
||||
}
|
||||
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
}
|
||||
|
||||
/* Deinitialize core metadata in attached metadata */
|
||||
void cache_mng_core_deinit_attached_meta(struct ocf_cache *cache, int core_id)
|
||||
{
|
||||
int retry = 1;
|
||||
uint64_t core_size = 0;
|
||||
ocf_cleaning_t clean_pol_type;
|
||||
ocf_data_obj_t core;
|
||||
|
||||
core = &cache->core_obj[core_id].obj;
|
||||
|
||||
core_size = ocf_data_obj_get_length(core);
|
||||
if (!core_size)
|
||||
core_size = ~0ULL;
|
||||
|
||||
OCF_METADATA_LOCK_WR();
|
||||
|
||||
clean_pol_type = cache->conf_meta->cleaning_policy_type;
|
||||
while (retry) {
|
||||
retry = 0;
|
||||
if (cleaning_policy_ops[clean_pol_type].purge_range) {
|
||||
retry = cleaning_policy_ops[clean_pol_type].purge_range(cache,
|
||||
core_id, 0, core_size);
|
||||
}
|
||||
|
||||
if (!retry) {
|
||||
/* Remove from collision_table and Partition. Put in FREELIST */
|
||||
retry = ocf_metadata_sparse_range(cache, core_id, 0,
|
||||
core_size);
|
||||
}
|
||||
|
||||
if (retry) {
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
env_msleep(100);
|
||||
OCF_METADATA_LOCK_WR();
|
||||
}
|
||||
}
|
||||
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
}
|
||||
|
||||
/* Mark core as removed in metadata */
|
||||
void cache_mng_core_remove_from_meta(struct ocf_cache *cache, int core_id)
|
||||
{
|
||||
OCF_METADATA_LOCK_WR();
|
||||
|
||||
/* In metadata mark data this core was removed from cache */
|
||||
cache->core_conf_meta[core_id].added = false;
|
||||
|
||||
/* Clear UUID of core */
|
||||
ocf_uuid_core_clear(cache, &cache->core_obj[core_id]);
|
||||
cache->core_conf_meta[core_id].seq_no = OCF_SEQ_NO_INVALID;
|
||||
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
}
|
||||
|
||||
/* Deinit in-memory structures related to this core */
|
||||
void cache_mng_core_remove_from_cache(struct ocf_cache *cache, int core_id)
|
||||
{
|
||||
env_free(cache->core_obj[core_id].counters);
|
||||
cache->core_obj[core_id].counters = NULL;
|
||||
env_bit_clear(core_id, cache->conf_meta->valid_object_bitmap);
|
||||
|
||||
if (!cache->core_obj[core_id].opened &&
|
||||
--cache->ocf_core_inactive_count == 0) {
|
||||
env_bit_clear(ocf_cache_state_incomplete, &cache->cache_state);
|
||||
}
|
||||
|
||||
cache->conf_meta->core_obj_count--;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait for the end of asynchronous cleaning
|
||||
*
|
||||
* @param cache OCF cache instance
|
||||
* @param timeout_ms Timeout for waiting in milliseconds
|
||||
* @note When timeout is less than zero it means wait forever
|
||||
*
|
||||
* @retval 0 cleaning finished
|
||||
* @retval non-zero timeout and cleaning still in progress
|
||||
*/
|
||||
static int _ocf_cleaning_wait_for_finish(struct ocf_cache *cache,
|
||||
const int32_t timeout_ms)
|
||||
{
|
||||
struct ocf_user_part *curr_part;
|
||||
ocf_part_id_t part_id;
|
||||
bool cleaning_active = ocf_cache_is_device_attached(cache);
|
||||
int64_t _timeout = timeout_ms;
|
||||
|
||||
while (cleaning_active) {
|
||||
cleaning_active = false;
|
||||
|
||||
OCF_METADATA_LOCK_WR();
|
||||
for_each_part(cache, curr_part, part_id) {
|
||||
if (env_atomic_read(&cache->cleaning[part_id])) {
|
||||
cleaning_active = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
|
||||
if (cleaning_active) {
|
||||
env_msleep(20);
|
||||
|
||||
if (timeout_ms >= 0) {
|
||||
_timeout -= 20;
|
||||
if (_timeout <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (cleaning_active)
|
||||
return -EBUSY;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ocf_mngt_cache_put(ocf_cache_t cache)
|
||||
{
|
||||
OCF_CHECK_NULL(cache);
|
||||
|
||||
if (env_atomic_dec_return(&cache->ref_count) == 0) {
|
||||
ocf_free_queues(cache);
|
||||
ocf_metadata_deinit(cache);
|
||||
env_vfree(cache);
|
||||
}
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_get(ocf_ctx_t ocf_ctx, ocf_cache_id_t id, ocf_cache_t *cache)
|
||||
{
|
||||
int error = 0;
|
||||
struct ocf_cache *instance = NULL;
|
||||
struct ocf_cache *iter = NULL;
|
||||
|
||||
OCF_CHECK_NULL(ocf_ctx);
|
||||
OCF_CHECK_NULL(cache);
|
||||
|
||||
*cache = NULL;
|
||||
|
||||
if ((id < OCF_CACHE_ID_MIN) || (id > OCF_CACHE_ID_MAX)) {
|
||||
/* Cache id out of range */
|
||||
return -OCF_ERR_INVAL;
|
||||
}
|
||||
|
||||
/* Lock caches list */
|
||||
env_mutex_lock(&ocf_ctx->lock);
|
||||
|
||||
list_for_each_entry(iter, &ocf_ctx->caches, list) {
|
||||
if (iter->cache_id == id) {
|
||||
instance = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (instance) {
|
||||
/* if cache is either fully initialized or during recovery */
|
||||
if (instance->valid_ocf_cache_device_t) {
|
||||
/* Increase reference counter */
|
||||
env_atomic_inc(&instance->ref_count);
|
||||
} else {
|
||||
/* Cache not initialized yet */
|
||||
instance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
env_mutex_unlock(&ocf_ctx->lock);
|
||||
|
||||
if (!instance)
|
||||
error = -OCF_ERR_CACHE_NOT_EXIST;
|
||||
else
|
||||
*cache = instance;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
bool ocf_mngt_is_cache_locked(ocf_cache_t cache)
|
||||
{
|
||||
if (env_rwsem_is_locked(&cache->lock))
|
||||
return true;
|
||||
|
||||
if (env_atomic_read(&cache->lock_waiter))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ocf_mngt_cache_unlock(ocf_cache_t cache)
|
||||
{
|
||||
OCF_CHECK_NULL(cache);
|
||||
env_rwsem_up_write(&cache->lock);
|
||||
ocf_mngt_cache_put(cache);
|
||||
}
|
||||
|
||||
void ocf_mngt_cache_read_unlock(ocf_cache_t cache)
|
||||
{
|
||||
OCF_CHECK_NULL(cache);
|
||||
env_rwsem_up_read(&cache->lock);
|
||||
ocf_mngt_cache_put(cache);
|
||||
}
|
||||
|
||||
int _ocf_mngt_cache_lock(ocf_cache_t cache, bool read)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Increment reference counter */
|
||||
env_atomic_inc(&cache->ref_count);
|
||||
|
||||
env_atomic_inc(&cache->lock_waiter);
|
||||
if (read)
|
||||
ret = env_rwsem_down_read_interruptible(&cache->lock);
|
||||
else
|
||||
ret = env_rwsem_down_write_interruptible(&cache->lock);
|
||||
env_atomic_dec(&cache->lock_waiter);
|
||||
|
||||
if (ret) {
|
||||
ocf_mngt_cache_put(cache);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (env_bit_test(ocf_cache_state_stopping, &cache->cache_state)) {
|
||||
/* Cache already stooping, do not allow any operation */
|
||||
ret = -OCF_ERR_CACHE_NOT_EXIST;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Return, when asynchronous cleaning is finished */
|
||||
if (_ocf_cleaning_wait_for_finish(cache, 60 * 1000)) {
|
||||
/* Because of some reasons, asynchronous cleaning still active,
|
||||
* cannot continue
|
||||
*/
|
||||
ret = -OCF_ERR_CACHE_IN_USE;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unlock:
|
||||
if (read)
|
||||
ocf_mngt_cache_read_unlock(cache);
|
||||
else
|
||||
ocf_mngt_cache_unlock(cache);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_lock(ocf_cache_t cache)
|
||||
{
|
||||
OCF_CHECK_NULL(cache);
|
||||
return _ocf_mngt_cache_lock(cache, false);
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_read_lock(ocf_cache_t cache)
|
||||
{
|
||||
OCF_CHECK_NULL(cache);
|
||||
return _ocf_mngt_cache_lock(cache, true);
|
||||
}
|
||||
|
||||
/* if cache is either fully initialized or during recovery */
|
||||
static ocf_cache_t _ocf_mngt_cache_try_get(ocf_cache_t cache)
|
||||
{
|
||||
if (!!cache->valid_ocf_cache_device_t) {
|
||||
/* Increase reference counter */
|
||||
env_atomic_inc(&cache->ref_count);
|
||||
return cache;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _ocf_mngt_cache_get_list_cpy(ocf_ctx_t ocf_ctx, ocf_cache_t **list,
|
||||
uint32_t *size)
|
||||
{
|
||||
int result = 0;
|
||||
uint32_t count = 0, i = 0;
|
||||
struct ocf_cache *iter, *this;
|
||||
|
||||
*list = NULL;
|
||||
*size = 0;
|
||||
|
||||
env_mutex_lock(&ocf_ctx->lock);
|
||||
|
||||
list_for_each_entry(iter, &ocf_ctx->caches, list) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
goto END;
|
||||
|
||||
*list = env_vmalloc(sizeof((*list)[0]) * count);
|
||||
if (*list == NULL) {
|
||||
result = -ENOMEM;
|
||||
goto END;
|
||||
}
|
||||
|
||||
list_for_each_entry(iter, &ocf_ctx->caches, list) {
|
||||
this = _ocf_mngt_cache_try_get(iter);
|
||||
|
||||
if (this) {
|
||||
(*list)[i] = this;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (i) {
|
||||
/* Update size if cache list */
|
||||
*size = i;
|
||||
} else {
|
||||
env_vfree(*list);
|
||||
*list = NULL;
|
||||
}
|
||||
|
||||
END:
|
||||
env_mutex_unlock(&ocf_ctx->lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_visit(ocf_ctx_t ocf_ctx, ocf_mngt_cache_visitor_t visitor,
|
||||
void *cntx)
|
||||
{
|
||||
ocf_cache_t *list;
|
||||
uint32_t size, i;
|
||||
int result;
|
||||
|
||||
OCF_CHECK_NULL(ocf_ctx);
|
||||
OCF_CHECK_NULL(visitor);
|
||||
|
||||
result = _ocf_mngt_cache_get_list_cpy(ocf_ctx, &list, &size);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
/* Iterate over caches */
|
||||
for (i = 0; i < size; i++) {
|
||||
ocf_cache_t this = list[i];
|
||||
|
||||
result = visitor(this, cntx);
|
||||
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Put caches */
|
||||
for (i = 0; i < size; i++)
|
||||
ocf_mngt_cache_put(list[i]);
|
||||
|
||||
env_vfree(list);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_visit_reverse(ocf_ctx_t ocf_ctx,
|
||||
ocf_mngt_cache_visitor_t visitor, void *cntx)
|
||||
{
|
||||
ocf_cache_t *list;
|
||||
uint32_t size, i;
|
||||
int result;
|
||||
|
||||
OCF_CHECK_NULL(ocf_ctx);
|
||||
OCF_CHECK_NULL(visitor);
|
||||
|
||||
result = _ocf_mngt_cache_get_list_cpy(ocf_ctx, &list, &size);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
/* Iterate over caches */
|
||||
for (i = size; i; i--) {
|
||||
ocf_cache_t this = list[i - 1];
|
||||
|
||||
result = visitor(this, cntx);
|
||||
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Put caches */
|
||||
for (i = 0; i < size; i++)
|
||||
ocf_mngt_cache_put(list[i]);
|
||||
|
||||
env_vfree(list);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ocf_mngt_wait_for_io_finish(ocf_cache_t cache)
|
||||
{
|
||||
uint32_t rq_active = 0;
|
||||
|
||||
do {
|
||||
rq_active = ocf_rq_get_allocated(cache);
|
||||
if (rq_active)
|
||||
env_msleep(500);
|
||||
} while (rq_active);
|
||||
}
|
||||
|
35
src/mngt/ocf_mngt_common.h
Normal file
35
src/mngt/ocf_mngt_common.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2018 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __OCF_MNGT_COMMON_H__
|
||||
#define __OCF_MNGT_COMMON_H__
|
||||
|
||||
int cache_mng_core_close(ocf_cache_t cache, ocf_core_id_t core_id);
|
||||
|
||||
void cache_mng_core_remove_from_meta(struct ocf_cache *cache, int core_id);
|
||||
|
||||
void cache_mng_core_remove_from_cache(struct ocf_cache *cache, int core_id);
|
||||
|
||||
void cache_mng_core_deinit_attached_meta(struct ocf_cache *cache, int core_id);
|
||||
|
||||
void cache_mng_core_remove_from_cleaning_pol(struct ocf_cache *cache,
|
||||
int core_id);
|
||||
|
||||
int _ocf_cleaning_thread(void *priv);
|
||||
|
||||
int cache_mng_thread_io_requests(void *data);
|
||||
|
||||
bool ocf_mngt_cache_is_dirty(ocf_cache_t cache);
|
||||
|
||||
void ocf_mngt_wait_for_io_finish(ocf_cache_t cache);
|
||||
|
||||
int ocf_mngt_add_partition_to_cache(struct ocf_cache *cache,
|
||||
ocf_part_id_t part_id, const char *name, uint32_t min_size,
|
||||
uint32_t max_size, uint8_t priority, bool valid);
|
||||
|
||||
bool ocf_mngt_is_cache_locked(ocf_cache_t cache);
|
||||
|
||||
#endif /* __OCF_MNGT_COMMON_H__ */
|
480
src/mngt/ocf_mngt_core.c
Normal file
480
src/mngt/ocf_mngt_core.c
Normal file
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2018 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "ocf/ocf.h"
|
||||
#include "ocf_mngt_common.h"
|
||||
#include "../ocf_priv.h"
|
||||
#include "../metadata/metadata.h"
|
||||
#include "../engine/cache_engine.h"
|
||||
#include "../utils/utils_device.h"
|
||||
#include "../ocf_stats_priv.h"
|
||||
#include "../ocf_def_priv.h"
|
||||
|
||||
static ocf_seq_no_t _ocf_mngt_get_core_seq_no(ocf_cache_t cache)
|
||||
{
|
||||
if (cache->conf_meta->curr_core_seq_no == OCF_SEQ_NO_MAX)
|
||||
return OCF_SEQ_NO_INVALID;
|
||||
|
||||
return ++cache->conf_meta->curr_core_seq_no;
|
||||
}
|
||||
|
||||
static int _ocf_mngt_cache_try_add_core(ocf_cache_t cache, ocf_core_t *core,
|
||||
struct ocf_mngt_core_config *cfg)
|
||||
{
|
||||
int result = 0;
|
||||
struct ocf_core *core_obj;
|
||||
ocf_data_obj_t obj;
|
||||
|
||||
core_obj = &cache->core_obj[cfg->core_id];
|
||||
obj = &core_obj->obj;
|
||||
|
||||
if (ocf_ctx_get_data_obj_type_id(cache->owner, obj->type) !=
|
||||
cfg->data_obj_type) {
|
||||
result = -OCF_ERR_INVAL_DATA_OBJ_TYPE;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
result = ocf_data_obj_open(obj);
|
||||
if (result)
|
||||
goto error_out;
|
||||
|
||||
if (!ocf_data_obj_get_length(obj)) {
|
||||
result = -OCF_ERR_CORE_NOT_AVAIL;
|
||||
goto error_after_open;
|
||||
}
|
||||
|
||||
cache->core_obj[cfg->core_id].opened = true;
|
||||
|
||||
if (!(--cache->ocf_core_inactive_count))
|
||||
env_bit_clear(ocf_cache_state_incomplete, &cache->cache_state);
|
||||
|
||||
*core = core_obj;
|
||||
return 0;
|
||||
|
||||
error_after_open:
|
||||
ocf_data_obj_close(obj);
|
||||
error_out:
|
||||
*core = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int _ocf_mngt_cache_add_core(ocf_cache_t cache, ocf_core_t *core,
|
||||
struct ocf_mngt_core_config *cfg)
|
||||
{
|
||||
int result = 0;
|
||||
struct ocf_core *core_obj;
|
||||
ocf_data_obj_t obj;
|
||||
ocf_seq_no_t core_sequence_no;
|
||||
ocf_cleaning_t clean_type;
|
||||
uint64_t length;
|
||||
|
||||
core_obj = &cache->core_obj[cfg->core_id];
|
||||
obj = &core_obj->obj;
|
||||
|
||||
core_obj->obj.cache = cache;
|
||||
|
||||
/* Set uuid */
|
||||
ocf_uuid_core_set(cache, core_obj, &cfg->uuid);
|
||||
|
||||
obj->type = ocf_ctx_get_data_obj_type(cache->owner, cfg->data_obj_type);
|
||||
if (!obj->type) {
|
||||
result = -OCF_ERR_INVAL_DATA_OBJ_TYPE;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (cfg->user_metadata.data && cfg->user_metadata.size > 0) {
|
||||
result = ocf_core_set_user_metadata_raw(core_obj,
|
||||
cfg->user_metadata.data,
|
||||
cfg->user_metadata.size);
|
||||
if (result)
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
result = ocf_data_obj_open(obj);
|
||||
if (result)
|
||||
goto error_out;
|
||||
|
||||
length = ocf_data_obj_get_length(obj);
|
||||
if (!length) {
|
||||
result = -OCF_ERR_CORE_NOT_AVAIL;
|
||||
goto error_after_open;
|
||||
}
|
||||
cache->core_conf_meta[cfg->core_id].length = length;
|
||||
|
||||
clean_type = cache->conf_meta->cleaning_policy_type;
|
||||
if (ocf_cache_is_device_attached(cache) &&
|
||||
cleaning_policy_ops[clean_type].add_core) {
|
||||
result = cleaning_policy_ops[clean_type].add_core(cache,
|
||||
cfg->core_id);
|
||||
if (result)
|
||||
goto error_after_open;
|
||||
}
|
||||
|
||||
/* When adding new core to cache, allocate stat counters */
|
||||
core_obj->counters =
|
||||
env_zalloc(sizeof(*core_obj->counters), ENV_MEM_NORMAL);
|
||||
if (!core_obj->counters) {
|
||||
result = -OCF_ERR_NO_MEM;
|
||||
goto error_after_clean_pol;
|
||||
}
|
||||
/* When adding new core to cache, reset all core/cache statistics */
|
||||
ocf_stats_init(core_obj);
|
||||
env_atomic_set(&cache->core_runtime_meta[cfg->core_id].
|
||||
cached_clines, 0);
|
||||
env_atomic_set(&cache->core_runtime_meta[cfg->core_id].
|
||||
dirty_clines, 0);
|
||||
env_atomic64_set(&cache->core_runtime_meta[cfg->core_id].
|
||||
dirty_since, 0);
|
||||
|
||||
/* In metadata mark data this core was added into cache */
|
||||
env_bit_set(cfg->core_id, cache->conf_meta->valid_object_bitmap);
|
||||
cache->core_conf_meta[cfg->core_id].added = true;
|
||||
cache->core_obj[cfg->core_id].opened = true;
|
||||
|
||||
/* Set default cache parameters for sequential */
|
||||
cache->core_conf_meta[cfg->core_id].seq_cutoff_policy =
|
||||
ocf_seq_cutoff_policy_default;
|
||||
cache->core_conf_meta[cfg->core_id].seq_cutoff_threshold =
|
||||
cfg->seq_cutoff_threshold;
|
||||
|
||||
/* Add core sequence number for atomic metadata matching */
|
||||
core_sequence_no = _ocf_mngt_get_core_seq_no(cache);
|
||||
if (core_sequence_no == OCF_SEQ_NO_INVALID) {
|
||||
result = -OCF_ERR_TOO_MANY_CORES;
|
||||
goto error_after_counters_allocation;
|
||||
}
|
||||
cache->core_conf_meta[cfg->core_id].seq_no = core_sequence_no;
|
||||
|
||||
/* Update super-block with core device addition */
|
||||
if (ocf_metadata_flush_superblock(cache)) {
|
||||
result = -OCF_ERR_WRITE_CACHE;
|
||||
goto error_after_counters_allocation;
|
||||
}
|
||||
|
||||
/* Increase value of added cores */
|
||||
cache->conf_meta->core_obj_count++;
|
||||
|
||||
*core = core_obj;
|
||||
return 0;
|
||||
|
||||
error_after_counters_allocation:
|
||||
env_bit_clear(cfg->core_id, cache->conf_meta->valid_object_bitmap);
|
||||
cache->core_conf_meta[cfg->core_id].added = false;
|
||||
cache->core_obj[cfg->core_id].opened = false;
|
||||
|
||||
/* An error when flushing metadata, try restore for safety reason
|
||||
* previous metadata sate on cache device.
|
||||
* But if that fails too, we are scr**ed... or maybe:
|
||||
* TODO: Handle situation when we can't flush metadata by
|
||||
* trying to flush all the dirty data and switching to non-wb
|
||||
* cache mode.
|
||||
*/
|
||||
ocf_metadata_flush_superblock(cache);
|
||||
|
||||
env_free(core_obj->counters);
|
||||
core_obj->counters = NULL;
|
||||
|
||||
error_after_clean_pol:
|
||||
if (cleaning_policy_ops[clean_type].remove_core)
|
||||
cleaning_policy_ops[clean_type].remove_core(cache, cfg->core_id);
|
||||
|
||||
error_after_open:
|
||||
ocf_data_obj_close(obj);
|
||||
error_out:
|
||||
ocf_uuid_core_clear(cache, core_obj);
|
||||
*core = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
static unsigned long _ffz(unsigned long word)
|
||||
{
|
||||
asm("rep; bsf %1,%0"
|
||||
: "=r" (word)
|
||||
: "r" (~word));
|
||||
return word;
|
||||
}
|
||||
|
||||
static unsigned long _ocf_mngt_find_first_free_core(const unsigned long *bitmap,
|
||||
unsigned long size)
|
||||
{
|
||||
unsigned long i;
|
||||
unsigned long ret = size;
|
||||
|
||||
/* check core 0 availability */
|
||||
bool zero_core_free = !(*bitmap & 0x1UL);
|
||||
|
||||
/* check if any core id is free except 0 */
|
||||
for (i = 0; i * sizeof(unsigned long) * 8 < size; i++) {
|
||||
unsigned long long ignore_mask = (i == 0) ? 1UL : 0UL;
|
||||
if (~(bitmap[i] | ignore_mask)) {
|
||||
ret = MIN(size, i * sizeof(unsigned long) * 8 +
|
||||
_ffz(bitmap[i] | ignore_mask));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* return 0 only if no other core is free */
|
||||
if (ret == size && zero_core_free)
|
||||
return 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __ocf_mngt_lookup_core_uuid(ocf_cache_t cache,
|
||||
struct ocf_mngt_core_config *cfg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < OCF_CORE_MAX; i++) {
|
||||
ocf_core_t core = &cache->core_obj[i];
|
||||
|
||||
if (!env_bit_test(i, cache->conf_meta->valid_object_bitmap))
|
||||
continue;
|
||||
|
||||
if (cache->core_obj[i].opened)
|
||||
continue;
|
||||
|
||||
if (ocf_ctx_get_data_obj_type_id(cache->owner, core->obj.type)
|
||||
!= cfg->data_obj_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!env_strncmp(core->obj.uuid.data, cfg->uuid.data,
|
||||
min(core->obj.uuid.size,
|
||||
cfg->uuid.size)))
|
||||
return i;
|
||||
}
|
||||
|
||||
return OCF_CORE_MAX;
|
||||
}
|
||||
|
||||
static int __ocf_mngt_try_find_core_id(ocf_cache_t cache,
|
||||
struct ocf_mngt_core_config *cfg, ocf_core_id_t tmp_core_id)
|
||||
{
|
||||
if (tmp_core_id == OCF_CORE_MAX) {
|
||||
/* FIXME: uuid.data could be not NULL-terminated ANSI string */
|
||||
ocf_cache_log(cache, log_err, "Core with uuid %s not found in "
|
||||
"cache metadata\n", (char*) cfg->uuid.data);
|
||||
return -OCF_ERR_CORE_NOT_AVAIL;
|
||||
}
|
||||
|
||||
if (cfg->core_id != tmp_core_id) {
|
||||
ocf_cache_log(cache, log_err,
|
||||
"Given core id doesn't match with metadata\n");
|
||||
return -OCF_ERR_CORE_NOT_AVAIL;
|
||||
}
|
||||
|
||||
|
||||
cfg->core_id = tmp_core_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __ocf_mngt_find_core_id(ocf_cache_t cache,
|
||||
struct ocf_mngt_core_config *cfg, ocf_core_id_t tmp_core_id)
|
||||
{
|
||||
if (tmp_core_id != OCF_CORE_MAX) {
|
||||
ocf_cache_log(cache, log_err,
|
||||
"Core ID already added as inactive with id:"
|
||||
" %hu.\n", tmp_core_id);
|
||||
return -OCF_ERR_CORE_NOT_AVAIL;
|
||||
}
|
||||
|
||||
if (cfg->core_id == OCF_CORE_MAX) {
|
||||
ocf_cache_log(cache, log_debug, "Core ID is unspecified - "
|
||||
"will set first available number\n");
|
||||
|
||||
/* Core is unspecified */
|
||||
cfg->core_id = _ocf_mngt_find_first_free_core(
|
||||
cache->conf_meta->valid_object_bitmap,
|
||||
OCF_CORE_MAX);
|
||||
/* no need to check if find_first_zero_bit failed and
|
||||
* *core_id == MAX_CORE_OBJS_PER_CACHE, as above there is check
|
||||
* for core_obj_count being greater or equal to
|
||||
* MAX_CORE_OBJS_PER_CACHE
|
||||
*/
|
||||
} else if (cfg->core_id < OCF_CORE_MAX) {
|
||||
/* check if id is not used already */
|
||||
if (env_bit_test(cfg->core_id,
|
||||
cache->conf_meta->valid_object_bitmap)) {
|
||||
ocf_cache_log(cache, log_debug,
|
||||
"Core ID already allocated: %d.\n",
|
||||
cfg->core_id);
|
||||
return -OCF_ERR_CORE_NOT_AVAIL;
|
||||
}
|
||||
} else {
|
||||
ocf_cache_log(cache, log_err,
|
||||
"Core ID exceeds maximum of %d.\n",
|
||||
OCF_CORE_MAX);
|
||||
return -OCF_ERR_CORE_NOT_AVAIL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _ocf_mngt_find_core_id(ocf_cache_t cache,
|
||||
struct ocf_mngt_core_config *cfg)
|
||||
{
|
||||
int result;
|
||||
ocf_core_id_t tmp_core_id;
|
||||
|
||||
if (cache->conf_meta->core_obj_count >= OCF_CORE_MAX)
|
||||
return -OCF_ERR_TOO_MANY_CORES;
|
||||
|
||||
tmp_core_id = __ocf_mngt_lookup_core_uuid(cache, cfg);
|
||||
|
||||
if (cfg->try_add)
|
||||
result = __ocf_mngt_try_find_core_id(cache, cfg, tmp_core_id);
|
||||
else
|
||||
result = __ocf_mngt_find_core_id(cache, cfg, tmp_core_id);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_add_core_nolock(ocf_cache_t cache, ocf_core_t *core,
|
||||
struct ocf_mngt_core_config *cfg)
|
||||
{
|
||||
int result;
|
||||
char core_name[OCF_CORE_NAME_SIZE];
|
||||
|
||||
OCF_CHECK_NULL(cache);
|
||||
OCF_CHECK_NULL(core);
|
||||
|
||||
result = _ocf_mngt_find_core_id(cache, cfg);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (cfg->name) {
|
||||
result = env_strncpy(core_name, sizeof(core_name), cfg->name,
|
||||
cfg->name_size);
|
||||
if (result)
|
||||
return result;
|
||||
} else {
|
||||
result = snprintf(core_name, sizeof(core_name), "%hu",
|
||||
cfg->core_id);
|
||||
if (result < 0)
|
||||
return result;
|
||||
}
|
||||
|
||||
result = ocf_core_set_name(&cache->core_obj[cfg->core_id], core_name,
|
||||
sizeof(core_name));
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
ocf_cache_log(cache, log_debug, "Inserting core %s\n", core_name);
|
||||
|
||||
if (cfg->try_add)
|
||||
result = _ocf_mngt_cache_try_add_core(cache, core, cfg);
|
||||
else
|
||||
result = _ocf_mngt_cache_add_core(cache, core, cfg);
|
||||
|
||||
if (!result) {
|
||||
ocf_core_log(*core, log_info, "Successfully added\n");
|
||||
} else {
|
||||
if (result == -OCF_ERR_CORE_NOT_AVAIL) {
|
||||
ocf_cache_log(cache, log_err, "Core %s is zero size\n",
|
||||
core_name);
|
||||
}
|
||||
ocf_cache_log(cache, log_err, "Adding core %s failed\n",
|
||||
core_name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_add_core(ocf_cache_t cache, ocf_core_t *core,
|
||||
struct ocf_mngt_core_config *cfg)
|
||||
{
|
||||
int result;
|
||||
|
||||
OCF_CHECK_NULL(cache);
|
||||
|
||||
result = ocf_mngt_cache_lock(cache);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = ocf_mngt_cache_add_core_nolock(cache, core, cfg);
|
||||
|
||||
ocf_mngt_cache_unlock(cache);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int _ocf_mngt_cache_remove_core(ocf_core_t core, bool detach)
|
||||
{
|
||||
struct ocf_cache *cache = core->obj.cache;
|
||||
ocf_core_id_t core_id = ocf_core_get_id(core);
|
||||
int status;
|
||||
|
||||
if (detach) {
|
||||
status = cache_mng_core_close(cache, core_id);
|
||||
if (!status) {
|
||||
cache->ocf_core_inactive_count++;
|
||||
env_bit_set(ocf_cache_state_incomplete,
|
||||
&cache->cache_state);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Deinit everything*/
|
||||
if (ocf_cache_is_device_attached(cache)) {
|
||||
cache_mng_core_deinit_attached_meta(cache, core_id);
|
||||
cache_mng_core_remove_from_cleaning_pol(cache, core_id);
|
||||
}
|
||||
cache_mng_core_remove_from_meta(cache, core_id);
|
||||
cache_mng_core_remove_from_cache(cache, core_id);
|
||||
cache_mng_core_close(cache, core_id);
|
||||
|
||||
/* Update super-block with core device removal */
|
||||
ocf_metadata_flush_superblock(cache);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_remove_core_nolock(ocf_cache_t cache, ocf_core_id_t core_id,
|
||||
bool detach)
|
||||
{
|
||||
int result;
|
||||
ocf_core_t core;
|
||||
const char *core_name;
|
||||
|
||||
OCF_CHECK_NULL(cache);
|
||||
|
||||
result = ocf_core_get(cache, core_id, &core);
|
||||
if (result < 0)
|
||||
return -OCF_ERR_CORE_NOT_AVAIL;
|
||||
|
||||
ocf_core_log(core, log_debug, "Removing core\n");
|
||||
|
||||
core_name = ocf_core_get_name(core);
|
||||
|
||||
result = _ocf_mngt_cache_remove_core(core, detach);
|
||||
if (!result) {
|
||||
ocf_cache_log(cache, log_info, "Core %s successfully removed\n",
|
||||
core_name);
|
||||
} else {
|
||||
ocf_cache_log(cache, log_err, "Removing core %s failed\n",
|
||||
core_name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_remove_core(ocf_cache_t cache, ocf_core_id_t core_id,
|
||||
bool detach)
|
||||
{
|
||||
int result;
|
||||
|
||||
OCF_CHECK_NULL(cache);
|
||||
|
||||
result = ocf_mngt_cache_lock(cache);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = ocf_mngt_cache_remove_core_nolock(cache, core_id, detach);
|
||||
|
||||
ocf_mngt_cache_unlock(cache);
|
||||
|
||||
return result;
|
||||
}
|
123
src/mngt/ocf_mngt_core_pool.c
Normal file
123
src/mngt/ocf_mngt_core_pool.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2018 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
|
||||
#include "ocf/ocf.h"
|
||||
#include "ocf_mngt_common.h"
|
||||
#include "../ocf_priv.h"
|
||||
#include "../ocf_core_priv.h"
|
||||
#include "../ocf_ctx_priv.h"
|
||||
|
||||
void ocf_mngt_core_pool_init(ocf_ctx_t ctx)
|
||||
{
|
||||
OCF_CHECK_NULL(ctx);
|
||||
INIT_LIST_HEAD(&ctx->core_pool.core_pool_head);
|
||||
}
|
||||
|
||||
int ocf_mngt_core_pool_get_count(ocf_ctx_t ctx)
|
||||
{
|
||||
int count;
|
||||
OCF_CHECK_NULL(ctx);
|
||||
env_mutex_lock(&ctx->lock);
|
||||
count = ctx->core_pool.core_pool_count;
|
||||
env_mutex_unlock(&ctx->lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
int ocf_mngt_core_pool_add(ocf_ctx_t ctx, ocf_uuid_t uuid, uint8_t type)
|
||||
{
|
||||
ocf_data_obj_t obj;
|
||||
|
||||
int result = 0;
|
||||
|
||||
OCF_CHECK_NULL(ctx);
|
||||
|
||||
result = ocf_ctx_data_obj_create(ctx, &obj, uuid, type);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = ocf_data_obj_open(obj);
|
||||
if (result) {
|
||||
ocf_data_obj_deinit(obj);
|
||||
return result;
|
||||
}
|
||||
|
||||
env_mutex_lock(&ctx->lock);
|
||||
list_add(&obj->core_pool_item, &ctx->core_pool.core_pool_head);
|
||||
ctx->core_pool.core_pool_count++;
|
||||
env_mutex_unlock(&ctx->lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
int ocf_mngt_core_pool_visit(ocf_ctx_t ctx,
|
||||
int (*visitor)(ocf_uuid_t, void *), void *visitor_ctx)
|
||||
{
|
||||
int result = 0;
|
||||
ocf_data_obj_t sobj;
|
||||
|
||||
OCF_CHECK_NULL(ctx);
|
||||
OCF_CHECK_NULL(visitor);
|
||||
|
||||
env_mutex_lock(&ctx->lock);
|
||||
list_for_each_entry(sobj, &ctx->core_pool.core_pool_head,
|
||||
core_pool_item) {
|
||||
result = visitor(&sobj->uuid, visitor_ctx);
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
env_mutex_unlock(&ctx->lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
ocf_data_obj_t ocf_mngt_core_pool_lookup(ocf_ctx_t ctx, ocf_uuid_t uuid,
|
||||
ocf_data_obj_type_t type)
|
||||
{
|
||||
ocf_data_obj_t sobj;
|
||||
|
||||
OCF_CHECK_NULL(ctx);
|
||||
OCF_CHECK_NULL(uuid);
|
||||
OCF_CHECK_NULL(uuid->data);
|
||||
|
||||
list_for_each_entry(sobj, &ctx->core_pool.core_pool_head,
|
||||
core_pool_item) {
|
||||
if (sobj->type == type && !env_strncmp(sobj->uuid.data,
|
||||
uuid->data, min(sobj->uuid.size, uuid->size))) {
|
||||
return sobj;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ocf_mngt_core_pool_remove(ocf_ctx_t ctx, ocf_data_obj_t obj)
|
||||
{
|
||||
OCF_CHECK_NULL(ctx);
|
||||
OCF_CHECK_NULL(obj);
|
||||
env_mutex_lock(&ctx->lock);
|
||||
ctx->core_pool.core_pool_count--;
|
||||
list_del(&obj->core_pool_item);
|
||||
env_mutex_unlock(&ctx->lock);
|
||||
ocf_data_obj_deinit(obj);
|
||||
}
|
||||
|
||||
void ocf_mngt_core_pool_close_and_remove(ocf_ctx_t ctx, ocf_data_obj_t obj)
|
||||
{
|
||||
OCF_CHECK_NULL(ctx);
|
||||
OCF_CHECK_NULL(obj);
|
||||
ocf_data_obj_close(obj);
|
||||
ocf_mngt_core_pool_remove(ctx, obj);
|
||||
}
|
||||
|
||||
void ocf_mngt_core_pool_deinit(ocf_ctx_t ctx)
|
||||
{
|
||||
ocf_data_obj_t sobj, tobj;
|
||||
|
||||
OCF_CHECK_NULL(ctx);
|
||||
|
||||
list_for_each_entry_safe(sobj, tobj, &ctx->core_pool.core_pool_head,
|
||||
core_pool_item) {
|
||||
ocf_mngt_core_pool_close_and_remove(ctx, sobj);
|
||||
}
|
||||
}
|
803
src/mngt/ocf_mngt_flush.c
Normal file
803
src/mngt/ocf_mngt_flush.c
Normal file
@@ -0,0 +1,803 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2018 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "ocf/ocf.h"
|
||||
#include "ocf_mngt_common.h"
|
||||
#include "../ocf_priv.h"
|
||||
#include "../metadata/metadata.h"
|
||||
#include "../cleaning/cleaning.h"
|
||||
#include "../engine/cache_engine.h"
|
||||
#include "../utils/utils_cleaner.h"
|
||||
#include "../utils/utils_cache_line.h"
|
||||
#include "../utils/utils_part.h"
|
||||
#include "../ocf_def_priv.h"
|
||||
|
||||
static inline void _ocf_mngt_begin_flush(struct ocf_cache *cache)
|
||||
{
|
||||
env_mutex_lock(&cache->flush_mutex);
|
||||
|
||||
env_atomic_set(&cache->flush_started, 1);
|
||||
|
||||
env_waitqueue_wait(cache->pending_dirty_wq,
|
||||
!env_atomic_read(&cache->pending_dirty_requests));
|
||||
}
|
||||
|
||||
static inline void _ocf_mngt_end_flush(struct ocf_cache *cache)
|
||||
{
|
||||
env_atomic_set(&cache->flush_started, 0);
|
||||
|
||||
env_mutex_unlock(&cache->flush_mutex);
|
||||
}
|
||||
|
||||
bool ocf_mngt_cache_is_dirty(ocf_cache_t cache)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < OCF_CORE_MAX; ++i) {
|
||||
if (!cache->core_conf_meta[i].added)
|
||||
continue;
|
||||
|
||||
if (env_atomic_read(&(cache->core_runtime_meta[i].
|
||||
dirty_clines))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/************************FLUSH CORE CODE**************************************/
|
||||
/* Returns:
|
||||
* 0 if OK and tbl & num is filled:
|
||||
* * tbl - table with sectors&cacheline
|
||||
* * num - number of items in this table.
|
||||
* other value means error.
|
||||
* NOTE:
|
||||
* Table is not sorted.
|
||||
*/
|
||||
static int _ocf_mngt_get_sectors(struct ocf_cache *cache, int core_id,
|
||||
struct flush_data **tbl, uint32_t *num)
|
||||
{
|
||||
uint64_t core_line;
|
||||
ocf_core_id_t i_core_id;
|
||||
struct flush_data *p;
|
||||
uint32_t i, j, dirty = 0;
|
||||
|
||||
dirty = env_atomic_read(&cache->core_runtime_meta[core_id].
|
||||
dirty_clines);
|
||||
if (!dirty) {
|
||||
*num = 0;
|
||||
*tbl = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = env_vmalloc(dirty * sizeof(**tbl));
|
||||
if (!p)
|
||||
return -OCF_ERR_NO_MEM;
|
||||
|
||||
for (i = 0, j = 0; i < cache->device->collision_table_entries; i++) {
|
||||
ocf_metadata_get_core_info(cache, i, &i_core_id, &core_line);
|
||||
|
||||
if (i_core_id != core_id)
|
||||
continue;
|
||||
|
||||
if (!metadata_test_valid_any(cache, i))
|
||||
continue;
|
||||
|
||||
if (!metadata_test_dirty(cache, i))
|
||||
continue;
|
||||
|
||||
if (ocf_cache_line_is_used(cache, i))
|
||||
continue;
|
||||
|
||||
/* It's core_id cacheline and it's valid and it's dirty! */
|
||||
p[j].cache_line = i;
|
||||
p[j].core_line = core_line;
|
||||
p[j].core_id = i_core_id;
|
||||
j++;
|
||||
/* stop if all cachelines were found */
|
||||
if (j == dirty)
|
||||
break;
|
||||
}
|
||||
|
||||
ocf_core_log(&cache->core_obj[core_id], log_debug,
|
||||
"%u dirty cache lines to clean\n", j);
|
||||
|
||||
if (dirty != j) {
|
||||
ocf_cache_log(cache, log_debug, "Wrong number of dirty "
|
||||
"blocks for flushing core %s (%u!=%u)\n",
|
||||
cache->core_obj[core_id].name, j, dirty);
|
||||
}
|
||||
|
||||
|
||||
*tbl = p;
|
||||
*num = j;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _ocf_mngt_free_sectors(void *tbl)
|
||||
{
|
||||
env_vfree(tbl);
|
||||
}
|
||||
|
||||
static int _ocf_mngt_get_flush_containers(ocf_cache_t cache,
|
||||
struct flush_container **fctbl, uint32_t *fcnum)
|
||||
{
|
||||
struct flush_container *fc;
|
||||
struct flush_container *curr;
|
||||
uint32_t *core_revmap;
|
||||
uint32_t num;
|
||||
uint64_t core_line;
|
||||
ocf_core_id_t core_id;
|
||||
uint32_t i, j, dirty = 0;
|
||||
int step = 0;
|
||||
|
||||
/*
|
||||
* TODO: Create containers for each physical device, not for
|
||||
* each core. Cores can be partitions of single device.
|
||||
*/
|
||||
|
||||
num = cache->conf_meta->core_obj_count;
|
||||
if (num == 0) {
|
||||
*fcnum = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
core_revmap = env_vzalloc(sizeof(*core_revmap) * OCF_CORE_MAX);
|
||||
if (!core_revmap)
|
||||
return -OCF_ERR_NO_MEM;
|
||||
|
||||
/* TODO: Alloc flush_containers and data tables in single allocation */
|
||||
fc = env_vzalloc(sizeof(**fctbl) * num);
|
||||
if (!fc) {
|
||||
env_vfree(core_revmap);
|
||||
return -OCF_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; i < OCF_CORE_MAX; i++) {
|
||||
if (!env_bit_test(i, cache->conf_meta->valid_object_bitmap))
|
||||
continue;
|
||||
|
||||
fc[j].core_id = i;
|
||||
core_revmap[i] = j;
|
||||
|
||||
/* Check for dirty blocks */
|
||||
fc[j].count = env_atomic_read(&cache->
|
||||
core_runtime_meta[i].dirty_clines);
|
||||
dirty += fc[j].count;
|
||||
|
||||
if (fc[j].count) {
|
||||
fc[j].flush_data = env_vmalloc(fc[j].count *
|
||||
sizeof(*fc[j].flush_data));
|
||||
}
|
||||
|
||||
if (++j == cache->conf_meta->core_obj_count)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!dirty) {
|
||||
env_vfree(core_revmap);
|
||||
env_vfree(fc);
|
||||
*fcnum = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; i < cache->device->collision_table_entries; i++) {
|
||||
ocf_metadata_get_core_info(cache, i, &core_id, &core_line);
|
||||
|
||||
if (!metadata_test_valid_any(cache, i))
|
||||
continue;
|
||||
|
||||
if (!metadata_test_dirty(cache, i))
|
||||
continue;
|
||||
|
||||
if (ocf_cache_line_is_used(cache, i))
|
||||
continue;
|
||||
|
||||
curr = &fc[core_revmap[core_id]];
|
||||
|
||||
ENV_BUG_ON(curr->iter >= curr->count);
|
||||
|
||||
/* It's core_id cacheline and it's valid and it's dirty! */
|
||||
curr->flush_data[curr->iter].cache_line = i;
|
||||
curr->flush_data[curr->iter].core_line = core_line;
|
||||
curr->flush_data[curr->iter].core_id = core_id;
|
||||
curr->iter++;
|
||||
|
||||
j++;
|
||||
/* stop if all cachelines were found */
|
||||
if (j == dirty)
|
||||
break;
|
||||
|
||||
OCF_COND_RESCHED(step, 1000000)
|
||||
}
|
||||
|
||||
if (dirty != j) {
|
||||
ocf_cache_log(cache, log_debug, "Wrong number of dirty "
|
||||
"blocks (%u!=%u)\n", j, dirty);
|
||||
for (i = 0; i < num; i++)
|
||||
fc[i].count = fc[i].iter;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
fc[i].iter = 0;
|
||||
|
||||
env_vfree(core_revmap);
|
||||
*fctbl = fc;
|
||||
*fcnum = num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _ocf_mngt_free_flush_containers(struct flush_container *fctbl,
|
||||
uint32_t num)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
env_vfree(fctbl[i].flush_data);
|
||||
env_vfree(fctbl);
|
||||
}
|
||||
|
||||
/*
|
||||
* OCF will try to guess disk speed etc. and adjust flushing block
|
||||
* size accordingly, however these bounds shall be respected regardless
|
||||
* of disk speed, cache line size configured etc.
|
||||
*/
|
||||
#define OCF_MNG_FLUSH_MIN (4*MiB / ocf_line_size(cache))
|
||||
#define OCF_MNG_FLUSH_MAX (100*MiB / ocf_line_size(cache))
|
||||
|
||||
static void _ocf_mngt_flush_portion(struct flush_container *fc)
|
||||
{
|
||||
ocf_cache_t cache = fc->cache;
|
||||
uint64_t flush_portion_div;
|
||||
uint32_t curr_count;
|
||||
|
||||
flush_portion_div = env_ticks_to_msecs(fc->ticks2 - fc->ticks1);
|
||||
if (unlikely(!flush_portion_div))
|
||||
flush_portion_div = 1;
|
||||
|
||||
fc->flush_portion = fc->flush_portion * 1000 / flush_portion_div;
|
||||
fc->flush_portion &= ~0x3ffULL;
|
||||
|
||||
/* regardless those calculations, limit flush portion to be
|
||||
* between OCF_MNG_FLUSH_MIN and OCF_MNG_FLUSH_MAX
|
||||
*/
|
||||
fc->flush_portion = MIN(fc->flush_portion, OCF_MNG_FLUSH_MAX);
|
||||
fc->flush_portion = MAX(fc->flush_portion, OCF_MNG_FLUSH_MIN);
|
||||
|
||||
curr_count = MIN(fc->count - fc->iter, fc->flush_portion);
|
||||
|
||||
ocf_cleaner_do_flush_data_async(fc->cache,
|
||||
&fc->flush_data[fc->iter],
|
||||
curr_count, &fc->attribs);
|
||||
|
||||
fc->iter += curr_count;
|
||||
}
|
||||
|
||||
static void _ocf_mngt_flush_end(void *private_data, int error)
|
||||
{
|
||||
struct flush_container *fc = private_data;
|
||||
|
||||
fc->ticks2 = env_get_tick_count();
|
||||
|
||||
env_atomic_cmpxchg(fc->error, 0, error);
|
||||
|
||||
env_atomic_set(&fc->completed, 1);
|
||||
env_atomic_inc(fc->progress);
|
||||
env_waitqueue_wake_up(fc->wq);
|
||||
}
|
||||
|
||||
static int _ocf_mngt_flush_containers(ocf_cache_t cache,
|
||||
struct flush_container *fctbl, uint32_t fcnum,
|
||||
bool allow_interruption)
|
||||
{
|
||||
uint32_t fc_to_flush;
|
||||
env_waitqueue wq;
|
||||
env_atomic progress; /* incremented each time flushing of a portion of a
|
||||
container is completed */
|
||||
env_atomic error;
|
||||
ocf_core_t core;
|
||||
bool interrupt = false;
|
||||
int i;
|
||||
|
||||
if (fcnum == 0)
|
||||
return 0;
|
||||
|
||||
env_waitqueue_init(&wq);
|
||||
|
||||
/* Sort data. Smallest sectors first (0...n). */
|
||||
ocf_cleaner_sort_flush_containers(fctbl, fcnum);
|
||||
|
||||
env_atomic_set(&error, 0);
|
||||
|
||||
for (i = 0; i < fcnum; i++) {
|
||||
fctbl[i].attribs.cache_line_lock = true;
|
||||
fctbl[i].attribs.metadata_locked = true;
|
||||
fctbl[i].attribs.cmpl_context = &fctbl[i];
|
||||
fctbl[i].attribs.cmpl_fn = _ocf_mngt_flush_end;
|
||||
fctbl[i].attribs.io_queue = 0;
|
||||
fctbl[i].cache = cache;
|
||||
fctbl[i].progress = &progress;
|
||||
fctbl[i].error = &error;
|
||||
fctbl[i].wq = &wq;
|
||||
fctbl[i].flush_portion = OCF_MNG_FLUSH_MIN;
|
||||
fctbl[i].ticks1 = 0;
|
||||
fctbl[i].ticks2 = UINT_MAX;
|
||||
env_atomic_set(&fctbl[i].completed, 1);
|
||||
}
|
||||
|
||||
for (fc_to_flush = fcnum; fc_to_flush > 0;) {
|
||||
env_atomic_set(&progress, 0);
|
||||
for (i = 0; i < fcnum; i++) {
|
||||
if (!env_atomic_read(&fctbl[i].completed))
|
||||
continue;
|
||||
|
||||
core = &cache->core_obj[fctbl[i].core_id];
|
||||
env_atomic_set(&core->flushed, fctbl[i].iter);
|
||||
env_atomic_set(&fctbl[i].completed, 0);
|
||||
|
||||
if (fctbl[i].iter == fctbl[i].count || interrupt ||
|
||||
env_atomic_read(&error)) {
|
||||
fc_to_flush--;
|
||||
continue;
|
||||
}
|
||||
|
||||
_ocf_mngt_flush_portion(&fctbl[i]);
|
||||
}
|
||||
if (fc_to_flush) {
|
||||
ocf_metadata_unlock(cache, OCF_METADATA_WR);
|
||||
env_cond_resched();
|
||||
env_waitqueue_wait(wq, env_atomic_read(&progress));
|
||||
ocf_metadata_lock(cache, OCF_METADATA_WR);
|
||||
}
|
||||
if (cache->flushing_interrupted && !interrupt) {
|
||||
if (allow_interruption) {
|
||||
interrupt = true;
|
||||
ocf_cache_log(cache, log_info,
|
||||
"Flushing interrupted by "
|
||||
"user\n");
|
||||
} else {
|
||||
ocf_cache_log(cache, log_err,
|
||||
"Cannot interrupt flushing\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return interrupt ? -OCF_ERR_FLUSHING_INTERRUPTED :
|
||||
env_atomic_read(&error);
|
||||
}
|
||||
|
||||
static int _ocf_mngt_flush_core(ocf_core_t core, bool allow_interruption)
|
||||
{
|
||||
ocf_core_id_t core_id = ocf_core_get_id(core);
|
||||
ocf_cache_t cache = core->obj.cache;
|
||||
struct flush_container fc;
|
||||
int ret;
|
||||
|
||||
ocf_metadata_lock(cache, OCF_METADATA_WR);
|
||||
|
||||
ret = _ocf_mngt_get_sectors(cache, core_id,
|
||||
&fc.flush_data, &fc.count);
|
||||
if (ret) {
|
||||
ocf_core_log(core, log_err, "Flushing operation aborted, "
|
||||
"no memory\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
fc.core_id = core_id;
|
||||
fc.iter = 0;
|
||||
|
||||
ret = _ocf_mngt_flush_containers(cache, &fc, 1, allow_interruption);
|
||||
|
||||
_ocf_mngt_free_sectors(fc.flush_data);
|
||||
|
||||
out:
|
||||
ocf_metadata_unlock(cache, OCF_METADATA_WR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _ocf_mngt_flush_all_cores(ocf_cache_t cache, bool allow_interruption)
|
||||
{
|
||||
struct flush_container *fctbl = NULL;
|
||||
uint32_t fcnum = 0;
|
||||
int ret;
|
||||
|
||||
ocf_metadata_lock(cache, OCF_METADATA_WR);
|
||||
|
||||
/* Get all 'dirty' sectors for all cores */
|
||||
ret = _ocf_mngt_get_flush_containers(cache, &fctbl, &fcnum);
|
||||
if (ret) {
|
||||
ocf_cache_log(cache, log_err, "Flushing operation aborted, "
|
||||
"no memory\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = _ocf_mngt_flush_containers(cache, fctbl, fcnum,
|
||||
allow_interruption);
|
||||
|
||||
_ocf_mngt_free_flush_containers(fctbl, fcnum);
|
||||
|
||||
out:
|
||||
ocf_metadata_unlock(cache, OCF_METADATA_WR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush all the dirty data stored on cache (all the cores attached to it)
|
||||
* @param cache cache instance to which operation applies
|
||||
* @param allow_interruption whenever to allow interruption of flushing process.
|
||||
* if set to 0, all requests to interrupt flushing will be ignored
|
||||
*/
|
||||
static int _ocf_mng_cache_flush_nolock(ocf_cache_t cache, bool interruption)
|
||||
{
|
||||
int result = 0;
|
||||
int i, j;
|
||||
|
||||
env_atomic_set(&cache->flush_in_progress, 1);
|
||||
cache->flushing_interrupted = 0;
|
||||
do {
|
||||
env_cond_resched();
|
||||
result = _ocf_mngt_flush_all_cores(cache, interruption);
|
||||
if (result) {
|
||||
/* Cleaning error */
|
||||
break;
|
||||
}
|
||||
} while (ocf_mngt_cache_is_dirty(cache));
|
||||
|
||||
env_atomic_set(&cache->flush_in_progress, 0);
|
||||
for (i = 0, j = 0; i < OCF_CORE_MAX; i++) {
|
||||
if (!env_bit_test(i, cache->conf_meta->valid_object_bitmap))
|
||||
continue;
|
||||
|
||||
env_atomic_set(&cache->core_obj[i].flushed, 0);
|
||||
|
||||
if (++j == cache->conf_meta->core_obj_count)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_flush_nolock(ocf_cache_t cache, bool interruption)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
OCF_CHECK_NULL(cache);
|
||||
|
||||
if (ocf_cache_is_incomplete(cache)) {
|
||||
ocf_cache_log(cache, log_err, "Cannot flush cache - "
|
||||
"cache is in incomplete state\n");
|
||||
return -OCF_ERR_CACHE_IN_INCOMPLETE_STATE;
|
||||
}
|
||||
|
||||
ocf_cache_log(cache, log_info, "Flushing cache\n");
|
||||
|
||||
_ocf_mngt_begin_flush(cache);
|
||||
|
||||
result = _ocf_mng_cache_flush_nolock(cache, interruption);
|
||||
|
||||
_ocf_mngt_end_flush(cache);
|
||||
|
||||
if (!result)
|
||||
ocf_cache_log(cache, log_info, "Flushing cache completed\n");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int _ocf_mng_core_flush_nolock(ocf_core_t core, bool interruption)
|
||||
{
|
||||
struct ocf_cache *cache = core->obj.cache;
|
||||
ocf_core_id_t core_id = ocf_core_get_id(core);
|
||||
int ret;
|
||||
|
||||
cache->flushing_interrupted = 0;
|
||||
do {
|
||||
env_cond_resched();
|
||||
ret = _ocf_mngt_flush_core(core, interruption);
|
||||
if (ret == -OCF_ERR_FLUSHING_INTERRUPTED ||
|
||||
ret == -OCF_ERR_WRITE_CORE) {
|
||||
break;
|
||||
}
|
||||
} while (env_atomic_read(&cache->core_runtime_meta[core_id].
|
||||
dirty_clines));
|
||||
|
||||
env_atomic_set(&core->flushed, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ocf_mngt_core_flush_nolock(ocf_cache_t cache, ocf_core_id_t id,
|
||||
bool interruption)
|
||||
{
|
||||
ocf_core_t core;
|
||||
int ret = 0;
|
||||
|
||||
OCF_CHECK_NULL(cache);
|
||||
|
||||
ret = ocf_core_get(cache, id, &core);
|
||||
if (ret < 0)
|
||||
return -OCF_ERR_CORE_NOT_AVAIL;
|
||||
|
||||
if (!core->opened) {
|
||||
ocf_core_log(core, log_err, "Cannot flush - core is in "
|
||||
"inactive state\n");
|
||||
return -OCF_ERR_CORE_IN_INACTIVE_STATE;
|
||||
}
|
||||
|
||||
ocf_core_log(core, log_info, "Flushing\n");
|
||||
|
||||
_ocf_mngt_begin_flush(cache);
|
||||
|
||||
ret = _ocf_mng_core_flush_nolock(core, interruption);
|
||||
|
||||
_ocf_mngt_end_flush(cache);
|
||||
|
||||
if (!ret)
|
||||
ocf_cache_log(cache, log_info, "Flushing completed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_flush(ocf_cache_t cache, bool interruption)
|
||||
{
|
||||
int result = ocf_mngt_cache_read_lock(cache);
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (!ocf_cache_is_device_attached(cache)) {
|
||||
result = -OCF_ERR_INVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
result = ocf_mngt_cache_flush_nolock(cache, interruption);
|
||||
|
||||
unlock:
|
||||
ocf_mngt_cache_read_unlock(cache);
|
||||
return result;
|
||||
}
|
||||
|
||||
int ocf_mngt_core_flush(ocf_cache_t cache, ocf_core_id_t id, bool interruption)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* lock read only */
|
||||
result = ocf_mngt_cache_read_lock(cache);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (!ocf_cache_is_device_attached(cache)) {
|
||||
result = -OCF_ERR_INVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
result = ocf_mngt_core_flush_nolock(cache, id, interruption);
|
||||
|
||||
unlock:
|
||||
ocf_mngt_cache_read_unlock(cache);
|
||||
return result;
|
||||
}
|
||||
|
||||
int ocf_mngt_core_purge(ocf_cache_t cache, ocf_core_id_t core_id, bool interruption)
|
||||
{
|
||||
int result = 0;
|
||||
uint64_t core_size = ~0ULL;
|
||||
ocf_core_t core;
|
||||
|
||||
OCF_CHECK_NULL(cache);
|
||||
|
||||
result = ocf_mngt_cache_read_lock(cache);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = ocf_core_get(cache, core_id, &core);
|
||||
if (result < 0) {
|
||||
ocf_mngt_cache_unlock(cache);
|
||||
return -OCF_ERR_CORE_NOT_AVAIL;
|
||||
}
|
||||
|
||||
core_size = ocf_data_obj_get_length(&cache->core_obj[core_id].obj);
|
||||
core_size = core_size ?: ~0ULL;
|
||||
|
||||
_ocf_mngt_begin_flush(cache);
|
||||
|
||||
ocf_core_log(core, log_info, "Purging\n");
|
||||
|
||||
result = _ocf_mng_core_flush_nolock(core, interruption);
|
||||
|
||||
if (result)
|
||||
goto err;
|
||||
|
||||
OCF_METADATA_LOCK_WR();
|
||||
result = ocf_metadata_sparse_range(cache, core_id, 0,
|
||||
core_size);
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
|
||||
err:
|
||||
_ocf_mngt_end_flush(cache);
|
||||
|
||||
ocf_mngt_cache_read_unlock(cache);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_purge(ocf_cache_t cache, bool interruption)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
result = ocf_mngt_cache_read_lock(cache);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
_ocf_mngt_begin_flush(cache);
|
||||
|
||||
ocf_cache_log(cache, log_info, "Purging\n");
|
||||
|
||||
result = _ocf_mng_cache_flush_nolock(cache, interruption);
|
||||
|
||||
if (result)
|
||||
goto err;
|
||||
|
||||
OCF_METADATA_LOCK_WR();
|
||||
result = ocf_metadata_sparse_range(cache, OCF_CORE_ID_INVALID, 0,
|
||||
~0ULL);
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
|
||||
err:
|
||||
_ocf_mngt_end_flush(cache);
|
||||
|
||||
ocf_mngt_cache_read_unlock(cache);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_flush_interrupt(ocf_cache_t cache)
|
||||
{
|
||||
OCF_CHECK_NULL(cache);
|
||||
ocf_cache_log(cache, log_alert, "Flushing interrupt\n");
|
||||
cache->flushing_interrupted = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_cleaning_set_policy(ocf_cache_t cache, ocf_cleaning_t type)
|
||||
{
|
||||
|
||||
ocf_cleaning_t old_type;
|
||||
int ret;
|
||||
|
||||
if (type < 0 || type >= ocf_cleaning_max)
|
||||
return -OCF_ERR_INVAL;
|
||||
|
||||
|
||||
ret = ocf_mngt_cache_lock(cache);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
old_type = cache->conf_meta->cleaning_policy_type;
|
||||
|
||||
if (type == old_type) {
|
||||
ocf_cache_log(cache, log_info, "Cleaning policy %s is already "
|
||||
"set\n", cleaning_policy_ops[old_type].name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ocf_metadata_lock(cache, OCF_METADATA_WR);
|
||||
|
||||
if (cleaning_policy_ops[old_type].deinitialize)
|
||||
cleaning_policy_ops[old_type].deinitialize(cache);
|
||||
|
||||
if (cleaning_policy_ops[type].initialize) {
|
||||
if (cleaning_policy_ops[type].initialize(cache, 1)) {
|
||||
/*
|
||||
* If initialization of new cleaning policy failed,
|
||||
* we set cleaning policy to nop.
|
||||
*/
|
||||
type = ocf_cleaning_nop;
|
||||
ret = -OCF_ERR_INVAL;
|
||||
}
|
||||
}
|
||||
|
||||
cache->conf_meta->cleaning_policy_type = type;
|
||||
|
||||
if (type != old_type) {
|
||||
/*
|
||||
* If operation was successfull or cleaning policy changed,
|
||||
* we need to flush superblock.
|
||||
*/
|
||||
if (ocf_metadata_flush_superblock(cache)) {
|
||||
ocf_cache_log(cache, log_err,
|
||||
"Failed to flush superblock! Changes "
|
||||
"in cache config are not persistent!\n");
|
||||
}
|
||||
}
|
||||
|
||||
ocf_cache_log(cache, log_info, "Changing cleaning policy from "
|
||||
"%s to %s\n", cleaning_policy_ops[old_type].name,
|
||||
cleaning_policy_ops[type].name);
|
||||
|
||||
ocf_metadata_unlock(cache, OCF_METADATA_WR);
|
||||
|
||||
out:
|
||||
ocf_mngt_cache_unlock(cache);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_cleaning_get_policy(ocf_cache_t cache, ocf_cleaning_t *type)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ocf_mngt_cache_read_lock(cache);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*type = cache->conf_meta->cleaning_policy_type;
|
||||
|
||||
ocf_mngt_cache_read_unlock(cache);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_cleaning_set_param(ocf_cache_t cache, ocf_cleaning_t type,
|
||||
uint32_t param_id, uint32_t param_value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (type < 0 || type >= ocf_cleaning_max)
|
||||
return -OCF_ERR_INVAL;
|
||||
|
||||
if (!cleaning_policy_ops[type].set_cleaning_param)
|
||||
return -OCF_ERR_INVAL;
|
||||
|
||||
ret = ocf_mngt_cache_lock(cache);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ocf_metadata_lock(cache, OCF_METADATA_WR);
|
||||
|
||||
ret = cleaning_policy_ops[type].set_cleaning_param(cache,
|
||||
param_id, param_value);
|
||||
|
||||
if (ret == 0) {
|
||||
/*
|
||||
* If operation was successfull or cleaning policy changed,
|
||||
* we need to flush superblock.
|
||||
*/
|
||||
if (ocf_metadata_flush_superblock(cache)) {
|
||||
ocf_cache_log(cache, log_err,
|
||||
"Failed to flush superblock! Changes "
|
||||
"in cache config are not persistent!\n");
|
||||
}
|
||||
}
|
||||
|
||||
ocf_metadata_unlock(cache, OCF_METADATA_WR);
|
||||
|
||||
ocf_mngt_cache_unlock(cache);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ocf_mngt_cache_cleaning_get_param(ocf_cache_t cache, ocf_cleaning_t type,
|
||||
uint32_t param_id, uint32_t *param_value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (type < 0 || type >= ocf_cleaning_max)
|
||||
return -OCF_ERR_INVAL;
|
||||
|
||||
if (!cleaning_policy_ops[type].get_cleaning_param)
|
||||
return -OCF_ERR_INVAL;
|
||||
|
||||
ret = ocf_mngt_cache_read_lock(cache);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cleaning_policy_ops[type].get_cleaning_param(cache,
|
||||
param_id, param_value);
|
||||
|
||||
ocf_mngt_cache_read_unlock(cache);
|
||||
|
||||
return ret;
|
||||
}
|
273
src/mngt/ocf_mngt_io_class.c
Normal file
273
src/mngt/ocf_mngt_io_class.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2018 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "ocf/ocf.h"
|
||||
#include "ocf_mngt_common.h"
|
||||
#include "../ocf_priv.h"
|
||||
#include "../metadata/metadata.h"
|
||||
#include "../engine/cache_engine.h"
|
||||
#include "../utils/utils_part.h"
|
||||
#include "../eviction/ops.h"
|
||||
#include "ocf_env.h"
|
||||
|
||||
static uint64_t _ocf_mngt_count_parts_min_size(struct ocf_cache *cache)
|
||||
{
|
||||
struct ocf_user_part *part;
|
||||
ocf_part_id_t part_id;
|
||||
uint64_t count = 0;
|
||||
|
||||
for_each_part(cache, part, part_id) {
|
||||
if (ocf_part_is_valid(part))
|
||||
count += part->config->min_size;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int ocf_mngt_add_partition_to_cache(struct ocf_cache *cache,
|
||||
ocf_part_id_t part_id, const char *name, uint32_t min_size,
|
||||
uint32_t max_size, uint8_t priority, bool valid)
|
||||
{
|
||||
uint32_t size;
|
||||
|
||||
if (!name)
|
||||
return -OCF_ERR_INVAL;
|
||||
|
||||
if (part_id >= OCF_IO_CLASS_MAX)
|
||||
return -OCF_ERR_INVAL;
|
||||
|
||||
if (cache->user_parts[part_id].config->flags.valid)
|
||||
return -OCF_ERR_INVAL;
|
||||
|
||||
if (max_size > PARTITION_SIZE_MAX)
|
||||
return -OCF_ERR_INVAL;
|
||||
|
||||
if (env_strnlen(name, OCF_IO_CLASS_NAME_MAX) >=
|
||||
OCF_IO_CLASS_NAME_MAX) {
|
||||
ocf_cache_log(cache, log_info,
|
||||
"Name of the partition is too long\n");
|
||||
return -OCF_ERR_INVAL;
|
||||
}
|
||||
|
||||
size = sizeof(cache->user_parts[part_id].config->name);
|
||||
if (env_strncpy(cache->user_parts[part_id].config->name, size, name, size))
|
||||
return -OCF_ERR_INVAL;
|
||||
|
||||
cache->user_parts[part_id].config->min_size = min_size;
|
||||
cache->user_parts[part_id].config->max_size = max_size;
|
||||
cache->user_parts[part_id].config->priority = priority;
|
||||
cache->user_parts[part_id].config->cache_mode = ocf_cache_mode_max;
|
||||
|
||||
ocf_part_set_valid(cache, part_id, valid);
|
||||
ocf_lst_add(&cache->lst_part, part_id);
|
||||
ocf_part_sort(cache);
|
||||
|
||||
cache->user_parts[part_id].config->flags.added = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _ocf_mngt_set_partition_size(struct ocf_cache *cache,
|
||||
ocf_part_id_t part_id, uint32_t min, uint32_t max)
|
||||
{
|
||||
struct ocf_user_part *part = &cache->user_parts[part_id];
|
||||
|
||||
if (min > max)
|
||||
return -OCF_ERR_INVAL;
|
||||
|
||||
if (_ocf_mngt_count_parts_min_size(cache) + min
|
||||
>= cache->device->collision_table_entries) {
|
||||
/* Illegal configuration in which sum of all min_sizes exceeds
|
||||
* cache size.
|
||||
*/
|
||||
return -OCF_ERR_INVAL;
|
||||
}
|
||||
|
||||
if (max > PARTITION_SIZE_MAX)
|
||||
max = PARTITION_SIZE_MAX;
|
||||
|
||||
part->config->min_size = min;
|
||||
part->config->max_size = max;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _ocf_mngt_io_class_configure(ocf_cache_t cache,
|
||||
const struct ocf_mngt_io_class_config *cfg)
|
||||
{
|
||||
int result = -1;
|
||||
struct ocf_user_part *dest_part;
|
||||
|
||||
ocf_part_id_t part_id = cfg->class_id;
|
||||
const char *name = cfg->name;
|
||||
int16_t prio = cfg->prio;
|
||||
ocf_cache_mode_t cache_mode = cfg->cache_mode;
|
||||
uint32_t min = cfg->min_size;
|
||||
uint32_t max = cfg->max_size;
|
||||
|
||||
OCF_CHECK_NULL(cache->device);
|
||||
|
||||
OCF_METADATA_LOCK_WR();
|
||||
|
||||
dest_part = &cache->user_parts[part_id];
|
||||
|
||||
if (!ocf_part_is_added(dest_part)) {
|
||||
ocf_cache_log(cache, log_info, "Setting IO class, id: %u, "
|
||||
"name: '%s' [ ERROR ]\n", part_id, dest_part->config->name);
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
return -OCF_ERR_INVAL;
|
||||
}
|
||||
|
||||
if (part_id == PARTITION_DEFAULT) {
|
||||
/* Special behavior for default partition */
|
||||
|
||||
if (!name[0]) {
|
||||
/* Removing of default partition is not allowed */
|
||||
ocf_cache_log(cache, log_info,
|
||||
"Cannot remove unclassified IO class, "
|
||||
"id: %u [ ERROR ]\n", part_id);
|
||||
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try set partition size */
|
||||
if (_ocf_mngt_set_partition_size(cache, part_id, min, max)) {
|
||||
ocf_cache_log(cache, log_info,
|
||||
"Setting IO class size, id: %u, name: '%s' "
|
||||
"[ ERROR ]\n", part_id, dest_part->config->name);
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
return -OCF_ERR_INVAL;
|
||||
}
|
||||
ocf_part_set_prio(cache, dest_part, prio);
|
||||
ocf_part_sort(cache);
|
||||
dest_part->config->cache_mode = cache_mode;
|
||||
|
||||
ocf_cache_log(cache, log_info,
|
||||
"Updating Unclassified IO class, id: "
|
||||
"%u [ OK ]\n", part_id);
|
||||
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (name[0]) {
|
||||
/* Setting */
|
||||
result = env_strncpy(dest_part->config->name, sizeof(dest_part->config->name), name,
|
||||
sizeof(dest_part->config->name));
|
||||
if (result) {
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Try set partition size */
|
||||
if (_ocf_mngt_set_partition_size(cache, part_id, min, max)) {
|
||||
ocf_cache_log(cache, log_info,
|
||||
"Setting IO class size, id: %u, name: '%s' "
|
||||
"[ ERROR ]\n", part_id, dest_part->config->name);
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
return -OCF_ERR_INVAL;
|
||||
}
|
||||
|
||||
if (ocf_part_is_valid(dest_part)) {
|
||||
/* Updating existing */
|
||||
ocf_cache_log(cache, log_info, "Updating existing IO "
|
||||
"class, id: %u, name: '%s' [ OK ]\n",
|
||||
part_id, dest_part->config->name);
|
||||
|
||||
} else {
|
||||
/* Adding new */
|
||||
ocf_part_set_valid(cache, part_id, true);
|
||||
|
||||
ocf_cache_log(cache, log_info, "Adding new IO class, "
|
||||
"id: %u, name: '%s' [ OK ]\n", part_id,
|
||||
dest_part->config->name);
|
||||
}
|
||||
|
||||
ocf_part_set_prio(cache, dest_part, prio);
|
||||
dest_part->config->cache_mode = cache_mode;
|
||||
|
||||
result = 0;
|
||||
|
||||
} else {
|
||||
/* Clearing */
|
||||
|
||||
if (ocf_part_is_valid(dest_part)) {
|
||||
/* Removing */
|
||||
|
||||
result = 0;
|
||||
|
||||
ocf_part_set_valid(cache, part_id, false);
|
||||
|
||||
ocf_cache_log(cache, log_info,
|
||||
"Removing IO class, id: %u [ %s ]\n",
|
||||
part_id, result ? "ERROR" : "OK");
|
||||
|
||||
} else {
|
||||
/* Does not exist */
|
||||
result = -OCF_ERR_IO_CLASS_NOT_EXIST;
|
||||
}
|
||||
}
|
||||
|
||||
ocf_part_sort(cache);
|
||||
|
||||
OCF_METADATA_UNLOCK_WR();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int _ocf_mngt_io_class_validate_cfg(ocf_cache_t cache,
|
||||
const struct ocf_mngt_io_class_config *cfg)
|
||||
{
|
||||
if (cfg->class_id >= OCF_IO_CLASS_MAX)
|
||||
return -OCF_ERR_INVAL;
|
||||
|
||||
/* TODO(r.baldyga): ocf_cache_mode_max is allowed for compatibility
|
||||
* with OCF 3.1 kernel adapter (upgrade in flight) and casadm.
|
||||
* Forbid ocf_cache_mode_max after fixing these problems.
|
||||
*/
|
||||
if (cfg->cache_mode < ocf_cache_mode_none ||
|
||||
cfg->cache_mode > ocf_cache_mode_max) {
|
||||
return -OCF_ERR_INVAL;
|
||||
}
|
||||
|
||||
if (!ocf_part_is_name_valid(cfg->name)) {
|
||||
ocf_cache_log(cache, log_info,
|
||||
"The name of the partition is not valid\n");
|
||||
return -OCF_ERR_INVAL;
|
||||
}
|
||||
|
||||
if (!ocf_part_is_prio_valid(cfg->prio)) {
|
||||
ocf_cache_log(cache, log_info,
|
||||
"Invalid value of the partition priority\n");
|
||||
return -OCF_ERR_INVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ocf_mngt_io_class_configure(ocf_cache_t cache,
|
||||
const struct ocf_mngt_io_class_config *cfg)
|
||||
{
|
||||
int result;
|
||||
|
||||
OCF_CHECK_NULL(cache);
|
||||
|
||||
result = _ocf_mngt_io_class_validate_cfg(cache, cfg);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = ocf_mngt_cache_lock(cache);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = _ocf_mngt_io_class_configure(cache, cfg);
|
||||
|
||||
ocf_mngt_cache_unlock(cache);
|
||||
return 0;
|
||||
}
|
||||
|
29
src/mngt/ocf_mngt_misc.c
Normal file
29
src/mngt/ocf_mngt_misc.c
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2018 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "ocf/ocf.h"
|
||||
#include "ocf_mngt_common.h"
|
||||
#include "../ocf_priv.h"
|
||||
#include "../metadata/metadata.h"
|
||||
#include "../engine/cache_engine.h"
|
||||
#include "../ocf_ctx_priv.h"
|
||||
|
||||
uint32_t ocf_mngt_cache_get_count(ocf_ctx_t ctx)
|
||||
{
|
||||
struct ocf_cache *cache;
|
||||
uint32_t count = 0;
|
||||
|
||||
OCF_CHECK_NULL(ctx);
|
||||
|
||||
env_mutex_lock(&ctx->lock);
|
||||
|
||||
/* currently, there are no macros in list.h to get list size.*/
|
||||
list_for_each_entry(cache, &ctx->caches, list)
|
||||
count++;
|
||||
|
||||
env_mutex_unlock(&ctx->lock);
|
||||
|
||||
return count;
|
||||
}
|
Reference in New Issue
Block a user