Merge pull request #433 from arutk/fast_evict_master

Parallel eviction
This commit is contained in:
Michał Mielewczyk 2021-03-05 13:47:51 +01:00 committed by GitHub
commit 6a93303d26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 1047 additions and 512 deletions

View File

@ -398,7 +398,7 @@ static ocf_cache_line_t _acp_trylock_dirty(struct ocf_cache *cache,
if (info.status == LOOKUP_HIT && if (info.status == LOOKUP_HIT &&
metadata_test_dirty(cache, info.coll_idx)) { metadata_test_dirty(cache, info.coll_idx)) {
locked = ocf_cache_line_try_lock_rd( locked = ocf_cache_line_try_lock_rd(
cache->device->concurrency.cache_line, ocf_cache_line_concurrency(cache),
info.coll_idx); info.coll_idx);
} }
@ -474,7 +474,7 @@ static void _acp_flush_end(void *priv, int error)
for (i = 0; i < flush->size; i++) { for (i = 0; i < flush->size; i++) {
ocf_cache_line_unlock_rd( ocf_cache_line_unlock_rd(
cache->device->concurrency.cache_line, ocf_cache_line_concurrency(cache),
flush->data[i].cache_line); flush->data[i].cache_line);
ACP_DEBUG_END(acp, flush->data[i].cache_line); ACP_DEBUG_END(acp, flush->data[i].cache_line);
} }
@ -496,7 +496,8 @@ static void _acp_flush(struct acp_context *acp)
struct ocf_cleaner_attribs attribs = { struct ocf_cleaner_attribs attribs = {
.cmpl_context = acp, .cmpl_context = acp,
.cmpl_fn = _acp_flush_end, .cmpl_fn = _acp_flush_end,
.cache_line_lock = false, .lock_cacheline = false,
.lock_metadata = true,
.do_sort = false, .do_sort = false,
.io_queue = cache->cleaner.io_queue, .io_queue = cache->cleaner.io_queue,
}; };

View File

@ -682,8 +682,7 @@ static bool block_is_busy(struct ocf_cache *cache,
if (!cache->core[core_id].opened) if (!cache->core[core_id].opened)
return true; return true;
if (ocf_cache_line_is_used( if (ocf_cache_line_is_used(ocf_cache_line_concurrency(cache),
cache->device->concurrency.cache_line,
cache_line)) { cache_line)) {
return true; return true;
} }
@ -816,7 +815,8 @@ void cleaning_alru_perform_cleaning(ocf_cache_t cache, ocf_cleaner_end_t cmpl)
fctx->attribs.cmpl_context = fctx; fctx->attribs.cmpl_context = fctx;
fctx->attribs.cmpl_fn = alru_clean_complete; fctx->attribs.cmpl_fn = alru_clean_complete;
fctx->attribs.cache_line_lock = true; fctx->attribs.lock_cacheline = true;
fctx->attribs.lock_metadata = false;
fctx->attribs.do_sort = true; fctx->attribs.do_sort = true;
fctx->attribs.io_queue = cache->cleaner.io_queue; fctx->attribs.io_queue = cache->cleaner.io_queue;

View File

@ -710,9 +710,19 @@ static inline void __remove_line_from_waiters_list(struct ocf_cache_line_concurr
__unlock_waiters_list(c, line, flags); __unlock_waiters_list(c, line, flags);
} }
static inline bool _ocf_req_needs_cl_lock(struct ocf_request *req, unsigned entry)
{
/* Remapped cachelines are assigned cacheline lock individually
* during eviction
*/
return req->map[entry].status != LOOKUP_MISS &&
req->map[entry].status != LOOKUP_REMAPPED;
}
/* Try to read-lock request without adding waiters. Function should be called /* Try to read-lock request without adding waiters. Function should be called
* under read lock, multiple threads may attempt to acquire the lock * under read lock, multiple threads may attempt to acquire the lock
* concurrently. */ * concurrently.
*/
static int _ocf_req_trylock_rd(struct ocf_cache_line_concurrency *c, static int _ocf_req_trylock_rd(struct ocf_cache_line_concurrency *c,
struct ocf_request *req) struct ocf_request *req)
{ {
@ -725,8 +735,8 @@ static int _ocf_req_trylock_rd(struct ocf_cache_line_concurrency *c,
ENV_BUG_ON(env_atomic_read(&req->lock_remaining)); ENV_BUG_ON(env_atomic_read(&req->lock_remaining));
for (i = 0; i < req->core_line_count; i++) { for (i = 0; i < req->core_line_count; i++) {
if (req->map[i].status == LOOKUP_MISS) { if (!_ocf_req_needs_cl_lock(req, i)) {
/* MISS nothing to lock */ /* nothing to lock */
continue; continue;
} }
@ -750,6 +760,11 @@ static int _ocf_req_trylock_rd(struct ocf_cache_line_concurrency *c,
if (ret == OCF_LOCK_NOT_ACQUIRED) { if (ret == OCF_LOCK_NOT_ACQUIRED) {
/* Request is not locked, discard acquired locks */ /* Request is not locked, discard acquired locks */
for (; i >= 0; i--) { for (; i >= 0; i--) {
if (!_ocf_req_needs_cl_lock(req, i)) {
/* nothing to discard */
continue;
}
line = req->map[i].coll_idx; line = req->map[i].coll_idx;
if (req->map[i].rd_locked) { if (req->map[i].rd_locked) {
@ -781,8 +796,8 @@ static int _ocf_req_lock_rd(struct ocf_cache_line_concurrency *c,
for (i = 0; i < req->core_line_count; i++) { for (i = 0; i < req->core_line_count; i++) {
if (req->map[i].status == LOOKUP_MISS) { if (!_ocf_req_needs_cl_lock(req, i)) {
/* MISS nothing to lock */ /* nothing to lock */
env_atomic_dec(&req->lock_remaining); env_atomic_dec(&req->lock_remaining);
continue; continue;
} }
@ -808,6 +823,9 @@ static int _ocf_req_lock_rd(struct ocf_cache_line_concurrency *c,
err: err:
for (; i >= 0; i--) { for (; i >= 0; i--) {
if (!_ocf_req_needs_cl_lock(req, i))
continue;
__remove_line_from_waiters_list(c, req, i, req, __remove_line_from_waiters_list(c, req, i, req,
OCF_READ); OCF_READ);
} }
@ -847,8 +865,8 @@ static int _ocf_req_trylock_wr(struct ocf_cache_line_concurrency *c,
ENV_BUG_ON(env_atomic_read(&req->lock_remaining)); ENV_BUG_ON(env_atomic_read(&req->lock_remaining));
for (i = 0; i < req->core_line_count; i++) { for (i = 0; i < req->core_line_count; i++) {
if (req->map[i].status == LOOKUP_MISS) { if (!_ocf_req_needs_cl_lock(req, i)) {
/* MISS nothing to lock */ /* nothing to lock */
continue; continue;
} }
@ -872,6 +890,9 @@ static int _ocf_req_trylock_wr(struct ocf_cache_line_concurrency *c,
if (ret == OCF_LOCK_NOT_ACQUIRED) { if (ret == OCF_LOCK_NOT_ACQUIRED) {
/* Request is not locked, discard acquired locks */ /* Request is not locked, discard acquired locks */
for (; i >= 0; i--) { for (; i >= 0; i--) {
if (!_ocf_req_needs_cl_lock(req, i))
continue;
line = req->map[i].coll_idx; line = req->map[i].coll_idx;
if (req->map[i].wr_locked) { if (req->map[i].wr_locked) {
@ -904,8 +925,8 @@ static int _ocf_req_lock_wr(struct ocf_cache_line_concurrency *c,
for (i = 0; i < req->core_line_count; i++) { for (i = 0; i < req->core_line_count; i++) {
if (req->map[i].status == LOOKUP_MISS) { if (!_ocf_req_needs_cl_lock(req, i)) {
/* MISS nothing to lock */ /* nothing to lock */
env_atomic_dec(&req->lock_remaining); env_atomic_dec(&req->lock_remaining);
continue; continue;
} }
@ -931,6 +952,9 @@ static int _ocf_req_lock_wr(struct ocf_cache_line_concurrency *c,
err: err:
for (; i >= 0; i--) { for (; i >= 0; i--) {
if (!_ocf_req_needs_cl_lock(req, i))
continue;
__remove_line_from_waiters_list(c, req, i, req, __remove_line_from_waiters_list(c, req, i, req,
OCF_WRITE); OCF_WRITE);
} }
@ -968,15 +992,16 @@ void ocf_req_unlock_rd(struct ocf_cache_line_concurrency *c, struct ocf_request
OCF_DEBUG_RQ(req, "Unlock"); OCF_DEBUG_RQ(req, "Unlock");
for (i = 0; i < req->core_line_count; i++) { for (i = 0; i < req->core_line_count; i++) {
ENV_BUG_ON(req->map[i].wr_locked);
if (req->map[i].status == LOOKUP_MISS) { if (req->map[i].status == LOOKUP_MISS)
/* MISS nothing to lock */ continue;
if (!req->map[i].rd_locked)
continue; continue;
}
line = req->map[i].coll_idx; line = req->map[i].coll_idx;
ENV_BUG_ON(!req->map[i].rd_locked);
ENV_BUG_ON(line >= c->num_clines); ENV_BUG_ON(line >= c->num_clines);
__unlock_cache_line_rd(c, line); __unlock_cache_line_rd(c, line);
@ -995,15 +1020,16 @@ void ocf_req_unlock_wr(struct ocf_cache_line_concurrency *c, struct ocf_request
OCF_DEBUG_RQ(req, "Unlock"); OCF_DEBUG_RQ(req, "Unlock");
for (i = 0; i < req->core_line_count; i++) { for (i = 0; i < req->core_line_count; i++) {
ENV_BUG_ON(req->map[i].rd_locked);
if (req->map[i].status == LOOKUP_MISS) { if (req->map[i].status == LOOKUP_MISS)
/* MISS nothing to lock */ continue;
if (!req->map[i].wr_locked)
continue; continue;
}
line = req->map[i].coll_idx; line = req->map[i].coll_idx;
ENV_BUG_ON(!req->map[i].wr_locked);
ENV_BUG_ON(line >= c->num_clines); ENV_BUG_ON(line >= c->num_clines);
__unlock_cache_line_wr(c, line); __unlock_cache_line_wr(c, line);
@ -1022,11 +1048,8 @@ void ocf_req_unlock(struct ocf_cache_line_concurrency *c, struct ocf_request *re
OCF_DEBUG_RQ(req, "Unlock"); OCF_DEBUG_RQ(req, "Unlock");
for (i = 0; i < req->core_line_count; i++) { for (i = 0; i < req->core_line_count; i++) {
if (req->map[i].status == LOOKUP_MISS)
if (req->map[i].status == LOOKUP_MISS) {
/* MISS nothing to lock */
continue; continue;
}
line = req->map[i].coll_idx; line = req->map[i].coll_idx;
ENV_BUG_ON(line >= c->num_clines); ENV_BUG_ON(line >= c->num_clines);
@ -1039,8 +1062,6 @@ void ocf_req_unlock(struct ocf_cache_line_concurrency *c, struct ocf_request *re
} else if (req->map[i].wr_locked) { } else if (req->map[i].wr_locked) {
__unlock_cache_line_wr(c, line); __unlock_cache_line_wr(c, line);
req->map[i].wr_locked = false; req->map[i].wr_locked = false;
} else {
ENV_BUG();
} }
} }
} }
@ -1104,6 +1125,25 @@ bool ocf_cache_line_are_waiters(struct ocf_cache_line_concurrency *c,
return are; return are;
} }
/* NOTE: it is caller responsibility to assure that noone acquires
* a lock in background */
bool ocf_cache_line_is_locked_exclusively(struct ocf_cache *cache,
ocf_cache_line_t line)
{
struct ocf_cache_line_concurrency *c =
ocf_cache_line_concurrency(cache);
env_atomic *access = &c->access[line];
int val = env_atomic_read(access);
ENV_BUG_ON(val == OCF_CACHE_LINE_ACCESS_IDLE);
if (ocf_cache_line_are_waiters(c, line))
return false;
return val == OCF_CACHE_LINE_ACCESS_ONE_RD ||
val == OCF_CACHE_LINE_ACCESS_WR;
}
/* /*
* *
*/ */

View File

@ -147,6 +147,9 @@ bool ocf_cache_line_is_used(struct ocf_cache_line_concurrency *c,
bool ocf_cache_line_are_waiters(struct ocf_cache_line_concurrency *c, bool ocf_cache_line_are_waiters(struct ocf_cache_line_concurrency *c,
ocf_cache_line_t line); ocf_cache_line_t line);
bool ocf_cache_line_is_locked_exclusively(struct ocf_cache *cache,
ocf_cache_line_t line);
/** /**
* @brief un_lock request map info entry from from write or read access. * @brief un_lock request map info entry from from write or read access.
* *
@ -199,4 +202,16 @@ void ocf_cache_line_unlock_wr(struct ocf_cache_line_concurrency *c,
bool ocf_cache_line_try_lock_wr(struct ocf_cache_line_concurrency *c, bool ocf_cache_line_try_lock_wr(struct ocf_cache_line_concurrency *c,
ocf_cache_line_t line); ocf_cache_line_t line);
/**
* @brief Get cacheline concurrency context
*
* @param cache - cache instance
* @return cacheline concurrency context
*/
static inline struct ocf_cache_line_concurrency *
ocf_cache_line_concurrency(ocf_cache_t cache)
{
return cache->device->concurrency.cache_line;
}
#endif /* OCF_CONCURRENCY_H_ */ #endif /* OCF_CONCURRENCY_H_ */

View File

@ -423,6 +423,28 @@ void ocf_hb_id_prot_unlock_wr(struct ocf_metadata_lock *metadata_lock,
for (hash = _MIN_HASH(req); hash <= _MAX_HASH(req); \ for (hash = _MIN_HASH(req); hash <= _MAX_HASH(req); \
hash = _HASH_NEXT(req, hash)) hash = _HASH_NEXT(req, hash))
/* Returns true if the the given LBA (determined by core_id
* and core_line) resolves to a hash value that is within the
* set of hashes for the given request (i.e. after the request
* hash bucket are locked, the given core line is hash bucket
* locked as well).
*/
bool ocf_req_hash_in_range(struct ocf_request *req,
ocf_core_id_t core_id, uint64_t core_line)
{
ocf_cache_line_t hash = ocf_metadata_hash_func(
req->cache, core_line, core_id);
if (!_HAS_GAP(req)) {
return (hash >= _MIN_HASH(req) &&
hash <= _MAX_HASH(req));
}
return (hash >= _MIN_HASH(req) && hash <= _GAP_START(req)) ||
(hash > _GAP_START(req) + _GAP_VAL(req) &&
hash <= _MAX_HASH(req));
}
void ocf_hb_req_prot_lock_rd(struct ocf_request *req) void ocf_hb_req_prot_lock_rd(struct ocf_request *req)
{ {
ocf_cache_line_t hash; ocf_cache_line_t hash;

View File

@ -142,16 +142,24 @@ void ocf_hb_id_prot_lock_wr(struct ocf_metadata_lock *metadata_lock,
void ocf_hb_id_prot_unlock_wr(struct ocf_metadata_lock *metadata_lock, void ocf_hb_id_prot_unlock_wr(struct ocf_metadata_lock *metadata_lock,
unsigned lock_idx, ocf_cache_line_t hash); unsigned lock_idx, ocf_cache_line_t hash);
/* caller must hold global metadata read lock */ /* Caller must hold global metadata read lock when acquiring naked hash bucket
* lock.
*/
bool ocf_hb_cline_naked_trylock_rd(struct ocf_metadata_lock *metadata_lock, bool ocf_hb_cline_naked_trylock_rd(struct ocf_metadata_lock *metadata_lock,
uint32_t core_id, uint64_t core_line); uint32_t core_id, uint64_t core_line);
void ocf_hb_cline_naked_unlock_rd(struct ocf_metadata_lock *metadata_lock, void ocf_hb_cline_naked_unlock_rd(struct ocf_metadata_lock *metadata_lock,
uint32_t core_id, uint64_t core_line); uint32_t core_id, uint64_t core_line);
bool ocf_hb_cline_naked_trylock_wr(struct ocf_metadata_lock *metadata_lock, bool ocf_hb_cline_naked_trylock_wr(struct ocf_metadata_lock *metadata_lock,
uint32_t core_id, uint64_t core_line); uint32_t core_id, uint64_t core_line);
void ocf_hb_cline_naked_unlock_wr(struct ocf_metadata_lock *metadata_lock, void ocf_hb_cline_naked_unlock_wr(struct ocf_metadata_lock *metadata_lock,
uint32_t core_id, uint64_t core_line); uint32_t core_id, uint64_t core_line);
void ocf_hb_id_naked_lock_wr(struct ocf_metadata_lock *metadata_lock,
ocf_cache_line_t hash);
void ocf_hb_id_naked_unlock_wr(struct ocf_metadata_lock *metadata_lock,
ocf_cache_line_t hash);
bool ocf_req_hash_in_range(struct ocf_request *req,
ocf_core_id_t core_id, uint64_t core_line);
/* lock entire request in deadlock-free manner */ /* lock entire request in deadlock-free manner */
void ocf_hb_req_prot_lock_rd(struct ocf_request *req); void ocf_hb_req_prot_lock_rd(struct ocf_request *req);

View File

@ -11,7 +11,8 @@ struct ocf_request;
#define LOOKUP_HIT 5 #define LOOKUP_HIT 5
#define LOOKUP_MISS 6 #define LOOKUP_MISS 6
#define LOOKUP_MAPPED 8 #define LOOKUP_INSERTED 8
#define LOOKUP_REMAPPED 9
typedef enum { typedef enum {
/* modes inherited from user API */ /* modes inherited from user API */

View File

@ -64,7 +64,7 @@ static void _ocf_backfill_complete(struct ocf_request *req, int error)
ocf_core_stats_cache_error_update(req->core, OCF_WRITE); ocf_core_stats_cache_error_update(req->core, OCF_WRITE);
ocf_engine_invalidate(req); ocf_engine_invalidate(req);
} else { } else {
ocf_req_unlock(cache->device->concurrency.cache_line, req); ocf_req_unlock(ocf_cache_line_concurrency(cache), req);
/* put the request at the last point of the completion path */ /* put the request at the last point of the completion path */
ocf_req_put(req); ocf_req_put(req);

View File

@ -93,20 +93,67 @@ static inline int _ocf_engine_check_map_entry(struct ocf_cache *cache,
return -1; return -1;
} }
void ocf_engine_update_req_info(struct ocf_cache *cache, /* Returns true if core lines on index 'entry' and 'entry + 1' within the request
struct ocf_request *req, uint32_t entry) * are physically contiguous.
*/
static inline bool ocf_engine_clines_phys_cont(struct ocf_request *req,
uint32_t entry)
{
struct ocf_map_info *entry1, *entry2;
ocf_cache_line_t phys1, phys2;
entry1 = &req->map[entry];
entry2 = &req->map[entry + 1];
if (entry1->status == LOOKUP_MISS || entry2->status == LOOKUP_MISS)
return false;
phys1 = ocf_metadata_map_lg2phy(req->cache, entry1->coll_idx);
phys2 = ocf_metadata_map_lg2phy(req->cache, entry2->coll_idx);
return phys1 < phys2 && phys1 + 1 == phys2;
}
void ocf_engine_patch_req_info(struct ocf_cache *cache,
struct ocf_request *req, uint32_t idx)
{
struct ocf_map_info *entry = &req->map[idx];
ENV_BUG_ON(entry->status != LOOKUP_REMAPPED);
req->info.insert_no++;
if (req->part_id != ocf_metadata_get_partition_id(cache,
entry->coll_idx)) {
/*
* Need to move this cache line into other partition
*/
entry->re_part = true;
req->info.re_part_no++;
}
if (idx > 0 && ocf_engine_clines_phys_cont(req, idx - 1))
req->info.seq_no++;
if (idx + 1 < req->core_line_count &&
ocf_engine_clines_phys_cont(req, idx)) {
req->info.seq_no++;
}
}
static void ocf_engine_update_req_info(struct ocf_cache *cache,
struct ocf_request *req, uint32_t idx)
{ {
uint8_t start_sector = 0; uint8_t start_sector = 0;
uint8_t end_sector = ocf_line_end_sector(cache); uint8_t end_sector = ocf_line_end_sector(cache);
struct ocf_map_info *_entry = &(req->map[entry]); struct ocf_map_info *entry = &(req->map[idx]);
start_sector = ocf_map_line_start_sector(req, entry); start_sector = ocf_map_line_start_sector(req, idx);
end_sector = ocf_map_line_end_sector(req, entry); end_sector = ocf_map_line_end_sector(req, idx);
/* Handle return value */ /* Handle return value */
switch (_entry->status) { switch (entry->status) {
case LOOKUP_HIT: case LOOKUP_HIT:
if (metadata_test_valid_sec(cache, _entry->coll_idx, if (metadata_test_valid_sec(cache, entry->coll_idx,
start_sector, end_sector)) { start_sector, end_sector)) {
req->info.hit_no++; req->info.hit_no++;
} else { } else {
@ -114,29 +161,30 @@ void ocf_engine_update_req_info(struct ocf_cache *cache,
} }
/* Check request is dirty */ /* Check request is dirty */
if (metadata_test_dirty(cache, _entry->coll_idx)) { if (metadata_test_dirty(cache, entry->coll_idx)) {
req->info.dirty_any++; req->info.dirty_any++;
/* Check if cache line is fully dirty */ /* Check if cache line is fully dirty */
if (metadata_test_dirty_all_sec(cache, _entry->coll_idx, if (metadata_test_dirty_all_sec(cache, entry->coll_idx,
start_sector, end_sector)) start_sector, end_sector))
req->info.dirty_all++; req->info.dirty_all++;
} }
if (req->part_id != ocf_metadata_get_partition_id(cache, if (req->part_id != ocf_metadata_get_partition_id(cache,
_entry->coll_idx)) { entry->coll_idx)) {
/* /*
* Need to move this cache line into other partition * Need to move this cache line into other partition
*/ */
_entry->re_part = true; entry->re_part = true;
req->info.re_part_no++; req->info.re_part_no++;
} }
break; break;
case LOOKUP_MISS: case LOOKUP_INSERTED:
req->info.seq_req = false; case LOOKUP_REMAPPED:
req->info.insert_no++;
break; break;
case LOOKUP_MAPPED: case LOOKUP_MISS:
break; break;
default: default:
ENV_BUG(); ENV_BUG();
@ -144,17 +192,29 @@ void ocf_engine_update_req_info(struct ocf_cache *cache,
} }
/* Check if cache hit is sequential */ /* Check if cache hit is sequential */
if (req->info.seq_req && entry) { if (idx > 0 && ocf_engine_clines_phys_cont(req, idx - 1))
if (ocf_metadata_map_lg2phy(cache, req->info.seq_no++;
(req->map[entry - 1].coll_idx)) + 1 != }
ocf_metadata_map_lg2phy(cache,
_entry->coll_idx)) { static void ocf_engine_set_hot(struct ocf_request *req)
req->info.seq_req = false; {
struct ocf_cache *cache = req->cache;
struct ocf_map_info *entry;
uint8_t status;
unsigned i;
for (i = 0; i < req->core_line_count; i++) {
entry = &(req->map[i]);
status = entry->status;
if (status == LOOKUP_HIT || status == LOOKUP_INSERTED) {
/* Update eviction (LRU) */
ocf_eviction_set_hot_cache_line(cache, entry->coll_idx);
} }
} }
} }
void ocf_engine_traverse(struct ocf_request *req) static void ocf_engine_lookup(struct ocf_request *req)
{ {
uint32_t i; uint32_t i;
uint64_t core_line; uint64_t core_line;
@ -165,7 +225,6 @@ void ocf_engine_traverse(struct ocf_request *req)
OCF_DEBUG_TRACE(req->cache); OCF_DEBUG_TRACE(req->cache);
ocf_req_clear_info(req); ocf_req_clear_info(req);
req->info.seq_req = true;
for (i = 0, core_line = req->core_line_first; for (i = 0, core_line = req->core_line_first;
core_line <= req->core_line_last; core_line++, i++) { core_line <= req->core_line_last; core_line++, i++) {
@ -176,8 +235,6 @@ void ocf_engine_traverse(struct ocf_request *req)
core_line); core_line);
if (entry->status != LOOKUP_HIT) { if (entry->status != LOOKUP_HIT) {
req->info.seq_req = false;
/* There is miss then lookup for next map entry */ /* There is miss then lookup for next map entry */
OCF_DEBUG_PARAM(cache, "Miss, core line = %llu", OCF_DEBUG_PARAM(cache, "Miss, core line = %llu",
entry->core_line); entry->core_line);
@ -187,14 +244,16 @@ void ocf_engine_traverse(struct ocf_request *req)
OCF_DEBUG_PARAM(cache, "Hit, cache line %u, core line = %llu", OCF_DEBUG_PARAM(cache, "Hit, cache line %u, core line = %llu",
entry->coll_idx, entry->core_line); entry->coll_idx, entry->core_line);
/* Update eviction (LRU) */
ocf_eviction_set_hot_cache_line(cache, entry->coll_idx);
ocf_engine_update_req_info(cache, req, i); ocf_engine_update_req_info(cache, req, i);
} }
OCF_DEBUG_PARAM(cache, "Sequential - %s", req->info.seq_req ? OCF_DEBUG_PARAM(cache, "Sequential - %s", ocf_engine_is_sequential(req)
"Yes" : "No"); ? "Yes" : "No");
}
void ocf_engine_traverse(struct ocf_request *req)
{
ocf_engine_lookup(req);
ocf_engine_set_hot(req);
} }
int ocf_engine_check(struct ocf_request *req) int ocf_engine_check(struct ocf_request *req)
@ -209,7 +268,6 @@ int ocf_engine_check(struct ocf_request *req)
OCF_DEBUG_TRACE(req->cache); OCF_DEBUG_TRACE(req->cache);
ocf_req_clear_info(req); ocf_req_clear_info(req);
req->info.seq_req = true;
for (i = 0, core_line = req->core_line_first; for (i = 0, core_line = req->core_line_first;
core_line <= req->core_line_last; core_line++, i++) { core_line <= req->core_line_last; core_line++, i++) {
@ -217,14 +275,12 @@ int ocf_engine_check(struct ocf_request *req)
struct ocf_map_info *entry = &(req->map[i]); struct ocf_map_info *entry = &(req->map[i]);
if (entry->status == LOOKUP_MISS) { if (entry->status == LOOKUP_MISS) {
req->info.seq_req = false;
continue; continue;
} }
if (_ocf_engine_check_map_entry(cache, entry, core_id)) { if (_ocf_engine_check_map_entry(cache, entry, core_id)) {
/* Mapping is invalid */ /* Mapping is invalid */
entry->invalid = true; entry->invalid = true;
req->info.seq_req = false;
OCF_DEBUG_PARAM(cache, "Invalid, Cache line %u", OCF_DEBUG_PARAM(cache, "Invalid, Cache line %u",
entry->coll_idx); entry->coll_idx);
@ -240,38 +296,26 @@ int ocf_engine_check(struct ocf_request *req)
} }
} }
OCF_DEBUG_PARAM(cache, "Sequential - %s", req->info.seq_req ? OCF_DEBUG_PARAM(cache, "Sequential - %s", ocf_engine_is_sequential(req)
"Yes" : "No"); ? "Yes" : "No");
return result; return result;
} }
static void ocf_engine_map_cache_line(struct ocf_request *req, void ocf_map_cache_line(struct ocf_request *req,
uint64_t core_line, unsigned int hash_index, unsigned int idx, ocf_cache_line_t cache_line)
ocf_cache_line_t *cache_line)
{ {
struct ocf_cache *cache = req->cache; ocf_cache_t cache = req->cache;
ocf_core_id_t core_id = ocf_core_get_id(req->core); ocf_core_id_t core_id = ocf_core_get_id(req->core);
ocf_part_id_t part_id = req->part_id;
ocf_cleaning_t clean_policy_type; ocf_cleaning_t clean_policy_type;
unsigned int hash_index = req->map[idx].hash;
if (!ocf_freelist_get_cache_line(cache->freelist, cache_line)) { uint64_t core_line = req->core_line_first + idx;
ocf_req_set_mapping_error(req);
return;
}
ocf_metadata_add_to_partition(cache, part_id, *cache_line);
/* Add the block to the corresponding collision list */ /* Add the block to the corresponding collision list */
ocf_metadata_start_collision_shared_access(cache, *cache_line); ocf_metadata_start_collision_shared_access(cache, cache_line);
ocf_metadata_add_to_collision(cache, core_id, core_line, hash_index, ocf_metadata_add_to_collision(cache, core_id, core_line, hash_index,
*cache_line); cache_line);
ocf_metadata_end_collision_shared_access(cache, *cache_line); ocf_metadata_end_collision_shared_access(cache, cache_line);
ocf_eviction_init_cache_line(cache, *cache_line);
/* Update LRU:: Move this node to head of lru list. */
ocf_eviction_set_hot_cache_line(cache, *cache_line);
/* Update dirty cache-block list */ /* Update dirty cache-block list */
clean_policy_type = cache->conf_meta->cleaning_policy_type; clean_policy_type = cache->conf_meta->cleaning_policy_type;
@ -280,7 +324,29 @@ static void ocf_engine_map_cache_line(struct ocf_request *req,
if (cleaning_policy_ops[clean_policy_type].init_cache_block != NULL) if (cleaning_policy_ops[clean_policy_type].init_cache_block != NULL)
cleaning_policy_ops[clean_policy_type]. cleaning_policy_ops[clean_policy_type].
init_cache_block(cache, *cache_line); init_cache_block(cache, cache_line);
req->map[idx].coll_idx = cache_line;
}
static void ocf_engine_map_cache_line(struct ocf_request *req,
unsigned int idx)
{
struct ocf_cache *cache = req->cache;
ocf_cache_line_t cache_line;
if (!ocf_freelist_get_cache_line(cache->freelist, &cache_line)) {
ocf_req_set_mapping_error(req);
return;
}
ocf_metadata_add_to_partition(cache, req->part_id, cache_line);
ocf_map_cache_line(req, idx, cache_line);
/* Update LRU:: Move this node to head of lru list. */
ocf_eviction_init_cache_line(cache, cache_line);
} }
static void ocf_engine_map_hndl_error(struct ocf_cache *cache, static void ocf_engine_map_hndl_error(struct ocf_cache *cache,
@ -297,7 +363,8 @@ static void ocf_engine_map_hndl_error(struct ocf_cache *cache,
case LOOKUP_MISS: case LOOKUP_MISS:
break; break;
case LOOKUP_MAPPED: case LOOKUP_INSERTED:
case LOOKUP_REMAPPED:
OCF_DEBUG_RQ(req, "Canceling cache line %u", OCF_DEBUG_RQ(req, "Canceling cache line %u",
entry->coll_idx); entry->coll_idx);
@ -312,7 +379,6 @@ static void ocf_engine_map_hndl_error(struct ocf_cache *cache,
ocf_metadata_end_collision_shared_access(cache, ocf_metadata_end_collision_shared_access(cache,
entry->coll_idx); entry->coll_idx);
break; break;
default: default:
@ -328,7 +394,6 @@ static void ocf_engine_map(struct ocf_request *req)
uint32_t i; uint32_t i;
struct ocf_map_info *entry; struct ocf_map_info *entry;
uint64_t core_line; uint64_t core_line;
int status = LOOKUP_MAPPED;
ocf_core_id_t core_id = ocf_core_get_id(req->core); ocf_core_id_t core_id = ocf_core_get_id(req->core);
if (!ocf_engine_unmapped_count(req)) if (!ocf_engine_unmapped_count(req))
@ -341,7 +406,6 @@ static void ocf_engine_map(struct ocf_request *req)
} }
ocf_req_clear_info(req); ocf_req_clear_info(req);
req->info.seq_req = true;
OCF_DEBUG_TRACE(req->cache); OCF_DEBUG_TRACE(req->cache);
@ -351,30 +415,26 @@ static void ocf_engine_map(struct ocf_request *req)
ocf_engine_lookup_map_entry(cache, entry, core_id, core_line); ocf_engine_lookup_map_entry(cache, entry, core_id, core_line);
if (entry->status != LOOKUP_HIT) { /* attempt mapping only if no mapping error previously,
ocf_engine_map_cache_line(req, entry->core_line, * otherwise continue the loop anyway to have request fully
entry->hash, &entry->coll_idx); * traversed after map()
if (ocf_req_test_mapping_error(req)) {
/*
* Eviction error (mapping error), need to
* clean, return and do pass through
*/ */
OCF_DEBUG_RQ(req, "Eviction ERROR when mapping"); if (entry->status != LOOKUP_HIT &&
ocf_engine_map_hndl_error(cache, req); !ocf_req_test_mapping_error(req)) {
break; ocf_engine_map_cache_line(req, i);
if (!ocf_req_test_mapping_error(req))
entry->status = LOOKUP_INSERTED;
} }
entry->status = status; if (entry->status != LOOKUP_MISS)
} ocf_engine_update_req_info(cache, req, i);
OCF_DEBUG_PARAM(req->cache, OCF_DEBUG_PARAM(req->cache,
"%s, cache line %u, core line = %llu", "%s, cache line %u, core line = %llu",
entry->status == LOOKUP_HIT ? "Hit" : "Map", entry->status == LOOKUP_HIT ? "Hit" :
entry->status == LOOKUP_MISS : "Miss" :
"Insert",
entry->coll_idx, entry->core_line); entry->coll_idx, entry->core_line);
ocf_engine_update_req_info(cache, req, i);
} }
if (!ocf_req_test_mapping_error(req)) { if (!ocf_req_test_mapping_error(req)) {
@ -383,8 +443,8 @@ static void ocf_engine_map(struct ocf_request *req)
ocf_promotion_req_purge(cache->promotion_policy, req); ocf_promotion_req_purge(cache->promotion_policy, req);
} }
OCF_DEBUG_PARAM(req->cache, "Sequential - %s", req->info.seq_req ? OCF_DEBUG_PARAM(req->cache, "Sequential - %s",
"Yes" : "No"); ocf_engine_is_sequential(req) ? "Yes" : "No");
} }
static void _ocf_engine_clean_end(void *private_data, int error) static void _ocf_engine_clean_end(void *private_data, int error)
@ -396,7 +456,7 @@ static void _ocf_engine_clean_end(void *private_data, int error)
req->error |= error; req->error |= error;
/* End request and do not processing */ /* End request and do not processing */
ocf_req_unlock(req->cache->device->concurrency.cache_line, ocf_req_unlock(ocf_cache_line_concurrency(req->cache),
req); req);
/* Complete request */ /* Complete request */
@ -411,96 +471,117 @@ static void _ocf_engine_clean_end(void *private_data, int error)
} }
} }
static int lock_clines(struct ocf_request *req, static void ocf_engine_evict(struct ocf_request *req)
const struct ocf_engine_callbacks *engine_cbs)
{ {
enum ocf_engine_lock_type lock_type = engine_cbs->get_lock_type(req); int status;
struct ocf_cache_line_concurrency *c =
req->cache->device->concurrency.cache_line; status = space_managment_evict_do(req);
if (status == LOOKUP_MISS) {
/* mark error */
ocf_req_set_mapping_error(req);
/* unlock cachelines locked during eviction */
ocf_req_unlock(ocf_cache_line_concurrency(req->cache),
req);
/* request cleaning */
ocf_req_set_clean_eviction(req);
/* unmap inserted and replaced cachelines */
ocf_engine_map_hndl_error(req->cache, req);
}
return;
}
static int lock_clines(struct ocf_request *req)
{
struct ocf_cache_line_concurrency *c = ocf_cache_line_concurrency(req->cache);
enum ocf_engine_lock_type lock_type =
req->engine_cbs->get_lock_type(req);
switch (lock_type) { switch (lock_type) {
case ocf_engine_lock_write: case ocf_engine_lock_write:
return ocf_req_async_lock_wr(c, req, engine_cbs->resume); return ocf_req_async_lock_wr(c, req, req->engine_cbs->resume);
case ocf_engine_lock_read: case ocf_engine_lock_read:
return ocf_req_async_lock_rd(c, req, engine_cbs->resume); return ocf_req_async_lock_rd(c, req, req->engine_cbs->resume);
default: default:
return OCF_LOCK_ACQUIRED; return OCF_LOCK_ACQUIRED;
} }
} }
static inline int ocf_prepare_clines_miss(struct ocf_request *req, /* Attempt to map cachelines marked as LOOKUP_MISS by evicting from cache.
const struct ocf_engine_callbacks *engine_cbs) * Caller must assure that request map info is up to date (request
* is traversed).
*/
static inline int ocf_prepare_clines_evict(struct ocf_request *req)
{
int lock_status = -OCF_ERR_NO_LOCK;
bool part_has_space;
part_has_space = ocf_part_has_space(req);
if (!part_has_space) {
/* adding more cachelines to target partition would overflow
it - requesting eviction from target partition only */
ocf_req_set_part_evict(req);
} else {
/* evict from any partition */
ocf_req_clear_part_evict(req);
}
ocf_engine_evict(req);
if (!ocf_req_test_mapping_error(req)) {
ocf_promotion_req_purge(req->cache->promotion_policy, req);
lock_status = lock_clines(req);
if (lock_status < 0)
ocf_req_set_mapping_error(req);
}
return lock_status;
}
static inline int ocf_prepare_clines_miss(struct ocf_request *req)
{ {
int lock_status = -OCF_ERR_NO_LOCK; int lock_status = -OCF_ERR_NO_LOCK;
struct ocf_metadata_lock *metadata_lock = &req->cache->metadata.lock;
/* requests to disabled partitions go in pass-through */ /* requests to disabled partitions go in pass-through */
if (!ocf_part_is_enabled(&req->cache->user_parts[req->part_id])) { if (!ocf_part_is_enabled(&req->cache->user_parts[req->part_id])) {
ocf_req_set_mapping_error(req); ocf_req_set_mapping_error(req);
ocf_hb_req_prot_unlock_rd(req);
return lock_status; return lock_status;
} }
if (!ocf_part_has_space(req)) { if (!ocf_part_has_space(req)) {
ocf_hb_req_prot_unlock_rd(req); ocf_engine_lookup(req);
goto eviction; return ocf_prepare_clines_evict(req);
} }
/* Mapping must be performed holding (at least) hash-bucket write lock */
ocf_hb_req_prot_lock_upgrade(req);
ocf_engine_map(req); ocf_engine_map(req);
if (!ocf_req_test_mapping_error(req)) { if (!ocf_req_test_mapping_error(req)) {
lock_status = lock_clines(req, engine_cbs); lock_status = lock_clines(req);
if (lock_status < 0) { if (lock_status < 0) {
/* Mapping succeeded, but we failed to acquire cacheline lock. /* Mapping succeeded, but we failed to acquire cacheline lock.
* Don't try to evict, just return error to caller */ * Don't try to evict, just return error to caller */
ocf_req_set_mapping_error(req); ocf_req_set_mapping_error(req);
} }
ocf_hb_req_prot_unlock_wr(req);
return lock_status; return lock_status;
} }
ocf_hb_req_prot_unlock_wr(req); /* Request mapping failed, but it is fully traversed as a side
* effect of ocf_engine_map(), so no need to repeat the traversation
eviction: * before eviction.
ocf_metadata_start_exclusive_access(metadata_lock); * */
req->info.mapping_error = false;
/* repeat traversation to pick up latest metadata status */ return ocf_prepare_clines_evict(req);
ocf_engine_traverse(req);
if (!ocf_part_has_space(req))
ocf_req_set_part_evict(req);
else
ocf_req_clear_part_evict(req);
if (space_managment_evict_do(req->cache, req,
ocf_engine_unmapped_count(req)) == LOOKUP_MISS) {
ocf_req_set_mapping_error(req);
goto unlock;
} }
ocf_engine_map(req); int ocf_engine_prepare_clines(struct ocf_request *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)
{ {
struct ocf_user_part *part = &req->cache->user_parts[req->part_id];
bool mapped; bool mapped;
bool promote = true; bool promote = true;
int lock = -OCF_ERR_NO_LOCK; int lock = -OCF_ERR_NO_LOCK;
int result;
/* Calculate hashes for hash-bucket locking */ /* Calculate hashes for hash-bucket locking */
ocf_req_hash(req); ocf_req_hash(req);
@ -510,13 +591,14 @@ int ocf_engine_prepare_clines(struct ocf_request *req,
* not change during traversation */ * not change during traversation */
ocf_hb_req_prot_lock_rd(req); ocf_hb_req_prot_lock_rd(req);
/* Traverse to check if request is mapped fully */ /* check CL status */
ocf_engine_traverse(req); ocf_engine_lookup(req);
mapped = ocf_engine_is_mapped(req); mapped = ocf_engine_is_mapped(req);
if (mapped) { if (mapped) {
lock = lock_clines(req, engine_cbs); lock = lock_clines(req);
ocf_hb_req_prot_unlock_rd(req); ocf_hb_req_prot_unlock_rd(req);
ocf_engine_set_hot(req);
return lock; return lock;
} }
@ -529,7 +611,20 @@ int ocf_engine_prepare_clines(struct ocf_request *req,
return lock; return lock;
} }
return ocf_prepare_clines_miss(req, engine_cbs); /* Mapping must be performed holding (at least) hash-bucket write lock */
ocf_hb_req_prot_lock_upgrade(req);
result = ocf_prepare_clines_miss(req);
ocf_hb_req_prot_unlock_wr(req);
if (ocf_req_test_clean_eviction(req)) {
ocf_eviction_flush_dirty(req->cache, part, req->io_queue,
128);
}
if (!ocf_req_test_mapping_error(req))
ocf_engine_set_hot(req);
return result;
} }
static int _ocf_engine_clean_getter(struct ocf_cache *cache, static int _ocf_engine_clean_getter(struct ocf_cache *cache,
@ -562,7 +657,8 @@ void ocf_engine_clean(struct ocf_request *req)
{ {
/* Initialize attributes for cleaner */ /* Initialize attributes for cleaner */
struct ocf_cleaner_attribs attribs = { struct ocf_cleaner_attribs attribs = {
.cache_line_lock = false, .lock_cacheline = false,
.lock_metadata = false,
.cmpl_context = req, .cmpl_context = req,
.cmpl_fn = _ocf_engine_clean_end, .cmpl_fn = _ocf_engine_clean_end,
@ -706,8 +802,7 @@ static int _ocf_engine_refresh(struct ocf_request *req)
req->complete(req, req->error); req->complete(req, req->error);
/* Release WRITE lock of request */ /* Release WRITE lock of request */
ocf_req_unlock(req->cache->device->concurrency.cache_line, ocf_req_unlock(ocf_cache_line_concurrency(req->cache), req);
req);
/* Release OCF request */ /* Release OCF request */
ocf_req_put(req); ocf_req_put(req);

View File

@ -112,6 +112,9 @@ 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); return req->core_line_count - (req->info.hit_no + req->info.invalid_no);
} }
void ocf_map_cache_line(struct ocf_request *req,
unsigned int idx, ocf_cache_line_t cache_line);
/** /**
* @brief Get number of cache lines to repart * @brief Get number of cache lines to repart
* *
@ -124,6 +127,12 @@ static inline uint32_t ocf_engine_repart_count(struct ocf_request *req)
return req->info.re_part_no; return req->info.re_part_no;
} }
static inline uint32_t ocf_engine_is_sequential(struct ocf_request *req)
{
return req->info.hit_no + req->info.insert_no == req->core_line_count
&& req->info.seq_no == req->core_line_count - 1;
}
/** /**
* @brief Get number of IOs to perform cache read or write * @brief Get number of IOs to perform cache read or write
* *
@ -133,7 +142,7 @@ static inline uint32_t ocf_engine_repart_count(struct ocf_request *req)
*/ */
static inline uint32_t ocf_engine_io_count(struct ocf_request *req) static inline uint32_t ocf_engine_io_count(struct ocf_request *req)
{ {
return req->info.seq_req ? 1 : req->core_line_count; return ocf_engine_is_sequential(req) ? 1 : req->core_line_count;
} }
static inline static inline
@ -233,11 +242,10 @@ struct ocf_engine_callbacks
* @param req OCF request * @param req OCF request
* *
* @returns eviction status * @returns eviction status
* @retval LOOKUP_MAPPED successfully evicted required number of cachelines * @retval LOOKUP_INSERTED successfully evicted required number of cachelines
* @retval LOOKUP_MISS eviction failure * @retval LOOKUP_MISS eviction failure
*/ */
int ocf_engine_prepare_clines(struct ocf_request *req, int ocf_engine_prepare_clines(struct ocf_request *req);
const struct ocf_engine_callbacks *engine_cbs);
/** /**
* @brief Traverse OCF request (lookup cache) * @brief Traverse OCF request (lookup cache)
@ -262,12 +270,14 @@ void ocf_engine_traverse(struct ocf_request *req);
int ocf_engine_check(struct ocf_request *req); int ocf_engine_check(struct ocf_request *req);
/** /**
* @brief Update OCF request info * @brief Update OCF request info after evicting a cacheline
* *
* @param cache OCF cache instance
* @param req OCF request * @param req OCF request
* @param idx cacheline index within the request
*/ */
void ocf_engine_update_req_info(struct ocf_cache *cache, void ocf_engine_patch_req_info(struct ocf_cache *cache,
struct ocf_request *req, uint32_t entry); struct ocf_request *req, uint32_t idx);
/** /**
* @brief Update OCF request block statistics for an exported object * @brief Update OCF request block statistics for an exported object

View File

@ -147,7 +147,7 @@ static void _ocf_discard_step_complete(struct ocf_request *req, int error)
OCF_DEBUG_RQ(req, "Completion"); OCF_DEBUG_RQ(req, "Completion");
/* Release WRITE lock of request */ /* Release WRITE lock of request */
ocf_req_unlock_wr(req->cache->device->concurrency.cache_line, req); ocf_req_unlock_wr(ocf_cache_line_concurrency(req->cache), req);
if (req->error) { if (req->error) {
ocf_metadata_error(req->cache); ocf_metadata_error(req->cache);
@ -236,7 +236,7 @@ static int _ocf_discard_step(struct ocf_request *req)
if (ocf_engine_mapped_count(req)) { if (ocf_engine_mapped_count(req)) {
/* Some cache line are mapped, lock request for WRITE access */ /* Some cache line are mapped, lock request for WRITE access */
lock = ocf_req_async_lock_wr( lock = ocf_req_async_lock_wr(
cache->device->concurrency.cache_line, ocf_cache_line_concurrency(cache),
req, _ocf_discard_on_resume); req, _ocf_discard_on_resume);
} else { } else {
lock = OCF_LOCK_ACQUIRED; lock = OCF_LOCK_ACQUIRED;

View File

@ -46,8 +46,7 @@ static void _ocf_read_fast_complete(struct ocf_request *req, int error)
ocf_core_stats_cache_error_update(req->core, OCF_READ); ocf_core_stats_cache_error_update(req->core, OCF_READ);
ocf_engine_push_req_front_pt(req); ocf_engine_push_req_front_pt(req);
} else { } else {
ocf_req_unlock(req->cache->device->concurrency.cache_line, ocf_req_unlock(ocf_cache_line_concurrency(req->cache), req);
req);
/* Complete request */ /* Complete request */
req->complete(req, req->error); req->complete(req, req->error);
@ -132,7 +131,7 @@ int ocf_read_fast(struct ocf_request *req)
if (hit && part_has_space) { if (hit && part_has_space) {
ocf_io_start(&req->ioi.io); ocf_io_start(&req->ioi.io);
lock = ocf_req_async_lock_rd( lock = ocf_req_async_lock_rd(
req->cache->device->concurrency.cache_line, ocf_cache_line_concurrency(req->cache),
req, ocf_engine_on_resume); req, ocf_engine_on_resume);
} }
@ -204,7 +203,7 @@ int ocf_write_fast(struct ocf_request *req)
if (mapped && part_has_space) { if (mapped && part_has_space) {
ocf_io_start(&req->ioi.io); ocf_io_start(&req->ioi.io);
lock = ocf_req_async_lock_wr( lock = ocf_req_async_lock_wr(
req->cache->device->concurrency.cache_line, ocf_cache_line_concurrency(req->cache),
req, ocf_engine_on_resume); req, ocf_engine_on_resume);
} }

View File

@ -31,7 +31,7 @@ static void _ocf_invalidate_req(struct ocf_request *req, int error)
if (req->error) if (req->error)
ocf_engine_error(req, true, "Failed to flush metadata to cache"); ocf_engine_error(req, true, "Failed to flush metadata to cache");
ocf_req_unlock(req->cache->device->concurrency.cache_line, req); ocf_req_unlock_wr(ocf_cache_line_concurrency(req->cache), req);
/* Put OCF request - decrease reference counter */ /* Put OCF request - decrease reference counter */
ocf_req_put(req); ocf_req_put(req);

View File

@ -34,7 +34,7 @@ static void _ocf_read_pt_complete(struct ocf_request *req, int error)
/* Complete request */ /* Complete request */
req->complete(req, req->error); req->complete(req, req->error);
ocf_req_unlock_rd(req->cache->device->concurrency.cache_line, req); ocf_req_unlock_rd(ocf_cache_line_concurrency(req->cache), req);
/* Release OCF request */ /* Release OCF request */
ocf_req_put(req); ocf_req_put(req);

View File

@ -24,8 +24,8 @@
static void _ocf_read_generic_hit_complete(struct ocf_request *req, int error) static void _ocf_read_generic_hit_complete(struct ocf_request *req, int error)
{ {
struct ocf_cache_line_concurrency *c = struct ocf_cache_line_concurrency *c = ocf_cache_line_concurrency(
req->cache->device->concurrency.cache_line; req->cache);
if (error) if (error)
req->error |= error; req->error |= error;
@ -242,8 +242,9 @@ int ocf_read_generic(struct ocf_request *req)
/* Set resume call backs */ /* Set resume call backs */
req->io_if = &_io_if_read_generic_resume; req->io_if = &_io_if_read_generic_resume;
req->engine_cbs = &_rd_engine_callbacks;
lock = ocf_engine_prepare_clines(req, &_rd_engine_callbacks); lock = ocf_engine_prepare_clines(req);
if (!ocf_req_test_mapping_error(req)) { if (!ocf_req_test_mapping_error(req)) {
if (lock >= 0) { if (lock >= 0) {

View File

@ -60,7 +60,7 @@ static void _ocf_write_wb_io_flush_metadata(struct ocf_request *req, int error)
if (req->error) if (req->error)
ocf_engine_error(req, true, "Failed to write data to cache"); ocf_engine_error(req, true, "Failed to write data to cache");
ocf_req_unlock_wr(req->cache->device->concurrency.cache_line, req); ocf_req_unlock_wr(ocf_cache_line_concurrency(req->cache), req);
req->complete(req, req->error); req->complete(req, req->error);
@ -189,10 +189,11 @@ int ocf_write_wb(struct ocf_request *req)
/* Set resume io_if */ /* Set resume io_if */
req->io_if = &_io_if_wb_resume; req->io_if = &_io_if_wb_resume;
req->engine_cbs = &_wb_engine_callbacks;
/* TODO: Handle fits into dirty */ /* TODO: Handle fits into dirty */
lock = ocf_engine_prepare_clines(req, &_wb_engine_callbacks); lock = ocf_engine_prepare_clines(req);
if (!ocf_req_test_mapping_error(req)) { if (!ocf_req_test_mapping_error(req)) {
if (lock >= 0) { if (lock >= 0) {

View File

@ -25,7 +25,7 @@ static const struct ocf_io_if _io_if_wi_update_metadata = {
int _ocf_write_wi_next_pass(struct ocf_request *req) int _ocf_write_wi_next_pass(struct ocf_request *req)
{ {
ocf_req_unlock_wr(req->cache->device->concurrency.cache_line, req); ocf_req_unlock_wr(ocf_cache_line_concurrency(req->cache), req);
if (req->wi_second_pass) { if (req->wi_second_pass) {
req->complete(req, req->error); req->complete(req, req->error);
@ -75,7 +75,7 @@ static void _ocf_write_wi_io_flush_metadata(struct ocf_request *req, int error)
if (req->error) if (req->error)
ocf_engine_error(req, true, "Failed to write data to cache"); ocf_engine_error(req, true, "Failed to write data to cache");
ocf_req_unlock_wr(req->cache->device->concurrency.cache_line, req); ocf_req_unlock_wr(ocf_cache_line_concurrency(req->cache), req);
req->complete(req, req->error); req->complete(req, req->error);
@ -128,8 +128,7 @@ static void _ocf_write_wi_core_complete(struct ocf_request *req, int error)
OCF_DEBUG_RQ(req, "Completion"); OCF_DEBUG_RQ(req, "Completion");
if (req->error) { if (req->error) {
ocf_req_unlock_wr(req->cache->device->concurrency.cache_line, ocf_req_unlock_wr(ocf_cache_line_concurrency(req->cache), req);
req);
req->complete(req, req->error); req->complete(req, req->error);
@ -200,7 +199,7 @@ int ocf_write_wi(struct ocf_request *req)
if (ocf_engine_mapped_count(req)) { if (ocf_engine_mapped_count(req)) {
/* Some cache line are mapped, lock request for WRITE access */ /* Some cache line are mapped, lock request for WRITE access */
lock = ocf_req_async_lock_wr( lock = ocf_req_async_lock_wr(
req->cache->device->concurrency.cache_line, ocf_cache_line_concurrency(req->cache),
req, _ocf_write_wi_on_resume); req, _ocf_write_wi_on_resume);
} else { } else {
lock = OCF_LOCK_ACQUIRED; lock = OCF_LOCK_ACQUIRED;

View File

@ -33,7 +33,7 @@ static void ocf_read_wo_cache_complete(struct ocf_request *req, int error)
if (req->error) if (req->error)
ocf_engine_error(req, true, "Failed to read data from cache"); ocf_engine_error(req, true, "Failed to read data from cache");
ocf_req_unlock_rd(req->cache->device->concurrency.cache_line, req); ocf_req_unlock_rd(ocf_cache_line_concurrency(req->cache), req);
/* Complete request */ /* Complete request */
req->complete(req, req->error); req->complete(req, req->error);
@ -169,8 +169,7 @@ static void _ocf_read_wo_core_complete(struct ocf_request *req, int error)
if (!req->info.dirty_any || req->error) { if (!req->info.dirty_any || req->error) {
OCF_DEBUG_RQ(req, "Completion"); OCF_DEBUG_RQ(req, "Completion");
req->complete(req, req->error); req->complete(req, req->error);
ocf_req_unlock_rd(req->cache->device->concurrency.cache_line, ocf_req_unlock_rd(ocf_cache_line_concurrency(req->cache), req);
req);
ocf_req_put(req); ocf_req_put(req);
return; return;
} }
@ -238,7 +237,7 @@ int ocf_read_wo(struct ocf_request *req)
* lock request for READ access * lock request for READ access
*/ */
lock = ocf_req_async_lock_rd( lock = ocf_req_async_lock_rd(
req->cache->device->concurrency.cache_line, ocf_cache_line_concurrency(req->cache),
req, ocf_engine_on_resume); req, ocf_engine_on_resume);
} }

View File

@ -34,8 +34,7 @@ static void _ocf_write_wt_req_complete(struct ocf_request *req)
ocf_engine_invalidate(req); ocf_engine_invalidate(req);
} else { } else {
/* Unlock reqest from WRITE access */ /* Unlock reqest from WRITE access */
ocf_req_unlock_wr(req->cache->device->concurrency.cache_line, ocf_req_unlock_wr(ocf_cache_line_concurrency(req->cache), req);
req);
/* Complete request */ /* Complete request */
req->complete(req, req->info.core_error ? req->error : 0); req->complete(req, req->info.core_error ? req->error : 0);
@ -178,8 +177,9 @@ int ocf_write_wt(struct ocf_request *req)
/* Set resume io_if */ /* Set resume io_if */
req->io_if = &_io_if_wt_resume; req->io_if = &_io_if_wt_resume;
req->engine_cbs = &_wt_engine_callbacks;
lock = ocf_engine_prepare_clines(req, &_wt_engine_callbacks); lock = ocf_engine_prepare_clines(req);
if (!ocf_req_test_mapping_error(req)) { if (!ocf_req_test_mapping_error(req)) {
if (lock >= 0) { if (lock >= 0) {

View File

@ -31,7 +31,7 @@ static int ocf_zero_purge(struct ocf_request *req)
ocf_hb_req_prot_unlock_wr(req); /*- END Metadata WR access ---------*/ ocf_hb_req_prot_unlock_wr(req); /*- END Metadata WR access ---------*/
} }
ocf_req_unlock_wr(req->cache->device->concurrency.cache_line, req); ocf_req_unlock_wr(ocf_cache_line_concurrency(req->cache), req);
req->complete(req, req->error); req->complete(req, req->error);
@ -153,7 +153,7 @@ void ocf_engine_zero_line(struct ocf_request *req)
/* Some cache line are mapped, lock request for WRITE access */ /* Some cache line are mapped, lock request for WRITE access */
lock = ocf_req_async_lock_wr( lock = ocf_req_async_lock_wr(
req->cache->device->concurrency.cache_line, ocf_cache_line_concurrency(req->cache),
req, ocf_engine_on_resume); req, ocf_engine_on_resume);
if (lock >= 0) { if (lock >= 0) {

View File

@ -6,6 +6,7 @@
#include "eviction.h" #include "eviction.h"
#include "ops.h" #include "ops.h"
#include "../utils/utils_part.h" #include "../utils/utils_part.h"
#include "../engine/engine_common.h"
struct eviction_policy_ops evict_policy_ops[ocf_eviction_max] = { struct eviction_policy_ops evict_policy_ops[ocf_eviction_max] = {
[ocf_eviction_lru] = { [ocf_eviction_lru] = {
@ -16,12 +17,13 @@ struct eviction_policy_ops evict_policy_ops[ocf_eviction_max] = {
.init_evp = evp_lru_init_evp, .init_evp = evp_lru_init_evp,
.dirty_cline = evp_lru_dirty_cline, .dirty_cline = evp_lru_dirty_cline,
.clean_cline = evp_lru_clean_cline, .clean_cline = evp_lru_clean_cline,
.flush_dirty = evp_lru_clean,
.name = "lru", .name = "lru",
}, },
}; };
static uint32_t ocf_evict_calculate(ocf_cache_t cache, static uint32_t ocf_evict_calculate(ocf_cache_t cache,
struct ocf_user_part *part, uint32_t to_evict, bool roundup) struct ocf_user_part *part, uint32_t to_evict)
{ {
uint32_t curr_part_size = ocf_part_get_occupancy(part); uint32_t curr_part_size = ocf_part_get_occupancy(part);
@ -35,33 +37,34 @@ static uint32_t ocf_evict_calculate(ocf_cache_t cache,
return 0; return 0;
} }
if (roundup && to_evict < OCF_TO_EVICTION_MIN)
to_evict = OCF_TO_EVICTION_MIN;
if (to_evict > (curr_part_size - min_part_size)) if (to_evict > (curr_part_size - min_part_size))
to_evict = curr_part_size - min_part_size; to_evict = curr_part_size - min_part_size;
return to_evict; return to_evict;
} }
static inline uint32_t ocf_evict_part_do(ocf_cache_t cache, static inline uint32_t ocf_evict_part_do(struct ocf_request *req,
ocf_queue_t io_queue, const uint32_t evict_cline_no,
struct ocf_user_part *target_part) struct ocf_user_part *target_part)
{ {
uint32_t unmapped = ocf_engine_unmapped_count(req);
uint32_t to_evict = 0; uint32_t to_evict = 0;
if (!evp_lru_can_evict(cache)) if (!evp_lru_can_evict(req->cache))
return 0; return 0;
to_evict = ocf_evict_calculate(cache, target_part, evict_cline_no, to_evict = ocf_evict_calculate(req->cache, target_part, unmapped);
false);
return ocf_eviction_need_space(cache, io_queue, if (to_evict < unmapped) {
target_part, to_evict); /* cannot evict enough cachelines to map request,
so no purpose in evicting anything */
return 0;
}
return ocf_eviction_need_space(req->cache, req, target_part, to_evict);
} }
static inline uint32_t ocf_evict_partitions(ocf_cache_t cache, static inline uint32_t ocf_evict_partitions(ocf_cache_t cache,
ocf_queue_t io_queue, uint32_t evict_cline_no, struct ocf_request *req, uint32_t evict_cline_no,
bool overflown_only, int16_t max_priority) bool overflown_only, int16_t max_priority)
{ {
uint32_t to_evict = 0, evicted = 0; uint32_t to_evict = 0, evicted = 0;
@ -98,7 +101,7 @@ static inline uint32_t ocf_evict_partitions(ocf_cache_t cache,
} }
to_evict = ocf_evict_calculate(cache, part, to_evict = ocf_evict_calculate(cache, part,
evict_cline_no - evicted, true); evict_cline_no - evicted);
if (to_evict == 0) { if (to_evict == 0) {
/* No cache lines to evict for this partition */ /* No cache lines to evict for this partition */
continue; continue;
@ -107,8 +110,7 @@ static inline uint32_t ocf_evict_partitions(ocf_cache_t cache,
if (overflown_only) if (overflown_only)
to_evict = OCF_MIN(to_evict, overflow_size); to_evict = OCF_MIN(to_evict, overflow_size);
evicted += ocf_eviction_need_space(cache, io_queue, evicted += ocf_eviction_need_space(cache, req, part, to_evict);
part, to_evict);
if (evicted >= evict_cline_no) { if (evicted >= evict_cline_no) {
/* Evicted requested number of cache line, stop /* Evicted requested number of cache line, stop
@ -122,10 +124,12 @@ out:
return evicted; return evicted;
} }
static inline uint32_t ocf_evict_do(ocf_cache_t cache, static inline uint32_t ocf_evict_do(struct ocf_request *req)
ocf_queue_t io_queue, uint32_t evict_cline_no,
struct ocf_user_part *target_part)
{ {
ocf_cache_t cache = req->cache;
ocf_part_id_t target_part_id = req->part_id;
struct ocf_user_part *target_part = &cache->user_parts[target_part_id];
uint32_t evict_cline_no = ocf_engine_unmapped_count(req);
uint32_t evicted; uint32_t evicted;
/* First attempt to evict overflown partitions in order to /* First attempt to evict overflown partitions in order to
@ -134,7 +138,7 @@ static inline uint32_t ocf_evict_do(ocf_cache_t cache,
* free its cachelines regardless of destination partition * free its cachelines regardless of destination partition
* priority. */ * priority. */
evicted = ocf_evict_partitions(cache, io_queue, evict_cline_no, evicted = ocf_evict_partitions(cache, req, evict_cline_no,
true, OCF_IO_CLASS_PRIO_PINNED); true, OCF_IO_CLASS_PRIO_PINNED);
if (evicted >= evict_cline_no) if (evicted >= evict_cline_no)
return evicted; return evicted;
@ -142,35 +146,26 @@ static inline uint32_t ocf_evict_do(ocf_cache_t cache,
* partitions with priority <= target partition and attempt * partitions with priority <= target partition and attempt
* to evict from those. */ * to evict from those. */
evict_cline_no -= evicted; evict_cline_no -= evicted;
evicted += ocf_evict_partitions(cache, io_queue, evict_cline_no, evicted += ocf_evict_partitions(cache, req, evict_cline_no,
false, target_part->config->priority); false, target_part->config->priority);
return evicted; return evicted;
} }
int space_managment_evict_do(struct ocf_cache *cache, int space_managment_evict_do(struct ocf_request *req)
struct ocf_request *req, uint32_t evict_cline_no)
{ {
uint32_t needed = ocf_engine_unmapped_count(req);
uint32_t evicted; uint32_t evicted;
uint32_t free; struct ocf_user_part *req_part = &req->cache->user_parts[req->part_id];
struct ocf_user_part *req_part = &cache->user_parts[req->part_id];
if (ocf_req_part_evict(req)) { if (ocf_req_part_evict(req)) {
evicted = ocf_evict_part_do(cache, req->io_queue, evict_cline_no, evicted = ocf_evict_part_do(req, req_part);
req_part);
} else { } else {
free = ocf_freelist_num_free(cache->freelist); evicted = ocf_evict_do(req);
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) if (needed <= evicted)
return LOOKUP_MAPPED; return LOOKUP_INSERTED;
ocf_req_set_mapping_error(req);
return LOOKUP_MISS; return LOOKUP_MISS;
} }

View File

@ -10,7 +10,6 @@
#include "lru.h" #include "lru.h"
#include "lru_structs.h" #include "lru_structs.h"
#define OCF_TO_EVICTION_MIN 128UL
#define OCF_PENDING_EVICTION_LIMIT 512UL #define OCF_PENDING_EVICTION_LIMIT 512UL
#define OCF_NUM_EVICTION_LISTS 32 #define OCF_NUM_EVICTION_LISTS 32
@ -40,11 +39,9 @@ struct eviction_policy_ops {
void (*rm_cline)(ocf_cache_t cache, void (*rm_cline)(ocf_cache_t cache,
ocf_cache_line_t cline); ocf_cache_line_t cline);
bool (*can_evict)(ocf_cache_t cache); bool (*can_evict)(ocf_cache_t cache);
uint32_t (*req_clines)(ocf_cache_t cache, uint32_t (*req_clines)(struct ocf_request *req, struct ocf_user_part *part,
ocf_queue_t io_queue, struct ocf_user_part *part,
uint32_t cline_no); uint32_t cline_no);
void (*hot_cline)(ocf_cache_t cache, void (*hot_cline)(ocf_cache_t cache, ocf_cache_line_t cline);
ocf_cache_line_t cline);
void (*init_evp)(ocf_cache_t cache, struct ocf_user_part *part); void (*init_evp)(ocf_cache_t cache, struct ocf_user_part *part);
void (*dirty_cline)(ocf_cache_t cache, void (*dirty_cline)(ocf_cache_t cache,
struct ocf_user_part *part, struct ocf_user_part *part,
@ -52,6 +49,8 @@ struct eviction_policy_ops {
void (*clean_cline)(ocf_cache_t cache, void (*clean_cline)(ocf_cache_t cache,
struct ocf_user_part *part, struct ocf_user_part *part,
uint32_t cline_no); uint32_t cline_no);
void (*flush_dirty)(ocf_cache_t cache, struct ocf_user_part *part,
ocf_queue_t io_queue, uint32_t count);
const char *name; const char *name;
}; };
@ -64,8 +63,7 @@ extern struct eviction_policy_ops evict_policy_ops[ocf_eviction_max];
* 'LOOKUP_HIT' if evicted enough cachelines to serve @req * 'LOOKUP_HIT' if evicted enough cachelines to serve @req
* 'LOOKUP_MISS' otherwise * 'LOOKUP_MISS' otherwise
*/ */
int space_managment_evict_do(ocf_cache_t cache, int space_managment_evict_do(struct ocf_request *req);
struct ocf_request *req, uint32_t evict_cline_no);
int space_management_free(ocf_cache_t cache, uint32_t count); int space_management_free(ocf_cache_t cache, uint32_t count);

View File

@ -12,6 +12,7 @@
#include "../mngt/ocf_mngt_common.h" #include "../mngt/ocf_mngt_common.h"
#include "../engine/engine_zero.h" #include "../engine/engine_zero.h"
#include "../ocf_request.h" #include "../ocf_request.h"
#include "../engine/engine_common.h"
#define OCF_EVICTION_MAX_SCAN 1024 #define OCF_EVICTION_MAX_SCAN 1024
@ -261,7 +262,8 @@ void evp_lru_rm_cline(ocf_cache_t cache, ocf_cache_line_t cline)
} }
static inline void lru_iter_init(struct ocf_lru_iter *iter, ocf_cache_t cache, static inline void lru_iter_init(struct ocf_lru_iter *iter, ocf_cache_t cache,
struct ocf_user_part *part, uint32_t start_evp, bool clean) struct ocf_user_part *part, uint32_t start_evp, bool clean,
bool cl_lock_write, _lru_hash_locked_pfn hash_locked, void *context)
{ {
uint32_t i; uint32_t i;
@ -275,11 +277,47 @@ static inline void lru_iter_init(struct ocf_lru_iter *iter, ocf_cache_t cache,
iter->evp = (start_evp + OCF_NUM_EVICTION_LISTS - 1) % OCF_NUM_EVICTION_LISTS; iter->evp = (start_evp + OCF_NUM_EVICTION_LISTS - 1) % OCF_NUM_EVICTION_LISTS;
iter->num_avail_evps = OCF_NUM_EVICTION_LISTS; iter->num_avail_evps = OCF_NUM_EVICTION_LISTS;
iter->next_avail_evp = ((1ULL << OCF_NUM_EVICTION_LISTS) - 1); iter->next_avail_evp = ((1ULL << OCF_NUM_EVICTION_LISTS) - 1);
iter->clean = clean;
iter->cl_lock_write = cl_lock_write;
iter->hash_locked = hash_locked;
iter->context = context;
for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++)
iter->curr_cline[i] = evp_lru_get_list(part, i, clean)->tail; iter->curr_cline[i] = evp_lru_get_list(part, i, clean)->tail;
} }
static inline void lru_iter_cleaning_init(struct ocf_lru_iter *iter,
ocf_cache_t cache, struct ocf_user_part *part,
uint32_t start_evp)
{
/* Lock cachelines for read, non-exclusive access */
lru_iter_init(iter, cache, part, start_evp, false, false,
NULL, NULL);
}
static bool _evp_lru_evict_hash_locked(void *context,
ocf_core_id_t core_id, uint64_t core_line)
{
struct ocf_request *req = context;
return ocf_req_hash_in_range(req, core_id, core_line);
}
static inline void lru_iter_eviction_init(struct ocf_lru_iter *iter,
ocf_cache_t cache, struct ocf_user_part *part,
uint32_t start_evp, bool cl_lock_write,
struct ocf_request *req)
{
/* Lock hash buckets for write, cachelines according to user request,
* however exclusive cacheline access is needed even in case of read
* access. _evp_lru_evict_hash_locked tells whether given hash bucket
* is already locked as part of request hash locking (to avoid attempt
* to acquire the same hash bucket lock twice) */
lru_iter_init(iter, cache, part, start_evp, true, cl_lock_write,
_evp_lru_evict_hash_locked, req);
}
static inline uint32_t _lru_next_evp(struct ocf_lru_iter *iter) static inline uint32_t _lru_next_evp(struct ocf_lru_iter *iter)
{ {
unsigned increment; unsigned increment;
@ -292,6 +330,8 @@ static inline uint32_t _lru_next_evp(struct ocf_lru_iter *iter)
return iter->evp; return iter->evp;
} }
static inline bool _lru_evp_is_empty(struct ocf_lru_iter *iter) static inline bool _lru_evp_is_empty(struct ocf_lru_iter *iter)
{ {
return !(iter->next_avail_evp & (1ULL << (OCF_NUM_EVICTION_LISTS - 1))); return !(iter->next_avail_evp & (1ULL << (OCF_NUM_EVICTION_LISTS - 1)));
@ -308,144 +348,253 @@ static inline bool _lru_evp_all_empty(struct ocf_lru_iter *iter)
return iter->num_avail_evps == 0; return iter->num_avail_evps == 0;
} }
/* get next non-empty lru list if available */ static bool inline _lru_trylock_cacheline(struct ocf_lru_iter *iter,
static inline ocf_cache_line_t lru_iter_next(struct ocf_lru_iter *iter) ocf_cache_line_t cline)
{ {
struct lru_eviction_policy_meta *node; struct ocf_cache_line_concurrency *c =
uint32_t curr_evp; ocf_cache_line_concurrency(iter->cache);
ocf_cache_line_t ret;
return iter->cl_lock_write ?
ocf_cache_line_try_lock_wr(c, cline) :
ocf_cache_line_try_lock_rd(c, cline);
}
static void inline _lru_unlock_cacheline(struct ocf_lru_iter *iter,
ocf_cache_line_t cline)
{
struct ocf_cache_line_concurrency *c =
ocf_cache_line_concurrency(iter->cache);
if (iter->cl_lock_write)
ocf_cache_line_unlock_wr(c, cline);
else
ocf_cache_line_unlock_rd(c, cline);
}
static bool inline _lru_trylock_hash(struct ocf_lru_iter *iter,
ocf_core_id_t core_id, uint64_t core_line)
{
if (iter->hash_locked != NULL && iter->hash_locked(
iter->context,
core_id, core_line)) {
return true;
}
return ocf_hb_cline_naked_trylock_wr(
&iter->cache->metadata.lock,
core_id, core_line);
}
static void inline _lru_unlock_hash(struct ocf_lru_iter *iter,
ocf_core_id_t core_id, uint64_t core_line)
{
if (iter->hash_locked != NULL && iter->hash_locked(
iter->context,
core_id, core_line)) {
return;
}
ocf_hb_cline_naked_unlock_wr(
&iter->cache->metadata.lock,
core_id, core_line);
}
static bool inline _lru_iter_evition_lock(struct ocf_lru_iter *iter,
ocf_cache_line_t cache_line,
ocf_core_id_t *core_id, uint64_t *core_line)
{
if (!_lru_trylock_cacheline(iter, cache_line))
return false;
ocf_metadata_get_core_info(iter->cache, cache_line,
core_id, core_line);
if (!_lru_trylock_hash(iter, *core_id, *core_line)) {
_lru_unlock_cacheline(iter, cache_line);
return false;
}
if (!ocf_cache_line_is_locked_exclusively(iter->cache,
cache_line)) {
_lru_unlock_hash(iter, *core_id, *core_line);
_lru_unlock_cacheline(iter, cache_line);
return false;
}
return true;
}
/* Get next clean cacheline from tail of lru lists. Caller must not hold any
* eviction list lock. Returned cacheline is read or write locked, depending on
* iter->write_lock. Returned cacheline has corresponding metadata hash bucket
* locked. Cacheline is moved to the head of lru list before being returned */
static inline ocf_cache_line_t lru_iter_eviction_next(struct ocf_lru_iter *iter,
ocf_core_id_t *core_id, uint64_t *core_line)
{
uint32_t curr_evp;
ocf_cache_line_t cline;
ocf_cache_t cache = iter->cache;
struct ocf_user_part *part = iter->part;
struct ocf_lru_list *list;
do {
curr_evp = _lru_next_evp(iter); curr_evp = _lru_next_evp(iter);
while (iter->curr_cline[curr_evp] == end_marker) { ocf_metadata_eviction_wr_lock(&cache->metadata.lock, curr_evp);
if (!_lru_evp_is_empty(iter)) {
list = evp_lru_get_list(part, curr_evp, iter->clean);
cline = list->tail;
while (cline != end_marker && !_lru_iter_evition_lock(iter,
cline, core_id, core_line)) {
cline = ocf_metadata_get_eviction_policy(
iter->cache, cline)->lru.prev;
}
if (cline != end_marker) {
remove_lru_list(cache, list, cline);
add_lru_head(cache, list, cline);
balance_lru_list(cache, list);
}
ocf_metadata_eviction_wr_unlock(&cache->metadata.lock, curr_evp);
if (cline == end_marker && !_lru_evp_is_empty(iter)) {
/* mark list as empty */ /* mark list as empty */
_lru_evp_set_empty(iter); _lru_evp_set_empty(iter);
} }
if (_lru_evp_all_empty(iter)) { } while (cline == end_marker && !_lru_evp_all_empty(iter));
/* all lists empty */
return end_marker; return cline;
} }
/* Get next dirty cacheline from tail of lru lists. Caller must hold all
* eviction list locks during entire iteration proces. Returned cacheline
* is read or write locked, depending on iter->write_lock */
static inline ocf_cache_line_t lru_iter_cleaning_next(struct ocf_lru_iter *iter)
{
uint32_t curr_evp;
ocf_cache_line_t cline;
do {
curr_evp = _lru_next_evp(iter); curr_evp = _lru_next_evp(iter);
cline = iter->curr_cline[curr_evp];
while (cline != end_marker && !_lru_trylock_cacheline(iter,
cline)) {
cline = ocf_metadata_get_eviction_policy(
iter->cache, cline)->lru.prev;
}
if (cline != end_marker) {
iter->curr_cline[curr_evp] =
ocf_metadata_get_eviction_policy(
iter->cache , cline)->lru.prev;
} }
node = &ocf_metadata_get_eviction_policy(iter->cache, if (cline == end_marker && !_lru_evp_is_empty(iter)) {
iter->curr_cline[curr_evp])->lru; /* mark list as empty */
ret = iter->curr_cline[curr_evp]; _lru_evp_set_empty(iter);
iter->curr_cline[curr_evp] = node->prev; }
} while (cline == end_marker && !_lru_evp_all_empty(iter));
return ret; return cline;
} }
static void evp_lru_clean_end(void *private_data, int error) static void evp_lru_clean_end(void *private_data, int error)
{ {
struct ocf_lru_iter *iter = private_data; struct ocf_part_cleaning_ctx *ctx = private_data;
unsigned i;
ocf_refcnt_dec(&iter->part->cleaning); for (i = 0; i < OCF_EVICTION_CLEAN_SIZE; i++) {
if (ctx->cline[i] != end_marker)
ocf_cache_line_unlock_rd(ctx->cache->device->concurrency
.cache_line, ctx->cline[i]);
} }
static int evp_lru_clean_getter(ocf_cache_t cache, void *getter_context, ocf_refcnt_dec(&ctx->counter);
uint32_t item, ocf_cache_line_t *line) }
static int evp_lru_clean_get(ocf_cache_t cache, void *getter_context,
uint32_t idx, ocf_cache_line_t *line)
{ {
struct ocf_lru_iter *iter = getter_context; struct ocf_part_cleaning_ctx *ctx = getter_context;
ocf_cache_line_t cline;
while (true) { if (ctx->cline[idx] == end_marker)
cline = lru_iter_next(iter); return -1;
if (cline == end_marker) ENV_BUG_ON(!metadata_test_dirty(ctx->cache, ctx->cline[idx]));
break; *line = ctx->cline[idx];
/* Prevent evicting already locked items */
if (ocf_cache_line_is_used(
cache->device->concurrency.cache_line,
cline)) {
continue;
}
ENV_BUG_ON(!metadata_test_dirty(cache, cline));
*line = cline;
return 0; return 0;
} }
return -1; void evp_lru_clean(ocf_cache_t cache, struct ocf_user_part *part,
} ocf_queue_t io_queue, uint32_t count)
static void evp_lru_clean(ocf_cache_t cache, ocf_queue_t io_queue,
struct ocf_user_part *part, uint32_t count)
{ {
struct ocf_refcnt *counter = &part->cleaning; struct ocf_part_cleaning_ctx *ctx = &part->cleaning;
struct ocf_cleaner_attribs attribs = { struct ocf_cleaner_attribs attribs = {
.cache_line_lock = true, .lock_cacheline = false,
.lock_metadata = true,
.do_sort = true, .do_sort = true,
.cmpl_context = &part->eviction_clean_iter, .cmpl_context = &part->cleaning,
.cmpl_fn = evp_lru_clean_end, .cmpl_fn = evp_lru_clean_end,
.getter = evp_lru_clean_getter, .getter = evp_lru_clean_get,
.getter_context = &part->eviction_clean_iter, .getter_context = &part->cleaning,
.count = count > 32 ? 32 : count, .count = min(count, OCF_EVICTION_CLEAN_SIZE),
.io_queue = io_queue .io_queue = io_queue
}; };
ocf_cache_line_t *cline = part->cleaning.cline;
struct ocf_lru_iter iter;
unsigned evp;
int cnt; int cnt;
unsigned i;
unsigned lock_idx;
if (ocf_mngt_cache_is_locked(cache)) if (ocf_mngt_cache_is_locked(cache))
return; return;
cnt = ocf_refcnt_inc(&ctx->counter);
cnt = ocf_refcnt_inc(counter);
if (!cnt) { if (!cnt) {
/* cleaner disabled by management operation */ /* cleaner disabled by management operation */
return; return;
} }
if (cnt > 1) { if (cnt > 1) {
/* cleaning already running for this partition */ /* cleaning already running for this partition */
ocf_refcnt_dec(counter); ocf_refcnt_dec(&ctx->counter);
return; return;
} }
lru_iter_init(&part->eviction_clean_iter, cache, part, part->cleaning.cache = cache;
part->eviction_clean_iter.evp, false); evp = io_queue->eviction_idx++ % OCF_NUM_EVICTION_LISTS;
lock_idx = ocf_metadata_concurrency_next_idx(io_queue);
ocf_metadata_start_shared_access(&cache->metadata.lock, lock_idx);
OCF_METADATA_EVICTION_WR_LOCK_ALL();
lru_iter_cleaning_init(&iter, cache, part, evp);
i = 0;
while (i < OCF_EVICTION_CLEAN_SIZE) {
cline[i] = lru_iter_cleaning_next(&iter);
if (cline[i] == end_marker)
break;
i++;
}
while (i < OCF_EVICTION_CLEAN_SIZE)
cline[i++] = end_marker;
OCF_METADATA_EVICTION_WR_UNLOCK_ALL();
ocf_metadata_end_shared_access(&cache->metadata.lock, lock_idx);
ocf_cleaner_fire(cache, &attribs); ocf_cleaner_fire(cache, &attribs);
} }
static void evp_lru_zero_line_complete(struct ocf_request *ocf_req, int error)
{
env_atomic_dec(&ocf_req->cache->pending_eviction_clines);
}
static void evp_lru_zero_line(ocf_cache_t cache, ocf_queue_t io_queue,
ocf_cache_line_t line)
{
struct ocf_request *req;
ocf_core_id_t id;
uint64_t addr, core_line;
ocf_metadata_get_core_info(cache, line, &id, &core_line);
addr = core_line * ocf_line_size(cache);
req = ocf_req_new(io_queue, &cache->core[id], addr,
ocf_line_size(cache), OCF_WRITE);
if (!req)
return;
if (req->d2c) {
/* cache device is being detached */
ocf_req_put(req);
return;
}
req->info.internal = true;
req->complete = evp_lru_zero_line_complete;
env_atomic_inc(&cache->pending_eviction_clines);
ocf_engine_zero_line(req);
}
bool evp_lru_can_evict(ocf_cache_t cache) bool evp_lru_can_evict(ocf_cache_t cache)
{ {
if (env_atomic_read(&cache->pending_eviction_clines) >= if (env_atomic_read(&cache->pending_eviction_clines) >=
@ -456,74 +605,86 @@ bool evp_lru_can_evict(ocf_cache_t cache)
return true; return true;
} }
static bool dirty_pages_present(ocf_cache_t cache, struct ocf_user_part *part)
{
uint32_t i;
for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) {
if (evp_lru_get_list(part, i, false)->tail != end_marker)
return true;
}
return false;
}
/* the caller must hold the metadata lock */ /* the caller must hold the metadata lock */
uint32_t evp_lru_req_clines(ocf_cache_t cache, ocf_queue_t io_queue, uint32_t evp_lru_req_clines(struct ocf_request *req,
struct ocf_user_part *part, uint32_t cline_no) struct ocf_user_part *part, uint32_t cline_no)
{ {
struct ocf_lru_iter iter; struct ocf_lru_iter iter;
uint32_t i; uint32_t i;
ocf_cache_line_t cline; ocf_cache_line_t cline;
uint64_t core_line;
ocf_core_id_t core_id;
ocf_cache_t cache = req->cache;
bool cl_write_lock =
(req->engine_cbs->get_lock_type(req) == ocf_engine_lock_write);
unsigned evp;
unsigned req_idx = 0;
if (cline_no == 0) if (cline_no == 0)
return 0; return 0;
lru_iter_init(&iter, cache, part, part->next_eviction_list, true); if (unlikely(ocf_engine_unmapped_count(req) < cline_no)) {
ocf_cache_log(req->cache, log_err, "Not enough space in"
"request: unmapped %u, requested %u",
ocf_engine_unmapped_count(req),
cline_no);
ENV_BUG();
}
evp = req->io_queue->eviction_idx++ % OCF_NUM_EVICTION_LISTS;
lru_iter_eviction_init(&iter, cache, part, evp, cl_write_lock, req);
i = 0; i = 0;
while (i < cline_no) { while (i < cline_no) {
cline = lru_iter_next(&iter); if (!evp_lru_can_evict(cache))
break;
cline = lru_iter_eviction_next(&iter, &core_id, &core_line);
if (cline == end_marker) if (cline == end_marker)
break; break;
if (!evp_lru_can_evict(cache))
break;
/* Prevent evicting already locked items */
if (ocf_cache_line_is_used(
cache->device->concurrency.cache_line,
cline)) {
continue;
}
ENV_BUG_ON(metadata_test_dirty(cache, cline)); ENV_BUG_ON(metadata_test_dirty(cache, cline));
if (ocf_volume_is_atomic(&cache->device->volume)) { /* TODO: if atomic mode is restored, need to zero metadata
/* atomic cache, we have to trim cache lines before * before proceeding with cleaning (see version <= 20.12) */
* eviction
*/ /* find next unmapped cacheline in request */
evp_lru_zero_line(cache, io_queue, cline); while (req_idx + 1 < req->core_line_count &&
continue; req->map[req_idx].status != LOOKUP_MISS) {
req_idx++;
} }
ENV_BUG_ON(req->map[req_idx].status != LOOKUP_MISS);
ocf_metadata_start_collision_shared_access( ocf_metadata_start_collision_shared_access(
cache, cline); cache, cline);
set_cache_line_invalid_no_flush(cache, 0, metadata_clear_valid_sec(cache, cline, 0, ocf_line_end_sector(cache));
ocf_line_end_sector(cache), ocf_metadata_remove_from_collision(cache, cline, part->id);
cline);
ocf_metadata_end_collision_shared_access( ocf_metadata_end_collision_shared_access(
cache, cline); cache, cline);
_lru_unlock_hash(&iter, core_id, core_line);
env_atomic_dec(&req->core->runtime_meta->cached_clines);
env_atomic_dec(&req->core->runtime_meta->
part_counters[part->id].cached_clines);
ocf_map_cache_line(req, req_idx, cline);
req->map[req_idx].status = LOOKUP_REMAPPED;
ocf_engine_patch_req_info(cache, req, req_idx);
if (cl_write_lock)
req->map[req_idx].wr_locked = true;
else
req->map[req_idx].rd_locked = true;
++req_idx;
++i; ++i;
} }
part->next_eviction_list = iter.evp;
if (i < cline_no && dirty_pages_present(cache, part))
evp_lru_clean(cache, io_queue, part, cline_no - i);
/* Return number of clines that were really evicted */
return i; return i;
} }

View File

@ -9,11 +9,12 @@
#include "lru_structs.h" #include "lru_structs.h"
struct ocf_user_part; struct ocf_user_part;
struct ocf_request;
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); void evp_lru_rm_cline(struct ocf_cache *cache, ocf_cache_line_t cline);
bool evp_lru_can_evict(struct ocf_cache *cache); bool evp_lru_can_evict(struct ocf_cache *cache);
uint32_t evp_lru_req_clines(struct ocf_cache *cache, ocf_queue_t io_queue, uint32_t evp_lru_req_clines(struct ocf_request *req,
struct ocf_user_part *part, uint32_t cline_no); struct ocf_user_part *part, uint32_t cline_no);
void evp_lru_hot_cline(struct ocf_cache *cache, ocf_cache_line_t cline); void evp_lru_hot_cline(struct ocf_cache *cache, ocf_cache_line_t cline);
void evp_lru_init_evp(struct ocf_cache *cache, struct ocf_user_part *part); void evp_lru_init_evp(struct ocf_cache *cache, struct ocf_user_part *part);
@ -21,5 +22,6 @@ void evp_lru_dirty_cline(struct ocf_cache *cache, struct ocf_user_part *part,
uint32_t cline); uint32_t cline);
void evp_lru_clean_cline(struct ocf_cache *cache, struct ocf_user_part *part, void evp_lru_clean_cline(struct ocf_cache *cache, struct ocf_user_part *part,
uint32_t cline); uint32_t cline);
void evp_lru_clean(ocf_cache_t cache, struct ocf_user_part *part,
ocf_queue_t io_queue, uint32_t count);
#endif #endif

View File

@ -52,8 +52,8 @@ static inline bool ocf_eviction_can_evict(struct ocf_cache *cache)
return true; return true;
} }
static inline uint32_t ocf_eviction_need_space(struct ocf_cache *cache, static inline uint32_t ocf_eviction_need_space(ocf_cache_t cache,
ocf_queue_t io_queue, struct ocf_user_part *part, struct ocf_request *req, struct ocf_user_part *part,
uint32_t clines) uint32_t clines)
{ {
uint8_t type; uint8_t type;
@ -64,11 +64,7 @@ static inline uint32_t ocf_eviction_need_space(struct ocf_cache *cache,
ENV_BUG_ON(type >= ocf_eviction_max); ENV_BUG_ON(type >= ocf_eviction_max);
if (likely(evict_policy_ops[type].req_clines)) { if (likely(evict_policy_ops[type].req_clines)) {
/* result = evict_policy_ops[type].req_clines(req,
* This is called under METADATA WR lock. No need to get
* eviction lock.
*/
result = evict_policy_ops[type].req_clines(cache, io_queue,
part, clines); part, clines);
} }
@ -101,4 +97,18 @@ static inline void ocf_eviction_initialize(struct ocf_cache *cache,
} }
} }
static inline void ocf_eviction_flush_dirty(ocf_cache_t cache,
struct ocf_user_part *part, ocf_queue_t io_queue,
uint32_t count)
{
uint8_t type = cache->conf_meta->eviction_policy_type;
ENV_BUG_ON(type >= ocf_eviction_max);
if (likely(evict_policy_ops[type].flush_dirty)) {
evict_policy_ops[type].flush_dirty(cache, part, io_queue,
count);
}
}
#endif /* LAYER_EVICTION_POLICY_OPS_H_ */ #endif /* LAYER_EVICTION_POLICY_OPS_H_ */

View File

@ -52,7 +52,7 @@ int ocf_metadata_actor(struct ocf_cache *cache,
uint64_t start_line, end_line; uint64_t start_line, end_line;
int ret = 0; int ret = 0;
struct ocf_cache_line_concurrency *c = struct ocf_cache_line_concurrency *c =
cache->device->concurrency.cache_line; ocf_cache_line_concurrency(cache);
start_line = ocf_bytes_2_lines(cache, start_byte); start_line = ocf_bytes_2_lines(cache, start_byte);
end_line = ocf_bytes_2_lines(cache, end_byte); end_line = ocf_bytes_2_lines(cache, end_byte);

View File

@ -33,9 +33,13 @@ struct ocf_user_part_runtime {
struct cleaning_policy cleaning; struct cleaning_policy cleaning;
}; };
typedef bool ( *_lru_hash_locked_pfn)(void *context,
ocf_core_id_t core_id, uint64_t core_line);
/* Iterator state, visiting all eviction lists within a partition /* Iterator state, visiting all eviction lists within a partition
in round robin order */ in round robin order */
struct ocf_lru_iter { struct ocf_lru_iter
{
/* cache object */ /* cache object */
ocf_cache_t cache; ocf_cache_t cache;
/* target partition */ /* target partition */
@ -49,16 +53,30 @@ struct ocf_lru_iter {
uint32_t num_avail_evps; uint32_t num_avail_evps;
/* current eviction list index */ /* current eviction list index */
uint32_t evp; uint32_t evp;
/* callback to determine whether given hash bucket is already
* locked by the caller */
_lru_hash_locked_pfn hash_locked;
/* hash_locked private data */
void *context;
/* 1 if iterating over clean lists, 0 if over dirty */
bool clean : 1;
/* 1 if cacheline is to be locked for write, 0 if for read*/
bool cl_lock_write : 1;
};
#define OCF_EVICTION_CLEAN_SIZE 32U
struct ocf_part_cleaning_ctx {
ocf_cache_t cache;
struct ocf_refcnt counter;
ocf_cache_line_t cline[OCF_EVICTION_CLEAN_SIZE];
}; };
struct ocf_user_part { struct ocf_user_part {
struct ocf_user_part_config *config; struct ocf_user_part_config *config;
struct ocf_user_part_runtime *runtime; struct ocf_user_part_runtime *runtime;
struct ocf_refcnt cleaning;
ocf_part_id_t id; ocf_part_id_t id;
struct ocf_part_cleaning_ctx cleaning;
struct ocf_lru_iter eviction_clean_iter;
uint32_t next_eviction_list;
struct ocf_lst_entry lst_valid; struct ocf_lst_entry lst_valid;
}; };

View File

@ -169,7 +169,7 @@ static void __init_partitions(ocf_cache_t cache)
/* Add other partition to the cache and make it as dummy */ /* Add other partition to the cache and make it as dummy */
for (i_part = 0; i_part < OCF_IO_CLASS_MAX; i_part++) { for (i_part = 0; i_part < OCF_IO_CLASS_MAX; i_part++) {
ocf_refcnt_freeze(&cache->user_parts[i_part].cleaning); ocf_refcnt_freeze(&cache->user_parts[i_part].cleaning.counter);
if (i_part == PARTITION_DEFAULT) if (i_part == PARTITION_DEFAULT)
continue; continue;

View File

@ -81,7 +81,7 @@ void cache_mngt_core_deinit_attached_meta(ocf_core_t core)
} }
if (!ocf_cache_line_try_lock_wr( if (!ocf_cache_line_try_lock_wr(
cache->device->concurrency.cache_line, ocf_cache_line_concurrency(cache),
curr_cline)) { curr_cline)) {
break; break;
} }
@ -90,7 +90,8 @@ void cache_mngt_core_deinit_attached_meta(ocf_core_t core)
ocf_purge_cleaning_policy(cache, curr_cline); ocf_purge_cleaning_policy(cache, curr_cline);
ocf_metadata_sparse_cache_line(cache, curr_cline); ocf_metadata_sparse_cache_line(cache, curr_cline);
ocf_cache_line_unlock_wr(cache->device->concurrency.cache_line, ocf_cache_line_unlock_wr(
ocf_cache_line_concurrency(cache),
curr_cline); curr_cline);
if (prev_cline != cache->device->collision_table_entries) if (prev_cline != cache->device->collision_table_entries)

View File

@ -438,7 +438,8 @@ static void _ocf_mngt_flush_container(
req->priv = fc; req->priv = fc;
fc->req = req; fc->req = req;
fc->attribs.cache_line_lock = true; fc->attribs.lock_cacheline = true;
fc->attribs.lock_metadata = false;
fc->attribs.cmpl_context = fc; fc->attribs.cmpl_context = fc;
fc->attribs.cmpl_fn = _ocf_mngt_flush_portion_end; fc->attribs.cmpl_fn = _ocf_mngt_flush_portion_end;
fc->attribs.io_queue = cache->mngt_queue; fc->attribs.io_queue = cache->mngt_queue;

View File

@ -21,6 +21,9 @@ struct ocf_queue {
/* per-queue free running global metadata lock index */ /* per-queue free running global metadata lock index */
unsigned lock_idx; unsigned lock_idx;
/* per-queue free running eviction list index */
unsigned eviction_idx;
/* Tracing reference counter */ /* Tracing reference counter */
env_atomic64 trace_ref_cntr; env_atomic64 trace_ref_cntr;

View File

@ -18,6 +18,8 @@ struct ocf_req_info {
unsigned int hit_no; unsigned int hit_no;
unsigned int invalid_no; unsigned int invalid_no;
unsigned int re_part_no; unsigned int re_part_no;
unsigned int seq_no;
unsigned int insert_no;
uint32_t dirty_all; uint32_t dirty_all;
/*!< Number of dirty line in request*/ /*!< Number of dirty line in request*/
@ -25,15 +27,15 @@ struct ocf_req_info {
uint32_t dirty_any; uint32_t dirty_any;
/*!< Indicates that at least one request is dirty */ /*!< Indicates that at least one request is dirty */
uint32_t seq_req : 1;
/*!< Sequential cache request flag. */
uint32_t flush_metadata : 1; uint32_t flush_metadata : 1;
/*!< This bit tells if metadata flushing is required */ /*!< This bit tells if metadata flushing is required */
uint32_t mapping_error : 1; uint32_t mapping_error : 1;
/*!< Core lines in this request were not mapped into cache */ /*!< Core lines in this request were not mapped into cache */
uint32_t clean_eviction : 1;
/*!< Eviction failed, need to request cleaning */
uint32_t core_error : 1; uint32_t core_error : 1;
/*!< Error occured during I/O on core device */ /*!< Error occured during I/O on core device */
@ -104,6 +106,9 @@ struct ocf_request {
struct ocf_io_internal ioi; struct ocf_io_internal ioi;
/*!< OCF IO associated with request */ /*!< OCF IO associated with request */
const struct ocf_engine_callbacks *engine_cbs;
/*!< Engine owning the request */
env_atomic ref_count; env_atomic ref_count;
/*!< Reference usage count, once OCF request reaches zero it /*!< Reference usage count, once OCF request reaches zero it
* will be de-initialed. Get/Put method are intended to modify * will be de-initialed. Get/Put method are intended to modify
@ -395,6 +400,16 @@ static inline bool ocf_req_test_mapping_error(struct ocf_request *req)
return req->info.mapping_error; return req->info.mapping_error;
} }
static inline void ocf_req_set_clean_eviction(struct ocf_request *req)
{
req->info.clean_eviction = true;
}
static inline bool ocf_req_test_clean_eviction(struct ocf_request *req)
{
return req->info.clean_eviction;
}
/** /**
* @brief Return OCF request reference count * @brief Return OCF request reference count
* *

View File

@ -9,8 +9,7 @@
#include "ocf/ocf_debug.h" #include "ocf/ocf_debug.h"
#include "utils/utils_cache_line.h" #include "utils/utils_cache_line.h"
#define SEQ_CUTOFF_FULL_MARGIN \ #define SEQ_CUTOFF_FULL_MARGIN OCF_PENDING_EVICTION_LIMIT
(OCF_TO_EVICTION_MIN + OCF_PENDING_EVICTION_LIMIT)
static inline bool ocf_seq_cutoff_is_on(ocf_cache_t cache, static inline bool ocf_seq_cutoff_is_on(ocf_cache_t cache,
struct ocf_request *req) struct ocf_request *req)

View File

@ -44,8 +44,7 @@ static void __set_cache_line_invalid(struct ocf_cache *cache, uint8_t start_bit,
* only valid bits * only valid bits
*/ */
if (!is_valid && !ocf_cache_line_are_waiters( if (!is_valid && !ocf_cache_line_are_waiters(
cache->device->concurrency.cache_line, ocf_cache_line_concurrency(cache), line)) {
line)) {
ocf_purge_eviction_policy(cache, line); ocf_purge_eviction_policy(cache, line);
ocf_metadata_remove_cache_line(cache, line); ocf_metadata_remove_cache_line(cache, line);
} }

View File

@ -12,6 +12,7 @@
#include "utils_part.h" #include "utils_part.h"
#include "utils_io.h" #include "utils_io.h"
#include "utils_cache_line.h" #include "utils_cache_line.h"
#include "../ocf_queue_priv.h"
#define OCF_UTILS_CLEANER_DEBUG 0 #define OCF_UTILS_CLEANER_DEBUG 0
@ -46,7 +47,7 @@ static struct ocf_request *_ocf_cleaner_alloc_req(struct ocf_cache *cache,
return NULL; return NULL;
req->info.internal = true; req->info.internal = true;
req->info.cleaner_cache_line_lock = attribs->cache_line_lock; req->info.cleaner_cache_line_lock = attribs->lock_cacheline;
/* Allocate pages for cleaning IO */ /* Allocate pages for cleaning IO */
req->data = ctx_data_alloc(cache->owner, req->data = ctx_data_alloc(cache->owner,
@ -213,8 +214,7 @@ static int _ocf_cleaner_cache_line_lock(struct ocf_request *req)
OCF_DEBUG_TRACE(req->cache); OCF_DEBUG_TRACE(req->cache);
return ocf_req_async_lock_rd( return ocf_req_async_lock_rd(ocf_cache_line_concurrency(req->cache),
req->cache->device->concurrency.cache_line,
req, _ocf_cleaner_on_resume); req, _ocf_cleaner_on_resume);
} }
@ -323,7 +323,6 @@ static int _ocf_cleaner_update_metadata(struct ocf_request *req)
OCF_DEBUG_TRACE(req->cache); OCF_DEBUG_TRACE(req->cache);
ocf_metadata_start_exclusive_access(&cache->metadata.lock);
/* Update metadata */ /* Update metadata */
for (i = 0; i < req->core_line_count; i++, iter++) { for (i = 0; i < req->core_line_count; i++, iter++) {
if (iter->status == LOOKUP_MISS) if (iter->status == LOOKUP_MISS)
@ -336,22 +335,29 @@ static int _ocf_cleaner_update_metadata(struct ocf_request *req)
cache_line = iter->coll_idx; cache_line = iter->coll_idx;
if (!metadata_test_dirty(cache, cache_line)) ocf_hb_cline_prot_lock_wr(&cache->metadata.lock,
continue; req->lock_idx, req->map[i].core_id,
req->map[i].core_line);
if (metadata_test_dirty(cache, cache_line)) {
ocf_metadata_get_core_and_part_id(cache, cache_line, ocf_metadata_get_core_and_part_id(cache, cache_line,
&core_id, &req->part_id); &core_id, &req->part_id);
req->core = &cache->core[core_id]; req->core = &cache->core[core_id];
ocf_metadata_start_collision_shared_access(cache, cache_line); ocf_metadata_start_collision_shared_access(cache,
set_cache_line_clean(cache, 0, ocf_line_end_sector(cache), req, cache_line);
i); set_cache_line_clean(cache, 0,
ocf_metadata_end_collision_shared_access(cache, cache_line); ocf_line_end_sector(cache), req, i);
ocf_metadata_end_collision_shared_access(cache,
cache_line);
}
ocf_hb_cline_prot_unlock_wr(&cache->metadata.lock,
req->lock_idx, req->map[i].core_id,
req->map[i].core_line);
} }
ocf_metadata_flush_do_asynch(cache, req, _ocf_cleaner_metadata_io_end); ocf_metadata_flush_do_asynch(cache, req, _ocf_cleaner_metadata_io_end);
ocf_metadata_end_exclusive_access(&cache->metadata.lock);
return 0; return 0;
} }
@ -577,6 +583,7 @@ static int _ocf_cleaner_fire_core(struct ocf_request *req)
{ {
uint32_t i; uint32_t i;
struct ocf_map_info *iter; struct ocf_map_info *iter;
ocf_cache_t cache = req->cache;
OCF_DEBUG_TRACE(req->cache); OCF_DEBUG_TRACE(req->cache);
@ -595,7 +602,15 @@ static int _ocf_cleaner_fire_core(struct ocf_request *req)
if (iter->status == LOOKUP_MISS) if (iter->status == LOOKUP_MISS)
continue; continue;
ocf_hb_cline_prot_lock_rd(&cache->metadata.lock,
req->lock_idx, req->map[i].core_id,
req->map[i].core_line);
_ocf_cleaner_core_submit_io(req, iter); _ocf_cleaner_core_submit_io(req, iter);
ocf_hb_cline_prot_unlock_rd(&cache->metadata.lock,
req->lock_idx, req->map[i].core_id,
req->map[i].core_line);
} }
/* Protect IO completion race */ /* Protect IO completion race */
@ -833,6 +848,7 @@ void ocf_cleaner_fire(struct ocf_cache *cache,
int err; int err;
ocf_core_id_t core_id; ocf_core_id_t core_id;
uint64_t core_sector; uint64_t core_sector;
bool skip;
/* Allocate master request */ /* Allocate master request */
master = _ocf_cleaner_alloc_master_req(cache, max, attribs); master = _ocf_cleaner_alloc_master_req(cache, max, attribs);
@ -855,7 +871,6 @@ void ocf_cleaner_fire(struct ocf_cache *cache,
env_atomic_inc(&master->master_remaining); env_atomic_inc(&master->master_remaining);
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
/* when request hasn't yet been allocated or is just issued */ /* when request hasn't yet been allocated or is just issued */
if (!req) { if (!req) {
if (max > count - i) { if (max > count - i) {
@ -886,12 +901,23 @@ void ocf_cleaner_fire(struct ocf_cache *cache,
continue; continue;
} }
/* Get mapping info */
ocf_metadata_get_core_info(cache, cache_line, &core_id,
&core_sector);
if (attribs->lock_metadata) {
ocf_hb_cline_prot_lock_rd(&cache->metadata.lock,
req->lock_idx, core_id, core_sector);
}
skip = false;
/* when line already cleaned - rare condition under heavy /* when line already cleaned - rare condition under heavy
* I/O workload. * I/O workload.
*/ */
if (!metadata_test_dirty(cache, cache_line)) { if (!metadata_test_dirty(cache, cache_line)) {
OCF_DEBUG_MSG(cache, "Not dirty"); OCF_DEBUG_MSG(cache, "Not dirty");
continue; skip = true;
} }
if (!metadata_test_valid_any(cache, cache_line)) { if (!metadata_test_valid_any(cache, cache_line)) {
@ -902,12 +928,16 @@ void ocf_cleaner_fire(struct ocf_cache *cache,
* Cache line (sector) cannot be dirty and not valid * Cache line (sector) cannot be dirty and not valid
*/ */
ENV_BUG(); ENV_BUG();
continue; skip = true;
} }
/* Get mapping info */ if (attribs->lock_metadata) {
ocf_metadata_get_core_info(cache, cache_line, &core_id, ocf_hb_cline_prot_unlock_rd(&cache->metadata.lock,
&core_sector); req->lock_idx, core_id, core_sector);
}
if (skip)
continue;
if (unlikely(!cache->core[core_id].opened)) { if (unlikely(!cache->core[core_id].opened)) {
OCF_DEBUG_MSG(cache, "Core object inactive"); OCF_DEBUG_MSG(cache, "Core object inactive");
@ -931,6 +961,7 @@ void ocf_cleaner_fire(struct ocf_cache *cache,
i_out = 0; i_out = 0;
req = NULL; req = NULL;
} }
} }
if (req) { if (req) {
@ -1022,7 +1053,7 @@ void ocf_cleaner_refcnt_freeze(ocf_cache_t cache)
ocf_part_id_t part_id; ocf_part_id_t part_id;
for_each_part(cache, curr_part, part_id) for_each_part(cache, curr_part, part_id)
ocf_refcnt_freeze(&curr_part->cleaning); ocf_refcnt_freeze(&curr_part->cleaning.counter);
} }
void ocf_cleaner_refcnt_unfreeze(ocf_cache_t cache) void ocf_cleaner_refcnt_unfreeze(ocf_cache_t cache)
@ -1031,7 +1062,7 @@ void ocf_cleaner_refcnt_unfreeze(ocf_cache_t cache)
ocf_part_id_t part_id; ocf_part_id_t part_id;
for_each_part(cache, curr_part, part_id) for_each_part(cache, curr_part, part_id)
ocf_refcnt_unfreeze(&curr_part->cleaning); ocf_refcnt_unfreeze(&curr_part->cleaning.counter);
} }
static void ocf_cleaner_refcnt_register_zero_cb_finish(void *priv) static void ocf_cleaner_refcnt_register_zero_cb_finish(void *priv)
@ -1055,7 +1086,7 @@ void ocf_cleaner_refcnt_register_zero_cb(ocf_cache_t cache,
for_each_part(cache, curr_part, part_id) { for_each_part(cache, curr_part, part_id) {
env_atomic_inc(&ctx->waiting); env_atomic_inc(&ctx->waiting);
ocf_refcnt_register_zero_cb(&curr_part->cleaning, ocf_refcnt_register_zero_cb(&curr_part->cleaning.counter,
ocf_cleaner_refcnt_register_zero_cb_finish, ctx); ocf_cleaner_refcnt_register_zero_cb_finish, ctx);
} }

View File

@ -26,7 +26,8 @@ typedef int (*ocf_cleaner_get_item)(struct ocf_cache *cache,
* @brief Cleaning attributes for clean request * @brief Cleaning attributes for clean request
*/ */
struct ocf_cleaner_attribs { struct ocf_cleaner_attribs {
uint8_t cache_line_lock : 1; /*!< Clean under cache line lock */ uint8_t lock_cacheline : 1; /*!< Cleaner to lock cachelines on its own */
uint8_t lock_metadata : 1; /*!< Cleaner to lock metadata on its own */
uint8_t do_sort : 1; /*!< Sort cache lines which will be cleaned */ uint8_t do_sort : 1; /*!< Sort cache lines which will be cleaned */
@ -40,7 +41,7 @@ struct ocf_cleaner_attribs {
void *getter_context; void *getter_context;
/*!< Context for getting cache lines */ /*!< Context for getting cache lines */
uint32_t getter_item; uint32_t getter_item;
/*!< Additional variable that can be used by cleaner caller /*!< Additional variable that can be used by cleaner call
* to iterate over items * to iterate over items
*/ */

View File

@ -103,7 +103,12 @@ void ocf_part_move(struct ocf_request *req)
continue; continue;
} }
if (entry->status != LOOKUP_HIT) { /* Moving cachelines to another partition is needed only
* for those already mapped before this request, which
* indicates either HIT or REMAPPED.
*/
if (entry->status != LOOKUP_HIT &&
entry->status != LOOKUP_REMAPPED) {
/* No HIT */ /* No HIT */
continue; continue;
} }

View File

@ -2,8 +2,8 @@
* <tested_file_path>src/engine/engine_common.c</tested_file_path> * <tested_file_path>src/engine/engine_common.c</tested_file_path>
* <tested_function>ocf_prepare_clines_miss</tested_function> * <tested_function>ocf_prepare_clines_miss</tested_function>
* <functions_to_leave> * <functions_to_leave>
* INSERT HERE LIST OF FUNCTIONS YOU WANT TO LEAVE * ocf_prepare_clines_evict
* ONE FUNCTION PER LINE * ocf_engine_evict
* </functions_to_leave> * </functions_to_leave>
*/ */
@ -36,6 +36,11 @@
#include "engine/engine_common.c/prepare_clines_miss_generated_wraps.c" #include "engine/engine_common.c/prepare_clines_miss_generated_wraps.c"
struct ocf_cache_line_concurrency *__wrap_ocf_cache_line_concurrency(ocf_cache_t cache)
{
return NULL;
}
void __wrap_ocf_req_hash_lock_upgrade(struct ocf_request *req) void __wrap_ocf_req_hash_lock_upgrade(struct ocf_request *req)
{ {
} }
@ -66,13 +71,6 @@ void __wrap_ocf_metadata_end_exclusive_access(
{ {
} }
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) bool __wrap_ocf_part_is_enabled(struct ocf_user_part *target_part)
{ {
return mock(); return mock();
@ -93,9 +91,21 @@ void __wrap_ocf_req_set_mapping_error(struct ocf_request *req)
function_called(); function_called();
} }
int __wrap_space_managment_evict_do(struct ocf_request *req)
{
function_called();
return mock();
}
uint32_t __wrap_ocf_engine_unmapped_count(struct ocf_request *req)
{
return 100;
}
static void ocf_prepare_clines_miss_test01(void **state) static void ocf_prepare_clines_miss_test01(void **state)
{ {
struct ocf_request req = {}; struct ocf_cache cache;
struct ocf_request req = {.cache = &cache };
print_test_description("Target part is disabled and empty\n"); print_test_description("Target part is disabled and empty\n");
will_return(__wrap_ocf_part_is_enabled, false); will_return(__wrap_ocf_part_is_enabled, false);
expect_function_call(__wrap_ocf_req_set_mapping_error); expect_function_call(__wrap_ocf_req_set_mapping_error);
@ -104,7 +114,9 @@ static void ocf_prepare_clines_miss_test01(void **state)
static void ocf_prepare_clines_miss_test02(void **state) static void ocf_prepare_clines_miss_test02(void **state)
{ {
struct ocf_request req = {}; struct ocf_cache cache;
struct ocf_request req = {.cache = &cache };
print_test_description("Target part is disabled but has cachelines assigned.\n"); print_test_description("Target part is disabled but has cachelines assigned.\n");
print_test_description("\tMark mapping error\n"); print_test_description("\tMark mapping error\n");
@ -116,20 +128,18 @@ static void ocf_prepare_clines_miss_test02(void **state)
static void ocf_prepare_clines_miss_test03(void **state) static void ocf_prepare_clines_miss_test03(void **state)
{ {
struct ocf_request req = {}; struct ocf_cache cache;
struct ocf_request req = {.cache = &cache };
print_test_description("Target part is enabled but doesn't have enough space.\n"); 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"); print_test_description("\tEviction is ok and cachelines lock is acquired.\n");
will_return(__wrap_ocf_part_is_enabled, true); will_return(__wrap_ocf_part_is_enabled, true);
will_return(__wrap_ocf_part_has_space, false); will_return_always(__wrap_ocf_part_has_space, false);
will_return(__wrap_ocf_part_has_space, false);
expect_function_call(__wrap_space_managment_evict_do); expect_function_call(__wrap_space_managment_evict_do);
will_return(__wrap_space_managment_evict_do, LOOKUP_MAPPED); will_return_always(__wrap_space_managment_evict_do, LOOKUP_INSERTED);
expect_function_call(__wrap_ocf_engine_map); will_return_always(__wrap_ocf_req_test_mapping_error, false);
will_return(__wrap_ocf_req_test_mapping_error, false);
will_return(__wrap_lock_clines, 0); will_return(__wrap_lock_clines, 0);
expect_function_call(__wrap_lock_clines); expect_function_call(__wrap_lock_clines);
@ -139,57 +149,38 @@ static void ocf_prepare_clines_miss_test03(void **state)
static void ocf_prepare_clines_miss_test04(void **state) static void ocf_prepare_clines_miss_test04(void **state)
{ {
struct ocf_request req = {}; struct ocf_cache cache;
struct ocf_request req = {.cache = &cache };
print_test_description("Target part is enabled but doesn't have enough space.\n"); print_test_description("Target part is enabled but doesn't have enough space.\n");
print_test_description("\tEviction failed\n"); print_test_description("\tEviction failed\n");
will_return(__wrap_ocf_part_is_enabled, true); will_return(__wrap_ocf_part_is_enabled, true);
will_return(__wrap_ocf_part_has_space, false); will_return_always(__wrap_ocf_part_has_space, false);
will_return(__wrap_ocf_part_has_space, false);
expect_function_call(__wrap_space_managment_evict_do); expect_function_call(__wrap_space_managment_evict_do);
will_return(__wrap_space_managment_evict_do, LOOKUP_MISS); will_return(__wrap_space_managment_evict_do, LOOKUP_MISS);
expect_function_call(__wrap_ocf_req_set_mapping_error); expect_function_call(__wrap_ocf_req_set_mapping_error);
will_return_always(__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_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_has_space, false);
will_return(__wrap_ocf_part_has_space, false);
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); assert_int_equal(ocf_prepare_clines_miss(&req, NULL), -OCF_ERR_NO_LOCK);
} }
static void ocf_prepare_clines_miss_test06(void **state) static void ocf_prepare_clines_miss_test06(void **state)
{ {
struct ocf_request req = {}; struct ocf_cache cache;
struct ocf_request req = {.cache = &cache };
print_test_description("Target part is enabled but doesn't have enough space.\n"); 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"); print_test_description("Eviction and mapping were ok, but failed to lock cachelines.\n");
will_return(__wrap_ocf_part_has_space, false); will_return_always(__wrap_ocf_part_has_space, false);
will_return(__wrap_ocf_part_has_space, false);
expect_function_call(__wrap_space_managment_evict_do); expect_function_call(__wrap_space_managment_evict_do);
will_return(__wrap_space_managment_evict_do, LOOKUP_HIT); will_return(__wrap_space_managment_evict_do, LOOKUP_HIT);
will_return(__wrap_ocf_part_is_enabled, true); will_return(__wrap_ocf_part_is_enabled, true);
will_return_always(__wrap_ocf_req_test_mapping_error, false);
expect_function_call(__wrap_ocf_engine_map);
will_return(__wrap_ocf_req_test_mapping_error, false);
expect_function_call(__wrap_lock_clines); expect_function_call(__wrap_lock_clines);
will_return(__wrap_lock_clines, -OCF_ERR_NO_LOCK); will_return(__wrap_lock_clines, -OCF_ERR_NO_LOCK);
@ -201,20 +192,20 @@ static void ocf_prepare_clines_miss_test06(void **state)
static void ocf_prepare_clines_miss_test07(void **state) static void ocf_prepare_clines_miss_test07(void **state)
{ {
struct ocf_request req = {}; struct ocf_cache cache;
struct ocf_request req = {.cache = &cache };
print_test_description("Target part is enabled but doesn't have enough space.\n"); 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"); print_test_description("Eviction and mapping were ok, lock not acquired.\n");
will_return(__wrap_ocf_part_has_space, false); will_return_always(__wrap_ocf_part_has_space, false);
will_return(__wrap_ocf_part_has_space, false);
expect_function_call(__wrap_space_managment_evict_do); expect_function_call(__wrap_space_managment_evict_do);
will_return(__wrap_space_managment_evict_do, LOOKUP_HIT); will_return(__wrap_space_managment_evict_do, LOOKUP_HIT);
will_return(__wrap_ocf_part_is_enabled, true); will_return(__wrap_ocf_part_is_enabled, true);
expect_function_call(__wrap_ocf_engine_map); will_return_always(__wrap_ocf_req_test_mapping_error, false);
will_return(__wrap_ocf_req_test_mapping_error, false);
expect_function_call(__wrap_lock_clines); expect_function_call(__wrap_lock_clines);
will_return(__wrap_lock_clines, OCF_LOCK_NOT_ACQUIRED); will_return(__wrap_lock_clines, OCF_LOCK_NOT_ACQUIRED);
@ -224,15 +215,17 @@ static void ocf_prepare_clines_miss_test07(void **state)
static void ocf_prepare_clines_miss_test08(void **state) static void ocf_prepare_clines_miss_test08(void **state)
{ {
struct ocf_request req = {}; struct ocf_cache cache;
struct ocf_request req = {.cache = &cache };
print_test_description("Target part is enabled has enough space.\n"); print_test_description("Target part is enabled has enough space.\n");
print_test_description("\tMapping and cacheline lock are both ok\n"); print_test_description("\tMapping and cacheline lock are both ok\n");
will_return(__wrap_ocf_part_is_enabled, true); will_return(__wrap_ocf_part_is_enabled, true);
will_return(__wrap_ocf_part_has_space, true); will_return_always(__wrap_ocf_part_has_space, true);
expect_function_call(__wrap_ocf_engine_map); expect_function_call(__wrap_ocf_engine_map);
will_return(__wrap_ocf_req_test_mapping_error, false); will_return_always(__wrap_ocf_req_test_mapping_error, false);
expect_function_call(__wrap_lock_clines); expect_function_call(__wrap_lock_clines);
will_return(__wrap_lock_clines, OCF_LOCK_ACQUIRED); will_return(__wrap_lock_clines, OCF_LOCK_ACQUIRED);
@ -247,7 +240,6 @@ int main(void)
cmocka_unit_test(ocf_prepare_clines_miss_test02), cmocka_unit_test(ocf_prepare_clines_miss_test02),
cmocka_unit_test(ocf_prepare_clines_miss_test03), cmocka_unit_test(ocf_prepare_clines_miss_test03),
cmocka_unit_test(ocf_prepare_clines_miss_test04), 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_test06),
cmocka_unit_test(ocf_prepare_clines_miss_test07), cmocka_unit_test(ocf_prepare_clines_miss_test07),
cmocka_unit_test(ocf_prepare_clines_miss_test08) cmocka_unit_test(ocf_prepare_clines_miss_test08)

View File

@ -27,9 +27,9 @@ struct test_cache
{ {
struct ocf_cache cache; struct ocf_cache cache;
struct ocf_user_part_config part[OCF_IO_CLASS_MAX]; struct ocf_user_part_config part[OCF_IO_CLASS_MAX];
struct ocf_user_part upart[OCF_IO_CLASS_MAX];
uint32_t overflow[OCF_IO_CLASS_MAX]; uint32_t overflow[OCF_IO_CLASS_MAX];
uint32_t evictable[OCF_IO_CLASS_MAX]; uint32_t evictable[OCF_IO_CLASS_MAX];
uint32_t req_unmapped;
}; };
bool __wrap_ocf_eviction_can_evict(ocf_cache_t cache) bool __wrap_ocf_eviction_can_evict(ocf_cache_t cache)
@ -62,10 +62,12 @@ uint32_t __wrap_ocf_eviction_need_space(struct ocf_cache *cache,
tcache->overflow[part->id] -= overflown_consumed; tcache->overflow[part->id] -= overflown_consumed;
tcache->evictable[part->id] -= clines; tcache->evictable[part->id] -= clines;
tcache->req_unmapped -= clines;
check_expected(part); check_expected(part);
check_expected(clines); check_expected(clines);
function_called(); function_called();
return mock(); return mock();
} }
@ -157,7 +159,7 @@ static struct ocf_lst_entry *_list_getter(
{ {
struct test_cache* tcache = cache; struct test_cache* tcache = cache;
return &tcache->upart[idx].lst_valid; return &tcache->cache.user_parts[idx].lst_valid;
} }
static void init_part_list(struct test_cache *tcache) static void init_part_list(struct test_cache *tcache)
@ -165,23 +167,30 @@ static void init_part_list(struct test_cache *tcache)
unsigned i; unsigned i;
for (i = 0; i < OCF_IO_CLASS_MAX; i++) { for (i = 0; i < OCF_IO_CLASS_MAX; i++) {
tcache->upart[i].id = i; tcache->cache.user_parts[i].id = i;
tcache->upart[i].config = &tcache->part[i]; tcache->cache.user_parts[i].config = &tcache->part[i];
tcache->upart[i].config->priority = i+1; tcache->cache.user_parts[i].config->priority = i+1;
tcache->upart[i].config->flags.eviction = 1; tcache->cache.user_parts[i].config->flags.eviction = 1;
} }
ocf_lst_init((ocf_cache_t)tcache, &tcache->cache.lst_part, OCF_IO_CLASS_MAX, ocf_lst_init((ocf_cache_t)tcache, &tcache->cache.lst_part, OCF_IO_CLASS_MAX,
_list_getter, ocf_part_lst_cmp_valid); _list_getter, ocf_part_lst_cmp_valid);
for (i = 0; i < OCF_IO_CLASS_MAX; i++) { for (i = 0; i < OCF_IO_CLASS_MAX; i++) {
ocf_lst_init_entry(&tcache->cache.lst_part, &tcache->upart[i].lst_valid); ocf_lst_init_entry(&tcache->cache.lst_part, &tcache->cache.user_parts[i].lst_valid);
ocf_lst_add_tail(&tcache->cache.lst_part, i); ocf_lst_add_tail(&tcache->cache.lst_part, i);
} }
} }
uint32_t __wrap_ocf_engine_unmapped_count(struct ocf_request *req)
{
struct test_cache* tcache = (struct test_cache*)req->cache;
return tcache->req_unmapped;
}
#define _expect_evict_call(tcache, part_id, req_count, ret_count) \ #define _expect_evict_call(tcache, part_id, req_count, ret_count) \
do { \ do { \
expect_value(__wrap_ocf_eviction_need_space, part, &tcache.upart[part_id]); \ expect_value(__wrap_ocf_eviction_need_space, part, &tcache.cache.user_parts[part_id]); \
expect_value(__wrap_ocf_eviction_need_space, clines, req_count); \ expect_value(__wrap_ocf_eviction_need_space, clines, req_count); \
expect_function_call(__wrap_ocf_eviction_need_space); \ expect_function_call(__wrap_ocf_eviction_need_space); \
will_return(__wrap_ocf_eviction_need_space, ret_count); \ will_return(__wrap_ocf_eviction_need_space, ret_count); \
@ -190,6 +199,7 @@ static void init_part_list(struct test_cache *tcache)
static void ocf_evict_do_test01(void **state) static void ocf_evict_do_test01(void **state)
{ {
struct test_cache tcache = {}; struct test_cache tcache = {};
struct ocf_request req = {.cache = &tcache.cache, .part_id = 0 };
unsigned evicted; unsigned evicted;
print_test_description("one IO class, no overflow\n"); print_test_description("one IO class, no overflow\n");
@ -197,16 +207,17 @@ static void ocf_evict_do_test01(void **state)
init_part_list(&tcache); init_part_list(&tcache);
tcache.evictable[10] = 100; tcache.evictable[10] = 100;
tcache.req_unmapped = 50;
_expect_evict_call(tcache, 10, 50, 50); _expect_evict_call(tcache, 10, 50, 50);
evicted = ocf_evict_do(&req);
evicted = ocf_evict_do((ocf_cache_t *)&tcache, NULL, 50, &tcache.upart[0]);
assert_int_equal(evicted, 50); assert_int_equal(evicted, 50);
} }
static void ocf_evict_do_test02(void **state) static void ocf_evict_do_test02(void **state)
{ {
struct test_cache tcache = {}; struct test_cache tcache = {};
struct ocf_request req = {.cache = &tcache.cache, .part_id = 0 };
unsigned i; unsigned i;
unsigned evicted; unsigned evicted;
@ -216,16 +227,18 @@ static void ocf_evict_do_test02(void **state)
tcache.evictable[10] = 100; tcache.evictable[10] = 100;
tcache.overflow[10] = 100; tcache.overflow[10] = 100;
tcache.req_unmapped = 50;
_expect_evict_call(tcache, 10, 50, 50); _expect_evict_call(tcache, 10, 50, 50);
evicted = ocf_evict_do((ocf_cache_t *)&tcache, NULL, 50, &tcache.upart[0]); evicted = ocf_evict_do(&req);
assert_int_equal(evicted, 50); assert_int_equal(evicted, 50);
} }
static void ocf_evict_do_test03(void **state) static void ocf_evict_do_test03(void **state)
{ {
struct test_cache tcache = {}; struct test_cache tcache = {};
struct ocf_request req = {.cache = &tcache.cache, .part_id = 0 };
unsigned i; unsigned i;
unsigned evicted; unsigned evicted;
@ -237,19 +250,21 @@ static void ocf_evict_do_test03(void **state)
tcache.evictable[12] = 100; tcache.evictable[12] = 100;
tcache.evictable[16] = 100; tcache.evictable[16] = 100;
tcache.evictable[17] = 100; tcache.evictable[17] = 100;
tcache.req_unmapped = 350;
_expect_evict_call(tcache, 10, 100, 100); _expect_evict_call(tcache, 10, 100, 100);
_expect_evict_call(tcache, 12, 100, 100); _expect_evict_call(tcache, 12, 100, 100);
_expect_evict_call(tcache, 16, 100, 100); _expect_evict_call(tcache, 16, 100, 100);
_expect_evict_call(tcache, 17, 50, 50); _expect_evict_call(tcache, 17, 50, 50);
evicted = ocf_evict_do((ocf_cache_t *)&tcache, NULL, 350, &tcache.upart[0]); evicted = ocf_evict_do(&req);
assert_int_equal(evicted, 350); assert_int_equal(evicted, 350);
} }
static void ocf_evict_do_test04(void **state) static void ocf_evict_do_test04(void **state)
{ {
struct test_cache tcache = {}; struct test_cache tcache = {};
struct ocf_request req = {.cache = &tcache.cache, .part_id = 0 };
unsigned i; unsigned i;
unsigned evicted; unsigned evicted;
@ -266,6 +281,7 @@ static void ocf_evict_do_test04(void **state)
tcache.evictable[17] = 100; tcache.evictable[17] = 100;
tcache.evictable[18] = 100; tcache.evictable[18] = 100;
tcache.overflow[18] = 100; tcache.overflow[18] = 100;
tcache.req_unmapped = 580;
_expect_evict_call(tcache, 12, 40, 40); _expect_evict_call(tcache, 12, 40, 40);
_expect_evict_call(tcache, 14, 100, 100); _expect_evict_call(tcache, 14, 100, 100);
@ -275,7 +291,7 @@ static void ocf_evict_do_test04(void **state)
_expect_evict_call(tcache, 16, 100, 100); _expect_evict_call(tcache, 16, 100, 100);
_expect_evict_call(tcache, 17, 80, 80); _expect_evict_call(tcache, 17, 80, 80);
evicted = ocf_evict_do((ocf_cache_t *)&tcache, NULL, 580, &tcache.upart[0]); evicted = ocf_evict_do(&req);
assert_int_equal(evicted, 580); assert_int_equal(evicted, 580);
} }
int main(void) int main(void)

View File

@ -10,6 +10,8 @@
* _lru_evp_set_empty * _lru_evp_set_empty
* _lru_evp_all_empty * _lru_evp_all_empty
* ocf_rotate_right * ocf_rotate_right
* lru_iter_eviction_next
* lru_iter_cleaning_next
* </functions_to_leave> * </functions_to_leave>
*/ */
@ -157,7 +159,26 @@ void write_test_case_description(void)
test_case++; test_case++;
} }
/* transform cacheline numbers so that they remain unique but have
* assignment to list modulo OCF_NUM_EVICTION_LISTS */
for (test_case = 0; test_case < num_cases; test_case++) {
for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) {
j = 0;
while (test_cases[j][i][test_case] != -1) {
test_cases[j][i][test_case] = test_cases[j][i][test_case] *
OCF_NUM_EVICTION_LISTS + i;
j++;
}
}
}
#ifdef DEBUG #ifdef DEBUG
static bool desc_printed = false;
if (desc_printed)
return;
desc_printed = true;
for (test_case = 0; test_case < num_cases; test_case++) { for (test_case = 0; test_case < num_cases; test_case++) {
print_message("test case no %d\n", test_case); print_message("test case no %d\n", test_case);
for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) {
@ -196,6 +217,11 @@ struct ocf_lru_list *__wrap_evp_lru_get_list(struct ocf_user_part *part,
list.num_nodes = i; list.num_nodes = i;
} }
#ifdef DEBUG
print_message("list for case %u evp %u: head: %u tail %u elems %u\n",
current_case, evp, list.head, list.tail, list.num_nodes);
#endif
return &list; return &list;
} }
@ -245,6 +271,76 @@ union eviction_policy_meta *__wrap_ocf_metadata_get_eviction_policy(
} }
void __wrap_add_lru_head(ocf_cache_t cache,
struct ocf_lru_list *list,
unsigned int collision_index)
{
unsigned list_head = list->head;
unsigned i, j = collision_index % OCF_NUM_EVICTION_LISTS;
i = 1;
while (test_cases[i][j][current_case] != -1)
i++;
test_cases[i+1][j][current_case] = -1;
while (i--)
test_cases[i + 1][j][current_case] = test_cases[i][j][current_case];
test_cases[0][j][current_case] = collision_index;
#ifdef DEBUG
print_message("case %u evp %u head set to %u\n", current_case, j, collision_index);
#endif
}
void __wrap_remove_lru_list(ocf_cache_t cache,
struct ocf_lru_list *list,
unsigned int collision_index)
{
bool found;
unsigned i, j;
found = false;
for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++)
{
j = 0;
while (test_cases[j][i][current_case] != -1) {
if (!found && test_cases[j][i][current_case] == collision_index) {
assert_int_equal(test_cases[0][i][current_case], list->head);
found = true;
}
if (found)
test_cases[j][i][current_case] = test_cases[j+1][i][current_case];
j++;
}
if (found)
break;
}
assert(found);
#ifdef DEBUG
print_message("case %u removed %u from evp %u\n", current_case, collision_index, i);
#endif
}
bool __wrap__lru_lock(struct ocf_lru_iter *iter,
ocf_cache_line_t cache_line,
ocf_core_id_t *core_id, uint64_t *core_line)
{
return true;
}
bool __wrap__lru_trylock_cacheline(struct ocf_lru_iter *iter,
ocf_cache_line_t cline)
{
return true;
}
static void _lru_run_test(unsigned test_case) static void _lru_run_test(unsigned test_case)
{ {
unsigned start_pos; unsigned start_pos;
@ -258,6 +354,8 @@ static void _lru_run_test(unsigned test_case)
unsigned pos[OCF_NUM_EVICTION_LISTS]; unsigned pos[OCF_NUM_EVICTION_LISTS];
unsigned i; unsigned i;
write_test_case_description();
for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++)
{ {
pos[i] = -1; pos[i] = -1;
@ -265,12 +363,10 @@ static void _lru_run_test(unsigned test_case)
pos[i]++; pos[i]++;
} }
lru_iter_init(&iter, NULL, NULL, start_pos, false); lru_iter_init(&iter, NULL, NULL, start_pos, false, false, false,
NULL, NULL);
do { do {
/* get cacheline from iterator */
cache_line = lru_iter_next(&iter);
/* check what is expected to be returned from iterator */ /* check what is expected to be returned from iterator */
if (pos[curr_evp] == -1) { if (pos[curr_evp] == -1) {
i = 1; i = 1;
@ -294,6 +390,9 @@ static void _lru_run_test(unsigned test_case)
pos[curr_evp]--; pos[curr_evp]--;
} }
/* get cacheline from iterator */
cache_line = lru_iter_cleaning_next(&iter);
assert_int_equal(cache_line, expected_cache_line); assert_int_equal(cache_line, expected_cache_line);
curr_evp = (curr_evp + 1) % OCF_NUM_EVICTION_LISTS; curr_evp = (curr_evp + 1) % OCF_NUM_EVICTION_LISTS;
@ -475,7 +574,5 @@ int main(void)
print_message("Unit test for lru_iter_next\n"); print_message("Unit test for lru_iter_next\n");
write_test_case_description();
return cmocka_run_group_tests(tests, NULL, NULL); return cmocka_run_group_tests(tests, NULL, NULL);
} }