ocf/src/mngt/ocf_mngt_common.c
Robert Baldyga 44ed250d41 Add API function ocf_cache_wait_for_io_finish()
Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
2019-02-20 11:16:06 +01:00

437 lines
8.8 KiB
C

/*
* 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_req.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[core_id].opened)
return -OCF_ERR_CORE_IN_INACTIVE_STATE;
ocf_volume_close(&cache->core[core_id].volume);
cache->core[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[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_volume_t core;
core = &cache->core[core_id].volume;
core_size = ocf_volume_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_metadata_clear_core_uuid(&cache->core[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[core_id].counters);
cache->core[core_id].counters = NULL;
env_bit_clear(core_id, cache->conf_meta->valid_core_bitmap);
if (!cache->core[core_id].opened &&
--cache->ocf_core_inactive_count == 0) {
env_bit_clear(ocf_cache_state_incomplete, &cache->cache_state);
}
cache->conf_meta->core_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;
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;
}