ocf/src/cleaning/alru.c
Adam Rutkowski 937b010ef6 Synchronize access to cleaner shared structures
Cleaning policy callbacks are typically  called with hash buckets or
cache lines locked. However cleaning policies maintain structures
which are shared across multiple cache lines (e.g. ALRU list).
Additional synchronization is required for theese structures to
maintain integrity.

ACP already implements hash bucket locks. This patch is adding
ALRU list mutex.

Signed-off-by: Adam Rutkowski <adam.j.rutkowski@intel.com>
2019-09-25 14:26:17 -04:00

834 lines
24 KiB
C

/*
* Copyright(c) 2012-2018 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include "ocf/ocf.h"
#include "../ocf_cache_priv.h"
#include "cleaning.h"
#include "alru.h"
#include "../metadata/metadata.h"
#include "../utils/utils_cleaner.h"
#include "../utils/utils_part.h"
#include "../utils/utils_realloc.h"
#include "../concurrency/ocf_cache_line_concurrency.h"
#include "../ocf_def_priv.h"
#include "cleaning_priv.h"
#define is_alru_head(x) (x == collision_table_entries)
#define is_alru_tail(x) (x == collision_table_entries)
#define OCF_CLEANING_DEBUG 0
#if 1 == OCF_CLEANING_DEBUG
#define OCF_DEBUG_PREFIX "[Clean] %s():%d "
#define OCF_DEBUG_LOG(cache, format, ...) \
ocf_cache_log_prefix(cache, log_info, OCF_DEBUG_PREFIX, \
format"\n", __func__, __LINE__, ##__VA_ARGS__)
#define OCF_DEBUG_TRACE(cache) OCF_DEBUG_LOG(cache, "")
#define OCF_DEBUG_MSG(cache, msg) OCF_DEBUG_LOG(cache, "- %s", msg)
#define OCF_DEBUG_PARAM(cache, format, ...) OCF_DEBUG_LOG(cache, "- "format, \
##__VA_ARGS__)
#else
#define OCF_DEBUG_PREFIX
#define OCF_DEBUG_LOG(cache, format, ...)
#define OCF_DEBUG_TRACE(cache)
#define OCF_DEBUG_MSG(cache, msg)
#define OCF_DEBUG_PARAM(cache, format, ...)
#endif
struct alru_flush_ctx {
struct ocf_cleaner_attribs attribs;
bool flush_perfomed;
uint32_t clines_no;
ocf_cache_t cache;
ocf_cleaner_end_t cmpl;
struct flush_data *flush_data;
size_t flush_data_limit;
};
struct alru_context {
struct alru_flush_ctx flush_ctx;
env_spinlock list_lock[OCF_IO_CLASS_MAX];
};
/* -- Start of ALRU functions -- */
/* Sets the given collision_index as the new _head_ of the ALRU list. */
static inline void update_alru_head(struct ocf_cache *cache,
int partition_id, unsigned int collision_index)
{
struct ocf_user_part *part = &cache->user_parts[partition_id];
part->runtime->cleaning.policy.alru.lru_head = collision_index;
}
/* Sets the given collision_index as the new _tail_ of the ALRU list. */
static inline void update_alru_tail(struct ocf_cache *cache,
int partition_id, unsigned int collision_index)
{
struct ocf_user_part *part = &cache->user_parts[partition_id];
part->runtime->cleaning.policy.alru.lru_tail = collision_index;
}
/* Sets the given collision_index as the new _head_ and _tail_
* of the ALRU list.
*/
static inline void update_alru_head_tail(struct ocf_cache *cache,
int partition_id, unsigned int collision_index)
{
update_alru_head(cache, partition_id, collision_index);
update_alru_tail(cache, partition_id, collision_index);
}
/* Adds the given collision_index to the _head_ of the ALRU list */
static void add_alru_head(struct ocf_cache *cache, int partition_id,
unsigned int collision_index)
{
unsigned int curr_head_index;
unsigned int collision_table_entries = cache->device->collision_table_entries;
struct ocf_user_part *part = &cache->user_parts[partition_id];
struct cleaning_policy_meta policy;
ENV_BUG_ON(!(collision_index < collision_table_entries));
ENV_BUG_ON(env_atomic_read(
&part->runtime->cleaning.policy.alru.size) < 0);
ENV_WARN_ON(!metadata_test_dirty(cache, collision_index));
ENV_WARN_ON(!metadata_test_valid_any(cache, collision_index));
/* First node to be added/ */
if (env_atomic_read(&part->runtime->cleaning.policy.alru.size) == 0) {
update_alru_head_tail(cache, partition_id, collision_index);
ocf_metadata_get_cleaning_policy(cache, collision_index,
&policy);
policy.meta.alru.lru_next = collision_table_entries;
policy.meta.alru.lru_prev = collision_table_entries;
policy.meta.alru.timestamp = env_ticks_to_secs(
env_get_tick_count());
ocf_metadata_set_cleaning_policy(cache, collision_index,
&policy);
} else {
/* Not the first node to be added. */
curr_head_index = part->runtime->cleaning.policy.alru.lru_head;
ENV_BUG_ON(!(curr_head_index < collision_table_entries));
ocf_metadata_get_cleaning_policy(cache, collision_index,
&policy);
policy.meta.alru.lru_next = curr_head_index;
policy.meta.alru.lru_prev = collision_table_entries;
policy.meta.alru.timestamp = env_ticks_to_secs(
env_get_tick_count());
ocf_metadata_set_cleaning_policy(cache, collision_index,
&policy);
ocf_metadata_get_cleaning_policy(cache, curr_head_index,
&policy);
policy.meta.alru.lru_prev = collision_index;
ocf_metadata_set_cleaning_policy(cache, curr_head_index,
&policy);
update_alru_head(cache, partition_id, collision_index);
}
env_atomic_inc(&part->runtime->cleaning.policy.alru.size);
}
/* Deletes the node with the given collision_index from the ALRU list */
static void remove_alru_list(struct ocf_cache *cache, int partition_id,
unsigned int collision_index)
{
uint32_t prev_lru_node, next_lru_node;
uint32_t collision_table_entries = cache->device->collision_table_entries;
struct ocf_user_part *part = &cache->user_parts[partition_id];
struct alru_cleaning_policy *cleaning_policy =
&part->runtime->cleaning.policy.alru;
struct cleaning_policy_meta policy;
ENV_BUG_ON(!(collision_index < collision_table_entries));
if (env_atomic_read(&part->runtime->cleaning.policy.alru.size) == 0) {
ocf_cache_log(cache, log_err, "ERROR: Attempt to remove item "
"from empty ALRU Cleaning Policy queue!\n");
ENV_BUG();
}
ocf_metadata_get_cleaning_policy(cache, collision_index, &policy);
/* Set prev and next (even if non existent) */
next_lru_node = policy.meta.alru.lru_next;
prev_lru_node = policy.meta.alru.lru_prev;
/* Check if entry is not part of the ALRU list */
if ((next_lru_node == collision_table_entries) &&
(prev_lru_node == collision_table_entries) &&
(cleaning_policy->lru_head != collision_index) &&
(cleaning_policy->lru_tail != collision_index)) {
return;
}
/* Case 0: If we are head AND tail, there is only one node. So unlink
* node and set that there is no node left in the list.
*/
if (cleaning_policy->lru_head == collision_index &&
cleaning_policy->lru_tail == collision_index) {
policy.meta.alru.lru_next = collision_table_entries;
policy.meta.alru.lru_prev = collision_table_entries;
ocf_metadata_set_cleaning_policy(cache, collision_index,
&policy);
update_alru_head_tail(cache, partition_id,
collision_table_entries);
}
/* Case 1: else if this collision_index is ALRU head, but not tail,
* update head and return
*/
else if ((cleaning_policy->lru_tail != collision_index) &&
(cleaning_policy->lru_head == collision_index)) {
struct cleaning_policy_meta next_policy;
ENV_BUG_ON(!(next_lru_node < collision_table_entries));
ocf_metadata_get_cleaning_policy(cache, next_lru_node,
&next_policy);
update_alru_head(cache, partition_id, next_lru_node);
policy.meta.alru.lru_next = collision_table_entries;
next_policy.meta.alru.lru_prev = collision_table_entries;
ocf_metadata_set_cleaning_policy(cache, collision_index,
&policy);
ocf_metadata_set_cleaning_policy(cache, next_lru_node,
&next_policy);
}
/* Case 2: else if this collision_index is ALRU tail, but not head,
* update tail and return
*/
else if ((cleaning_policy->lru_head != collision_index) &&
(cleaning_policy->lru_tail == collision_index)) {
struct cleaning_policy_meta prev_policy;
ENV_BUG_ON(!(prev_lru_node < collision_table_entries));
ocf_metadata_get_cleaning_policy(cache, prev_lru_node,
&prev_policy);
update_alru_tail(cache, partition_id, prev_lru_node);
policy.meta.alru.lru_prev = collision_table_entries;
prev_policy.meta.alru.lru_next = collision_table_entries;
ocf_metadata_set_cleaning_policy(cache, collision_index,
&policy);
ocf_metadata_set_cleaning_policy(cache, prev_lru_node,
&prev_policy);
}
/* Case 3: else this collision_index is a middle node. There is no
* change to the head and the tail pointers.
*/
else {
struct cleaning_policy_meta next_policy;
struct cleaning_policy_meta prev_policy;
ENV_BUG_ON(!(next_lru_node < collision_table_entries));
ENV_BUG_ON(!(prev_lru_node < collision_table_entries));
ocf_metadata_get_cleaning_policy(cache, prev_lru_node,
&prev_policy);
ocf_metadata_get_cleaning_policy(cache, next_lru_node,
&next_policy);
/* Update prev and next nodes */
prev_policy.meta.alru.lru_next = policy.meta.alru.lru_next;
next_policy.meta.alru.lru_prev = policy.meta.alru.lru_prev;
/* Update the given node */
policy.meta.alru.lru_next = collision_table_entries;
policy.meta.alru.lru_prev = collision_table_entries;
ocf_metadata_set_cleaning_policy(cache, collision_index,
&policy);
ocf_metadata_set_cleaning_policy(cache, prev_lru_node,
&prev_policy);
ocf_metadata_set_cleaning_policy(cache, next_lru_node,
&next_policy);
}
env_atomic_dec(&part->runtime->cleaning.policy.alru.size);
}
static bool is_on_alru_list(struct ocf_cache *cache, int partition_id,
unsigned int collision_index)
{
uint32_t prev_lru_node, next_lru_node;
uint32_t collision_table_entries = cache->device->collision_table_entries;
struct ocf_user_part *part = &cache->user_parts[partition_id];
struct alru_cleaning_policy *cleaning_policy =
&part->runtime->cleaning.policy.alru;
struct cleaning_policy_meta policy;
ENV_BUG_ON(!(collision_index < collision_table_entries));
ocf_metadata_get_cleaning_policy(cache, collision_index, &policy);
next_lru_node = policy.meta.alru.lru_next;
prev_lru_node = policy.meta.alru.lru_prev;
return cleaning_policy->lru_tail == collision_index ||
cleaning_policy->lru_head == collision_index ||
next_lru_node != collision_table_entries ||
prev_lru_node != collision_table_entries;
}
/* -- End of ALRU functions -- */
void cleaning_policy_alru_init_cache_block(struct ocf_cache *cache,
uint32_t cache_line)
{
struct cleaning_policy_meta policy;
ocf_metadata_get_cleaning_policy(cache, cache_line, &policy);
policy.meta.alru.timestamp = 0;
policy.meta.alru.lru_prev = cache->device->collision_table_entries;
policy.meta.alru.lru_next = cache->device->collision_table_entries;
ocf_metadata_set_cleaning_policy(cache, cache_line, &policy);
}
void cleaning_policy_alru_purge_cache_block(struct ocf_cache *cache,
uint32_t cache_line)
{
struct alru_context *alru = cache->cleaner.cleaning_policy_context;
ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache,
cache_line);
env_spinlock_lock(&alru->list_lock[part_id]);
remove_alru_list(cache, part_id, cache_line);
env_spinlock_unlock(&alru->list_lock[part_id]);
}
static void __cleaning_policy_alru_purge_cache_block_any(
struct ocf_cache *cache, uint32_t cache_line)
{
struct alru_context *alru = cache->cleaner.cleaning_policy_context;
ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache,
cache_line);
env_spinlock_lock(&alru->list_lock[part_id]);
if (is_on_alru_list(cache, part_id, cache_line))
remove_alru_list(cache, part_id, cache_line);
env_spinlock_unlock(&alru->list_lock[part_id]);
}
int cleaning_policy_alru_purge_range(struct ocf_cache *cache, int core_id,
uint64_t start_byte, uint64_t end_byte) {
struct ocf_user_part *part;
ocf_part_id_t part_id;
int ret = 0;
for_each_part(cache, part, part_id) {
if (env_atomic_read(&part->runtime->cleaning.
policy.alru.size) == 0)
continue;
ret |= ocf_metadata_actor(cache, part_id,
core_id, start_byte, end_byte,
__cleaning_policy_alru_purge_cache_block_any);
}
return ret;
}
void cleaning_policy_alru_set_hot_cache_line(struct ocf_cache *cache,
uint32_t cache_line)
{
struct alru_context *alru = cache->cleaner.cleaning_policy_context;
ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache,
cache_line);
struct ocf_user_part *part = &cache->user_parts[part_id];
uint32_t prev_lru_node, next_lru_node;
uint32_t collision_table_entries = cache->device->collision_table_entries;
struct cleaning_policy_meta policy;
ENV_WARN_ON(!metadata_test_dirty(cache, cache_line));
ENV_WARN_ON(!metadata_test_valid_any(cache, cache_line));
env_spinlock_lock(&alru->list_lock[part_id]);
ocf_metadata_get_cleaning_policy(cache, cache_line, &policy);
next_lru_node = policy.meta.alru.lru_next;
prev_lru_node = policy.meta.alru.lru_prev;
if ((next_lru_node != collision_table_entries) ||
(prev_lru_node != collision_table_entries) ||
((part->runtime->cleaning.policy.
alru.lru_head == cache_line) &&
(part->runtime->cleaning.policy.
alru.lru_tail == cache_line)))
remove_alru_list(cache, part_id, cache_line);
add_alru_head(cache, part_id, cache_line);
env_spinlock_unlock(&alru->list_lock[part_id]);
}
static void _alru_rebuild(struct ocf_cache *cache)
{
struct ocf_user_part *part;
ocf_part_id_t part_id;
ocf_core_id_t core_id;
ocf_cache_line_t cline;
uint32_t step = 0;
for_each_part(cache, part, part_id) {
/* ALRU initialization */
env_atomic_set(&part->runtime->cleaning.policy.alru.size, 0);
part->runtime->cleaning.policy.alru.lru_head =
cache->device->collision_table_entries;
part->runtime->cleaning.policy.alru.lru_tail =
cache->device->collision_table_entries;
cache->device->runtime_meta->cleaning_thread_access = 0;
}
for (cline = 0; cline < cache->device->collision_table_entries; cline++) {
ocf_metadata_get_core_and_part_id(cache, cline, &core_id,
NULL);
OCF_COND_RESCHED_DEFAULT(step);
if (core_id == OCF_CORE_MAX)
continue;
cleaning_policy_alru_init_cache_block(cache, cline);
if (!metadata_test_dirty(cache, cline))
continue;
cleaning_policy_alru_set_hot_cache_line(cache, cline);
}
}
static int cleaning_policy_alru_initialize_part(struct ocf_cache *cache,
struct ocf_user_part *part, int init_metadata)
{
if (init_metadata) {
/* ALRU initialization */
env_atomic_set(&part->runtime->cleaning.policy.alru.size, 0);
part->runtime->cleaning.policy.alru.lru_head =
cache->device->collision_table_entries;
part->runtime->cleaning.policy.alru.lru_tail =
cache->device->collision_table_entries;
}
cache->device->runtime_meta->cleaning_thread_access = 0;
return 0;
}
void cleaning_policy_alru_setup(struct ocf_cache *cache)
{
struct alru_cleaning_policy_config *config;
config = (void *)&cache->conf_meta->cleaning[ocf_cleaning_alru].data;
config->thread_wakeup_time = OCF_ALRU_DEFAULT_WAKE_UP;
config->stale_buffer_time = OCF_ALRU_DEFAULT_STALENESS_TIME;
config->flush_max_buffers = OCF_ALRU_DEFAULT_FLUSH_MAX_BUFFERS;
config->activity_threshold = OCF_ALRU_DEFAULT_ACTIVITY_THRESHOLD;
}
int cleaning_policy_alru_initialize(ocf_cache_t cache, int init_metadata)
{
struct ocf_user_part *part;
ocf_part_id_t part_id;
struct alru_context *alru;
unsigned i;
alru = env_vzalloc(sizeof(*alru));
if (!alru) {
ocf_cache_log(cache, log_err, "alru context allocation error\n");
return -OCF_ERR_NO_MEM;
}
for (i = 0; i < OCF_IO_CLASS_MAX; i++)
env_spinlock_init(&alru->list_lock[i]);
cache->cleaner.cleaning_policy_context = alru;
for_each_part(cache, part, part_id) {
cleaning_policy_alru_initialize_part(cache,
part, init_metadata);
}
if (init_metadata)
_alru_rebuild(cache);
ocf_kick_cleaner(cache);
return 0;
}
void cleaning_policy_alru_deinitialize(struct ocf_cache *cache)
{
struct alru_context *alru = cache->cleaner.cleaning_policy_context;
unsigned i;
for (i = 0; i < OCF_IO_CLASS_MAX; i++)
env_spinlock_destroy(&alru->list_lock[i]);
env_vfree(cache->cleaner.cleaning_policy_context);
cache->cleaner.cleaning_policy_context = NULL;
}
int cleaning_policy_alru_set_cleaning_param(ocf_cache_t cache,
uint32_t param_id, uint32_t param_value)
{
struct alru_cleaning_policy_config *config;
config = (void *)&cache->conf_meta->cleaning[ocf_cleaning_alru].data;
switch (param_id) {
case ocf_alru_wake_up_time:
OCF_CLEANING_CHECK_PARAM(cache, param_value,
OCF_ALRU_MIN_WAKE_UP,
OCF_ALRU_MAX_WAKE_UP,
"thread_wakeup_time");
config->thread_wakeup_time = param_value;
ocf_cache_log(cache, log_info, "Write-back flush thread "
"wake-up time: %d\n", config->thread_wakeup_time);
ocf_kick_cleaner(cache);
break;
case ocf_alru_stale_buffer_time:
OCF_CLEANING_CHECK_PARAM(cache, param_value,
OCF_ALRU_MIN_STALENESS_TIME,
OCF_ALRU_MAX_STALENESS_TIME,
"stale_buffer_time");
config->stale_buffer_time = param_value;
ocf_cache_log(cache, log_info, "Write-back flush thread "
"staleness time: %d\n", config->stale_buffer_time);
break;
case ocf_alru_flush_max_buffers:
OCF_CLEANING_CHECK_PARAM(cache, param_value,
OCF_ALRU_MIN_FLUSH_MAX_BUFFERS,
OCF_ALRU_MAX_FLUSH_MAX_BUFFERS,
"flush_max_buffers");
config->flush_max_buffers = param_value;
ocf_cache_log(cache, log_info, "Write-back flush thread max "
"buffers flushed per iteration: %d\n",
config->flush_max_buffers);
break;
case ocf_alru_activity_threshold:
OCF_CLEANING_CHECK_PARAM(cache, param_value,
OCF_ALRU_MIN_ACTIVITY_THRESHOLD,
OCF_ALRU_MAX_ACTIVITY_THRESHOLD,
"activity_threshold");
config->activity_threshold = param_value;
ocf_cache_log(cache, log_info, "Write-back flush thread "
"activity time threshold: %d\n",
config->activity_threshold);
break;
default:
return -OCF_ERR_INVAL;
}
return 0;
}
int cleaning_policy_alru_get_cleaning_param(ocf_cache_t cache,
uint32_t param_id, uint32_t *param_value)
{
struct alru_cleaning_policy_config *config;
config = (void *)&cache->conf_meta->cleaning[ocf_cleaning_alru].data;
switch (param_id) {
case ocf_alru_wake_up_time:
*param_value = config->thread_wakeup_time;
break;
case ocf_alru_stale_buffer_time:
*param_value = config->stale_buffer_time;
break;
case ocf_alru_flush_max_buffers:
*param_value = config->flush_max_buffers;
break;
case ocf_alru_activity_threshold:
*param_value = config->activity_threshold;
break;
default:
return -OCF_ERR_INVAL;
}
return 0;
}
static inline uint32_t compute_timestamp(
const struct alru_cleaning_policy_config *config)
{
unsigned long time;
time = env_get_tick_count();
time -= env_secs_to_ticks(config->stale_buffer_time);
time = env_ticks_to_secs(time);
return (uint32_t) time;
}
static int check_for_io_activity(struct ocf_cache *cache,
struct alru_cleaning_policy_config *config)
{
unsigned int now, last;
now = env_ticks_to_msecs(env_get_tick_count());
last = env_atomic_read(&cache->last_access_ms);
if ((now - last) < config->activity_threshold)
return 1;
return 0;
}
static bool clean_later(ocf_cache_t cache, uint32_t *delta)
{
struct alru_cleaning_policy_config *config;
config = (void *)&cache->conf_meta->cleaning[ocf_cleaning_alru].data;
*delta = env_ticks_to_secs(env_get_tick_count()) -
cache->device->runtime_meta->cleaning_thread_access;
if (*delta <= config->thread_wakeup_time)
return true;
return false;
}
static bool is_cleanup_possible(ocf_cache_t cache)
{
struct alru_cleaning_policy_config *config;
uint32_t delta;
config = (void *)&cache->conf_meta->cleaning[ocf_cleaning_alru].data;
if (check_for_io_activity(cache, config)) {
OCF_DEBUG_PARAM(cache, "IO activity detected");
return false;
}
if (clean_later(cache, &delta)) {
OCF_DEBUG_PARAM(cache,
"Cleaning policy configured to clean later "
"delta=%u wake_up=%u", delta,
config->thread_wakeup_time);
return false;
}
//Cleaning policy configured to not clean anything
if (config->flush_max_buffers == 0)
return false;
return true;
}
static void get_block_to_flush(struct flush_data* dst,
ocf_cache_line_t cache_line, struct ocf_cache* cache)
{
ocf_core_id_t core_id;
uint64_t core_line;
ocf_metadata_get_core_info(cache, cache_line,
&core_id, &core_line);
dst->cache_line = cache_line;
dst->core_id = core_id;
dst->core_line = core_line;
}
static bool more_blocks_to_flush(struct ocf_cache *cache,
ocf_cache_line_t cache_line, uint32_t last_access)
{
struct cleaning_policy_meta policy;
if (cache_line >= cache->device->collision_table_entries)
return false;
ocf_metadata_get_cleaning_policy(cache, cache_line, &policy);
if (policy.meta.alru.timestamp >= last_access)
return false;
return true;
}
static bool block_is_busy(struct ocf_cache *cache,
ocf_cache_line_t cache_line)
{
ocf_core_id_t core_id;
uint64_t core_line;
ocf_metadata_get_core_info(cache, cache_line,
&core_id, &core_line);
if (!cache->core[core_id].opened)
return true;
if (ocf_cache_line_is_used(cache, cache_line))
return true;
return false;
}
static int get_data_to_flush(struct alru_context *alru)
{
struct alru_flush_ctx *fctx = &alru->flush_ctx;
ocf_cache_t cache = fctx->cache;
struct alru_cleaning_policy_config *config;
struct cleaning_policy_meta policy;
ocf_cache_line_t cache_line;
struct ocf_user_part *part;
uint32_t last_access;
int to_flush = 0;
int part_id = OCF_IO_CLASS_ID_MAX;
config = (void *)&cache->conf_meta->cleaning[ocf_cleaning_alru].data;
for_each_part(cache, part, part_id) {
env_spinlock_lock(&alru->list_lock[part_id]);
cache_line = part->runtime->cleaning.policy.alru.lru_tail;
last_access = compute_timestamp(config);
OCF_DEBUG_PARAM(cache, "Last access=%u, timestamp=%u rel=%d",
last_access, policy.meta.alru.timestamp,
policy.meta.alru.timestamp < last_access);
while (more_blocks_to_flush(cache, cache_line, last_access)) {
if (to_flush >= fctx->clines_no) {
env_spinlock_unlock(&alru->list_lock[part_id]);
goto end;
}
if (!block_is_busy(cache, cache_line)) {
get_block_to_flush(&fctx->flush_data[to_flush], cache_line,
cache);
to_flush++;
}
ocf_metadata_get_cleaning_policy(cache, cache_line, &policy);
cache_line = policy.meta.alru.lru_prev;
}
env_spinlock_unlock(&alru->list_lock[part_id]);
}
end:
OCF_DEBUG_PARAM(cache, "Collected items_to_clean=%u", to_flush);
return to_flush;
}
static void alru_clean_complete(void *priv, int err)
{
struct alru_cleaning_policy_config *config;
struct alru_flush_ctx *fctx = priv;
ocf_cache_t cache = fctx->cache;
int interval;
OCF_REALLOC_DEINIT(&fctx->flush_data, &fctx->flush_data_limit);
config = (void *)&cache->conf_meta->cleaning[ocf_cleaning_alru].data;
interval = fctx->flush_perfomed ? 0 : config->thread_wakeup_time * 1000;
fctx->cmpl(&fctx->cache->cleaner, interval);
}
static void alru_clean(struct alru_context *alru)
{
struct alru_flush_ctx *fctx = &alru->flush_ctx;
ocf_cache_t cache = fctx->cache;
int to_clean;
if (!is_cleanup_possible(cache)) {
alru_clean_complete(fctx, 0);
return;
}
if (ocf_metadata_try_start_exclusive_access(&cache->metadata.lock)) {
alru_clean_complete(fctx, 0);
return;
}
OCF_REALLOC(&fctx->flush_data, sizeof(fctx->flush_data[0]),
fctx->clines_no, &fctx->flush_data_limit);
if (!fctx->flush_data) {
ocf_cache_log(cache, log_warn, "No memory to allocate flush "
"data for ALRU cleaning policy");
goto end;
}
to_clean = get_data_to_flush(alru);
if (to_clean > 0) {
fctx->flush_perfomed = true;
ocf_cleaner_do_flush_data_async(cache, fctx->flush_data, to_clean,
&fctx->attribs);
ocf_metadata_end_exclusive_access(&cache->metadata.lock);
return;
}
/* Update timestamp only if there are no items to be cleaned */
cache->device->runtime_meta->cleaning_thread_access =
env_ticks_to_secs(env_get_tick_count());
end:
ocf_metadata_end_exclusive_access(&cache->metadata.lock);
alru_clean_complete(fctx, 0);
}
void cleaning_alru_perform_cleaning(ocf_cache_t cache, ocf_cleaner_end_t cmpl)
{
struct alru_context *alru = cache->cleaner.cleaning_policy_context;
struct alru_flush_ctx *fctx = &alru->flush_ctx;
struct alru_cleaning_policy_config *config;
config = (void *)&cache->conf_meta->cleaning[ocf_cleaning_alru].data;
OCF_REALLOC_INIT(&fctx->flush_data, &fctx->flush_data_limit);
fctx->attribs.cmpl_context = fctx;
fctx->attribs.cmpl_fn = alru_clean_complete;
fctx->attribs.cache_line_lock = true;
fctx->attribs.do_sort = true;
fctx->attribs.io_queue = cache->cleaner.io_queue;
fctx->clines_no = config->flush_max_buffers;
fctx->cache = cache;
fctx->cmpl = cmpl;
fctx->flush_perfomed = false;
alru_clean(alru);
}