Merge pull request #247 from arutk/percpu_freelist_rebased

Per-execution-context freelists
This commit is contained in:
Michał Mielewczyk 2019-09-09 14:29:56 +02:00 committed by GitHub
commit 9a46c402b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1454 additions and 226 deletions

58
env/posix/ocf_env.c vendored
View File

@ -130,8 +130,64 @@ void env_stack_trace(void)
}
/* *** CRC *** */
uint32_t env_crc32(uint32_t crc, uint8_t const *data, size_t len)
{
return crc32(crc, data, len);
}
/* *** execution contexts *** */
pthread_mutex_t *exec_context_mutex;
static void __attribute__((constructor)) init_execution_context(void)
{
unsigned count = env_get_execution_context_count();
unsigned i;
ENV_BUG_ON(count == 0);
exec_context_mutex = malloc(count * sizeof(exec_context_mutex[0]));
ENV_BUG_ON(exec_context_mutex == NULL);
for (i = 0; i < count; i++)
ENV_BUG_ON(pthread_mutex_init(&exec_context_mutex[i], NULL));
}
static void __attribute__((destructor)) deinit_execution_context(void)
{
unsigned count = env_get_execution_context_count();
unsigned i;
ENV_BUG_ON(count == 0);
ENV_BUG_ON(exec_context_mutex == NULL);
for (i = 0; i < count; i++)
ENV_BUG_ON(pthread_mutex_destroy(&exec_context_mutex[i]));
free(exec_context_mutex);
}
/* get_execuction_context must assure that after the call finishes, the caller
* will not get preempted from current execution context. For userspace env
* we simulate this behavior by acquiring per execution context mutex. As a
* result the caller might actually get preempted, but no other thread will
* execute in this context by the time the caller puts current execution ctx. */
unsigned env_get_execution_context(void)
{
unsigned cpu;
cpu = sched_getcpu();
cpu = (cpu == -1) ? 0 : cpu;
ENV_BUG_ON(pthread_mutex_lock(&exec_context_mutex[cpu]));
return cpu;
}
void env_put_execution_context(unsigned ctx)
{
pthread_mutex_unlock(&exec_context_mutex[ctx]);
}
unsigned env_get_execution_context_count(void)
{
int num = sysconf(_SC_NPROCESSORS_ONLN);
return (num == -1) ? 0 : num;
}

9
env/posix/ocf_env.h vendored
View File

@ -455,6 +455,11 @@ static inline void env_spinlock_init(env_spinlock *l)
ENV_BUG_ON(pthread_spin_init(&l->lock, 0));
}
static inline int env_spinlock_trylock(env_spinlock *l)
{
return pthread_spin_trylock(&l->lock) ? -OCF_ERR_NO_LOCK : 0;
}
static inline void env_spinlock_lock(env_spinlock *l)
{
ENV_BUG_ON(pthread_spin_lock(&l->lock));
@ -644,4 +649,8 @@ uint32_t env_crc32(uint32_t crc, uint8_t const *data, size_t len);
#define ENV_PRIu64 "lu"
unsigned env_get_execution_context(void);
void env_put_execution_context(unsigned ctx);
unsigned env_get_execution_context_count(void);
#endif /* __OCF_ENV_H__ */

View File

@ -169,7 +169,8 @@ static inline bool ocf_seq_cutoff_is_on(ocf_cache_t cache)
if (!ocf_cache_is_device_attached(cache))
return false;
return (cache->device->freelist_part->curr_size <= SEQ_CUTOFF_FULL_MARGIN);
return (ocf_freelist_num_free(cache->freelist) <=
SEQ_CUTOFF_FULL_MARGIN);
}
bool ocf_seq_cutoff_check(ocf_core_t core, uint32_t dir, uint64_t addr,

View File

@ -7,6 +7,7 @@
#include "../ocf_priv.h"
#include "../ocf_cache_priv.h"
#include "../ocf_queue_priv.h"
#include "../ocf_freelist.h"
#include "engine_common.h"
#define OCF_ENGINE_DEBUG_IO_NAME "common"
#include "engine_debug.h"
@ -250,19 +251,11 @@ static void ocf_engine_map_cache_line(struct ocf_request *req,
ocf_part_id_t part_id = req->part_id;
ocf_cleaning_t clean_policy_type;
if (cache->device->freelist_part->curr_size == 0) {
if (!ocf_freelist_get_cache_line(cache->freelist, cache_line)) {
req->info.mapping_error = 1;
return;
}
*cache_line = cache->device->freelist_part->head;
/* add_to_collision_list changes .next_col and other fields for entry
* so updated last_cache_line_give must be updated before calling it.
*/
ocf_metadata_remove_from_free_list(cache, *cache_line);
ocf_metadata_add_to_partition(cache, part_id, *cache_line);
/* Add the block to the corresponding collision list */

View File

@ -107,11 +107,13 @@ int space_managment_evict_do(struct ocf_cache *cache,
struct ocf_request *req, uint32_t evict_cline_no)
{
uint32_t evicted;
uint32_t free;
if (evict_cline_no <= cache->device->freelist_part->curr_size)
free = ocf_freelist_num_free(cache->freelist);
if (evict_cline_no <= free)
return LOOKUP_MAPPED;
evict_cline_no -= cache->device->freelist_part->curr_size;
evict_cline_no -= free;
evicted = ocf_evict_do(cache, req->io_queue, evict_cline_no,
req->part_id);

View File

@ -32,8 +32,7 @@ union eviction_policy_meta {
* set core_id to -2 to purge the whole cache partition
*/
struct eviction_policy_ops {
void (*init_cline)(ocf_cache_t cache,
ocf_cache_line_t cline);
void (*init_cline)(ocf_cache_t cache, ocf_cache_line_t cline);
void (*rm_cline)(ocf_cache_t cache,
ocf_cache_line_t cline);
bool (*can_evict)(ocf_cache_t cache);

View File

@ -8,8 +8,7 @@
#include "eviction.h"
#include "lru_structs.h"
void evp_lru_init_cline(struct ocf_cache *cache,
ocf_cache_line_t cline);
void evp_lru_init_cline(struct ocf_cache *cache, ocf_cache_line_t cline);
void evp_lru_rm_cline(struct ocf_cache *cache, ocf_cache_line_t cline);
bool evp_lru_can_evict(struct ocf_cache *cache);
uint32_t evp_lru_req_clines(struct ocf_cache *cache, ocf_queue_t io_queue,

View File

@ -53,18 +53,18 @@ int ocf_metadata_init_variable_size(struct ocf_cache *cache, uint64_t device_siz
cache_line_size, layout);
}
void ocf_metadata_init_freelist_partition(struct ocf_cache *cache)
{
OCF_DEBUG_TRACE(cache);
cache->metadata.iface.init_freelist(cache);
}
void ocf_metadata_init_hash_table(struct ocf_cache *cache)
{
OCF_DEBUG_TRACE(cache);
cache->metadata.iface.init_hash_table(cache);
}
void ocf_metadata_init_collision(struct ocf_cache *cache)
{
OCF_DEBUG_TRACE(cache);
cache->metadata.iface.init_collision(cache);
}
void ocf_metadata_deinit(struct ocf_cache *cache)
{
OCF_DEBUG_TRACE(cache);

View File

@ -60,6 +60,13 @@ void ocf_metadata_init_freelist_partition(struct ocf_cache *cache);
*/
void ocf_metadata_init_hash_table(struct ocf_cache *cache);
/**
* @brief Initialize collision table
*
* @param cache - Cache instance
*/
void ocf_metadata_init_collision(struct ocf_cache *cache);
/**
* @brief De-Initialize metadata
*
@ -207,4 +214,10 @@ typedef void (*ocf_metadata_load_properties_end_t)(void *priv, int error,
void ocf_metadata_load_properties(ocf_volume_t volume,
ocf_metadata_load_properties_end_t cmpl, void *priv);
static inline ocf_cache_line_t ocf_metadata_collision_table_entries(
struct ocf_cache *cache)
{
return cache->device->collision_table_entries;
}
#endif /* METADATA_H_ */

View File

@ -13,6 +13,7 @@
#include "../utils/utils_pipeline.h"
#include "../ocf_def_priv.h"
#include "../ocf_priv.h"
#include "../ocf_freelist.h"
#define OCF_METADATA_HASH_DEBUG 0
@ -1025,14 +1026,10 @@ finalize:
}
static inline void _ocf_init_collision_entry(struct ocf_cache *cache,
ocf_cache_line_t idx, ocf_cache_line_t next,
ocf_cache_line_t prev)
ocf_cache_line_t idx)
{
ocf_cache_line_t invalid_idx = cache->device->collision_table_entries;
ocf_part_id_t invalid_part_id = PARTITION_INVALID;
ocf_metadata_set_partition_info(cache, idx,
invalid_part_id, next, prev);
ocf_metadata_set_collision_info(cache, idx, invalid_idx, invalid_idx);
ocf_metadata_set_core_info(cache, idx,
OCF_CORE_MAX, ULONG_MAX);
@ -1040,36 +1037,17 @@ static inline void _ocf_init_collision_entry(struct ocf_cache *cache,
}
/*
* Modified initialization of freelist partition
* Initialize collision table
*/
static void ocf_metadata_hash_init_freelist(struct ocf_cache *cache)
static void ocf_metadata_hash_init_collision(struct ocf_cache *cache)
{
uint32_t step = 0;
unsigned int i;
ocf_cache_line_t prev, next;
ocf_cache_line_t idx;
ocf_cache_line_t collision_table_entries =
cache->device->collision_table_entries;
prev = collision_table_entries;
idx = 0;
for (i = 0; i < cache->device->collision_table_entries - 1; i++) {
next = ocf_metadata_map_phy2lg(cache, i + 1);
_ocf_init_collision_entry(cache, idx, next, prev);
prev = idx;
idx = next;
OCF_COND_RESCHED_DEFAULT(step);
for (i = 0; i < cache->device->collision_table_entries; i++) {
_ocf_init_collision_entry(cache, i);
}
_ocf_init_collision_entry(cache, idx, collision_table_entries, prev);
/* Initialize freelist partition */
cache->device->freelist_part->head = 0;
cache->device->freelist_part->tail = idx;
cache->device->freelist_part->curr_size = cache->device->
collision_table_entries;
}
/*
* Initialize hash table
*/
@ -1850,8 +1828,6 @@ static void _recovery_rebuild_cline_metadata(ocf_cache_t cache,
part_id = PARTITION_DEFAULT;
ocf_metadata_remove_from_free_list(cache, cache_line);
ocf_metadata_add_to_partition(cache, part_id, cache_line);
hash_index = ocf_metadata_hash_func(cache, core_line, core_id);
@ -1917,10 +1893,12 @@ static void _recovery_rebuild_metadata(ocf_pipeline_t pipeline,
ocf_core_id_t core_id;
uint64_t core_line;
unsigned char step = 0;
const uint64_t collision_table_entries =
ocf_metadata_collision_table_entries(cache);
OCF_METADATA_LOCK_WR();
for (cline = 0; cline < cache->device->collision_table_entries; cline++) {
for (cline = 0; cline < collision_table_entries; cline++) {
ocf_metadata_get_core_info(cache, cline, &core_id, &core_line);
if (core_id != OCF_CORE_MAX &&
(!dirty_only || metadata_test_dirty(cache,
@ -2648,7 +2626,7 @@ static const struct ocf_metadata_iface metadata_hash_iface = {
.init_variable_size = ocf_metadata_hash_init_variable_size,
.deinit_variable_size = ocf_metadata_hash_deinit_variable_size,
.init_hash_table = ocf_metadata_hash_init_hash_table,
.init_freelist = ocf_metadata_hash_init_freelist,
.init_collision = ocf_metadata_hash_init_collision,
.layout_iface = NULL,
.pages = ocf_metadata_hash_pages,

View File

@ -5,6 +5,7 @@
#include "ocf/ocf.h"
#include "metadata.h"
#include "../ocf_freelist.h"
#include "../utils/utils_cache_line.h"
static bool _is_cache_line_acting(struct ocf_cache *cache,
@ -100,7 +101,7 @@ void ocf_metadata_sparse_cache_line(struct ocf_cache *cache,
ocf_metadata_remove_from_partition(cache, partition_id, cache_line);
ocf_metadata_add_to_free_list(cache, cache_line);
ocf_freelist_put_cache_line(cache->freelist, cache_line);
}
static void _ocf_metadata_sparse_cache_line(struct ocf_cache *cache,

View File

@ -16,99 +16,6 @@ static void update_partition_head(struct ocf_cache *cache,
part->runtime->head = line;
}
void ocf_metadata_remove_from_free_list(struct ocf_cache *cache,
ocf_cache_line_t cline)
{
struct ocf_part *free_list = cache->device->freelist_part;
int is_head, is_tail;
ocf_part_id_t invalid_part_id = PARTITION_INVALID;
ocf_cache_line_t prev, next;
ocf_cache_line_t line_entries = cache->device->collision_table_entries;
ENV_BUG_ON(cline >= line_entries);
/* Get Partition info */
ocf_metadata_get_partition_info(cache, cline, NULL, &next, &prev);
/* Find out if this node is Partition _head_ */
is_head = (prev == line_entries);
is_tail = (next == line_entries);
/* Case 1: If we are head and there is only one node. So unlink node
* and set that there is no node left in the list.
*/
if (is_head && (free_list->curr_size == 1)) {
ocf_metadata_set_partition_info(cache, cline, invalid_part_id,
line_entries, line_entries);
free_list->head = line_entries;
free_list->tail = line_entries;
} else if (is_head) {
/* Case 2: else if this collision_index is partition list head,
* but many nodes, update head and return
*/
ENV_BUG_ON(next >= line_entries);
free_list->head = next;
ocf_metadata_set_partition_prev(cache, next, line_entries);
ocf_metadata_set_partition_next(cache, cline, line_entries);
} else if (is_tail) {
/* Case 3: else if this cline is partition list tail */
ENV_BUG_ON(prev >= line_entries);
free_list->tail = prev;
ocf_metadata_set_partition_prev(cache, cline, line_entries);
ocf_metadata_set_partition_next(cache, prev, line_entries);
} else {
/* Case 4: else this collision_index is a middle node.
* There is no change to the head and the tail pointers.
*/
ENV_BUG_ON(next >= line_entries || prev >= line_entries);
/* Update prev and next nodes */
ocf_metadata_set_partition_prev(cache, next, prev);
ocf_metadata_set_partition_next(cache, prev, next);
/* Update the given node */
ocf_metadata_set_partition_info(cache, cline, invalid_part_id,
line_entries, line_entries);
}
free_list->curr_size--;
}
void ocf_metadata_add_to_free_list(struct ocf_cache *cache,
ocf_cache_line_t line)
{
struct ocf_part *free_list = cache->device->freelist_part;
ocf_cache_line_t tail;
ocf_cache_line_t line_entries = cache->device->collision_table_entries;
ocf_part_id_t invalid_part_id = PARTITION_INVALID;
ENV_BUG_ON(line >= line_entries);
if (free_list->curr_size == 0) {
free_list->head = line;
free_list->tail = line;
ocf_metadata_set_partition_info(cache, line, invalid_part_id,
line_entries, line_entries);
} else {
tail = free_list->tail;
ENV_BUG_ON(tail >= line_entries);
ocf_metadata_set_partition_info(cache, line, invalid_part_id,
line_entries, tail);
ocf_metadata_set_partition_next(cache, tail, line);
free_list->tail = line;
}
free_list->curr_size++;
}
/* Adds the given collision_index to the _head_ of the Partition list */
void ocf_metadata_add_to_partition(struct ocf_cache *cache,
ocf_part_id_t part_id, ocf_cache_line_t line)

View File

@ -75,12 +75,6 @@ static inline void ocf_metadata_set_partition_info(
next_line, prev_line);
}
void ocf_metadata_add_to_free_list(struct ocf_cache *cache,
ocf_cache_line_t cline);
void ocf_metadata_remove_from_free_list(struct ocf_cache *cache,
ocf_cache_line_t cline);
void ocf_metadata_add_to_partition(struct ocf_cache *cache,
ocf_part_id_t part_id, ocf_cache_line_t line);

View File

@ -10,12 +10,6 @@
#include "../cleaning/cleaning.h"
#include "../eviction/eviction.h"
struct ocf_part {
ocf_cache_line_t head;
ocf_cache_line_t tail;
uint32_t curr_size;
};
struct ocf_user_part_config {
char name[OCF_IO_CLASS_NAME_MAX];
uint32_t min_size;

View File

@ -124,13 +124,6 @@ struct ocf_metadata_iface {
struct ocf_volume_uuid *uuid, uint32_t count,
ocf_metadata_query_cores_end_t cmpl, void *priv);
/**
* @brief Initialize freelist partition
*
* @param cache - Cache instance
*/
void (*init_freelist)(struct ocf_cache *cache);
/**
* @brief Metadata cache line location on pages interface
@ -144,6 +137,13 @@ struct ocf_metadata_iface {
*/
void (*init_hash_table)(struct ocf_cache *cache);
/**
* @brief Initialize collision table
*
* @param cache - Cache instance
*/
void (*init_collision)(struct ocf_cache *cache);
/**
* @brief De-Initialize metadata
*

View File

@ -59,8 +59,6 @@ struct ocf_superblock_config {
* @brief OCF cache metadata runtime superblock
*/
struct ocf_superblock_runtime {
struct ocf_part freelist_part;
uint32_t cleaning_thread_access;
};

View File

@ -21,6 +21,7 @@
#include "../concurrency/ocf_concurrency.h"
#include "../eviction/ops.h"
#include "../ocf_ctx_priv.h"
#include "../ocf_freelist.h"
#include "../cleaning/cleaning.h"
#define OCF_ASSERT_PLUGGED(cache) ENV_BUG_ON(!(cache)->device)
@ -117,6 +118,8 @@ struct ocf_cache_attach_context {
* load or recovery
*/
bool freelist_inited : 1;
bool concurrency_inited : 1;
} flags;
@ -156,18 +159,6 @@ struct ocf_cache_attach_context {
ocf_pipeline_t pipeline;
};
static void __init_hash_table(ocf_cache_t cache)
{
/* Initialize hash table*/
ocf_metadata_init_hash_table(cache);
}
static void __init_freelist(ocf_cache_t cache)
{
/* Initialize free list partition*/
ocf_metadata_init_freelist_partition(cache);
}
static void __init_partitions(ocf_cache_t cache)
{
ocf_part_id_t i_part;
@ -204,6 +195,14 @@ static void __init_partitions_attached(ocf_cache_t cache)
}
}
static void __init_freelist(ocf_cache_t cache)
{
uint64_t free_clines = ocf_metadata_collision_table_entries(cache) -
ocf_get_cache_occupancy(cache);
ocf_freelist_populate(cache->freelist, free_clines);
}
static ocf_error_t __init_cleaning_policy(ocf_cache_t cache)
{
ocf_cleaning_t cleaning_policy = ocf_cleaning_default;
@ -295,9 +294,10 @@ static ocf_error_t init_attached_data_structures(ocf_cache_t cache,
/* Lock to ensure consistency */
OCF_METADATA_LOCK_WR();
__init_hash_table(cache);
__init_freelist(cache);
ocf_metadata_init_hash_table(cache);
ocf_metadata_init_collision(cache);
__init_partitions_attached(cache);
__init_freelist(cache);
result = __init_cleaning_policy(cache);
if (result) {
@ -325,8 +325,8 @@ static ocf_error_t init_attached_data_structures(ocf_cache_t cache,
static void init_attached_data_structures_recovery(ocf_cache_t cache)
{
OCF_METADATA_LOCK_WR();
__init_hash_table(cache);
__init_freelist(cache);
ocf_metadata_init_hash_table(cache);
ocf_metadata_init_collision(cache);
__init_partitions_attached(cache);
__reset_stats(cache);
__init_metadata_version(cache);
@ -469,6 +469,8 @@ void _ocf_mngt_init_instance_load_complete(void *priv, int error)
OCF_PL_FINISH_RET(context->pipeline, -OCF_ERR_START_CACHE_FAIL);
}
__init_freelist(cache);
result = __init_promotion_policy(cache);
if (result) {
ocf_cache_log(cache, log_err,
@ -985,8 +987,10 @@ static void _ocf_mngt_attach_prepare_metadata(ocf_pipeline_t pipeline,
OCF_PL_FINISH_RET(context->pipeline, -OCF_ERR_START_CACHE_FAIL);
}
cache->device->freelist_part = &cache->device->runtime_meta->freelist_part;
cache->freelist = ocf_freelist_init(cache);
if (!cache->freelist)
OCF_PL_FINISH_RET(context->pipeline, -OCF_ERR_START_CACHE_FAIL);
context->flags.freelist_inited = true;
ret = ocf_concurrency_init(cache);
if (ret)
@ -1143,6 +1147,9 @@ static void _ocf_mngt_attach_handle_error(
if (context->flags.concurrency_inited)
ocf_concurrency_deinit(cache);
if (context->flags.freelist_inited)
ocf_freelist_deinit(cache->freelist);
if (context->flags.volume_inited)
ocf_volume_deinit(&cache->device->volume);
@ -1725,6 +1732,7 @@ static void _ocf_mngt_cache_unplug_complete(void *priv, int error)
ocf_metadata_deinit_variable_size(cache);
ocf_concurrency_deinit(cache);
ocf_freelist_deinit(cache->freelist);
ocf_volume_deinit(&cache->device->volume);

View File

@ -21,6 +21,7 @@
#include "ocf_logger_priv.h"
#include "ocf/ocf_trace.h"
#include "promotion/promotion.h"
#include "ocf_freelist.h"
#define DIRTY_FLUSHED 1
#define DIRTY_NOT_FLUSHED 0
@ -82,8 +83,6 @@ struct ocf_cache_device {
uint64_t metadata_offset;
struct ocf_part *freelist_part;
struct {
struct ocf_cache_line_concurrency *cache_line;
} concurrency;
@ -110,6 +109,8 @@ struct ocf_cache {
struct ocf_metadata metadata;
ocf_freelist_t freelist;
ocf_eviction_t eviction_policy_init;
struct {
@ -199,4 +200,16 @@ static inline ocf_core_t ocf_cache_get_core(ocf_cache_t cache,
#define ocf_cache_log_rl(cache) \
ocf_log_rl(ocf_cache_get_ctx(cache))
static inline uint64_t ocf_get_cache_occupancy(ocf_cache_t cache)
{
uint64_t result = 0;
ocf_core_t core;
ocf_core_id_t core_id;
for_each_core(cache, core, core_id)
result += env_atomic_read(&core->runtime_meta->cached_clines);
return result;
}
#endif /* __OCF_CACHE_PRIV_H__ */

420
src/ocf_freelist.c Normal file
View File

@ -0,0 +1,420 @@
/*
* Copyright(c) 2019-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include "ocf/ocf.h"
#include "metadata/metadata.h"
struct ocf_part {
ocf_cache_line_t head;
ocf_cache_line_t tail;
env_atomic64 curr_size;
};
struct ocf_freelist {
/* parent cache */
struct ocf_cache *cache;
/* partition list array */
struct ocf_part *part;
/* freelist lock array */
env_spinlock *lock;
/* number of free lists */
uint32_t count;
/* next slowpath victim idx */
env_atomic slowpath_victim_idx;
/* total number of free lines */
env_atomic64 total_free;
};
static void ocf_freelist_lock(ocf_freelist_t freelist, uint32_t ctx)
{
env_spinlock_lock(&freelist->lock[ctx]);
}
static int ocf_freelist_trylock(ocf_freelist_t freelist, uint32_t ctx)
{
return env_spinlock_trylock(&freelist->lock[ctx]);
}
static void ocf_freelist_unlock(ocf_freelist_t freelist, uint32_t ctx)
{
env_spinlock_unlock(&freelist->lock[ctx]);
}
/* Sets the given collision_index as the new _head_ of the Partition list. */
static void _ocf_freelist_remove_cache_line(ocf_freelist_t freelist,
uint32_t ctx, ocf_cache_line_t cline)
{
struct ocf_cache *cache = freelist->cache;
struct ocf_part *freelist_part = &freelist->part[ctx];
int is_head, is_tail;
ocf_part_id_t invalid_part_id = PARTITION_INVALID;
ocf_cache_line_t prev, next;
ocf_cache_line_t line_entries = ocf_metadata_collision_table_entries(
freelist->cache);
uint32_t free;
ENV_BUG_ON(cline >= line_entries);
/* Get Partition info */
ocf_metadata_get_partition_info(cache, cline, NULL, &next, &prev);
/* Find out if this node is Partition _head_ */
is_head = (prev == line_entries);
is_tail = (next == line_entries);
free = env_atomic64_read(&freelist_part->curr_size);
/* Case 1: If we are head and there is only one node. So unlink node
* and set that there is no node left in the list.
*/
if (is_head && free == 1) {
ocf_metadata_set_partition_info(cache, cline, invalid_part_id,
line_entries, line_entries);
freelist_part->head = line_entries;
freelist_part->tail = line_entries;
} else if (is_head) {
/* Case 2: else if this collision_index is partition list head,
* but many nodes, update head and return
*/
ENV_BUG_ON(next >= line_entries);
freelist_part->head = next;
ocf_metadata_set_partition_prev(cache, next, line_entries);
ocf_metadata_set_partition_next(cache, cline, line_entries);
} else if (is_tail) {
/* Case 3: else if this cline is partition list tail */
ENV_BUG_ON(prev >= line_entries);
freelist_part->tail = prev;
ocf_metadata_set_partition_prev(cache, cline, line_entries);
ocf_metadata_set_partition_next(cache, prev, line_entries);
} else {
/* Case 4: else this collision_index is a middle node.
* There is no change to the head and the tail pointers.
*/
ENV_BUG_ON(next >= line_entries || prev >= line_entries);
/* Update prev and next nodes */
ocf_metadata_set_partition_prev(cache, next, prev);
ocf_metadata_set_partition_next(cache, prev, next);
/* Update the given node */
ocf_metadata_set_partition_info(cache, cline, invalid_part_id,
line_entries, line_entries);
}
env_atomic64_dec(&freelist_part->curr_size);
env_atomic64_dec(&freelist->total_free);
}
static ocf_cache_line_t next_phys_invalid(ocf_cache_t cache,
ocf_cache_line_t phys)
{
ocf_cache_line_t lg;
ocf_cache_line_t collision_table_entries =
ocf_metadata_collision_table_entries(cache);
if (phys == collision_table_entries)
return collision_table_entries;
lg = ocf_metadata_map_phy2lg(cache, phys);
while (metadata_test_valid_any(cache, lg)) {
++phys;
if (phys == collision_table_entries)
break;
lg = ocf_metadata_map_phy2lg(cache, phys);
}
return phys;
}
/* Assign unused cachelines to freelist */
void ocf_freelist_populate(ocf_freelist_t freelist,
ocf_cache_line_t num_free_clines)
{
unsigned step = 0;
ocf_cache_t cache = freelist->cache;
unsigned num_freelists = freelist->count;
ocf_cache_line_t prev, next, idx;
ocf_cache_line_t phys;
ocf_cache_line_t collision_table_entries =
ocf_metadata_collision_table_entries(cache);
unsigned freelist_idx;
uint64_t freelist_size;
phys = 0;
for (freelist_idx = 0; freelist_idx < num_freelists; freelist_idx++)
{
/* calculate current freelist size */
freelist_size = num_free_clines / num_freelists;
if (freelist_idx < (num_free_clines % num_freelists))
++freelist_size;
env_atomic64_set(&freelist->part[freelist_idx].curr_size,
freelist_size);
if (!freelist_size) {
/* init empty freelist and move to next one */
freelist->part[freelist_idx].head =
collision_table_entries;
freelist->part[freelist_idx].tail =
collision_table_entries;
continue;
}
/* find first invalid cacheline */
phys = next_phys_invalid(cache, phys);
ENV_BUG_ON(phys == collision_table_entries);
idx = ocf_metadata_map_phy2lg(cache, phys);
++phys;
/* store freelist head */
freelist->part[freelist_idx].head = idx;
/* link freelist elements using partition list */
prev = collision_table_entries;
while (--freelist_size) {
phys = next_phys_invalid(cache, phys);
ENV_BUG_ON(phys == collision_table_entries);
next = ocf_metadata_map_phy2lg(cache, phys);
++phys;
ocf_metadata_set_partition_info(cache, idx,
PARTITION_INVALID, next, prev);
prev = idx;
idx = next;
OCF_COND_RESCHED_DEFAULT(step);
}
/* terminate partition list */
ocf_metadata_set_partition_info(cache, idx, PARTITION_INVALID,
collision_table_entries, prev);
/* store freelist tail */
freelist->part[freelist_idx].tail = idx;
}
/* we should have reached the last invalid cache line */
phys = next_phys_invalid(cache, phys);
ENV_BUG_ON(phys != collision_table_entries);
env_atomic64_set(&freelist->total_free, num_free_clines);
}
static void ocf_freelist_add_cache_line(ocf_freelist_t freelist,
uint32_t ctx, ocf_cache_line_t line)
{
struct ocf_cache *cache = freelist->cache;
struct ocf_part *freelist_part = &freelist->part[ctx];
ocf_cache_line_t tail;
ocf_cache_line_t line_entries = ocf_metadata_collision_table_entries(
freelist->cache);
ocf_part_id_t invalid_part_id = PARTITION_INVALID;
ENV_BUG_ON(line >= line_entries);
if (env_atomic64_read(&freelist_part->curr_size) == 0) {
freelist_part->head = line;
freelist_part->tail = line;
ocf_metadata_set_partition_info(cache, line, invalid_part_id,
line_entries, line_entries);
} else {
tail = freelist_part->tail;
ENV_BUG_ON(tail >= line_entries);
ocf_metadata_set_partition_info(cache, line, invalid_part_id,
line_entries, tail);
ocf_metadata_set_partition_next(cache, tail, line);
freelist_part->tail = line;
}
env_atomic64_inc(&freelist_part->curr_size);
env_atomic64_inc(&freelist->total_free);
}
typedef enum {
OCF_FREELIST_ERR_NOLOCK = 1,
OCF_FREELIST_ERR_LIST_EMPTY,
} ocf_freelist_get_err_t;
static ocf_freelist_get_err_t ocf_freelist_get_cache_line_ctx(
ocf_freelist_t freelist, uint32_t ctx, bool can_wait,
ocf_cache_line_t *cline)
{
if (env_atomic64_read(&freelist->part[ctx].curr_size) == 0)
return -OCF_FREELIST_ERR_LIST_EMPTY;
if (!can_wait && ocf_freelist_trylock(freelist, ctx))
return -OCF_FREELIST_ERR_NOLOCK;
if (can_wait)
ocf_freelist_lock(freelist, ctx);
if (env_atomic64_read(&freelist->part[ctx].curr_size) == 0) {
ocf_freelist_unlock(freelist, ctx);
return -OCF_FREELIST_ERR_LIST_EMPTY;
}
*cline = freelist->part[ctx].head;
_ocf_freelist_remove_cache_line(freelist, ctx, *cline);
ocf_freelist_unlock(freelist, ctx);
return 0;
}
static int get_next_victim_freelist(ocf_freelist_t freelist)
{
int ctx, next;
do {
ctx = env_atomic_read(&freelist->slowpath_victim_idx);
next = (ctx + 1) % freelist->count;
} while (ctx != env_atomic_cmpxchg(&freelist->slowpath_victim_idx, ctx,
next));
return ctx;
}
static bool ocf_freelist_get_cache_line_slow(ocf_freelist_t freelist,
ocf_cache_line_t *cline)
{
int i, ctx;
int err;
bool lock_err;
/* try slowpath without waiting on lock */
lock_err = false;
for (i = 0; i < freelist->count; i++) {
ctx = get_next_victim_freelist(freelist);
err = ocf_freelist_get_cache_line_ctx(freelist, ctx, false,
cline);
if (!err)
return true;
if (err == -OCF_FREELIST_ERR_NOLOCK)
lock_err = true;
}
if (!lock_err) {
/* Slowpath failed due to empty freelists - no point in
* iterating through contexts to attempt slowpath with full
* lock */
return false;
}
/* slow path with waiting on lock */
for (i = 0; i < freelist->count; i++) {
ctx = get_next_victim_freelist(freelist);
if (!ocf_freelist_get_cache_line_ctx(freelist, ctx, true,
cline)) {
return true;
}
}
return false;
}
static bool ocf_freelist_get_cache_line_fast(ocf_freelist_t freelist,
ocf_cache_line_t *cline)
{
bool ret;
uint32_t ctx = env_get_execution_context();
ret = !ocf_freelist_get_cache_line_ctx(freelist, ctx, false, cline);
env_put_execution_context(ctx);
return ret;
}
bool ocf_freelist_get_cache_line(ocf_freelist_t freelist,
ocf_cache_line_t *cline)
{
if (env_atomic64_read(&freelist->total_free) == 0)
return false;
if (!ocf_freelist_get_cache_line_fast(freelist, cline))
return ocf_freelist_get_cache_line_slow(freelist, cline);
return true;
}
void ocf_freelist_put_cache_line(ocf_freelist_t freelist,
ocf_cache_line_t cline)
{
uint32_t ctx = env_get_execution_context();
ocf_freelist_lock(freelist, ctx);
ocf_freelist_add_cache_line(freelist, ctx, cline);
ocf_freelist_unlock(freelist, ctx);
env_put_execution_context(ctx);
}
ocf_freelist_t ocf_freelist_init(struct ocf_cache *cache)
{
uint32_t num;
int i;
ocf_freelist_t freelist;
ocf_cache_line_t line_entries = ocf_metadata_collision_table_entries(
cache);
freelist = env_vzalloc(sizeof(*freelist));
if (!freelist)
return NULL;
num = env_get_execution_context_count();
freelist->cache = cache;
freelist->count = num;
env_atomic64_set(&freelist->total_free, 0);
freelist->lock = env_vzalloc(sizeof(freelist->lock[0]) * num);
freelist->part = env_vzalloc(sizeof(freelist->part[0]) * num);
if (!freelist->lock || !freelist->part) {
env_vfree(freelist->lock);
env_vfree(freelist->part);
env_vfree(freelist);
return NULL;
}
for (i = 0; i < num; i++) {
env_spinlock_init(&freelist->lock[i]);
freelist->part[i].head = line_entries;
freelist->part[i].tail = line_entries;
env_atomic64_set(&freelist->part[i].curr_size, 0);
}
return freelist;
}
void ocf_freelist_deinit(ocf_freelist_t freelist)
{
int i;
for (i = 0; i < freelist->count; i++)
env_spinlock_destroy(&freelist->lock[i]);
env_vfree(freelist->lock);
env_vfree(freelist->part);
env_vfree(freelist);
}
ocf_cache_line_t ocf_freelist_num_free(ocf_freelist_t freelist)
{
return env_atomic64_read(&freelist->total_free);
}

34
src/ocf_freelist.h Normal file
View File

@ -0,0 +1,34 @@
/*
* Copyright(c) 2019-2019 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __OCF_FREELIST_H__
#define __OCF_FREELIST_H__
#include "ocf_cache_priv.h"
struct ocf_freelist;
typedef struct ocf_freelist *ocf_freelist_t;
/* Init / deinit freelist runtime structures */
ocf_freelist_t ocf_freelist_init(struct ocf_cache *cache);
void ocf_freelist_deinit(ocf_freelist_t freelist);
/* Assign unused cachelines to freelist */
void ocf_freelist_populate(ocf_freelist_t freelist,
ocf_cache_line_t num_free_clines);
/* Get cacheline from freelist */
bool ocf_freelist_get_cache_line(ocf_freelist_t freelist,
ocf_cache_line_t *cline);
/* Put cacheline back to freelist */
void ocf_freelist_put_cache_line(ocf_freelist_t freelist,
ocf_cache_line_t cline);
/* Return total number of free cachelines */
ocf_cache_line_t ocf_freelist_num_free(ocf_freelist_t freelist);
#endif /* __OCF_FREELIST_H__ */

View File

@ -44,18 +44,6 @@ static uint64_t _bytes4k(uint64_t bytes)
return (bytes + 4095UL) >> 12;
}
static uint64_t _get_cache_occupancy(ocf_cache_t cache)
{
uint64_t result = 0;
ocf_core_t core;
ocf_core_id_t core_id;
for_each_core(cache, core, core_id)
result += env_atomic_read(&core->runtime_meta->cached_clines);
return result;
}
static void _set(struct ocf_stat *stat, uint64_t value, uint64_t denominator)
{
stat->value = value;
@ -169,7 +157,7 @@ int ocf_stats_collect_core(ocf_core_t core,
cache = ocf_core_get_cache(core);
cache_line_size = ocf_cache_get_line_size(cache);
cache_size = cache->conf_meta->cachelines;
cache_occupancy = _get_cache_occupancy(cache);
cache_occupancy = ocf_get_cache_occupancy(cache);
_ocf_stats_zero(usage);
_ocf_stats_zero(req);

View File

@ -181,7 +181,7 @@ bool nhit_req_should_promote(ocf_promotion_policy_t policy,
uint64_t core_line;
uint64_t occupied_cachelines =
ocf_metadata_get_cachelines_count(policy->owner) -
policy->owner->device->freelist_part->curr_size;
ocf_freelist_num_free(policy->owner->freelist);
if (occupied_cachelines > env_atomic64_read(&ctx->trigger_threshold))
return true;

View File

@ -104,7 +104,7 @@ class TestGenerator(object):
def get_empty_test_function(self):
ret = "static void " + self.get_tested_function_name() + "_test01(void **state)\n"
ret += "{\n"
ret += "\tprint_test_description(\"Put test description here\");\n"
ret += "\tprint_test_description(\"Put test description here\\n\");\n"
ret += "\tassert_int_equal(1,1);\n"
ret += "}\n\n"
@ -116,7 +116,7 @@ class TestGenerator(object):
ret += "\tconst struct CMUnitTest tests[] = {\n"
ret += "\t\tcmocka_unit_test(" + self.get_tested_function_name() + "_test01)\n"
ret += "\t};\n\n"
ret += "\tprint_message(\"Unit test of " + self.get_tested_file_path() + "\");\n\n"
ret += "\tprint_message(\"Unit test for " + self.get_tested_function_name() + "\\n\");\n\n"
ret += "\treturn cmocka_run_group_tests(tests, NULL, NULL);\n"
ret += "}"

View File

@ -14,7 +14,7 @@ MAIN_DIRECTORY_OF_UNIT_TESTS = "../tests/"
# Paths to all directories, in which tests are stored. All paths should be relative to
# MAIN_DIRECTORY_OF_UNIT_TESTS
DIRECTORIES_WITH_TESTS_LIST = ["cleaning/", "metadata/", "mngt/", "concurrency/", "engine/",
"eviction/", "utils/", "promotion/"]
"eviction/", "utils/", "promotion/", "ocf_freelist.c/"]
# Paths to all directories containing files with sources. All paths should be relative to
# MAIN_DIRECTORY_OF_TESTED_PROJECT

View File

@ -150,28 +150,25 @@ void env_completion_complete(env_completion *completion)
int env_mutex_init(env_mutex *mutex)
{
function_called();
check_expected_ptr(mutex);
return mock();
return 0;
}
int env_mutex_destroy(env_mutex *mutex)
{
return 0;
}
void env_mutex_lock(env_mutex *mutex)
{
function_called();
check_expected_ptr(mutex);
}
int env_mutex_lock_interruptible(env_mutex *mutex)
{
function_called();
check_expected_ptr(mutex);
return mock();
return 0;
}
void env_mutex_unlock(env_mutex *mutex)
{
function_called();
check_expected_ptr(mutex);
}
int env_rmutex_init(env_rmutex *rmutex)
@ -361,22 +358,27 @@ long env_atomic64_cmpxchg(env_atomic64 *a, long old, long new)
return oldval;
}
void env_spinlock_init(env_spinlock *l)
int env_spinlock_init(env_spinlock *l)
{
function_called();
check_expected_ptr(l);
return 0;
}
int env_spinlock_destroy(env_spinlock *l)
{
return 0;
}
void env_spinlock_lock(env_spinlock *l)
{
function_called();
check_expected_ptr(l);
}
int env_spinlock_trylock(env_spinlock *l)
{
return 0;
}
void env_spinlock_unlock(env_spinlock *l)
{
function_called();
check_expected_ptr(l);
}
void env_rwlock_init(env_rwlock *l)
@ -535,3 +537,7 @@ uint32_t env_crc32(uint32_t crc, uint8_t const *data, size_t len)
check_expected_ptr(data);
return mock();
}
void env_cond_resched(void)
{
}

View File

@ -71,7 +71,13 @@ typedef uint64_t sector_t;
abort(); \
})
#define ENV_BUG_ON(cond) bug_on((int)cond);
#define ENV_BUG_ON(cond) ({ \
int eval = cond; \
if (eval) { \
print_message("%s:%u BUG: %s\n", __FILE__, __LINE__, #cond); \
bug_on(eval); \
} \
})
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
@ -128,6 +134,8 @@ typedef struct {
int env_mutex_init(env_mutex *mutex);
int env_mutex_destroy(env_mutex *mutex);
void env_mutex_lock(env_mutex *mutex);
int env_mutex_lock_interruptible(env_mutex *mutex);
@ -231,10 +239,14 @@ void env_completion_complete(env_completion *completion);
typedef struct {
} env_spinlock;
void env_spinlock_init(env_spinlock *l);
int env_spinlock_init(env_spinlock *l);
int env_spinlock_destroy(env_spinlock *l);
void env_spinlock_lock(env_spinlock *l);
int env_spinlock_trylock(env_spinlock *l);
void env_spinlock_unlock(env_spinlock *l);
#define env_spinlock_lock_irqsave(l, flags) \
@ -327,4 +339,6 @@ void env_msleep(uint64_t n);
uint32_t env_crc32(uint32_t crc, uint8_t const *data, size_t len);
void env_cond_resched(void);
#endif /* __OCF_ENV_H__ */

View File

@ -0,0 +1,382 @@
/*
* <tested_file_path>src/ocf_freelist.c</tested_file_path>
* <tested_function>ocf_freelist_get_cache_line</tested_function>
* <functions_to_leave>
* ocf_freelist_init
* ocf_freelist_deinit
* ocf_freelist_populate
* next_phys_invalid
* ocf_freelist_lock
* ocf_freelist_trylock
* ocf_freelist_unlock
* _ocf_freelist_remove_cache_line
* ocf_freelist_get_cache_line_fast
* ocf_freelist_get_cache_line_slow
* ocf_freelist_add_cache_line
* ocf_freelist_get_cache_line_ctx
* get_next_victim_freelist
* ocf_freelist_put_cache_line
* </functions_to_leave>
*/
#undef static
#undef inline
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include "print_desc.h"
#include "ocf/ocf.h"
#include "metadata/metadata.h"
#include "ocf_freelist.c/ocf_freelist_get_put_generated_warps.c"
ocf_cache_line_t __wrap_ocf_metadata_collision_table_entries(ocf_cache_t cache)
{
return mock();
}
unsigned __wrap_env_get_execution_context_count(void)
{
return mock();
}
unsigned __wrap_env_get_execution_context(void)
{
return mock();
}
void __wrap_env_put_execution_context(unsigned ctx)
{
}
/* simulate no striping */
ocf_cache_line_t __wrap_ocf_metadata_map_phy2lg(ocf_cache_t cache, ocf_cache_line_t phy)
{
return phy;
}
bool __wrap_metadata_test_valid_any(ocf_cache_t cache, ocf_cache_line_t cline)
{
return mock();
}
/* metadata partition info interface mock: */
#define max_clines 100
struct {
ocf_cache_line_t prev;
ocf_cache_line_t next;
} partition_list[max_clines];
void __wrap_ocf_metadata_set_partition_info(struct ocf_cache *cache,
ocf_cache_line_t line, ocf_part_id_t part_id,
ocf_cache_line_t next_line, ocf_cache_line_t prev_line)
{
assert_int_equal(part_id, PARTITION_INVALID);
partition_list[line].prev = prev_line;
partition_list[line].next = next_line;
}
void __wrap_ocf_metadata_get_partition_info(struct ocf_cache *cache,
ocf_cache_line_t line, ocf_part_id_t *part_id,
ocf_cache_line_t *next_line, ocf_cache_line_t *prev_line)
{
if (part_id)
*part_id = PARTITION_INVALID;
if (prev_line)
*prev_line = partition_list[line].prev;
if (next_line)
*next_line = partition_list[line].next;
}
void __wrap_ocf_metadata_set_partition_prev(struct ocf_cache *cache,
ocf_cache_line_t line, ocf_cache_line_t prev_line)
{
partition_list[line].prev = prev_line;
}
void __wrap_ocf_metadata_set_partition_next(struct ocf_cache *cache,
ocf_cache_line_t line, ocf_cache_line_t next_line)
{
partition_list[line].next = next_line;
}
static void ocf_freelist_get_cache_line_get_fast(void **state)
{
unsigned num_cls = 8;
unsigned num_ctxts = 3;
ocf_freelist_t freelist;
unsigned ctx_iter, cl_iter;
ocf_cache_line_t line;
print_test_description("Verify get free cache line get fast path");
will_return_maybe(__wrap_ocf_metadata_collision_table_entries, num_cls);
will_return_maybe(__wrap_env_get_execution_context_count, num_ctxts);
will_return_maybe(__wrap_metadata_test_valid_any, false);
freelist = ocf_freelist_init(NULL);
ocf_freelist_populate(freelist, num_cls);
/* now there are following cachelines on per-context lists:
* ctx 0: 0, 1, 2
* ctx 1: 3, 4, 5
* ctx 2: 6, 7
*/
/* get cline from context 1 */
will_return(__wrap_env_get_execution_context, 1);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 3);
/* ctx 0: 0, 1, 2
* ctx 1: _, 4, 5
* ctx 2: 6, 7 */
/* get cline from context 2 */
will_return(__wrap_env_get_execution_context, 2);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 6);
/* ctx 0: 0, 1, 2
* ctx 1: _, 4, 5
* ctx 2: _, 7 */
/* get cline from context 1 */
will_return(__wrap_env_get_execution_context, 1);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 4);
/* ctx 0: 0, 1, 2
* ctx 1: _, _, 5
* ctx 2: _, 7 */
/* get cline from context 0 */
will_return(__wrap_env_get_execution_context, 0);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 0);
/* ctx 0: _, 1, 2
* ctx 1: _, _, 5
* ctx 2: _, 7 */
/* get cline from context 0 */
will_return(__wrap_env_get_execution_context, 0);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 1);
/* ctx 0: _, _, 2
* ctx 1: _, _, 5
* ctx 2: _, 7 */
/* get cline from context 0 */
will_return(__wrap_env_get_execution_context, 0);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 2);
/* ctx 0: _, _, _,
* ctx 1: _, _, 5
* ctx 2: _, 7 */
/* get cline from context 2 */
will_return(__wrap_env_get_execution_context, 2);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 7);
/* ctx 0: _, _, _,
* ctx 1: _, _, _5
* ctx 2: _, _ */
/* get cline from context 1 */
will_return(__wrap_env_get_execution_context, 1);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 5);
/* ctx 0: _, _, _,
* ctx 1: _, _, _
* ctx 2: _, _ */
ocf_freelist_deinit(freelist);
}
static void ocf_freelist_get_cache_line_get_slow(void **state)
{
unsigned num_cls = 8;
unsigned num_ctxts = 3;
ocf_freelist_t freelist;
unsigned ctx_iter, cl_iter;
ocf_cache_line_t line;
print_test_description("Verify get free cache line get slow path");
will_return_maybe(__wrap_ocf_metadata_collision_table_entries, num_cls);
will_return_maybe(__wrap_env_get_execution_context_count, num_ctxts);
will_return_maybe(__wrap_metadata_test_valid_any, false);
/* always return exec ctx 0 */
will_return_maybe(__wrap_env_get_execution_context, 0);
freelist = ocf_freelist_init(NULL);
ocf_freelist_populate(freelist, num_cls);
/* now there are following cachelines on per-context lists:
* ctx 0: 0, 1, 2
* ctx 1: 3, 4, 5
* ctx 2: 6, 7
*/
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 0);
/* ctx 0: _, 1, 2
* ctx 1: 3, 4, 5
* ctx 2: 6, 7 */
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 1);
/* ctx 0: _, _, 2
* ctx 1: 3, 4, 5
* ctx 2: 6, 7 */
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 2);
/* ctx 0: _, _, _
* ctx 1: 3, 4, 5
* ctx 2: 6, 7 */
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 3);
/* ctx 0: _, _, _
* ctx 1: _, 4, 5
* ctx 2: 6, 7 */
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 6);
/* ctx 0: _, _, _
* ctx 1: _, 4, 5
* ctx 2: _, 7 */
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 4);
/* ctx 0: _, _, _
* ctx 1: _, _, 5
* ctx 2: _, 7 */
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 7);
/* ctx 0: _, _, _
* ctx 1: _, _, 5
* ctx 2: _, _ */
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 5);
/* ctx 0: _, _, _,
* ctx 1: _, _, _
* ctx 2: _, _ */
ocf_freelist_deinit(freelist);
}
static void ocf_freelist_get_cache_line_put(void **state)
{
unsigned num_cls = 8;
unsigned num_ctxts = 3;
ocf_freelist_t freelist;
unsigned ctx_iter, cl_iter;
ocf_cache_line_t line;
print_test_description("Verify freelist cacheline put");
will_return_maybe(__wrap_ocf_metadata_collision_table_entries, num_cls);
will_return_maybe(__wrap_env_get_execution_context_count, num_ctxts);
will_return_maybe(__wrap_metadata_test_valid_any, false);
freelist = ocf_freelist_init(NULL);
ocf_freelist_populate(freelist, num_cls);
/* get some clines from the freelists */
will_return(__wrap_env_get_execution_context, 0);
ocf_freelist_get_cache_line(freelist, &line);
will_return(__wrap_env_get_execution_context, 0);
ocf_freelist_get_cache_line(freelist, &line);
will_return(__wrap_env_get_execution_context, 0);
ocf_freelist_get_cache_line(freelist, &line);
will_return(__wrap_env_get_execution_context, 0);
ocf_freelist_get_cache_line(freelist, &line);
will_return(__wrap_env_get_execution_context, 0);
ocf_freelist_get_cache_line(freelist, &line);
/* ctx 0:
* ctx 1: 4, 5
* ctx 2: 7 */
will_return(__wrap_env_get_execution_context, 1);
ocf_freelist_put_cache_line(freelist, 0);
will_return(__wrap_env_get_execution_context, 1);
ocf_freelist_put_cache_line(freelist, 2);
will_return(__wrap_env_get_execution_context, 2);
ocf_freelist_put_cache_line(freelist, 3);
/* ctx 0:
* ctx 1: 4, 5, 0, 2
* ctx 2: 7, 3*/
will_return(__wrap_env_get_execution_context, 1);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 4);
will_return(__wrap_env_get_execution_context, 1);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 5);
will_return(__wrap_env_get_execution_context, 1);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 0);
will_return(__wrap_env_get_execution_context, 1);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 2);
will_return(__wrap_env_get_execution_context, 2);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 7);
will_return(__wrap_env_get_execution_context, 2);
assert(ocf_freelist_get_cache_line(freelist, &line));
assert_int_equal(line, 3);
ocf_freelist_deinit(freelist);
}
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(ocf_freelist_get_cache_line_get_fast),
cmocka_unit_test(ocf_freelist_get_cache_line_get_slow),
cmocka_unit_test(ocf_freelist_get_cache_line_put)
};
print_message("Unit test for ocf_freelist_get_cache_line\n");
return cmocka_run_group_tests(tests, NULL, NULL);
}

View File

@ -0,0 +1,68 @@
/*
* <tested_file_path>src/ocf_freelist.c</tested_file_path>
* <tested_function>ocf_freelist_populate</tested_function>
* <functions_to_leave>
* ocf_freelist_init
* ocf_freelist_deinit
* </functions_to_leave>
*/
#undef static
#undef inline
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include "print_desc.h"
#include "ocf/ocf.h"
#include "metadata/metadata.h"
#include "ocf_freelist.c/ocf_freelist_init_generated_warps.c"
ocf_cache_line_t __wrap_ocf_metadata_collision_table_entries(ocf_cache_t cache)
{
function_called();
return mock();
}
ocf_cache_line_t __wrap_env_get_execution_context_count(ocf_cache_t cache)
{
function_called();
return mock();
}
static void ocf_freelist_init_test01(void **state)
{
unsigned num_cls = 9;
unsigned num_ctxts = 3;
ocf_freelist_t freelist;
ocf_cache_t cache = 0x1234;
print_test_description("Freelist initialization test");
expect_function_call(__wrap_ocf_metadata_collision_table_entries);
will_return(__wrap_ocf_metadata_collision_table_entries, num_cls);
expect_function_call(__wrap_env_get_execution_context_count);
will_return(__wrap_env_get_execution_context_count, num_ctxts);
freelist = ocf_freelist_init(cache);
assert(freelist != NULL);
ocf_freelist_deinit(freelist);
}
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(ocf_freelist_init_test01)
};
print_message("Unit test of ocf_freelist_init\n");
return cmocka_run_group_tests(tests, NULL, NULL);
}

View File

@ -0,0 +1,213 @@
/*
* <tested_file_path>src/ocf_freelist.c</tested_file_path>
* <tested_function>ocf_freelist_get_cache_line</tested_function>
* <functions_to_leave>
* ocf_freelist_init
* ocf_freelist_deinit
* ocf_freelist_populate
* next_phys_invalid
* ocf_freelist_unlock
* _ocf_freelist_remove_cache_line
* ocf_freelist_get_cache_line_fast
* ocf_freelist_get_cache_line_slow
* ocf_freelist_add_cache_line
* ocf_freelist_get_cache_line_ctx
* get_next_victim_freelist
* ocf_freelist_put_cache_line
* </functions_to_leave>
*/
#undef static
#undef inline
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include "print_desc.h"
#include "ocf/ocf.h"
#include "metadata/metadata.h"
#include "ocf_freelist.c/ocf_freelist_get_put_generated_warps.c"
ocf_cache_line_t __wrap_ocf_metadata_collision_table_entries(ocf_cache_t cache)
{
return mock();
}
unsigned __wrap_env_get_execution_context_count(void)
{
return mock();
}
unsigned __wrap_env_get_execution_context(void)
{
return mock();
}
void __wrap_env_put_execution_context(unsigned ctx)
{
}
/* simulate no striping */
ocf_cache_line_t __wrap_ocf_metadata_map_phy2lg(ocf_cache_t cache, ocf_cache_line_t phy)
{
return phy;
}
bool __wrap_metadata_test_valid_any(ocf_cache_t cache, ocf_cache_line_t cline)
{
return mock();
}
void __wrap_ocf_freelist_lock(ocf_freelist_t freelist, uint32_t ctx)
{
function_called();
check_expected(ctx);
}
int __wrap_ocf_freelist_trylock(ocf_freelist_t freelist, uint32_t ctx)
{
function_called();
check_expected(ctx);
return mock();
}
/* metadata partition info interface mock: */
#define max_clines 100
struct {
ocf_cache_line_t prev;
ocf_cache_line_t next;
} partition_list[max_clines];
void __wrap_ocf_metadata_set_partition_info(struct ocf_cache *cache,
ocf_cache_line_t line, ocf_part_id_t part_id,
ocf_cache_line_t next_line, ocf_cache_line_t prev_line)
{
assert_int_equal(part_id, PARTITION_INVALID);
partition_list[line].prev = prev_line;
partition_list[line].next = next_line;
}
void __wrap_ocf_metadata_get_partition_info(struct ocf_cache *cache,
ocf_cache_line_t line, ocf_part_id_t *part_id,
ocf_cache_line_t *next_line, ocf_cache_line_t *prev_line)
{
if (part_id)
*part_id = PARTITION_INVALID;
if (prev_line)
*prev_line = partition_list[line].prev;
if (next_line)
*next_line = partition_list[line].next;
}
void __wrap_ocf_metadata_set_partition_prev(struct ocf_cache *cache,
ocf_cache_line_t line, ocf_cache_line_t prev_line)
{
partition_list[line].prev = prev_line;
}
void __wrap_ocf_metadata_set_partition_next(struct ocf_cache *cache,
ocf_cache_line_t line, ocf_cache_line_t next_line)
{
partition_list[line].next = next_line;
}
static void ocf_freelist_get_put_locks(void **state)
{
unsigned num_cls = 4;
unsigned num_ctxts = 3;
ocf_freelist_t freelist;
unsigned ctx_iter, cl_iter;
ocf_cache_line_t line;
print_test_description("Verify lock/trylock sequence in get free cacheline");
will_return_maybe(__wrap_ocf_metadata_collision_table_entries, num_cls);
will_return_maybe(__wrap_env_get_execution_context_count, num_ctxts);
will_return_maybe(__wrap_metadata_test_valid_any, false);
/* simulate context 1 for the entire test duration */
will_return_maybe(__wrap_env_get_execution_context, 1);
freelist = ocf_freelist_init(NULL);
ocf_freelist_populate(freelist, num_cls);
/****************************************************************/
/* verify fast path locking - scucessfull trylock */
/* ctx 0: 0, 3
* ctx 1: 1
* ctx 2: 2
* slowpath next victim: 0
*/
expect_value(__wrap_ocf_freelist_trylock, ctx, 1);
expect_function_call(__wrap_ocf_freelist_trylock);
will_return(__wrap_ocf_freelist_trylock, 0);
ocf_freelist_get_cache_line(freelist, &line);
/****************************************************************/
/* verify fast path locking - scucessfull trylock in slowpath */
/* ctx 0: 0, 3
* ctx 1:
* ctx 2: 2
* slowpath next victim: 0 */
/* we expect trylock for context 0, since context 1 has empty list */
expect_value(__wrap_ocf_freelist_trylock, ctx, 0);
expect_function_call(__wrap_ocf_freelist_trylock);
will_return(__wrap_ocf_freelist_trylock, 0);
ocf_freelist_get_cache_line(freelist, &line);
/****************************************************************/
/* verify fast path locking - trylock failure in slowpath */
/* ctx 0: 3
* ctx 1:
* ctx 2: 2
* slowpath next victim: 1 */
/* fastpath will fail immediately - context 1 list is empty */
/* next slowpath victim context (1) is empty - will move to ctx 2 */
/* so now we expect trylock for context no 2 - injecting error here*/
expect_value(__wrap_ocf_freelist_trylock, ctx, 2);
expect_function_call(__wrap_ocf_freelist_trylock);
will_return(__wrap_ocf_freelist_trylock, 1);
/* slowpath will attempt to trylock next non-empty context - 0
* - injecting error here as well */
expect_value(__wrap_ocf_freelist_trylock, ctx, 0);
expect_function_call(__wrap_ocf_freelist_trylock);
will_return(__wrap_ocf_freelist_trylock, 1);
/* slowpath trylock loop failed - expecting full lock */
expect_value(__wrap_ocf_freelist_lock, ctx, 2);
expect_function_call(__wrap_ocf_freelist_lock);
/* execute freelist_get_cache_line */
ocf_freelist_get_cache_line(freelist, &line);
/****************************************************************/
ocf_freelist_deinit(freelist);
}
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(ocf_freelist_get_put_locks)
};
print_message("Unit test for ocf_freelist_get_cache_line locking\n");
return cmocka_run_group_tests(tests, NULL, NULL);
}

View File

@ -0,0 +1,138 @@
/*
* <tested_file_path>src/ocf_freelist.c</tested_file_path>
* <tested_function>ocf_freelist_populate</tested_function>
* <functions_to_leave>
* ocf_freelist_init
* ocf_freelist_deinit
* ocf_freelist_populate
* next_phys_invalid
* </functions_to_leave>
*/
#undef static
#undef inline
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include "print_desc.h"
#include "ocf/ocf.h"
#include "metadata/metadata.h"
#include "ocf_freelist.c/ocf_freelist_populate_generated_warps.c"
ocf_cache_line_t __wrap_ocf_metadata_collision_table_entries(ocf_cache_t cache)
{
return mock();
}
ocf_cache_line_t __wrap_env_get_execution_context_count(ocf_cache_t cache)
{
return mock();
}
/* simulate no striping */
ocf_cache_line_t __wrap_ocf_metadata_map_phy2lg(ocf_cache_t cache, ocf_cache_line_t phy)
{
return phy;
}
bool __wrap_metadata_test_valid_any(ocf_cache_t cache, ocf_cache_line_t cline)
{
return mock();
}
void __wrap_ocf_metadata_set_partition_info(struct ocf_cache *cache,
ocf_cache_line_t line, ocf_part_id_t part_id,
ocf_cache_line_t next_line, ocf_cache_line_t prev_line)
{
print_message("%s %u %u %u\n", __func__, prev_line, line, next_line);
check_expected(line);
check_expected(part_id);
check_expected(next_line);
check_expected(prev_line);
}
#define expect_set_info(curr, part, next, prev) \
expect_value(__wrap_ocf_metadata_set_partition_info, line, curr); \
expect_value(__wrap_ocf_metadata_set_partition_info, part_id, part); \
expect_value(__wrap_ocf_metadata_set_partition_info, next_line, next); \
expect_value(__wrap_ocf_metadata_set_partition_info, prev_line, prev);
static void ocf_freelist_populate_test01(void **state)
{
unsigned num_cls = 8;
unsigned num_ctxts = 3;
ocf_freelist_t freelist;
unsigned ctx_iter, cl_iter;
print_test_description("Verify proper set_partition_info order and arguments - empty cache");
will_return_maybe(__wrap_ocf_metadata_collision_table_entries, num_cls);
will_return_maybe(__wrap_env_get_execution_context_count, num_ctxts);
will_return_maybe(__wrap_metadata_test_valid_any, false);
freelist = ocf_freelist_init(NULL);
expect_set_info(0, PARTITION_INVALID, 1 , num_cls);
expect_set_info(1, PARTITION_INVALID, 2 , 0);
expect_set_info(2, PARTITION_INVALID, num_cls, 1);
expect_set_info(3, PARTITION_INVALID, 4 , num_cls);
expect_set_info(4, PARTITION_INVALID, 5 , 3);
expect_set_info(5, PARTITION_INVALID, num_cls, 4);
expect_set_info(6, PARTITION_INVALID, 7 , num_cls);
expect_set_info(7, PARTITION_INVALID, num_cls, 6);
ocf_freelist_populate(freelist, num_cls);
ocf_freelist_deinit(freelist);
}
static void ocf_freelist_populate_test02(void **state)
{
unsigned num_cls = 8;
unsigned num_ctxts = 3;
ocf_freelist_t freelist;
unsigned ctx_iter, cl_iter;
print_test_description("Verify proper set_partition_info order and arguments - some valid clines");
will_return_maybe(__wrap_ocf_metadata_collision_table_entries, num_cls);
will_return_maybe(__wrap_env_get_execution_context_count, num_ctxts);
freelist = ocf_freelist_init(NULL);
/* simulate only cachelines 2, 3, 4, 7 invalid */
will_return(__wrap_metadata_test_valid_any, true);
will_return(__wrap_metadata_test_valid_any, true);
will_return(__wrap_metadata_test_valid_any, false);
will_return(__wrap_metadata_test_valid_any, false);
will_return(__wrap_metadata_test_valid_any, false);
will_return(__wrap_metadata_test_valid_any, true);
will_return(__wrap_metadata_test_valid_any, true);
will_return(__wrap_metadata_test_valid_any, false);
expect_set_info(2, PARTITION_INVALID, 3 , num_cls);
expect_set_info(3, PARTITION_INVALID, num_cls, 2);
expect_set_info(4, PARTITION_INVALID, num_cls, num_cls);
expect_set_info(7, PARTITION_INVALID, num_cls, num_cls);
ocf_freelist_populate(freelist, 4);
ocf_freelist_deinit(freelist);
}
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(ocf_freelist_populate_test01),
cmocka_unit_test(ocf_freelist_populate_test02)
};
print_message("Unit test of src/ocf_freelist.c\n");
return cmocka_run_group_tests(tests, NULL, NULL);
}