diff --git a/src/concurrency/ocf_metadata_concurrency.c b/src/concurrency/ocf_metadata_concurrency.c index dbe2f0e..a36d3b8 100644 --- a/src/concurrency/ocf_metadata_concurrency.c +++ b/src/concurrency/ocf_metadata_concurrency.c @@ -9,19 +9,23 @@ int ocf_metadata_concurrency_init(struct ocf_metadata_lock *metadata_lock) { int err = 0; - unsigned i; + unsigned evp_iter; + unsigned part_iter; - err = env_spinlock_init(&metadata_lock->eviction); - if (err) - return err; + for (evp_iter = 0; evp_iter < OCF_NUM_EVICTION_LISTS; evp_iter++) { + err = env_spinlock_init(&metadata_lock->eviction[evp_iter]); + if (err) + goto eviction_err; + } env_rwlock_init(&metadata_lock->status); + err = env_rwsem_init(&metadata_lock->global); if (err) goto rwsem_err; - for (i = 0; i < OCF_IO_CLASS_MAX; i++) { - err = env_spinlock_init(&metadata_lock->partition[i]); + for (part_iter = 0; part_iter < OCF_IO_CLASS_MAX; part_iter++) { + err = env_spinlock_init(&metadata_lock->partition[part_iter]); if (err) goto spinlocks_err; } @@ -29,11 +33,15 @@ int ocf_metadata_concurrency_init(struct ocf_metadata_lock *metadata_lock) return err; spinlocks_err: - while (i--) - env_spinlock_destroy(&metadata_lock->partition[i]); + while (part_iter--) + env_spinlock_destroy(&metadata_lock->partition[part_iter]); rwsem_err: env_rwlock_destroy(&metadata_lock->status); - env_spinlock_destroy(&metadata_lock->eviction); + +eviction_err: + while (evp_iter--) + env_spinlock_destroy(&metadata_lock->eviction[evp_iter]); + return err; } @@ -41,11 +49,12 @@ void ocf_metadata_concurrency_deinit(struct ocf_metadata_lock *metadata_lock) { unsigned i; - for (i = 0; i < OCF_IO_CLASS_MAX; i++) { + for (i = 0; i < OCF_IO_CLASS_MAX; i++) env_spinlock_destroy(&metadata_lock->partition[i]); - } - env_spinlock_destroy(&metadata_lock->eviction); + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) + env_spinlock_destroy(&metadata_lock->eviction[i]); + env_rwlock_destroy(&metadata_lock->status); env_rwsem_destroy(&metadata_lock->global); } diff --git a/src/concurrency/ocf_metadata_concurrency.h b/src/concurrency/ocf_metadata_concurrency.h index a20cc53..2ae5721 100644 --- a/src/concurrency/ocf_metadata_concurrency.h +++ b/src/concurrency/ocf_metadata_concurrency.h @@ -3,6 +3,7 @@ * SPDX-License-Identifier: BSD-3-Clause-Clear */ #include "../ocf_cache_priv.h" +#include "../eviction/eviction.h" #ifndef __OCF_METADATA_CONCURRENCY_H__ #define __OCF_METADATA_CONCURRENCY_H__ @@ -22,17 +23,49 @@ void ocf_metadata_concurrency_attached_deinit( struct ocf_metadata_lock *metadata_lock); static inline void ocf_metadata_eviction_lock( - struct ocf_metadata_lock *metadata_lock) + struct ocf_metadata_lock *metadata_lock, unsigned ev_list) { - env_spinlock_lock(&metadata_lock->eviction); + env_spinlock_lock(&metadata_lock->eviction[ev_list]); } static inline void ocf_metadata_eviction_unlock( + struct ocf_metadata_lock *metadata_lock, unsigned ev_list) +{ + env_spinlock_unlock(&metadata_lock->eviction[ev_list]); +} + +static inline void ocf_metadata_eviction_lock_all( struct ocf_metadata_lock *metadata_lock) { - env_spinlock_unlock(&metadata_lock->eviction); + uint32_t i; + + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) + ocf_metadata_eviction_lock(metadata_lock, i); } +static inline void ocf_metadata_eviction_unlock_all( + struct ocf_metadata_lock *metadata_lock) +{ + uint32_t i; + + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) + ocf_metadata_eviction_unlock(metadata_lock, i); +} + +#define OCF_METADATA_EVICTION_LOCK(cline) \ + ocf_metadata_eviction_lock(&cache->metadata.lock, \ + cline % OCF_NUM_EVICTION_LISTS) + +#define OCF_METADATA_EVICTION_UNLOCK(cline) \ + ocf_metadata_eviction_unlock(&cache->metadata.lock, \ + cline % OCF_NUM_EVICTION_LISTS) + +#define OCF_METADATA_EVICTION_LOCK_ALL() \ + ocf_metadata_eviction_lock_all(&cache->metadata.lock) + +#define OCF_METADATA_EVICTION_UNLOCK_ALL() \ + ocf_metadata_eviction_unlock_all(&cache->metadata.lock) + static inline void ocf_metadata_partition_lock( struct ocf_metadata_lock *metadata_lock, ocf_part_id_t part_id) @@ -47,12 +80,6 @@ static inline void ocf_metadata_partition_unlock( env_spinlock_unlock(&metadata_lock->partition[part_id]); } -#define OCF_METADATA_EVICTION_LOCK() \ - ocf_metadata_eviction_lock(&cache->metadata.lock) - -#define OCF_METADATA_EVICTION_UNLOCK() \ - ocf_metadata_eviction_unlock(&cache->metadata.lock) - void ocf_metadata_start_exclusive_access( struct ocf_metadata_lock *metadata_lock); diff --git a/src/engine/engine_common.c b/src/engine/engine_common.c index 4f18201..9e8b0ee 100644 --- a/src/engine/engine_common.c +++ b/src/engine/engine_common.c @@ -266,7 +266,7 @@ static void ocf_engine_map_cache_line(struct ocf_request *req, *cache_line); ocf_metadata_end_collision_shared_access(cache, *cache_line); - ocf_eviction_init_cache_line(cache, *cache_line, part_id); + 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); diff --git a/src/eviction/eviction.c b/src/eviction/eviction.c index 8909e01..5a41935 100644 --- a/src/eviction/eviction.c +++ b/src/eviction/eviction.c @@ -42,11 +42,10 @@ static uint32_t ocf_evict_calculate(struct ocf_user_part *part, static inline uint32_t ocf_evict_do(ocf_cache_t cache, ocf_queue_t io_queue, const uint32_t evict_cline_no, - ocf_part_id_t target_part_id) + struct ocf_user_part *target_part) { uint32_t to_evict = 0, evicted = 0; struct ocf_user_part *part; - struct ocf_user_part *target_part = &cache->user_parts[target_part_id]; ocf_part_id_t part_id; /* For each partition from the lowest priority to highest one */ @@ -68,7 +67,7 @@ static inline uint32_t ocf_evict_do(ocf_cache_t cache, /* It seams that no more partition for eviction */ break; } - if (part_id == target_part_id) { + if (part_id == target_part->id) { /* Omit targeted, evict from different first */ continue; } @@ -84,7 +83,7 @@ static inline uint32_t ocf_evict_do(ocf_cache_t cache, } evicted += ocf_eviction_need_space(cache, io_queue, - part_id, to_evict); + part, to_evict); } if (!ocf_eviction_can_evict(cache)) @@ -95,7 +94,7 @@ static inline uint32_t ocf_evict_do(ocf_cache_t cache, to_evict = ocf_evict_calculate(target_part, evict_cline_no); if (to_evict) { evicted += ocf_eviction_need_space(cache, io_queue, - target_part_id, to_evict); + target_part, to_evict); } } @@ -108,14 +107,14 @@ int space_managment_evict_do(struct ocf_cache *cache, { uint32_t evicted; uint32_t free; + struct ocf_user_part *req_part = &cache->user_parts[req->part_id]; free = ocf_freelist_num_free(cache->freelist); if (evict_cline_no <= free) return LOOKUP_MAPPED; evict_cline_no -= free; - evicted = ocf_evict_do(cache, req->io_queue, evict_cline_no, - req->part_id); + evicted = ocf_evict_do(cache, req->io_queue, evict_cline_no, req_part); if (evict_cline_no <= evicted) return LOOKUP_MAPPED; diff --git a/src/eviction/eviction.h b/src/eviction/eviction.h index 90a722d..4843d37 100644 --- a/src/eviction/eviction.h +++ b/src/eviction/eviction.h @@ -14,6 +14,10 @@ #define OCF_TO_EVICTION_MIN 128UL #define OCF_PENDING_EVICTION_LIMIT 512UL +#define OCF_NUM_EVICTION_LISTS 32 + +struct ocf_user_part; + struct eviction_policy { union { struct lru_eviction_policy lru; @@ -37,17 +41,16 @@ struct eviction_policy_ops { ocf_cache_line_t cline); bool (*can_evict)(ocf_cache_t cache); uint32_t (*req_clines)(ocf_cache_t cache, - ocf_queue_t io_queue, ocf_part_id_t part_id, + ocf_queue_t io_queue, struct ocf_user_part *part, uint32_t cline_no); void (*hot_cline)(ocf_cache_t cache, ocf_cache_line_t cline); - void (*init_evp)(ocf_cache_t cache, - ocf_part_id_t part_id); + void (*init_evp)(ocf_cache_t cache, struct ocf_user_part *part); void (*dirty_cline)(ocf_cache_t cache, - ocf_part_id_t part_id, + struct ocf_user_part *part, uint32_t cline_no); void (*clean_cline)(ocf_cache_t cache, - ocf_part_id_t part_id, + struct ocf_user_part *part, uint32_t cline_no); const char *name; }; diff --git a/src/eviction/lru.c b/src/eviction/lru.c index 5abc815..96ef568 100644 --- a/src/eviction/lru.c +++ b/src/eviction/lru.c @@ -163,48 +163,137 @@ void evp_lru_init_cline(ocf_cache_t cache, ocf_cache_line_t cline) node->next = end_marker; } +static struct ocf_lru_list *evp_lru_get_list(struct ocf_user_part *part, + uint32_t evp, bool clean) +{ + return clean ? &part->runtime->eviction[evp].policy.lru.clean : + &part->runtime->eviction[evp].policy.lru.dirty; +} + +static inline struct ocf_lru_list *evp_get_cline_list(ocf_cache_t cache, + ocf_cache_line_t cline) +{ + ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache, cline); + struct ocf_user_part *part = &cache->user_parts[part_id]; + uint32_t ev_list = (cline % OCF_NUM_EVICTION_LISTS); + + return evp_lru_get_list(part, ev_list, + !metadata_test_dirty(cache, cline)); +} /* the caller must hold the metadata lock */ void evp_lru_rm_cline(ocf_cache_t cache, ocf_cache_line_t cline) { - ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache, cline); - struct ocf_user_part *part = &cache->user_parts[part_id]; struct ocf_lru_list *list; - list = metadata_test_dirty(cache, cline) ? - &part->runtime->eviction.policy.lru.dirty : - &part->runtime->eviction.policy.lru.clean; - + list = evp_get_cline_list(cache, cline); remove_lru_list(cache, list, cline); } +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) +{ + uint32_t i; + + /* entire iterator implementation depends on gcc builtins for + bit operations which works on 64 bit integers at most */ + ENV_BUILD_BUG_ON(OCF_NUM_EVICTION_LISTS > sizeof(iter->evp) * 8); + + iter->cache = cache; + iter->part = part; + /* set iterator value to start_evp - 1 modulo 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->next_avail_evp = ((1ULL << OCF_NUM_EVICTION_LISTS) - 1); + + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) + iter->curr_cline[i] = evp_lru_get_list(part, i, clean)->tail; +} + +static inline uint32_t _lru_next_evp(struct ocf_lru_iter *iter) +{ + unsigned increment; + + increment = __builtin_ffsll(iter->next_avail_evp); + iter->next_avail_evp = ocf_rotate_right(iter->next_avail_evp, + increment, OCF_NUM_EVICTION_LISTS); + iter->evp = (iter->evp + increment) % OCF_NUM_EVICTION_LISTS; + + return iter->evp; +} + +static inline bool _lru_evp_is_empty(struct ocf_lru_iter *iter) +{ + return !(iter->next_avail_evp & (1ULL << (OCF_NUM_EVICTION_LISTS - 1))); +} + +static inline void _lru_evp_set_empty(struct ocf_lru_iter *iter) +{ + iter->next_avail_evp &= ~(1ULL << (OCF_NUM_EVICTION_LISTS - 1)); + iter->num_avail_evps--; +} + +static inline bool _lru_evp_all_empty(struct ocf_lru_iter *iter) +{ + return iter->num_avail_evps == 0; +} + +/* get next non-empty lru list if available */ +static inline ocf_cache_line_t lru_iter_next(struct ocf_lru_iter *iter) +{ + struct lru_eviction_policy_meta *node; + uint32_t curr_evp; + ocf_cache_line_t ret; + + curr_evp = _lru_next_evp(iter); + + while (iter->curr_cline[curr_evp] == end_marker) { + if (!_lru_evp_is_empty(iter)) { + /* mark list as empty */ + _lru_evp_set_empty(iter); + } + if (_lru_evp_all_empty(iter)) { + /* all lists empty */ + return end_marker; + } + curr_evp = _lru_next_evp(iter); + } + + node = &ocf_metadata_get_eviction_policy(iter->cache, + iter->curr_cline[curr_evp])->lru; + ret = iter->curr_cline[curr_evp]; + iter->curr_cline[curr_evp] = node->prev; + + return ret; +} + static void evp_lru_clean_end(void *private_data, int error) { - struct ocf_refcnt *counter = private_data; + struct ocf_lru_iter *iter = private_data; - ocf_refcnt_dec(counter); + ocf_refcnt_dec(&iter->part->cleaning); } -static int evp_lru_clean_getter(ocf_cache_t cache, - void *getter_context, uint32_t item, ocf_cache_line_t *line) +static int evp_lru_clean_getter(ocf_cache_t cache, void *getter_context, + uint32_t item, ocf_cache_line_t *line) { - struct ocf_cleaner_attribs *attribs = getter_context; - ocf_cache_line_t prev_cline, curr_cline = attribs->getter_item; + struct ocf_lru_iter *iter = getter_context; + ocf_cache_line_t cline; - while (curr_cline != end_marker) { - prev_cline = ocf_metadata_get_eviction_policy(cache, - curr_cline)->lru.prev; + while (true) { + cline = lru_iter_next(iter); + + if (cline == end_marker) + break; /* Prevent evicting already locked items */ - if (ocf_cache_line_is_used(cache, curr_cline)) { - curr_cline = prev_cline; + if (ocf_cache_line_is_used(cache, cline)) { continue; } - ENV_BUG_ON(!metadata_test_dirty(cache, curr_cline)); + ENV_BUG_ON(!metadata_test_dirty(cache, cline)); - *line = curr_cline; - attribs->getter_item = prev_cline; + *line = cline; return 0; } @@ -212,20 +301,18 @@ static int evp_lru_clean_getter(ocf_cache_t cache, } static void evp_lru_clean(ocf_cache_t cache, ocf_queue_t io_queue, - ocf_part_id_t part_id, uint32_t count) + struct ocf_user_part *part, uint32_t count) { - struct ocf_refcnt *counter = &cache->refcnt.cleaning[part_id]; - struct ocf_user_part *part = &cache->user_parts[part_id]; + struct ocf_refcnt *counter = &part->cleaning; struct ocf_cleaner_attribs attribs = { .cache_line_lock = true, .do_sort = true, - .cmpl_context = counter, + .cmpl_context = &part->eviction_clean_iter, .cmpl_fn = evp_lru_clean_end, .getter = evp_lru_clean_getter, - .getter_context = &attribs, - .getter_item = part->runtime->eviction.policy.lru.dirty.tail, + .getter_context = &part->eviction_clean_iter, .count = count > 32 ? 32 : count, @@ -247,6 +334,9 @@ static void evp_lru_clean(ocf_cache_t cache, ocf_queue_t io_queue, return; } + lru_iter_init(&part->eviction_clean_iter, cache, part, + part->eviction_clean_iter.evp, false); + ocf_cleaner_fire(cache, &attribs); } @@ -294,64 +384,69 @@ bool evp_lru_can_evict(ocf_cache_t cache) return true; } -/* the caller must hold the metadata lock */ -uint32_t evp_lru_req_clines(ocf_cache_t cache, ocf_queue_t io_queue, - ocf_part_id_t part_id, uint32_t cline_no) +static bool dirty_pages_present(ocf_cache_t cache, struct ocf_user_part *part) { uint32_t i; - ocf_cache_line_t curr_cline, prev_cline; - struct ocf_user_part *part = &cache->user_parts[part_id]; + + 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 */ +uint32_t evp_lru_req_clines(ocf_cache_t cache, ocf_queue_t io_queue, + struct ocf_user_part *part, uint32_t cline_no) +{ + struct ocf_lru_iter iter; + uint32_t i; + ocf_cache_line_t cline; if (cline_no == 0) return 0; - i = 0; - curr_cline = part->runtime->eviction.policy.lru.clean.tail; - /* Find cachelines to be evicted. */ + lru_iter_init(&iter, cache, part, part->next_eviction_list, true); + + i = 0; while (i < cline_no) { + cline = lru_iter_next(&iter); + + if (cline == end_marker) + break; + if (!evp_lru_can_evict(cache)) break; - if (curr_cline == end_marker) - break; - - prev_cline = ocf_metadata_get_eviction_policy(cache, - curr_cline)->lru.prev; - /* Prevent evicting already locked items */ - if (ocf_cache_line_is_used(cache, curr_cline)) { - curr_cline = prev_cline; + if (ocf_cache_line_is_used(cache, cline)) continue; - } - ENV_BUG_ON(metadata_test_dirty(cache, curr_cline)); + ENV_BUG_ON(metadata_test_dirty(cache, cline)); if (ocf_volume_is_atomic(&cache->device->volume)) { /* atomic cache, we have to trim cache lines before * eviction */ - evp_lru_zero_line(cache, io_queue, curr_cline); - - } else { - ocf_metadata_start_collision_shared_access(cache, - curr_cline); - set_cache_line_invalid_no_flush(cache, 0, - ocf_line_end_sector(cache), - curr_cline); - ocf_metadata_end_collision_shared_access(cache, - curr_cline); - - /* Goto next item. */ - i++; + evp_lru_zero_line(cache, io_queue, cline); + continue; } - curr_cline = prev_cline; + ocf_metadata_start_collision_shared_access( + cache, cline); + set_cache_line_invalid_no_flush(cache, 0, + ocf_line_end_sector(cache), + cline); + ocf_metadata_end_collision_shared_access( + cache, cline); + ++i; } - if (i < cline_no && part->runtime->eviction.policy.lru.dirty.tail != - end_marker) { - evp_lru_clean(cache, io_queue, part_id, cline_no - 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; @@ -360,18 +455,11 @@ uint32_t evp_lru_req_clines(ocf_cache_t cache, ocf_queue_t io_queue, /* the caller must hold the metadata lock */ void evp_lru_hot_cline(ocf_cache_t cache, ocf_cache_line_t cline) { - ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache, cline); - struct ocf_user_part *part = &cache->user_parts[part_id]; struct lru_eviction_policy_meta *node; - int cline_dirty; struct ocf_lru_list *list; node = &ocf_metadata_get_eviction_policy(cache, cline)->lru; - - cline_dirty = metadata_test_dirty(cache, cline); - list = cline_dirty ? - &part->runtime->eviction.policy.lru.dirty : - &part->runtime->eviction.policy.lru.clean; + list = evp_get_cline_list(cache, cline); if (node->next != end_marker || node->prev != end_marker || @@ -390,48 +478,50 @@ static inline void _lru_init(struct ocf_lru_list *list) list->tail = end_marker; } -void evp_lru_init_evp(ocf_cache_t cache, ocf_part_id_t part_id) +void evp_lru_init_evp(ocf_cache_t cache, struct ocf_user_part *part) { - struct ocf_user_part *part = &cache->user_parts[part_id]; struct ocf_lru_list *clean_list; struct ocf_lru_list *dirty_list; + uint32_t i; - clean_list = &part->runtime->eviction.policy.lru.clean; - dirty_list = &part->runtime->eviction.policy.lru.dirty; + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { + clean_list = evp_lru_get_list(part, i, true); + dirty_list = evp_lru_get_list(part, i, false); - _lru_init(clean_list); - _lru_init(dirty_list); + _lru_init(clean_list); + _lru_init(dirty_list); + } } -void evp_lru_clean_cline(ocf_cache_t cache, ocf_part_id_t part_id, +void evp_lru_clean_cline(ocf_cache_t cache, struct ocf_user_part *part, uint32_t cline) { - struct ocf_user_part *part = &cache->user_parts[part_id]; + uint32_t ev_list = (cline % OCF_NUM_EVICTION_LISTS); struct ocf_lru_list *clean_list; struct ocf_lru_list *dirty_list; - clean_list = &part->runtime->eviction.policy.lru.clean; - dirty_list = &part->runtime->eviction.policy.lru.dirty; + clean_list = evp_lru_get_list(part, ev_list, true); + dirty_list = evp_lru_get_list(part, ev_list, false); - OCF_METADATA_EVICTION_LOCK(); + OCF_METADATA_EVICTION_LOCK(cline); remove_lru_list(cache, dirty_list, cline); add_lru_head(cache, clean_list, cline); - OCF_METADATA_EVICTION_UNLOCK(); + OCF_METADATA_EVICTION_UNLOCK(cline); } -void evp_lru_dirty_cline(ocf_cache_t cache, ocf_part_id_t part_id, +void evp_lru_dirty_cline(ocf_cache_t cache, struct ocf_user_part *part, uint32_t cline) { - struct ocf_user_part *part = &cache->user_parts[part_id]; + uint32_t ev_list = (cline % OCF_NUM_EVICTION_LISTS); struct ocf_lru_list *clean_list; struct ocf_lru_list *dirty_list; - clean_list = &part->runtime->eviction.policy.lru.clean; - dirty_list = &part->runtime->eviction.policy.lru.dirty; + clean_list = evp_lru_get_list(part, ev_list, true); + dirty_list = evp_lru_get_list(part, ev_list, false); - OCF_METADATA_EVICTION_LOCK(); + OCF_METADATA_EVICTION_LOCK(cline); remove_lru_list(cache, clean_list, cline); add_lru_head(cache, dirty_list, cline); - OCF_METADATA_EVICTION_UNLOCK(); + OCF_METADATA_EVICTION_UNLOCK(cline); } diff --git a/src/eviction/lru.h b/src/eviction/lru.h index 601ec89..e8ec81c 100644 --- a/src/eviction/lru.h +++ b/src/eviction/lru.h @@ -8,14 +8,18 @@ #include "eviction.h" #include "lru_structs.h" +struct ocf_user_part; + void evp_lru_init_cline(struct ocf_cache *cache, ocf_cache_line_t cline); void evp_lru_rm_cline(struct ocf_cache *cache, ocf_cache_line_t cline); bool evp_lru_can_evict(struct ocf_cache *cache); uint32_t evp_lru_req_clines(struct ocf_cache *cache, ocf_queue_t io_queue, - ocf_part_id_t part_id, 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_init_evp(struct ocf_cache *cache, ocf_part_id_t part_id); -void evp_lru_dirty_cline(struct ocf_cache *cache, ocf_part_id_t part_id, uint32_t cline); -void evp_lru_clean_cline(struct ocf_cache *cache, ocf_part_id_t part_id, uint32_t cline); +void evp_lru_init_evp(struct ocf_cache *cache, struct ocf_user_part *part); +void evp_lru_dirty_cline(struct ocf_cache *cache, struct ocf_user_part *part, + uint32_t cline); +void evp_lru_clean_cline(struct ocf_cache *cache, struct ocf_user_part *part, + uint32_t cline); #endif diff --git a/src/eviction/ops.h b/src/eviction/ops.h index 9646e60..2952171 100644 --- a/src/eviction/ops.h +++ b/src/eviction/ops.h @@ -16,7 +16,7 @@ * @note This operation is called under WR metadata lock */ static inline void ocf_eviction_init_cache_line(struct ocf_cache *cache, - ocf_cache_line_t line, ocf_part_id_t part_id) + ocf_cache_line_t line) { uint8_t type; @@ -36,13 +36,12 @@ static inline void ocf_eviction_purge_cache_line( ENV_BUG_ON(type >= ocf_eviction_max); if (likely(evict_policy_ops[type].rm_cline)) { - OCF_METADATA_EVICTION_LOCK(); + OCF_METADATA_EVICTION_LOCK(line); evict_policy_ops[type].rm_cline(cache, line); - OCF_METADATA_EVICTION_UNLOCK(); + OCF_METADATA_EVICTION_UNLOCK(line); } } - static inline bool ocf_eviction_can_evict(struct ocf_cache *cache) { uint8_t type = cache->conf_meta->eviction_policy_type; @@ -54,7 +53,8 @@ static inline bool ocf_eviction_can_evict(struct ocf_cache *cache) } static inline uint32_t ocf_eviction_need_space(struct ocf_cache *cache, - ocf_queue_t io_queue, ocf_part_id_t part_id, uint32_t clines) + ocf_queue_t io_queue, struct ocf_user_part *part, + uint32_t clines) { uint8_t type; uint32_t result = 0; @@ -69,7 +69,7 @@ static inline uint32_t ocf_eviction_need_space(struct ocf_cache *cache, * eviction lock. */ result = evict_policy_ops[type].req_clines(cache, io_queue, - part_id, clines); + part, clines); } return result; @@ -83,23 +83,23 @@ static inline void ocf_eviction_set_hot_cache_line( ENV_BUG_ON(type >= ocf_eviction_max); if (likely(evict_policy_ops[type].hot_cline)) { - OCF_METADATA_EVICTION_LOCK(); + OCF_METADATA_EVICTION_LOCK(line); evict_policy_ops[type].hot_cline(cache, line); - OCF_METADATA_EVICTION_UNLOCK(); + OCF_METADATA_EVICTION_UNLOCK(line); } } static inline void ocf_eviction_initialize(struct ocf_cache *cache, - ocf_part_id_t part_id) + struct ocf_user_part *part) { uint8_t type = cache->conf_meta->eviction_policy_type; ENV_BUG_ON(type >= ocf_eviction_max); if (likely(evict_policy_ops[type].init_evp)) { - OCF_METADATA_EVICTION_LOCK(); - evict_policy_ops[type].init_evp(cache, part_id); - OCF_METADATA_EVICTION_UNLOCK(); + OCF_METADATA_EVICTION_LOCK_ALL(); + evict_policy_ops[type].init_evp(cache, part); + OCF_METADATA_EVICTION_UNLOCK_ALL(); } } diff --git a/src/metadata/metadata_hash.c b/src/metadata/metadata_hash.c index 031e66c..1e72bbf 100644 --- a/src/metadata/metadata_hash.c +++ b/src/metadata/metadata_hash.c @@ -563,6 +563,7 @@ int ocf_metadata_hash_init(struct ocf_cache *cache, for (i = 0; i < OCF_IO_CLASS_MAX + 1; i++) { cache->user_parts[i].config = &part_config[i]; cache->user_parts[i].runtime = &part_runtime[i]; + cache->user_parts[i].id = i; } /* Set core metadata */ @@ -1949,7 +1950,7 @@ static void _recovery_rebuild_cline_metadata(ocf_cache_t cache, ocf_metadata_add_to_collision(cache, core_id, core_line, hash_index, cache_line); - ocf_eviction_init_cache_line(cache, cache_line, part_id); + ocf_eviction_init_cache_line(cache, cache_line); ocf_eviction_set_hot_cache_line(cache, cache_line); diff --git a/src/metadata/metadata_partition_structs.h b/src/metadata/metadata_partition_structs.h index ffdec74..baa8c88 100644 --- a/src/metadata/metadata_partition_structs.h +++ b/src/metadata/metadata_partition_structs.h @@ -29,15 +29,37 @@ struct ocf_user_part_config { struct ocf_user_part_runtime { uint32_t curr_size; uint32_t head; - struct eviction_policy eviction; + struct eviction_policy eviction[OCF_NUM_EVICTION_LISTS]; struct cleaning_policy cleaning; }; -struct ocf_user_part { - struct ocf_user_part_config *config; - struct ocf_user_part_runtime *runtime; +/* Iterator state, visiting all eviction lists within a partition + in round robin order */ +struct ocf_lru_iter { + /* cache object */ + ocf_cache_t cache; + /* target partition */ + struct ocf_user_part *part; + /* per-partition cacheline iterator */ + ocf_cache_line_t curr_cline[OCF_NUM_EVICTION_LISTS]; + /* available (non-empty) eviction list bitmap rotated so that current + @evp is on the most significant bit */ + unsigned long long next_avail_evp; + /* number of available eviction lists */ + uint32_t num_avail_evps; + /* current eviction list index */ + uint32_t evp; +}; - struct ocf_lst_entry lst_valid; +struct ocf_user_part { + struct ocf_user_part_config *config; + struct ocf_user_part_runtime *runtime; + struct ocf_refcnt cleaning; + ocf_part_id_t id; + + struct ocf_lru_iter eviction_clean_iter; + uint32_t next_eviction_list; + struct ocf_lst_entry lst_valid; }; diff --git a/src/metadata/metadata_structs.h b/src/metadata/metadata_structs.h index f46576f..a592051 100644 --- a/src/metadata/metadata_structs.h +++ b/src/metadata/metadata_structs.h @@ -412,7 +412,7 @@ struct ocf_metadata_lock { env_rwsem global; /*!< global metadata lock (GML) */ env_rwlock status; /*!< Fast lock for status bits */ - env_spinlock eviction; /*!< Fast lock for eviction policy */ + env_spinlock eviction[OCF_NUM_EVICTION_LISTS]; /*!< Fast lock for eviction policy */ env_rwsem *hash; /*!< Hash bucket locks */ env_rwsem *collision_pages; /*!< Collision table page locks */ env_spinlock partition[OCF_IO_CLASS_MAX]; /* partition lock */ diff --git a/src/mngt/ocf_mngt_cache.c b/src/mngt/ocf_mngt_cache.c index 6f57d48..5286ffc 100644 --- a/src/mngt/ocf_mngt_cache.c +++ b/src/mngt/ocf_mngt_cache.c @@ -177,7 +177,7 @@ static void __init_partitions(ocf_cache_t cache) /* Add other partition to the cache and make it as dummy */ for (i_part = 0; i_part < OCF_IO_CLASS_MAX; i_part++) { - ocf_refcnt_freeze(&cache->refcnt.cleaning[i_part]); + ocf_refcnt_freeze(&cache->user_parts[i_part].cleaning); if (i_part == PARTITION_DEFAULT) continue; @@ -191,14 +191,15 @@ static void __init_partitions(ocf_cache_t cache) static void __init_partitions_attached(ocf_cache_t cache) { + struct ocf_user_part *part; ocf_part_id_t part_id; for (part_id = 0; part_id < OCF_IO_CLASS_MAX; part_id++) { - cache->user_parts[part_id].runtime->head = - cache->device->collision_table_entries; - cache->user_parts[part_id].runtime->curr_size = 0; + part = &cache->user_parts[part_id]; - ocf_eviction_initialize(cache, part_id); + part->runtime->head = cache->device->collision_table_entries; + part->runtime->curr_size = 0; + ocf_eviction_initialize(cache, part); } } diff --git a/src/ocf_cache_priv.h b/src/ocf_cache_priv.h index d0578ca..660ae39 100644 --- a/src/ocf_cache_priv.h +++ b/src/ocf_cache_priv.h @@ -122,8 +122,6 @@ struct ocf_cache { /* # of requests accessing attached metadata, excluding * management reqs */ struct ocf_refcnt metadata; - /* # of forced cleaning requests (eviction path) */ - struct ocf_refcnt cleaning[OCF_IO_CLASS_MAX]; } refcnt; uint32_t fallback_pt_error_threshold; diff --git a/src/ocf_def_priv.h b/src/ocf_def_priv.h index 78fa538..b1d8cf2 100644 --- a/src/ocf_def_priv.h +++ b/src/ocf_def_priv.h @@ -49,4 +49,11 @@ /* call conditional reschedule with default interval */ #define OCF_COND_RESCHED_DEFAULT(cnt) OCF_COND_RESCHED(cnt, 1000000) +static inline unsigned long long +ocf_rotate_right(unsigned long long bits, unsigned shift, unsigned width) +{ + return ((bits >> shift) | (bits << (width - shift))) & + ((1ULL << width) - 1); +} + #endif diff --git a/src/utils/utils_cache_line.c b/src/utils/utils_cache_line.c index 3acdb4a..342500a 100644 --- a/src/utils/utils_cache_line.c +++ b/src/utils/utils_cache_line.c @@ -101,6 +101,7 @@ void set_cache_line_clean(struct ocf_cache *cache, uint8_t start_bit, { ocf_cache_line_t line = req->map[map_idx].coll_idx; ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache, line); + struct ocf_user_part *part = &cache->user_parts[part_id]; uint8_t evp_type = cache->conf_meta->eviction_policy_type; bool line_is_clean; @@ -130,7 +131,7 @@ void set_cache_line_clean(struct ocf_cache *cache, uint8_t start_bit, part_counters[part_id].dirty_clines); if (likely(evict_policy_ops[evp_type].clean_cline)) - evict_policy_ops[evp_type].clean_cline(cache, part_id, line); + evict_policy_ops[evp_type].clean_cline(cache, part, line); ocf_purge_cleaning_policy(cache, line); } @@ -143,6 +144,7 @@ void set_cache_line_dirty(struct ocf_cache *cache, uint8_t start_bit, { ocf_cache_line_t line = req->map[map_idx].coll_idx; ocf_part_id_t part_id = ocf_metadata_get_partition_id(cache, line); + struct ocf_user_part *part = &cache->user_parts[part_id]; uint8_t evp_type = cache->conf_meta->eviction_policy_type; bool line_was_dirty; @@ -170,7 +172,7 @@ void set_cache_line_dirty(struct ocf_cache *cache, uint8_t start_bit, part_counters[part_id].dirty_clines); if (likely(evict_policy_ops[evp_type].dirty_cline)) - evict_policy_ops[evp_type].dirty_cline(cache, part_id, line); + evict_policy_ops[evp_type].dirty_cline(cache, part, line); } } diff --git a/src/utils/utils_cleaner.c b/src/utils/utils_cleaner.c index db2620d..d11528b 100644 --- a/src/utils/utils_cleaner.c +++ b/src/utils/utils_cleaner.c @@ -1017,7 +1017,7 @@ void ocf_cleaner_refcnt_freeze(ocf_cache_t cache) ocf_part_id_t part_id; for_each_part(cache, curr_part, part_id) - ocf_refcnt_freeze(&cache->refcnt.cleaning[part_id]); + ocf_refcnt_freeze(&curr_part->cleaning); } void ocf_cleaner_refcnt_unfreeze(ocf_cache_t cache) @@ -1026,7 +1026,7 @@ void ocf_cleaner_refcnt_unfreeze(ocf_cache_t cache) ocf_part_id_t part_id; for_each_part(cache, curr_part, part_id) - ocf_refcnt_unfreeze(&cache->refcnt.cleaning[part_id]); + ocf_refcnt_unfreeze(&curr_part->cleaning); } static void ocf_cleaner_refcnt_register_zero_cb_finish(void *priv) @@ -1050,7 +1050,7 @@ void ocf_cleaner_refcnt_register_zero_cb(ocf_cache_t cache, for_each_part(cache, curr_part, part_id) { env_atomic_inc(&ctx->waiting); - ocf_refcnt_register_zero_cb(&cache->refcnt.cleaning[part_id], + ocf_refcnt_register_zero_cb(&curr_part->cleaning, ocf_cleaner_refcnt_register_zero_cb_finish, ctx); } diff --git a/src/utils/utils_cleaner.h b/src/utils/utils_cleaner.h index 5de122d..99e456e 100644 --- a/src/utils/utils_cleaner.h +++ b/src/utils/utils_cleaner.h @@ -19,7 +19,8 @@ * @retval Non-zero Means skip this cache line and do not clean it */ typedef int (*ocf_cleaner_get_item)(struct ocf_cache *cache, - void *getter_context, uint32_t item, ocf_cache_line_t *line); + void *getter_context, uint32_t item, + ocf_cache_line_t *line); /** * @brief Cleaning attributes for clean request diff --git a/src/utils/utils_part.c b/src/utils/utils_part.c index 06140f4..3da853b 100644 --- a/src/utils/utils_part.c +++ b/src/utils/utils_part.c @@ -142,7 +142,7 @@ void ocf_part_move(struct ocf_request *req) ocf_metadata_add_to_partition(cache, id_new, line); /* Add to new eviction */ - ocf_eviction_init_cache_line(cache, line, id_new); + ocf_eviction_init_cache_line(cache, line); ocf_eviction_set_hot_cache_line(cache, line); /* Check if cache line is dirty. If yes then need to change diff --git a/tests/unit/framework/README b/tests/unit/framework/README index e3e98e8..b74c5a1 100644 --- a/tests/unit/framework/README +++ b/tests/unit/framework/README @@ -9,3 +9,9 @@ GENERATING NEW TEST RUNNING SINGLE TEST Executable tests files are stored by default in 'UT_dir/build/sources_to_test_repository/' + +REQUIREMENTS FOR BUILD: +- cmocka +- cmake +- ctags (Exuberant version) +- nm, gcc, make diff --git a/tests/unit/tests/eviction/lru.c/lru_iter.c b/tests/unit/tests/eviction/lru.c/lru_iter.c new file mode 100644 index 0000000..8d97492 --- /dev/null +++ b/tests/unit/tests/eviction/lru.c/lru_iter.c @@ -0,0 +1,481 @@ +/* + * src/eviction/lru.c + * lru_iter_next + * + * INSERT HERE LIST OF FUNCTIONS YOU WANT TO LEAVE + * ONE FUNCTION PER LINE + * lru_iter_init + * _lru_next_evp + * _lru_evp_is_empty + * _lru_evp_set_empty + * _lru_evp_all_empty + * ocf_rotate_right + * + */ + +#undef static + +#undef inline + + +#include +#include +#include +#include +#include "print_desc.h" + +#include "eviction.h" +#include "lru.h" +#include "ops.h" +#include "../utils/utils_cleaner.h" +#include "../utils/utils_cache_line.h" +#include "../concurrency/ocf_concurrency.h" +#include "../mngt/ocf_mngt_common.h" +#include "../engine/engine_zero.h" +#include "../ocf_request.h" + +#include "eviction/lru.c/lru_iter_generated_wraps.c" + +//#define DEBUG + +ocf_cache_line_t test_cases[10 * OCF_NUM_EVICTION_LISTS][OCF_NUM_EVICTION_LISTS][20]; +unsigned num_cases = 20; + +void write_test_case_description(void) +{ + unsigned i, j, l; + unsigned test_case = 0; + + // case 0 - all lists empty + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { + test_cases[0][i][test_case] = -1; + } + + // case 1 - all lists with single element + test_case++; + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { + test_cases[0][i][test_case] = 10 * i; + test_cases[1][i][test_case] = -1; + } + + // case 2 - all lists have between 1 and 5 elements, increasingly + test_case++; + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { + unsigned num_elements = 1 + i / (OCF_NUM_EVICTION_LISTS / 4); + + for (j = 0; j < num_elements; j++) + test_cases[j][i][test_case] = 10 * i + j; + test_cases[j][i][test_case] = -1; + } + + // case 3 - all lists have between 1 and 5 elements, modulo index + test_case++; + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { + unsigned num_elements = 1 + (i % 5); + + for (j = 0; j < num_elements; j++) + test_cases[j][i][test_case] = 10 * i + j; + test_cases[j][i][test_case] = -1; + } + + // case 4 - all lists have between 0 and 4 elements, increasingly + test_case++; + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { + unsigned num_elements = i / (OCF_NUM_EVICTION_LISTS / 4); + + for (j = 0; j < num_elements; j++) + test_cases[j][i][test_case] = 10 * i + j; + test_cases[j][i][test_case] = -1; + } + + // case 5 - all lists have between 0 and 4 elements, modulo index + test_case++; + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { + unsigned num_elements = (i % 5); + + for (j = 0; j < num_elements; j++) + test_cases[j][i][test_case] = 10 * i + j; + test_cases[j][i][test_case] = -1; + } + + // case 6 - list length increasing by 1 from 0 + test_case++; + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { + unsigned num_elements = i; + + for (j = 0; j < num_elements; j++) + test_cases[j][i][test_case] = OCF_NUM_EVICTION_LISTS * i + j; + test_cases[j][i][test_case] = -1; + } + + // case 7 - list length increasing by 1 from 1 + test_case++; + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { + unsigned num_elements = i + 1; + + for (j = 0; j < num_elements; j++) + test_cases[j][i][test_case] = 2 * OCF_NUM_EVICTION_LISTS * i + j; + test_cases[j][i][test_case] = -1; + } + + // case 8 - list length increasing by 4 from 0 + test_case++; + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { + unsigned num_elements = 4 * i; + + for (j = 0; j < num_elements; j++) + test_cases[j][i][test_case] = 4 * OCF_NUM_EVICTION_LISTS * i + j; + test_cases[j][i][test_case] = -1; + } + + // case 9 - list length increasing by 4 from 1 + test_case++; + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { + unsigned num_elements = 4 * i + 1; + + for (j = 0; j < num_elements; j++) + test_cases[j][i][test_case] = 5 * OCF_NUM_EVICTION_LISTS * i + j; + test_cases[j][i][test_case] = -1; + } + + // cases 10-19: cases 0-9 rotated right by 4 + l = test_case; + test_case++; + while(test_case < 2 * (l + 1)) { + unsigned matching_case = test_case - l - 1; + + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { + unsigned curr_list = (i + 4) % OCF_NUM_EVICTION_LISTS; + j = 0; + while(test_cases[j][i][matching_case] != -1) { + test_cases[j][curr_list][test_case] = + test_cases[j][i][matching_case]; + j++; + } + test_cases[j][curr_list][test_case] = -1; + } + test_case++; + } + +#ifdef DEBUG + for (test_case = 0; test_case < num_cases; test_case++) { + print_message("test case no %d\n", test_case); + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) { + print_message("list %02u: ", i); + j = 0; + while (test_cases[j][i][test_case] != -1) { + print_message("%u ", test_cases[j][i][test_case]); + j++; + } + print_message("\n"); + } + print_message("========\n\n"); + } +#endif +} + +unsigned current_case; + +struct ocf_lru_list list; + +struct ocf_lru_list *__wrap_evp_lru_get_list(struct ocf_user_part *part, + uint32_t evp, bool clean) +{ + unsigned i = 0; + + while (test_cases[i][evp][current_case] != -1) + i++; + + if (i == 0) { + list.head = -1; + list.tail = -1; + list.num_nodes = 0; + } else { + list.head = test_cases[0][evp][current_case]; + list.tail = test_cases[i - 1][evp][current_case]; + list.num_nodes = i; + } + + return &list; +} + +inline struct ocf_lru_list *__wrap_evp_get_cline_list(ocf_cache_t cache, + ocf_cache_line_t cline) +{ + return __wrap_evp_lru_get_list(NULL, cline % OCF_NUM_EVICTION_LISTS, true); +} + + +union eviction_policy_meta policy; + +union eviction_policy_meta *__wrap_ocf_metadata_get_eviction_policy( + struct ocf_cache *cache, ocf_cache_line_t line) +{ + unsigned i, j; + + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) + { + j = 0; + + while (test_cases[j][i][current_case] != -1) { + if (test_cases[j][i][current_case] == line) { + if (j == 0) { + policy.lru.prev = -1; + } else { + policy.lru.prev = + test_cases[j - 1][i][current_case]; + } + + policy.lru.next = test_cases[j + 1][i][current_case]; +#ifdef DEBUG + print_message("[%u] next %u prev %u\n", + line, policy.lru.next, + policy.lru.prev); +#endif + return &policy; + } + j++; + } + + } + + print_message("use case %d cache line %d not found\n", + current_case, line); + assert(false); +} + + +static void _lru_run_test(unsigned test_case) +{ + unsigned start_pos; + current_case = test_case; + + for (start_pos = 0; start_pos < OCF_NUM_EVICTION_LISTS; start_pos++) + { + struct ocf_lru_iter iter; + ocf_cache_line_t cache_line, expected_cache_line; + unsigned curr_evp = start_pos; + unsigned pos[OCF_NUM_EVICTION_LISTS]; + unsigned i; + + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) + { + pos[i] = -1; + while(test_cases[pos[i] + 1][i][test_case] != -1) + pos[i]++; + } + + lru_iter_init(&iter, NULL, NULL, start_pos, false); + + do { + /* get cacheline from iterator */ + cache_line = lru_iter_next(&iter); + + /* check what is expected to be returned from iterator */ + if (pos[curr_evp] == -1) { + i = 1; + while (i < OCF_NUM_EVICTION_LISTS && + pos[(curr_evp + i) % OCF_NUM_EVICTION_LISTS] + == -1) { + i++; + } + if (i == OCF_NUM_EVICTION_LISTS) { + /* reached end of lists */ + expected_cache_line = -1; + } else { + curr_evp = (curr_evp + i) % OCF_NUM_EVICTION_LISTS; + expected_cache_line = test_cases[pos[curr_evp]] + [curr_evp][test_case]; + pos[curr_evp]--; + } + } else { + expected_cache_line = test_cases[pos[curr_evp]] + [curr_evp][test_case]; + pos[curr_evp]--; + } + + assert_int_equal(cache_line, expected_cache_line); + + curr_evp = (curr_evp + 1) % OCF_NUM_EVICTION_LISTS; + } while (cache_line != -1); + + /* make sure all cachelines are visited */ + for (i = 0; i < OCF_NUM_EVICTION_LISTS; i++) + { + assert_int_equal((unsigned)-1, pos[i]); + } + } +} + +static void lru_iter_next_test00(void **state) +{ + print_test_description("lru iter test case 00\n"); + _lru_run_test(0); + return; +} + +static void lru_iter_next_test01(void **state) +{ + print_test_description("lru iter test case 01\n"); + _lru_run_test(1); + return; +} + +static void lru_iter_next_test02(void **state) +{ + print_test_description("lru iter test case 02\n"); + _lru_run_test(2); + return; +} + +static void lru_iter_next_test03(void **state) +{ + print_test_description("lru iter test case 03\n"); + _lru_run_test(3); + return; +} + +static void lru_iter_next_test04(void **state) +{ + print_test_description("lru iter test case 04\n"); + _lru_run_test(4); + return; +} + +static void lru_iter_next_test05(void **state) +{ + + print_test_description("lru iter test case 05\n"); + _lru_run_test(5); + return; +} + +static void lru_iter_next_test06(void **state) +{ + print_test_description("lru iter test case 06\n"); + _lru_run_test(6); + return; +} + +static void lru_iter_next_test07(void **state) +{ + print_test_description("lru iter test case 07\n"); + _lru_run_test(7); + return; +} + +static void lru_iter_next_test08(void **state) +{ + print_test_description("lru iter test case 08\n"); + _lru_run_test(8); + return; +} + +static void lru_iter_next_test09(void **state) +{ + print_test_description("lru iter test case 09\n"); + _lru_run_test(9); + return; +} + +static void lru_iter_next_test10(void **state) +{ + print_test_description("lru iter test case 00\n"); + _lru_run_test(10); + return; +} + +static void lru_iter_next_test11(void **state) +{ + print_test_description("lru iter test case 11\n"); + _lru_run_test(11); + return; +} + +static void lru_iter_next_test12(void **state) +{ + print_test_description("lru iter test case 12\n"); + _lru_run_test(12); + return; +} + +static void lru_iter_next_test13(void **state) +{ + print_test_description("lru iter test case 13\n"); + _lru_run_test(13); + return; +} + +static void lru_iter_next_test14(void **state) +{ + print_test_description("lru iter test case 14\n"); + _lru_run_test(14); + return; +} + +static void lru_iter_next_test15(void **state) +{ + print_test_description("lru iter test case 15\n"); + _lru_run_test(15); + return; +} + +static void lru_iter_next_test16(void **state) +{ + print_test_description("lru iter test case 16\n"); + _lru_run_test(16); + return; +} + +static void lru_iter_next_test17(void **state) +{ + print_test_description("lru iter test case 17\n"); + _lru_run_test(17); + return; +} + +static void lru_iter_next_test18(void **state) +{ + print_test_description("lru iter test case 18\n"); + _lru_run_test(18); + return; +} + +static void lru_iter_next_test19(void **state) +{ + print_test_description("lru iter test case 19\n"); + _lru_run_test(19); + return; +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(lru_iter_next_test00), + cmocka_unit_test(lru_iter_next_test01), + cmocka_unit_test(lru_iter_next_test02), + cmocka_unit_test(lru_iter_next_test03), + cmocka_unit_test(lru_iter_next_test04), + cmocka_unit_test(lru_iter_next_test05), + cmocka_unit_test(lru_iter_next_test06), + cmocka_unit_test(lru_iter_next_test07), + cmocka_unit_test(lru_iter_next_test08), + cmocka_unit_test(lru_iter_next_test09), + cmocka_unit_test(lru_iter_next_test10), + cmocka_unit_test(lru_iter_next_test11), + cmocka_unit_test(lru_iter_next_test12), + cmocka_unit_test(lru_iter_next_test13), + cmocka_unit_test(lru_iter_next_test14), + cmocka_unit_test(lru_iter_next_test15), + cmocka_unit_test(lru_iter_next_test16), + cmocka_unit_test(lru_iter_next_test17), + cmocka_unit_test(lru_iter_next_test18), + cmocka_unit_test(lru_iter_next_test19) + }; + + print_message("Unit test for lru_iter_next\n"); + + write_test_case_description(); + + return cmocka_run_group_tests(tests, NULL, NULL); +}