Merge pull request #413 from mmichal10/occ-per-ioclass

Occupancy per ioclass
This commit is contained in:
Robert Baldyga 2020-12-21 23:43:54 +01:00 committed by GitHub
commit 7f60d73511
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1725 additions and 166 deletions

1
env/posix/ocf_env.h vendored
View File

@ -46,6 +46,7 @@
#define min(a,b) MIN(a,b)
#define ENV_PRIu64 "lu"
#define ENV_PRId64 "ld"
typedef uint8_t u8;
typedef uint16_t u16;

View File

@ -40,8 +40,8 @@ struct ocf_io_class_info {
uint32_t max_size;
/*!< Maximum number of cache lines that might be assigned into
* this IO class. If current size reach maximum size no more
* allocation for this IO class takes place
* this IO class. If current size reaches maximum size then some
* of ioclass's cachelines are evicted.
*/
uint8_t eviction_policy_type;

View File

@ -14,6 +14,7 @@
#include "../utils/utils_cache_line.h"
#include "../ocf_request.h"
#include "../utils/utils_cleaner.h"
#include "../utils/utils_part.h"
#include "../metadata/metadata.h"
#include "../eviction/eviction.h"
#include "../promotion/promotion.h"
@ -127,7 +128,8 @@ void ocf_engine_update_req_info(struct ocf_cache *cache,
/*
* Need to move this cache line into other partition
*/
_entry->re_part = req->info.re_part = true;
_entry->re_part = true;
req->info.re_part_no++;
}
break;
@ -254,7 +256,7 @@ static void ocf_engine_map_cache_line(struct ocf_request *req,
ocf_cleaning_t clean_policy_type;
if (!ocf_freelist_get_cache_line(cache->freelist, cache_line)) {
req->info.mapping_error = 1;
ocf_req_set_mapping_error(req);
return;
}
@ -334,7 +336,7 @@ static void ocf_engine_map(struct ocf_request *req)
if (ocf_engine_unmapped_count(req) >
ocf_freelist_num_free(cache->freelist)) {
req->info.mapping_error = 1;
ocf_req_set_mapping_error(req);
return;
}
@ -353,7 +355,7 @@ static void ocf_engine_map(struct ocf_request *req)
ocf_engine_map_cache_line(req, entry->core_line,
entry->hash, &entry->coll_idx);
if (req->info.mapping_error) {
if (ocf_req_test_mapping_error(req)) {
/*
* Eviction error (mapping error), need to
* clean, return and do pass through
@ -375,7 +377,7 @@ static void ocf_engine_map(struct ocf_request *req)
}
if (!req->info.mapping_error) {
if (!ocf_req_test_mapping_error(req)) {
/* request has been inserted into cache - purge it from promotion
* policy */
ocf_promotion_req_purge(cache->promotion_policy, req);
@ -408,15 +410,6 @@ static void _ocf_engine_clean_end(void *private_data, int error)
}
}
static int ocf_engine_evict(struct ocf_request *req)
{
if (!ocf_engine_unmapped_count(req))
return 0;
return space_managment_evict_do(req->cache, req,
ocf_engine_unmapped_count(req));
}
static int lock_clines(struct ocf_request *req,
const struct ocf_engine_callbacks *engine_cbs)
{
@ -432,13 +425,139 @@ static int lock_clines(struct ocf_request *req,
}
}
static inline int ocf_prepare_clines_hit(struct ocf_request *req,
const struct ocf_engine_callbacks *engine_cbs)
{
int lock_status = -OCF_ERR_NO_LOCK;
struct ocf_metadata_lock *metadata_lock = &req->cache->metadata.lock;
uint32_t clines_to_evict;
int res;
/* Cachelines are mapped in correct partition */
if (ocf_part_is_enabled(&req->cache->user_parts[req->part_id]) &&
!ocf_engine_needs_repart(req)) {
lock_status = lock_clines(req, engine_cbs);
ocf_req_hash_unlock_rd(req);
return lock_status;
}
res = ocf_part_check_space(req, &clines_to_evict);
if (res == OCF_PART_HAS_SPACE)
lock_status = lock_clines(req, engine_cbs);
/* Since target part is empty and disabled, request should be submited in
* pass-through */
if (res == OCF_PART_IS_DISABLED)
ocf_req_set_mapping_error(req);
ocf_req_hash_unlock_rd(req);
if (res != OCF_PART_IS_FULL)
return lock_status;
ocf_metadata_start_exclusive_access(metadata_lock);
ocf_part_check_space(req, &clines_to_evict);
if (space_managment_evict_do(req->cache, req, clines_to_evict) ==
LOOKUP_MISS) {
ocf_req_set_mapping_error(req);
goto unlock;
}
if (!ocf_part_is_enabled(&req->cache->user_parts[req->part_id])) {
/* Target part is disabled but had some cachelines assigned. Submit
* request in pass-through after eviction has been made */
ocf_req_set_mapping_error(req);
goto unlock;
}
lock_status = lock_clines(req, engine_cbs);
unlock:
ocf_metadata_end_exclusive_access(metadata_lock);
return lock_status;
}
static inline int ocf_prepare_clines_miss(struct ocf_request *req,
const struct ocf_engine_callbacks *engine_cbs)
{
int lock_status = -OCF_ERR_NO_LOCK;
struct ocf_metadata_lock *metadata_lock = &req->cache->metadata.lock;
uint32_t clines_to_evict = 0;
int res;
/* Mapping must be performed holding (at least) hash-bucket write lock */
ocf_req_hash_lock_upgrade(req);
/* Verify whether partition occupancy threshold is not reached yet or cache
* is not out of free cachelines */
res = ocf_part_check_space(req, &clines_to_evict);
if (res == OCF_PART_IS_DISABLED) {
ocf_req_set_mapping_error(req);
ocf_req_hash_unlock_wr(req);
return lock_status;
}
if (res == OCF_PART_HAS_SPACE) {
ocf_engine_map(req);
if (ocf_req_test_mapping_error(req)) {
goto eviction;
}
lock_status = lock_clines(req, engine_cbs);
if (lock_status < 0) {
/* Mapping succeeded, but we failed to acquire cacheline lock.
* Don't try to evict, just return error to caller */
ocf_req_set_mapping_error(req);
}
ocf_req_hash_unlock_wr(req);
return lock_status;
}
eviction:
ocf_req_hash_unlock_wr(req);
ocf_metadata_start_exclusive_access(metadata_lock);
ocf_part_check_space(req, &clines_to_evict);
if (space_managment_evict_do(req->cache, req, clines_to_evict) ==
LOOKUP_MISS) {
ocf_req_set_mapping_error(req);
goto unlock;
}
if (!ocf_part_is_enabled(&req->cache->user_parts[req->part_id])) {
/* Partition is disabled but it had cachelines assigned. Now, that they
* are evicted, don't try to map cachelines - we don't want to insert
* new cachelines - the request should be submited in pass through mode
* instead */
ocf_req_set_mapping_error(req);
goto unlock;
}
ocf_engine_map(req);
if (ocf_req_test_mapping_error(req))
goto unlock;
lock_status = lock_clines(req, engine_cbs);
if (lock_status < 0)
ocf_req_set_mapping_error(req);
unlock:
ocf_metadata_end_exclusive_access(metadata_lock);
return lock_status;
}
int ocf_engine_prepare_clines(struct ocf_request *req,
const struct ocf_engine_callbacks *engine_cbs)
{
bool mapped;
bool promote = true;
int lock = -ENOENT;
struct ocf_metadata_lock *metadata_lock = &req->cache->metadata.lock;
int lock = -OCF_ERR_NO_LOCK;
/* Calculate hashes for hash-bucket locking */
ocf_req_hash(req);
@ -452,52 +571,21 @@ int ocf_engine_prepare_clines(struct ocf_request *req,
ocf_engine_traverse(req);
mapped = ocf_engine_is_mapped(req);
if (mapped) {
/* Request cachelines are already mapped, acquire cacheline
* lock */
lock = lock_clines(req, engine_cbs);
} else {
if (mapped)
return ocf_prepare_clines_hit(req, engine_cbs);
/* check if request should promote cachelines */
promote = ocf_promotion_req_should_promote(
req->cache->promotion_policy, req);
if (!promote)
req->info.mapping_error = 1;
}
if (mapped || !promote) {
/* Will not attempt mapping - release hash bucket lock */
if (!promote) {
ocf_req_set_mapping_error(req);
ocf_req_hash_unlock_rd(req);
} else {
/* Need to map (potentially evict) cachelines. Mapping must be
* performed holding (at least) hash-bucket write lock */
ocf_req_hash_lock_upgrade(req);
ocf_engine_map(req);
if (!req->info.mapping_error)
lock = lock_clines(req, engine_cbs);
ocf_req_hash_unlock_wr(req);
if (req->info.mapping_error) {
/* Not mapped - evict cachelines under global exclusive
* lock*/
ocf_metadata_start_exclusive_access(metadata_lock);
/* Now there is exclusive access for metadata. May
* traverse once again and evict cachelines if needed.
*/
if (ocf_engine_evict(req) == LOOKUP_MAPPED)
ocf_engine_map(req);
if (!req->info.mapping_error)
lock = lock_clines(req, engine_cbs);
ocf_metadata_end_exclusive_access(metadata_lock);
}
}
return lock;
}
return ocf_prepare_clines_miss(req, engine_cbs);
}
static int _ocf_engine_clean_getter(struct ocf_cache *cache,
void *getter_context, uint32_t item, ocf_cache_line_t *line)
{

View File

@ -47,6 +47,20 @@ static inline bool ocf_engine_is_hit(struct ocf_request *req)
*/
#define ocf_engine_is_miss(req) (!ocf_engine_is_hit(req))
/**
* @brief Check if all the cache lines are assigned to a good partition
*
* @param req OCF request
*
* @retval true request's cache lines are assigned to a good partition
* @retval false some of the request's cache lines needs to be reassigned to
* a target partition
*/
static inline bool ocf_engine_needs_repart(struct ocf_request *req)
{
return req->info.re_part_no > 0;
}
/**
* @brief Check if all cache lines are mapped fully
*
@ -98,6 +112,18 @@ static inline uint32_t ocf_engine_unmapped_count(struct ocf_request *req)
return req->core_line_count - (req->info.hit_no + req->info.invalid_no);
}
/**
* @brief Get number of cache lines to repart
*
* @param req OCF request
*
* @retval Number of cache lines to repart
*/
static inline uint32_t ocf_engine_repart_count(struct ocf_request *req)
{
return req->info.re_part_no;
}
/**
* @brief Get number of IOs to perform cache read or write
*

View File

@ -69,7 +69,7 @@ static int _ocf_read_fast_do(struct ocf_request *req)
/* Get OCF request - increase reference counter */
ocf_req_get(req);
if (req->info.re_part) {
if (ocf_engine_needs_repart(req)) {
OCF_DEBUG_RQ(req, "Re-Part");
ocf_req_hash_lock_wr(req);
@ -108,6 +108,7 @@ int ocf_read_fast(struct ocf_request *req)
{
bool hit;
int lock = OCF_LOCK_NOT_ACQUIRED;
bool part_has_space = false;
/* Get OCF request - increase reference counter */
ocf_req_get(req);
@ -124,14 +125,18 @@ int ocf_read_fast(struct ocf_request *req)
ocf_engine_traverse(req);
hit = ocf_engine_is_hit(req);
if (hit) {
if (ocf_part_check_space(req, NULL) == OCF_PART_HAS_SPACE)
part_has_space = true;
if (hit && part_has_space) {
ocf_io_start(&req->ioi.io);
lock = ocf_req_async_lock_rd(req, ocf_engine_on_resume);
}
ocf_req_hash_unlock_rd(req);
if (hit) {
if (hit && part_has_space) {
OCF_DEBUG_RQ(req, "Fast path success");
if (lock >= 0) {
@ -154,10 +159,7 @@ int ocf_read_fast(struct ocf_request *req)
/* Put OCF request - decrease reference counter */
ocf_req_put(req);
if (hit)
return OCF_FAST_PATH_YES;
else
return OCF_FAST_PATH_NO;
return (hit && part_has_space) ? OCF_FAST_PATH_YES : OCF_FAST_PATH_NO;
}
/* __ __ _ _ ______ _ _____ _ _
@ -177,6 +179,7 @@ int ocf_write_fast(struct ocf_request *req)
{
bool mapped;
int lock = OCF_LOCK_NOT_ACQUIRED;
int part_has_space = false;
/* Get OCF request - increase reference counter */
ocf_req_get(req);
@ -193,14 +196,18 @@ int ocf_write_fast(struct ocf_request *req)
ocf_engine_traverse(req);
mapped = ocf_engine_is_mapped(req);
if (mapped) {
if (ocf_part_check_space(req, NULL) == OCF_PART_HAS_SPACE)
part_has_space = true;
if (mapped && part_has_space) {
ocf_io_start(&req->ioi.io);
lock = ocf_req_async_lock_wr(req, ocf_engine_on_resume);
}
ocf_req_hash_unlock_rd(req);
if (mapped) {
if (mapped && part_has_space) {
if (lock >= 0) {
OCF_DEBUG_RQ(req, "Fast path success");
@ -223,6 +230,5 @@ int ocf_write_fast(struct ocf_request *req)
/* Put OCF request - decrease reference counter */
ocf_req_put(req);
return mapped ? OCF_FAST_PATH_YES : OCF_FAST_PATH_NO;
return (mapped && part_has_space) ? OCF_FAST_PATH_YES : OCF_FAST_PATH_NO;
}

View File

@ -67,7 +67,7 @@ int ocf_read_pt_do(struct ocf_request *req)
return 0;
}
if (req->info.re_part) {
if (ocf_engine_needs_repart(req)) {
OCF_DEBUG_RQ(req, "Re-Part");
ocf_req_hash_lock_wr(req);

View File

@ -172,7 +172,7 @@ static int _ocf_read_generic_do(struct ocf_request *req)
ocf_req_hash_unlock_rd(req);
}
if (req->info.re_part) {
if (ocf_engine_needs_repart(req)) {
OCF_DEBUG_RQ(req, "Re-Part");
ocf_req_hash_lock_wr(req);
@ -243,7 +243,7 @@ int ocf_read_generic(struct ocf_request *req)
lock = ocf_engine_prepare_clines(req, &_rd_engine_callbacks);
if (!req->info.mapping_error) {
if (!ocf_req_test_mapping_error(req)) {
if (lock >= 0) {
if (lock != OCF_LOCK_ACQUIRED) {
/* Lock was not acquired, need to wait for resume */

View File

@ -121,7 +121,7 @@ static inline void _ocf_write_wb_submit(struct ocf_request *req)
* 3. Then continue processing request (flush metadata)
*/
if (req->info.re_part) {
if (ocf_engine_needs_repart(req)) {
OCF_DEBUG_RQ(req, "Re-Part");
ocf_req_hash_lock_wr(req);
@ -189,7 +189,7 @@ int ocf_write_wb(struct ocf_request *req)
lock = ocf_engine_prepare_clines(req, &_wb_engine_callbacks);
if (!req->info.mapping_error) {
if (!ocf_req_test_mapping_error(req)) {
if (lock >= 0) {
if (lock != OCF_LOCK_ACQUIRED) {
/* WR lock was not acquired, need to wait for resume */

View File

@ -118,7 +118,7 @@ static void _ocf_write_wt_update_bits(struct ocf_request *req)
ocf_req_hash_unlock_wr(req);
}
if (req->info.re_part) {
if (ocf_engine_needs_repart(req)) {
OCF_DEBUG_RQ(req, "Re-Part");
ocf_req_hash_lock_wr(req);
@ -183,7 +183,7 @@ int ocf_write_wt(struct ocf_request *req)
lock = ocf_engine_prepare_clines(req, &_wt_engine_callbacks);
if (!req->info.mapping_error) {
if (!ocf_req_test_mapping_error(req)) {
if (lock >= 0) {
if (lock != OCF_LOCK_ACQUIRED) {
/* WR lock was not acquired, need to wait for resume */

View File

@ -20,10 +20,14 @@ struct eviction_policy_ops evict_policy_ops[ocf_eviction_max] = {
},
};
static uint32_t ocf_evict_calculate(struct ocf_user_part *part,
uint32_t to_evict)
static uint32_t ocf_evict_calculate(ocf_cache_t cache,
struct ocf_user_part *part, uint32_t to_evict, bool roundup)
{
if (part->runtime->curr_size <= part->config->min_size) {
uint32_t curr_part_size = ocf_part_get_occupancy(part);
uint32_t min_part_size = ocf_part_get_min_size(cache, part);
if (curr_part_size <= min_part_size) {
/*
* Cannot evict from this partition because current size
* is less than minimum size
@ -31,15 +35,31 @@ static uint32_t ocf_evict_calculate(struct ocf_user_part *part,
return 0;
}
if (to_evict < OCF_TO_EVICTION_MIN)
if (roundup && to_evict < OCF_TO_EVICTION_MIN)
to_evict = OCF_TO_EVICTION_MIN;
if (to_evict > (part->runtime->curr_size - part->config->min_size))
to_evict = part->runtime->curr_size - part->config->min_size;
if (to_evict > (curr_part_size - min_part_size))
to_evict = curr_part_size - min_part_size;
return to_evict;
}
static inline uint32_t ocf_evict_part_do(ocf_cache_t cache,
ocf_queue_t io_queue, const uint32_t evict_cline_no,
struct ocf_user_part *target_part)
{
uint32_t to_evict = 0;
if (!evp_lru_can_evict(cache))
return 0;
to_evict = ocf_evict_calculate(cache, target_part, evict_cline_no,
false);
return ocf_eviction_need_space(cache, io_queue,
target_part, to_evict);
}
static inline uint32_t ocf_evict_do(ocf_cache_t cache,
ocf_queue_t io_queue, const uint32_t evict_cline_no,
struct ocf_user_part *target_part)
@ -67,16 +87,13 @@ static inline uint32_t ocf_evict_do(ocf_cache_t cache,
/* It seams that no more partition for eviction */
break;
}
if (part_id == target_part->id) {
/* Omit targeted, evict from different first */
continue;
}
if (evicted >= evict_cline_no) {
/* Evicted requested number of cache line, stop */
goto out;
}
to_evict = ocf_evict_calculate(part, evict_cline_no);
to_evict = ocf_evict_calculate(cache, part, evict_cline_no,
true);
if (to_evict == 0) {
/* No cache lines to evict for this partition */
continue;
@ -86,18 +103,6 @@ static inline uint32_t ocf_evict_do(ocf_cache_t cache,
part, to_evict);
}
if (!ocf_eviction_can_evict(cache))
goto out;
if (evicted < evict_cline_no) {
/* Now we can evict form targeted partition */
to_evict = ocf_evict_calculate(target_part, evict_cline_no);
if (to_evict) {
evicted += ocf_eviction_need_space(cache, io_queue,
target_part, to_evict);
}
}
out:
return evicted;
}
@ -109,16 +114,22 @@ int space_managment_evict_do(struct ocf_cache *cache,
uint32_t free;
struct ocf_user_part *req_part = &cache->user_parts[req->part_id];
if (ocf_req_part_evict(req)) {
evicted = ocf_evict_part_do(cache, req->io_queue, evict_cline_no,
req_part);
} else {
free = ocf_freelist_num_free(cache->freelist);
if (evict_cline_no <= free)
return LOOKUP_MAPPED;
evict_cline_no -= free;
evicted = ocf_evict_do(cache, req->io_queue, evict_cline_no, req_part);
}
if (evict_cline_no <= evicted)
return LOOKUP_MAPPED;
req->info.mapping_error |= true;
ocf_req_set_mapping_error(req);
return LOOKUP_MISS;
}

View File

@ -58,11 +58,11 @@ struct eviction_policy_ops {
extern struct eviction_policy_ops evict_policy_ops[ocf_eviction_max];
/*
* Deallocates space from low priority partitions.
* Deallocates space according to eviction priorities.
*
* Returns -1 on error
* or the destination partition ID for the free buffers
* (it matches label and is part of the object (#core_id) IO group)
* @returns:
* 'LOOKUP_HIT' if evicted enough cachelines to serve @req
* 'LOOKUP_MISS' otherwise
*/
int space_managment_evict_do(ocf_cache_t cache,
struct ocf_request *req, uint32_t evict_cline_no);

View File

@ -11,7 +11,7 @@
#define PARTITION_DEFAULT 0
#define PARTITION_INVALID ((ocf_part_id_t)-1)
#define PARTITION_SIZE_MAX ((ocf_cache_line_t)-1)
#define PARTITION_SIZE_MAX 100
void ocf_metadata_get_partition_info(
struct ocf_cache *cache, ocf_cache_line_t line,

View File

@ -31,6 +31,8 @@ int ocf_mngt_add_partition_to_cache(struct ocf_cache *cache,
uint32_t max_size, uint8_t priority, bool valid)
{
uint32_t size;
struct ocf_lst_entry *iter;
uint32_t iter_id;
if (!name)
return -OCF_ERR_INVAL;
@ -41,6 +43,9 @@ int ocf_mngt_add_partition_to_cache(struct ocf_cache *cache,
if (cache->user_parts[part_id].config->flags.valid)
return -OCF_ERR_INVAL;
if (min_size > max_size)
return -OCF_ERR_INVAL;
if (max_size > PARTITION_SIZE_MAX)
return -OCF_ERR_INVAL;
@ -51,6 +56,14 @@ int ocf_mngt_add_partition_to_cache(struct ocf_cache *cache,
return -OCF_ERR_INVAL;
}
for_each_lst(&cache->lst_part, iter, iter_id) {
if (iter_id == part_id) {
ocf_cache_log(cache, log_err,
"Part with id %hu already exists\n", part_id);
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;
@ -77,8 +90,7 @@ static int _ocf_mngt_set_partition_size(struct ocf_cache *cache,
if (min > max)
return -OCF_ERR_INVAL;
if (_ocf_mngt_count_parts_min_size(cache) + min
>= cache->device->collision_table_entries) {
if (_ocf_mngt_count_parts_min_size(cache) + min > PARTITION_SIZE_MAX) {
/* Illegal configuration in which sum of all min_sizes exceeds
* cache size.
*/
@ -126,17 +138,17 @@ static int _ocf_mngt_io_class_configure(ocf_cache_t cache,
/* 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);
"Setting IO class size, id: %u, name: '%s', max size: %u%%"
" [ ERROR ]\n", part_id, dest_part->config->name, max);
return -OCF_ERR_INVAL;
}
ocf_part_set_prio(cache, dest_part, prio);
dest_part->config->cache_mode = cache_mode;
ocf_cache_log(cache, log_info,
"Updating unclassified IO class, id: "
"%u [ OK ]\n", part_id);
"Updating unclassified IO class, id: %u, name :'%s',"
"max size: %u%% [ OK ]\n",
part_id, dest_part->config->name, max);
return 0;
}
@ -150,23 +162,23 @@ static int _ocf_mngt_io_class_configure(ocf_cache_t cache,
/* 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);
"Setting IO class size, id: %u, name: '%s', max size %u%%"
"[ ERROR ]\n", part_id, dest_part->config->name, max);
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);
"class, id: %u, name: '%s', max size %u%% [ OK ]\n",
part_id, dest_part->config->name, max);
} 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);
"id: %u, name: '%s', max size %u%% [ OK ]\n", part_id,
dest_part->config->name, max);
}
ocf_part_set_prio(cache, dest_part, prio);

View File

@ -13,9 +13,10 @@
struct ocf_req_allocator;
struct ocf_req_info {
/* Number of hits, invalid, misses. */
/* Number of hits, invalid, misses, reparts. */
unsigned int hit_no;
unsigned int invalid_no;
unsigned int re_part_no;
uint32_t dirty_all;
/*!< Number of dirty line in request*/
@ -32,11 +33,6 @@ struct ocf_req_info {
uint32_t mapping_error : 1;
/*!< Core lines in this request were not mapped into cache */
uint32_t re_part : 1;
/*!< This bit indicate that in the request some cache lines
* has to be moved to another partition
*/
uint32_t core_error : 1;
/*!< Error occured during I/O on core device */
@ -191,6 +187,9 @@ struct ocf_request {
uint8_t wi_second_pass : 1;
/*!< Set after first pass of WI write is completed */
uint8_t part_evict : 1;
/* !< Some cachelines from request's partition must be evicted */
log_sid_t sid;
/*!< Tracing sequence ID */
@ -332,6 +331,40 @@ void ocf_req_clear_map(struct ocf_request *req);
*/
void ocf_req_hash(struct ocf_request *req);
/**
* @brief Request should trigger eviction from it's target partition
*
* @param req - OCF request
*/
static inline void ocf_req_set_part_evict(struct ocf_request *req)
{
req->part_evict = true;
}
/**
* @brief Request shouldn't trigger eviction from it's target partition
*
* @param req - OCF request
*/
static inline void ocf_req_clear_part_evict(struct ocf_request *req)
{
req->part_evict = false;
}
/**
* @brief Check wheter request shouldn't trigger eviction from it's target
* partition or any partition
*
* @param req - OCF request
* @return true - Eviciton should be triggered from request's target partition
* @return false - Eviction should be triggered with respect to eviction
* priority
*/
static inline bool ocf_req_part_evict(struct ocf_request *req)
{
return req->part_evict;
}
int ocf_req_set_dirty(struct ocf_request *req);
/**
@ -348,6 +381,16 @@ static inline void ocf_req_clear(struct ocf_request *req)
env_atomic_set(&req->req_remaining, 0);
}
static inline void ocf_req_set_mapping_error(struct ocf_request *req)
{
req->info.mapping_error = true;
}
static inline bool ocf_req_test_mapping_error(struct ocf_request *req)
{
return req->info.mapping_error;
}
/**
* @brief Return OCF request reference count
*

View File

@ -276,10 +276,7 @@ int ocf_core_io_class_get_stats(ocf_core_t core, ocf_part_id_t part_id,
struct ocf_stats_io_class *stats)
{
ocf_cache_t cache;
uint32_t cache_occupancy_total = 0;
struct ocf_counters_part *part_stat;
ocf_core_t i_core;
ocf_core_id_t i_core_id;
OCF_CHECK_NULL(core);
OCF_CHECK_NULL(stats);
@ -292,11 +289,6 @@ int ocf_core_io_class_get_stats(ocf_core_t core, ocf_part_id_t part_id,
if (!ocf_part_is_valid(&cache->user_parts[part_id]))
return -OCF_ERR_IO_CLASS_NOT_EXIST;
for_each_core(cache, i_core, i_core_id) {
cache_occupancy_total += env_atomic_read(
&i_core->runtime_meta->cached_clines);
}
part_stat = &core->counters->part_counters[part_id];
stats->occupancy_clines = env_atomic_read(&core->runtime_meta->
@ -304,8 +296,7 @@ int ocf_core_io_class_get_stats(ocf_core_t core, ocf_part_id_t part_id,
stats->dirty_clines = env_atomic_read(&core->runtime_meta->
part_counters[part_id].dirty_clines);
stats->free_clines = cache->conf_meta->cachelines -
cache_occupancy_total;
stats->free_clines = 0;
copy_req_stats(&stats->read_reqs, &part_stat->read_reqs);
copy_req_stats(&stats->write_reqs, &part_stat->write_reqs);

View File

@ -229,15 +229,9 @@ static void _ocf_stats_part_fill(ocf_cache_t cache, ocf_part_id_t part_id,
_lines4k(stats->occupancy_clines, cache_line_size),
_lines4k(cache_size, cache_line_size));
if (part_id == PARTITION_DEFAULT) {
_set(&usage->free,
_lines4k(stats->free_clines, cache_line_size),
_lines4k(cache_size, cache_line_size));
} else {
_set(&usage->free,
_lines4k(0, cache_line_size),
_lines4k(0, cache_line_size));
}
_set(&usage->clean,
_lines4k(stats->occupancy_clines - stats->dirty_clines,

View File

@ -190,3 +190,82 @@ void ocf_part_set_valid(struct ocf_cache *cache, ocf_part_id_t id,
}
}
}
static inline uint32_t ocf_part_evict_size(struct ocf_request *req)
{
uint32_t needed_cache_lines, part_available, cache_lines_to_evict;
uint32_t part_occupancy, part_occupancy_debt;
struct ocf_user_part *target_part = &req->cache->user_parts[req->part_id];
uint32_t part_occupancy_limit =
ocf_part_get_max_size(req->cache, target_part);
needed_cache_lines = ocf_engine_repart_count(req) +
ocf_engine_unmapped_count(req);
part_occupancy = ocf_part_get_occupancy(target_part);
if (part_occupancy_limit >= part_occupancy) {
part_available = part_occupancy_limit - part_occupancy;
part_occupancy_debt = 0;
} else {
/* Occupancy is greater than occupancy limit. Evict missing number of
* cachelines, but no more than single eviction limit */
part_occupancy_debt = min((uint32_t)OCF_PENDING_EVICTION_LIMIT,
part_occupancy - part_occupancy_limit);
part_available = 0;
}
if (ocf_freelist_num_free(req->cache->freelist) <
ocf_engine_unmapped_count(req)) {
/* Number of cachelines to insert greater than number of free
* cachelines */
if (part_available >= needed_cache_lines) {
/* Cache is full, but target's part occupancy limit is not reached
*/
ocf_req_clear_part_evict(req);
cache_lines_to_evict = needed_cache_lines;
} else {
/* Cache is full and target part reached it's occupancy limit */
ocf_req_set_part_evict(req);
cache_lines_to_evict = needed_cache_lines - part_available;
}
} else if (part_available < needed_cache_lines) {
/* Enough of free cache lines, but partition reached it's occupancy
* limit */
cache_lines_to_evict = needed_cache_lines - part_available;
ocf_req_set_part_evict(req);
} else if (part_available >= needed_cache_lines) {
/* Enough free cachelines available and they can be assigned to target
* partition */
cache_lines_to_evict = 0;
}
return cache_lines_to_evict + part_occupancy_debt;
}
uint32_t ocf_part_check_space(struct ocf_request *req, uint32_t *to_evict)
{
uint32_t ret = OCF_PART_IS_FULL;
uint32_t _to_evict;
struct ocf_user_part *target_part = &req->cache->user_parts[req->part_id];
if (!ocf_part_is_enabled(target_part) &&
ocf_part_get_occupancy(target_part) == 0) {
/* If partition is disabled, but has assigned cachelines, eviction has
* to be triggered */
return OCF_PART_IS_DISABLED;
}
_to_evict = ocf_part_evict_size(req);
if (_to_evict == 0)
ret = OCF_PART_HAS_SPACE;
if (to_evict)
*to_evict = _to_evict;
return ret;
}

View File

@ -8,6 +8,7 @@
#include "../ocf_request.h"
#include "../engine/cache_engine.h"
#include "../engine/engine_common.h"
#include "../metadata/metadata_partition.h"
void ocf_part_init(struct ocf_cache *cache);
@ -50,6 +51,38 @@ static inline ocf_part_id_t ocf_part_class2id(ocf_cache_t cache, uint64_t class)
return PARTITION_DEFAULT;
}
static inline uint32_t ocf_part_get_occupancy(struct ocf_user_part *part)
{
return part->runtime->curr_size;
}
static inline uint32_t ocf_part_get_min_size(ocf_cache_t cache,
struct ocf_user_part *part)
{
uint64_t ioclass_size;
ioclass_size = part->config->min_size * cache->conf_meta->cachelines;
ioclass_size /= 100;
return (uint32_t)ioclass_size;
}
static inline uint32_t ocf_part_get_max_size(ocf_cache_t cache,
struct ocf_user_part *part)
{
uint64_t ioclass_size, max_size, cache_size;
max_size = part->config->max_size;
cache_size = cache->conf_meta->cachelines;
ioclass_size = max_size * cache_size;
ioclass_size = OCF_DIV_ROUND_UP(ioclass_size, 100);
return (uint32_t)ioclass_size;
}
void ocf_part_move(struct ocf_request *req);
#define for_each_part(cache, part, id) \
@ -61,7 +94,27 @@ static inline void ocf_part_sort(struct ocf_cache *cache)
ocf_lst_sort(&cache->lst_part);
}
static inline ocf_cache_mode_t ocf_part_get_cache_mode(struct ocf_cache *cache,
static inline bool ocf_part_is_enabled(struct ocf_user_part *part)
{
return part->config->max_size != 0;
}
#define OCF_PART_HAS_SPACE 0
#define OCF_PART_IS_FULL 1
#define OCF_PART_IS_DISABLED 2
/**
* Check whether there is enough free cachelines to serve request. If partition
* occupancy limit is reached, `req->part_evict` is set to true. Otherwise
* flag is set to false and eviction from any partition should be triggered.
*
* @return
* OCF_PART_HAS_SPACE when cachelines alloted successfully
* OCF_PART_IS_FULL when need to evict some cachelines to serve request
* OCF_PART_IS_DISABLED when caching for particular partition is disabled
*/
uint32_t ocf_part_check_space(struct ocf_request *req, uint32_t *to_evict);
static inline ocf_cache_mode_t ocf_part_get_cache_mode(ocf_cache_t cache,
ocf_part_id_t part_id)
{
if (part_id < OCF_IO_CLASS_MAX)

View File

@ -25,6 +25,7 @@ from ..ocf import OcfLib
from .shared import (
Uuid,
OcfError,
OcfErrorCode,
CacheLineSize,
CacheLines,
OcfCompletion,
@ -34,6 +35,7 @@ from ..utils import Size, struct_to_dict
from .core import Core
from .queue import Queue
from .stats.cache import CacheInfo
from .ioclass import IoClassesInfo, IoClassInfo
from .stats.shared import UsageStats, RequestsStats, BlocksStats, ErrorsStats
@ -75,6 +77,9 @@ class ConfValidValues:
promotion_nhit_trigger_threshold_range = range(0, 100)
CACHE_MODE_NONE = -1
class CacheMode(IntEnum):
WT = 0
WB = 1
@ -299,6 +304,93 @@ class Cache:
if status:
raise OcfError("Error setting cache seq cut off policy", status)
def get_partition_info(self, part_id: int):
ioclass_info = IoClassInfo()
self.read_lock()
status = self.owner.lib.ocf_cache_io_class_get_info(
self.cache_handle, part_id, byref(ioclass_info)
)
self.read_unlock()
if status:
raise OcfError("Error retriving ioclass info", status)
return {
"_name": ioclass_info._name.decode("ascii"),
"_cache_mode": ioclass_info._cache_mode,
"_priority": int(ioclass_info._priority),
"_curr_size": (ioclass_info._curr_size),
"_min_size": int(ioclass_info._min_size),
"_max_size": int(ioclass_info._max_size),
"_eviction_policy_type": int(ioclass_info._eviction_policy_type),
"_cleaning_policy_type": int(ioclass_info._cleaning_policy_type),
}
def add_partition(
self, part_id: int, name: str, min_size: int, max_size: int, priority: int, valid: bool
):
self.write_lock()
_name = name.encode("ascii")
status = self.owner.lib.ocf_mngt_add_partition_to_cache(
self.cache_handle, part_id, _name, min_size, max_size, priority, valid
)
self.write_unlock()
if status:
raise OcfError("Error adding partition to cache", status)
def configure_partition(
self,
part_id: int,
name: str,
min_size: int,
max_size: int,
priority: int,
cache_mode=CACHE_MODE_NONE,
):
ioclasses_info = IoClassesInfo()
self.read_lock()
for i in range(IoClassesInfo.MAX_IO_CLASSES):
ioclass_info = IoClassInfo()
status = self.owner.lib.ocf_cache_io_class_get_info(
self.cache_handle, i, byref(ioclass_info)
)
if status not in [0, -OcfErrorCode.OCF_ERR_IO_CLASS_NOT_EXIST]:
raise OcfError("Error retriving existing ioclass config", status)
ioclasses_info._config[i]._class_id = i
ioclasses_info._config[i]._name = (
ioclass_info._name if len(ioclass_info._name) > 0 else 0
)
ioclasses_info._config[i]._prio = ioclass_info._priority
ioclasses_info._config[i]._cache_mode = ioclass_info._cache_mode
ioclasses_info._config[i]._min_size = ioclass_info._min_size
ioclasses_info._config[i]._max_size = ioclass_info._max_size
self.read_unlock()
ioclasses_info._config[part_id]._name = name.encode("ascii")
ioclasses_info._config[part_id]._cache_mode = int(cache_mode)
ioclasses_info._config[part_id]._prio = priority
ioclasses_info._config[part_id]._min_size = min_size
ioclasses_info._config[part_id]._max_size = max_size
self.write_lock()
status = self.owner.lib.ocf_mngt_cache_io_classes_configure(
self.cache_handle, byref(ioclasses_info)
)
self.write_unlock()
if status:
raise OcfError("Error adding partition to cache", status)
def configure_device(
self, device, force=False, perform_test=True, cache_line_size=None
):
@ -339,6 +431,21 @@ class Cache:
if c.results["error"]:
raise OcfError("Attaching cache device failed", c.results["error"])
def detach_device(self):
self.write_lock()
c = OcfCompletion([("cache", c_void_p), ("priv", c_void_p), ("error", c_int)])
self.owner.lib.ocf_mngt_cache_detach(
self.cache_handle, c, None
)
c.wait()
self.write_unlock()
if c.results["error"]:
raise OcfError("Attaching cache device failed", c.results["error"])
def load_cache(self, device):
self.configure_device(device)
c = OcfCompletion([("cache", c_void_p), ("priv", c_void_p), ("error", c_int)])
@ -605,3 +712,17 @@ lib.ocf_mngt_cache_cleaning_set_param.argtypes = [
c_uint32,
]
lib.ocf_mngt_cache_cleaning_set_param.restype = c_int
lib.ocf_cache_io_class_get_info.restype = c_int
lib.ocf_cache_io_class_get_info.argtypes = [c_void_p, c_uint32, c_void_p]
lib.ocf_mngt_add_partition_to_cache.restype = c_int
lib.ocf_mngt_add_partition_to_cache.argtypes = [
c_void_p,
c_uint16,
c_char_p,
c_uint32,
c_uint32,
c_uint8,
c_bool,
]
lib.ocf_mngt_cache_io_classes_configure.restype = c_int
lib.ocf_mngt_cache_io_classes_configure.argtypes = [c_void_p, c_void_p]

View File

@ -0,0 +1,36 @@
#
# Copyright(c) 2019-2020 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#
from ctypes import c_uint8, c_uint32, c_int, c_int16, c_uint16, c_char, c_char_p, Structure
class IoClassInfo(Structure):
MAX_IO_CLASS_NAME_SIZE = 1024
_fields_ = [
("_name", c_char * MAX_IO_CLASS_NAME_SIZE),
("_cache_mode", c_int),
("_priority", c_int16),
("_curr_size", c_uint32),
("_min_size", c_uint32),
("_max_size", c_uint32),
("_eviction_policy_type", c_uint8),
("_cleaning_policy_type", c_int),
]
class IoClassConfig(Structure):
_fields_ = [
("_class_id", c_uint32),
("_name", c_char_p),
("_prio", c_uint16),
("_cache_mode", c_int),
("_min_size", c_uint32),
("_max_size", c_uint32),
]
class IoClassesInfo(Structure):
MAX_IO_CLASSES = 33
_fields_ = [("_config", IoClassConfig * MAX_IO_CLASSES)]

View File

@ -0,0 +1,121 @@
#
# Copyright(c) 2019-2020 Intel Corporation
# SPDX-License-Identifier: BSD-3-Clause-Clear
#
import logging
from ctypes import c_int, c_void_p, byref, c_uint32, memmove, cast
from random import randrange
from itertools import count
import pytest
from pyocf.ocf import OcfLib
from pyocf.types.cache import (
Cache,
CacheMode,
MetadataLayout,
EvictionPolicy,
CleaningPolicy,
)
from pyocf.types.core import Core
from pyocf.types.data import Data
from pyocf.types.io import IoDir
from pyocf.types.shared import (
OcfError,
OcfCompletion,
CacheLines,
CacheLineSize,
SeqCutOffPolicy,
)
from pyocf.types.volume import Volume
from pyocf.utils import Size
logger = logging.getLogger(__name__)
@pytest.mark.parametrize("cls", CacheLineSize)
@pytest.mark.parametrize("mode", [CacheMode.WB, CacheMode.WT, CacheMode.WO])
@pytest.mark.parametrize("new_cache_size", [25, 45])
def test_attach_different_size(
pyocf_ctx, new_cache_size, mode: CacheMode, cls: CacheLineSize
):
"""Start cache and add partition with limited occupancy. Fill partition with data,
attach cache with different size and trigger IO. Verify if occupancy thresold is
respected with both original and new cache device.
"""
cache_device = Volume(Size.from_MiB(35))
core_device = Volume(Size.from_MiB(100))
cache = Cache.start_on_device(cache_device, cache_mode=mode, cache_line_size=cls)
core = Core.using_device(core_device)
cache.add_core(core)
cache.configure_partition(
part_id=1, name="test_part", min_size=0, max_size=50, priority=1
)
cache.set_seq_cut_off_policy(SeqCutOffPolicy.NEVER)
cache_size = cache.get_stats()["conf"]["size"]
block_size = 4096
data = bytes(block_size)
for i in range(cache_size.blocks_4k):
io_to_exp_obj(core, block_size * i, block_size, data, 0, IoDir.WRITE, 1, 0)
part_current_size = CacheLines(
cache.get_partition_info(part_id=1)["_curr_size"], cls
)
assert part_current_size.blocks_4k == cache_size.blocks_4k * 0.5
cache.detach_device()
new_cache_device = Volume(Size.from_MiB(new_cache_size))
cache.attach_device(new_cache_device, force=True)
cache_size = cache.get_stats()["conf"]["size"]
for i in range(cache_size.blocks_4k):
io_to_exp_obj(core, block_size * i, block_size, data, 0, IoDir.WRITE, 1, 0)
part_current_size = CacheLines(
cache.get_partition_info(part_id=1)["_curr_size"], cls
)
assert part_current_size.blocks_4k == cache_size.blocks_4k * 0.5
def io_to_exp_obj(core, address, size, data, offset, direction, target_ioclass, flags):
return _io(
core.new_io,
core.cache.get_default_queue(),
address,
size,
data,
offset,
direction,
target_ioclass,
flags,
)
def _io(new_io, queue, address, size, data, offset, direction, target_ioclass, flags):
io = new_io(queue, address, size, direction, target_ioclass, flags)
if direction == IoDir.READ:
_data = Data.from_bytes(bytes(size))
else:
_data = Data.from_bytes(data, offset, size)
ret = __io(io, queue, address, size, _data, direction)
if not ret and direction == IoDir.READ:
memmove(cast(data, c_void_p).value + offset, _data.handle, size)
return ret
def __io(io, queue, address, size, data, direction):
io.set_data(data, 0)
completion = OcfCompletion([("err", c_int)])
io.callback = completion.callback
io.submit()
completion.wait()
return int(completion.results["err"])

View File

@ -0,0 +1,198 @@
/*
* <tested_file_path>src/engine/engine_common.c</tested_file_path>
* <tested_function>ocf_prepare_clines_hit</tested_function>
* <functions_to_leave>
* INSERT HERE LIST OF FUNCTIONS YOU WANT TO LEAVE
* ONE FUNCTION PER 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 "../ocf_priv.h"
#include "../ocf_cache_priv.h"
#include "../ocf_queue_priv.h"
#include "../ocf_freelist.h"
#include "engine_common.h"
#include "engine_debug.h"
#include "../utils/utils_cache_line.h"
#include "../ocf_request.h"
#include "../utils/utils_cleaner.h"
#include "../utils/utils_part.h"
#include "../metadata/metadata.h"
#include "../eviction/eviction.h"
#include "../promotion/promotion.h"
#include "../concurrency/ocf_concurrency.h"
#include "engine/engine_common.c/prepare_clines_miss_generated_wraps.c"
void __wrap_ocf_req_hash_unlock_rd(struct ocf_request *req)
{
}
uint32_t __wrap_ocf_part_check_space(struct ocf_request *req,
uint32_t *to_evict)
{
return mock();
}
int __wrap_lock_clines(struct ocf_request *req,
const struct ocf_engine_callbacks *engine_cbs)
{
function_called();
return mock();
}
void __wrap_ocf_metadata_start_exclusive_access(
struct ocf_metadata_lock *metadata_lock)
{
}
void __wrap_ocf_metadata_end_exclusive_access(
struct ocf_metadata_lock *metadata_lock)
{
}
int __wrap_space_managment_evict_do(struct ocf_cache *cache,
struct ocf_request *req, uint32_t evict_cline_no)
{
return mock();
}
bool __wrap_ocf_part_is_enabled(struct ocf_user_part *target_part)
{
return mock();
}
bool __wrap_ocf_engine_needs_repart(struct ocf_request *req)
{
return mock();
}
void __wrap_ocf_req_set_mapping_error(struct ocf_request *req)
{
function_called();
}
static void ocf_prepare_clines_hit_test01(void **state)
{
struct ocf_request req = {};
print_test_description("Request is hit and part is enabled\n");
will_return(__wrap_ocf_part_is_enabled, true);
will_return(__wrap_ocf_engine_needs_repart, false);
will_return(__wrap_lock_clines, 0);
expect_function_call(__wrap_lock_clines);
assert_int_equal(ocf_prepare_clines_hit(&req, NULL), 0);
}
static void ocf_prepare_clines_hit_test02(void **state)
{
struct ocf_request req = {};
print_test_description("Request is hit but part is disabled - tigger eviction\n");
will_return(__wrap_ocf_part_is_enabled, false);
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_DISABLED);
expect_function_call(__wrap_ocf_req_set_mapping_error);
assert_int_equal(ocf_prepare_clines_hit(&req, NULL), -OCF_ERR_NO_LOCK);
}
static void ocf_prepare_clines_hit_test03(void **state)
{
struct ocf_request req = {};
print_test_description("Request needs repart, part has enough of a free space\n");
will_return(__wrap_ocf_part_is_enabled, true);
will_return(__wrap_ocf_engine_needs_repart, true);
will_return(__wrap_ocf_part_check_space, OCF_PART_HAS_SPACE);
expect_function_call(__wrap_lock_clines);
will_return(__wrap_lock_clines, 0);
assert_int_equal(ocf_prepare_clines_hit(&req, NULL), 0);
}
static void ocf_prepare_clines_hit_test04(void **state)
{
struct ocf_request req = {};
print_test_description("Request needs repart, eviction fails\n");
will_return(__wrap_ocf_part_is_enabled, true);
will_return(__wrap_ocf_engine_needs_repart, true);
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
will_return(__wrap_space_managment_evict_do, LOOKUP_MISS);
expect_function_call(__wrap_ocf_req_set_mapping_error);
assert_int_equal(ocf_prepare_clines_hit(&req, NULL), -OCF_ERR_NO_LOCK);
}
static void ocf_prepare_clines_hit_test05(void **state)
{
struct ocf_request req = {};
print_test_description("Request needs repart, eviction passed, no lock\n");
will_return(__wrap_ocf_part_is_enabled, true);
will_return(__wrap_ocf_engine_needs_repart, true);
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
will_return(__wrap_space_managment_evict_do, LOOKUP_HIT);
expect_function_call(__wrap_lock_clines);
will_return(__wrap_lock_clines, OCF_ERR_NO_LOCK);
will_return(__wrap_ocf_part_is_enabled, true);
assert_int_equal(ocf_prepare_clines_hit(&req, NULL), OCF_ERR_NO_LOCK);
}
static void ocf_prepare_clines_hit_test06(void **state)
{
struct ocf_request req = {};
print_test_description("Partition is disabled, but has some cachelines assigned.\n");
print_test_description("Trigger eviction and but don't lock cachelines\n");
will_return(__wrap_ocf_part_is_enabled, false);
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
will_return(__wrap_space_managment_evict_do, LOOKUP_HIT);
will_return(__wrap_ocf_part_is_enabled, false);
expect_function_call(__wrap_ocf_req_set_mapping_error);
assert_int_equal(ocf_prepare_clines_hit(&req, NULL), -OCF_ERR_NO_LOCK);
}
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(ocf_prepare_clines_hit_test01),
cmocka_unit_test(ocf_prepare_clines_hit_test02),
cmocka_unit_test(ocf_prepare_clines_hit_test03),
cmocka_unit_test(ocf_prepare_clines_hit_test04),
cmocka_unit_test(ocf_prepare_clines_hit_test05),
cmocka_unit_test(ocf_prepare_clines_hit_test06)
};
print_message("Unit test for ocf_prepare_clines_hit\n");
return cmocka_run_group_tests(tests, NULL, NULL);
}

View File

@ -0,0 +1,263 @@
/*
* <tested_file_path>src/engine/engine_common.c</tested_file_path>
* <tested_function>ocf_prepare_clines_miss</tested_function>
* <functions_to_leave>
* INSERT HERE LIST OF FUNCTIONS YOU WANT TO LEAVE
* ONE FUNCTION PER 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 "../ocf_priv.h"
#include "../ocf_cache_priv.h"
#include "../ocf_queue_priv.h"
#include "../ocf_freelist.h"
#include "engine_common.h"
#include "engine_debug.h"
#include "../utils/utils_cache_line.h"
#include "../ocf_request.h"
#include "../utils/utils_cleaner.h"
#include "../utils/utils_part.h"
#include "../metadata/metadata.h"
#include "../eviction/eviction.h"
#include "../promotion/promotion.h"
#include "../concurrency/ocf_concurrency.h"
#include "engine/engine_common.c/prepare_clines_miss_generated_wraps.c"
void __wrap_ocf_req_hash_lock_upgrade(struct ocf_request *req)
{
}
void __wrap_ocf_req_hash_unlock_wr(struct ocf_request *req)
{
}
uint32_t __wrap_ocf_part_check_space(struct ocf_request *req,
uint32_t *to_evict)
{
return mock();
}
int __wrap_lock_clines(struct ocf_request *req,
const struct ocf_engine_callbacks *engine_cbs)
{
function_called();
return mock();
}
void __wrap_ocf_metadata_start_exclusive_access(
struct ocf_metadata_lock *metadata_lock)
{
}
void __wrap_ocf_metadata_end_exclusive_access(
struct ocf_metadata_lock *metadata_lock)
{
}
int __wrap_space_managment_evict_do(struct ocf_cache *cache,
struct ocf_request *req, uint32_t evict_cline_no)
{
function_called();
return mock();
}
bool __wrap_ocf_part_is_enabled(struct ocf_user_part *target_part)
{
return mock();
}
void __wrap_ocf_engine_map(struct ocf_request *req)
{
function_called();
}
bool __wrap_ocf_req_test_mapping_error(struct ocf_request *req)
{
return mock();
}
void __wrap_ocf_req_set_mapping_error(struct ocf_request *req)
{
function_called();
}
static void ocf_prepare_clines_miss_test01(void **state)
{
struct ocf_request req = {};
print_test_description("Target part is disabled and empty\n");
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_DISABLED);
expect_function_call(__wrap_ocf_req_set_mapping_error);
assert_int_equal(ocf_prepare_clines_miss(&req, NULL), -OCF_ERR_NO_LOCK);
}
static void ocf_prepare_clines_miss_test02(void **state)
{
struct ocf_request req = {};
print_test_description("Target part is disabled but has cachelines assigned.\n");
print_test_description("\tTrigger eviction and mark mapping error\n");
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
expect_function_call(__wrap_space_managment_evict_do);
will_return(__wrap_space_managment_evict_do, LOOKUP_MAPPED);
will_return(__wrap_ocf_part_is_enabled, false);
expect_function_call(__wrap_ocf_req_set_mapping_error);
assert_int_equal(ocf_prepare_clines_miss(&req, NULL), -OCF_ERR_NO_LOCK);
}
static void ocf_prepare_clines_miss_test03(void **state)
{
struct ocf_request req = {};
print_test_description("Target part is enabled but doesn't have enough space.\n");
print_test_description("\tEviction is ok and cachelines lock is acquired.\n");
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
expect_function_call(__wrap_space_managment_evict_do);
will_return(__wrap_space_managment_evict_do, LOOKUP_MAPPED);
will_return(__wrap_ocf_part_is_enabled, true);
expect_function_call(__wrap_ocf_engine_map);
will_return(__wrap_ocf_req_test_mapping_error, false);
will_return(__wrap_lock_clines, 0);
expect_function_call(__wrap_lock_clines);
assert_int_equal(ocf_prepare_clines_miss(&req, NULL), 0);
}
static void ocf_prepare_clines_miss_test04(void **state)
{
struct ocf_request req = {};
print_test_description("Target part is enabled but doesn't have enough space.\n");
print_test_description("\tEviction failed\n");
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
expect_function_call(__wrap_space_managment_evict_do);
will_return(__wrap_space_managment_evict_do, LOOKUP_MISS);
expect_function_call(__wrap_ocf_req_set_mapping_error);
assert_int_equal(ocf_prepare_clines_miss(&req, NULL), -OCF_ERR_NO_LOCK);
}
static void ocf_prepare_clines_miss_test05(void **state)
{
struct ocf_request req = {};
print_test_description("Target part is enabled but doesn't have enough space.\n");
print_test_description("Eviction is ok, but mapping failed.\n");
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
expect_function_call(__wrap_space_managment_evict_do);
will_return(__wrap_space_managment_evict_do, LOOKUP_HIT);
will_return(__wrap_ocf_part_is_enabled, true);
expect_function_call(__wrap_ocf_engine_map);
will_return(__wrap_ocf_req_test_mapping_error, true);
assert_int_equal(ocf_prepare_clines_miss(&req, NULL), -OCF_ERR_NO_LOCK);
}
static void ocf_prepare_clines_miss_test06(void **state)
{
struct ocf_request req = {};
print_test_description("Target part is enabled but doesn't have enough space.\n");
print_test_description("Eviction and mapping were ok, but failed to lock cachelines.\n");
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
expect_function_call(__wrap_space_managment_evict_do);
will_return(__wrap_space_managment_evict_do, LOOKUP_HIT);
will_return(__wrap_ocf_part_is_enabled, true);
expect_function_call(__wrap_ocf_engine_map);
will_return(__wrap_ocf_req_test_mapping_error, false);
expect_function_call(__wrap_lock_clines);
will_return(__wrap_lock_clines, -OCF_ERR_NO_LOCK);
expect_function_call(__wrap_ocf_req_set_mapping_error);
assert_int_equal(ocf_prepare_clines_miss(&req, NULL), -OCF_ERR_NO_LOCK);
}
static void ocf_prepare_clines_miss_test07(void **state)
{
struct ocf_request req = {};
print_test_description("Target part is enabled but doesn't have enough space.\n");
print_test_description("Eviction and mapping were ok, lock not acquired.\n");
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
will_return(__wrap_ocf_part_check_space, OCF_PART_IS_FULL);
expect_function_call(__wrap_space_managment_evict_do);
will_return(__wrap_space_managment_evict_do, LOOKUP_HIT);
will_return(__wrap_ocf_part_is_enabled, true);
expect_function_call(__wrap_ocf_engine_map);
will_return(__wrap_ocf_req_test_mapping_error, false);
expect_function_call(__wrap_lock_clines);
will_return(__wrap_lock_clines, OCF_LOCK_NOT_ACQUIRED);
assert_int_equal(ocf_prepare_clines_miss(&req, NULL), OCF_LOCK_NOT_ACQUIRED);
}
static void ocf_prepare_clines_miss_test08(void **state)
{
struct ocf_request req = {};
print_test_description("Target part is enabled has enough space.\n");
print_test_description("\tMapping and cacheline lock are both ok\n");
will_return(__wrap_ocf_part_check_space, OCF_PART_HAS_SPACE);
expect_function_call(__wrap_ocf_engine_map);
will_return(__wrap_ocf_req_test_mapping_error, false);
expect_function_call(__wrap_lock_clines);
will_return(__wrap_lock_clines, OCF_LOCK_ACQUIRED);
assert_int_equal(ocf_prepare_clines_miss(&req, NULL), OCF_LOCK_ACQUIRED);
}
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(ocf_prepare_clines_miss_test01),
cmocka_unit_test(ocf_prepare_clines_miss_test02),
cmocka_unit_test(ocf_prepare_clines_miss_test03),
cmocka_unit_test(ocf_prepare_clines_miss_test04),
cmocka_unit_test(ocf_prepare_clines_miss_test05),
cmocka_unit_test(ocf_prepare_clines_miss_test06),
cmocka_unit_test(ocf_prepare_clines_miss_test07),
cmocka_unit_test(ocf_prepare_clines_miss_test08)
};
print_message("Unit test for ocf_prepare_clines_miss\n");
return cmocka_run_group_tests(tests, NULL, NULL);
}

View File

@ -0,0 +1,516 @@
/*
* <tested_file_path>src/utils/utils_part.c</tested_file_path>
* <tested_function>ocf_part_evict_size</tested_function>
* <functions_to_leave>
* INSERT HERE LIST OF FUNCTIONS YOU WANT TO LEAVE
* ONE FUNCTION PER 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 "../ocf_cache_priv.h"
#include "../ocf_request.h"
#include "../metadata/metadata.h"
#include "../engine/cache_engine.h"
#include "../eviction/ops.h"
#include "utils_part.h"
#include "utils/utils_part.c/ocf_part_evict_size_generated_wraps.c"
uint32_t __wrap_ocf_part_get_max_size(ocf_cache_t cache,
struct ocf_user_part *target_part)
{
return mock();
}
uint32_t __wrap_ocf_engine_repart_count(struct ocf_request *req)
{
return mock();
}
uint32_t __wrap_ocf_engine_unmapped_count(struct ocf_request *req)
{
return mock();
}
uint32_t __wrap_ocf_part_get_occupancy(struct ocf_user_part *target_part)
{
return mock();
}
ocf_cache_line_t __wrap_ocf_freelist_num_free(ocf_freelist_t freelist)
{
return mock();
}
void __wrap_ocf_req_set_part_evict(struct ocf_request *req)
{
function_called();
}
void __wrap_ocf_req_clear_part_evict(struct ocf_request *req)
{
function_called();
}
static void ocf_part_evict_size_test01(void **state)
{
uint32_t max_part_size = 1024;
uint32_t cachelines_to_repart = 0;
uint32_t cachelines_to_map = 320;
uint32_t part_occupied_cachelines = 512;
uint32_t freelist_size = 500;
struct ocf_request req;
req.cache = test_malloc(sizeof(struct ocf_cache));
print_test_description("Enough free space available");
will_return(__wrap_ocf_part_get_max_size, max_part_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
will_return(__wrap_ocf_engine_repart_count, cachelines_to_repart);
will_return(__wrap_ocf_part_get_occupancy, part_occupied_cachelines);
// Enough free cachelines to map a whole request
will_return(__wrap_ocf_freelist_num_free, freelist_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
assert_int_equal(ocf_part_evict_size(&req), 0);
test_free(req.cache);
}
static void ocf_part_evict_size_test02(void **state)
{
uint32_t max_part_size = 1024;
uint32_t cachelines_to_repart = 0;
uint32_t cachelines_to_map = 320;
uint32_t part_occupied_cachelines = 960;
uint32_t freelist_size = 500;
struct ocf_request req;
req.cache = test_malloc(sizeof(struct ocf_cache));
uint32_t available_cachelines = max_part_size - part_occupied_cachelines;
uint32_t cachelines_to_evict = cachelines_to_map - available_cachelines;
print_test_description("Cache has enough free cachelines,"
" but target partition must be evicted");
will_return(__wrap_ocf_part_get_max_size, max_part_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
will_return(__wrap_ocf_engine_repart_count, cachelines_to_repart);
will_return(__wrap_ocf_part_get_occupancy, part_occupied_cachelines);
will_return(__wrap_ocf_freelist_num_free, freelist_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
expect_function_call(__wrap_ocf_req_set_part_evict);
assert_int_equal(ocf_part_evict_size(&req), cachelines_to_evict);
test_free(req.cache);
}
static void ocf_part_evict_size_test03(void **state)
{
uint32_t max_part_size = 1024;
uint32_t cachelines_to_repart = 320;
uint32_t cachelines_to_map = 0;
uint32_t part_occupied_cachelines = 512;
uint32_t freelist_size = 0;
struct ocf_request req;
req.cache = test_malloc(sizeof(struct ocf_cache));
uint32_t cachelines_to_evict = 0;
print_test_description("Only repart (no mapping). Freelist is empty but "
"space in a target part is availabe,");
print_test_description("\tso no cachelines should be "
" evcited from cache");
will_return(__wrap_ocf_part_get_max_size, max_part_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
will_return(__wrap_ocf_engine_repart_count, cachelines_to_repart);
will_return(__wrap_ocf_part_get_occupancy, part_occupied_cachelines);
will_return(__wrap_ocf_freelist_num_free, freelist_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
assert_int_equal(ocf_part_evict_size(&req), cachelines_to_evict);
test_free(req.cache);
}
static void ocf_part_evict_size_test04(void **state)
{
uint32_t max_part_size = 1024;
uint32_t cachelines_to_repart = 320;
uint32_t cachelines_to_map = 0;
uint32_t part_occupied_cachelines = 1100;
uint32_t freelist_size = 0;
struct ocf_request req;
req.cache = test_malloc(sizeof(struct ocf_cache));
uint32_t cachelines_debt = part_occupied_cachelines - max_part_size;
uint32_t cachelines_to_evict = cachelines_to_repart + cachelines_debt;
print_test_description("Only repart (no mapping). Freelist is empty and no"
" space in target part is availabe.");
print_test_description("\tEvict only from target partition");
will_return(__wrap_ocf_part_get_max_size, max_part_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
will_return(__wrap_ocf_engine_repart_count, cachelines_to_repart);
will_return(__wrap_ocf_part_get_occupancy, part_occupied_cachelines);
will_return(__wrap_ocf_freelist_num_free, freelist_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
expect_function_call(__wrap_ocf_req_set_part_evict);
assert_int_equal(ocf_part_evict_size(&req), cachelines_to_evict);
test_free(req.cache);
}
static void ocf_part_evict_size_test05(void **state)
{
uint32_t max_part_size = 1024;
uint32_t cachelines_to_repart = 0;
uint32_t cachelines_to_map = 320;
uint32_t part_occupied_cachelines = 960;
uint32_t freelist_size = 0;
struct ocf_request req;
req.cache = test_malloc(sizeof(struct ocf_cache));
uint32_t available_cachelines = max_part_size - part_occupied_cachelines;
uint32_t cachelines_to_evict = cachelines_to_map - available_cachelines;
print_test_description("Freelist is empty and no space in the target part "
"is available");
will_return(__wrap_ocf_part_get_max_size, max_part_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
will_return(__wrap_ocf_engine_repart_count, cachelines_to_repart);
will_return(__wrap_ocf_part_get_occupancy, part_occupied_cachelines);
will_return(__wrap_ocf_freelist_num_free, freelist_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
expect_function_call(__wrap_ocf_req_set_part_evict);
assert_int_equal(ocf_part_evict_size(&req), cachelines_to_evict);
test_free(req.cache);
}
static void ocf_part_evict_size_test06(void **state)
{
uint32_t max_part_size = 1024;
uint32_t cachelines_to_repart = 0;
uint32_t cachelines_to_map = 320;
uint32_t part_occupied_cachelines = 320;
uint32_t freelist_size = 0;
struct ocf_request req;
req.cache = test_malloc(sizeof(struct ocf_cache));
uint32_t available_cachelines = max_part_size - part_occupied_cachelines;
uint32_t cachelines_to_evict = cachelines_to_map;
print_test_description("Freelist is empty but target part has enough space");
will_return(__wrap_ocf_part_get_max_size, max_part_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
will_return(__wrap_ocf_engine_repart_count, cachelines_to_repart);
will_return(__wrap_ocf_part_get_occupancy, part_occupied_cachelines);
will_return(__wrap_ocf_freelist_num_free, freelist_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
expect_function_call(__wrap_ocf_req_clear_part_evict);
assert_int_equal(ocf_part_evict_size(&req), cachelines_to_evict);
test_free(req.cache);
}
static void ocf_part_evict_size_test07(void **state)
{
uint32_t max_part_size = 1024;
uint32_t cachelines_to_repart = 0;
uint32_t cachelines_to_map = 320;
uint32_t part_occupied_cachelines = 1280;
uint32_t freelist_size = 0;
struct ocf_request req;
req.cache = test_malloc(sizeof(struct ocf_cache));
uint32_t debt_cachelines = part_occupied_cachelines - max_part_size;
uint32_t cachelines_to_evict = cachelines_to_map + debt_cachelines;
print_test_description("Freelist is empty and part occupancy exceeded");
will_return(__wrap_ocf_part_get_max_size, max_part_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
will_return(__wrap_ocf_engine_repart_count, cachelines_to_repart);
will_return(__wrap_ocf_part_get_occupancy, part_occupied_cachelines);
will_return(__wrap_ocf_freelist_num_free, freelist_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
expect_function_call(__wrap_ocf_req_set_part_evict);
assert_int_equal(ocf_part_evict_size(&req),
(part_occupied_cachelines - max_part_size) + cachelines_to_map);
test_free(req.cache);
}
static void ocf_part_evict_size_test08(void **state)
{
uint32_t max_part_size = 1024;
uint32_t cachelines_to_repart = 320;
uint32_t cachelines_to_map = 0;
uint32_t part_occupied_cachelines = 1280;
uint32_t freelist_size = 0;
struct ocf_request req;
req.cache = test_malloc(sizeof(struct ocf_cache));
uint32_t debt_cachelines = part_occupied_cachelines - max_part_size;
uint32_t cachelines_to_evict = debt_cachelines + cachelines_to_repart;
print_test_description("Target part occupancy limit is exceeded during "
"repart");
will_return(__wrap_ocf_part_get_max_size, max_part_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
will_return(__wrap_ocf_engine_repart_count, cachelines_to_repart);
will_return(__wrap_ocf_part_get_occupancy, part_occupied_cachelines);
will_return(__wrap_ocf_freelist_num_free, freelist_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
expect_function_call(__wrap_ocf_req_set_part_evict);
assert_int_equal(ocf_part_evict_size(&req),
(part_occupied_cachelines - max_part_size) + cachelines_to_repart);
test_free(req.cache);
}
static void ocf_part_evict_size_test09(void **state)
{
uint32_t max_part_size = 1024;
uint32_t cachelines_to_repart = 320;
uint32_t cachelines_to_map = 0;
uint32_t part_occupied_cachelines = 320;
uint32_t freelist_size = 0;
struct ocf_request req;
req.cache = test_malloc(sizeof(struct ocf_cache));
uint32_t cachelines_to_evict = 0;
print_test_description("Repart while target part has enough of available "
"space");
will_return(__wrap_ocf_part_get_max_size, max_part_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
will_return(__wrap_ocf_engine_repart_count, cachelines_to_repart);
will_return(__wrap_ocf_part_get_occupancy, part_occupied_cachelines);
will_return(__wrap_ocf_freelist_num_free, freelist_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
assert_int_equal(ocf_part_evict_size(&req), cachelines_to_evict);
test_free(req.cache);
}
static void ocf_part_evict_size_test10(void **state)
{
uint32_t max_part_size = 1024;
uint32_t cachelines_to_repart = 0;
uint32_t cachelines_to_map = 320;
uint32_t part_occupied_cachelines = 320;
uint32_t freelist_size = 320;
struct ocf_request req;
req.cache = test_malloc(sizeof(struct ocf_cache));
uint32_t cachelines_to_evict = 0;
print_test_description("Enough of available cachelines in target part, "
"freelist has exactly required number of free cachelines");
will_return(__wrap_ocf_part_get_max_size, max_part_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
will_return(__wrap_ocf_engine_repart_count, cachelines_to_repart);
will_return(__wrap_ocf_part_get_occupancy, part_occupied_cachelines);
will_return(__wrap_ocf_freelist_num_free, freelist_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
assert_int_equal(ocf_part_evict_size(&req), cachelines_to_evict);
test_free(req.cache);
}
static void ocf_part_evict_size_test11(void **state)
{
uint32_t max_part_size = 1024;
uint32_t cachelines_to_repart = 320;
uint32_t cachelines_to_map = 0;
uint32_t part_occupied_cachelines = 384;
uint32_t freelist_size = 0;
struct ocf_request req;
req.cache = test_malloc(sizeof(struct ocf_cache));
uint32_t cachelines_to_evict = 0;
print_test_description("Number of cachelines to repart is equal to number "
"of cachelines available in the target partition");
will_return(__wrap_ocf_part_get_max_size, max_part_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
will_return(__wrap_ocf_engine_repart_count, cachelines_to_repart);
will_return(__wrap_ocf_part_get_occupancy, part_occupied_cachelines);
will_return(__wrap_ocf_freelist_num_free, freelist_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
assert_int_equal(ocf_part_evict_size(&req), cachelines_to_evict);
test_free(req.cache);
}
static void ocf_part_evict_size_test12(void **state)
{
uint32_t max_part_size = 0;
uint32_t cachelines_to_repart = 0;
uint32_t cachelines_to_map = 320;
uint32_t part_occupied_cachelines = 384;
uint32_t freelist_size = 0;
struct ocf_request req;
req.cache = test_malloc(sizeof(struct ocf_cache));
uint32_t cachelines_to_evict =
part_occupied_cachelines + cachelines_to_map;
print_test_description("Freelist IS empty. Max occupancy set to 0, but "
"some cachelines are still assigned to traget part - evict them");
will_return(__wrap_ocf_part_get_max_size, max_part_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
will_return(__wrap_ocf_engine_repart_count, cachelines_to_repart);
will_return(__wrap_ocf_part_get_occupancy, part_occupied_cachelines);
will_return(__wrap_ocf_freelist_num_free, freelist_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
expect_function_call(__wrap_ocf_req_set_part_evict);
assert_true(ocf_part_evict_size(&req) >= part_occupied_cachelines);
test_free(req.cache);
}
static void ocf_part_evict_size_test13(void **state)
{
uint32_t max_part_size = 0;
uint32_t cachelines_to_repart = 0;
uint32_t cachelines_to_map = 320;
uint32_t part_occupied_cachelines = 384;
uint32_t freelist_size = 1024;
struct ocf_request req;
req.cache = test_malloc(sizeof(struct ocf_cache));
uint32_t cachelines_to_evict =
part_occupied_cachelines + cachelines_to_map;
print_test_description("Freelist IS NOT empty. Max occupancy set to 0, but"
" some cachelines are still assigned to traget part - evict them");
will_return(__wrap_ocf_part_get_max_size, max_part_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
will_return(__wrap_ocf_engine_repart_count, cachelines_to_repart);
will_return(__wrap_ocf_part_get_occupancy, part_occupied_cachelines);
will_return(__wrap_ocf_freelist_num_free, freelist_size);
will_return(__wrap_ocf_engine_unmapped_count, cachelines_to_map);
expect_function_call(__wrap_ocf_req_set_part_evict);
assert_true(ocf_part_evict_size(&req) >= part_occupied_cachelines);
test_free(req.cache);
}
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(ocf_part_evict_size_test01),
cmocka_unit_test(ocf_part_evict_size_test02),
cmocka_unit_test(ocf_part_evict_size_test03),
cmocka_unit_test(ocf_part_evict_size_test04),
cmocka_unit_test(ocf_part_evict_size_test05),
cmocka_unit_test(ocf_part_evict_size_test06),
cmocka_unit_test(ocf_part_evict_size_test07),
cmocka_unit_test(ocf_part_evict_size_test08),
cmocka_unit_test(ocf_part_evict_size_test09),
cmocka_unit_test(ocf_part_evict_size_test10),
cmocka_unit_test(ocf_part_evict_size_test11),
cmocka_unit_test(ocf_part_evict_size_test12),
cmocka_unit_test(ocf_part_evict_size_test13)
};
print_message("Unit test for ocf_part_evict_size\n");
return cmocka_run_group_tests(tests, NULL, NULL);
}