From 7f3f2ad11581053f811bdbd45b279aa3348618ae Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Tue, 16 Feb 2021 03:50:27 -0500 Subject: [PATCH 1/2] Evict from overflown pinned ioclass If an ioclass is pinned but it exceeded its occupancy limit, it should be evicted anyway. Signed-off-by: Michal Mielewczyk --- src/eviction/eviction.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/eviction/eviction.c b/src/eviction/eviction.c index 9738b57..7e6bd21 100644 --- a/src/eviction/eviction.c +++ b/src/eviction/eviction.c @@ -62,7 +62,7 @@ static inline uint32_t ocf_evict_part_do(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, - bool overflown_only, uint32_t max_priority) + bool overflown_only, int16_t max_priority) { uint32_t to_evict = 0, evicted = 0; struct ocf_user_part *part; @@ -84,8 +84,9 @@ static inline uint32_t ocf_evict_partitions(ocf_cache_t cache, */ break; } - if (!part->config->flags.eviction) { - /* no more partitions available for viction + if (!overflown_only && !part->config->flags.eviction) { + /* If partition is overflown it should be evcited + * even if its pinned */ break; } @@ -132,8 +133,9 @@ static inline uint32_t ocf_evict_do(ocf_cache_t cache, * priority in this case, as overflown partitions should * free its cachelines regardless of destination partition * priority. */ + evicted = ocf_evict_partitions(cache, io_queue, evict_cline_no, - true, OCF_IO_CLASS_PRIO_HIGHEST); + true, OCF_IO_CLASS_PRIO_PINNED); if (evicted >= evict_cline_no) return evicted; /* Not enough cachelines in overflown partitions. Go through From 83f142c9875d94cf2959540df6abf628733017ed Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Tue, 16 Feb 2021 03:59:22 -0500 Subject: [PATCH 2/2] Functional test for overflown pinned ioclass Signed-off-by: Michal Mielewczyk --- .../tests/eviction/test_eviction.py | 86 ++++++++++++++++++- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/tests/functional/tests/eviction/test_eviction.py b/tests/functional/tests/eviction/test_eviction.py index a55ce14..7a004c9 100644 --- a/tests/functional/tests/eviction/test_eviction.py +++ b/tests/functional/tests/eviction/test_eviction.py @@ -4,6 +4,7 @@ # import logging +from math import ceil, isclose from ctypes import c_int import pytest @@ -12,7 +13,7 @@ from pyocf.types.cache import Cache, CacheMode from pyocf.types.core import Core from pyocf.types.data import Data from pyocf.types.io import IoDir -from pyocf.types.shared import OcfCompletion, CacheLineSize, SeqCutOffPolicy +from pyocf.types.shared import OcfCompletion, CacheLineSize, SeqCutOffPolicy, CacheLines from pyocf.types.volume import Volume from pyocf.utils import Size @@ -74,10 +75,89 @@ def test_write_size_greater_than_cache(pyocf_ctx, mode: CacheMode, cls: CacheLin io_size_bigger_than_cache.blocks_4k -def send_io(exported_obj: Core, data: Data, addr: int = 0): +@pytest.mark.parametrize("cls", CacheLineSize) +def test_evict_overflown_pinned(pyocf_ctx, cls: CacheLineSize): + """ Verify if overflown pinned ioclass is evicted """ + cache_device = Volume(Size.from_MiB(35)) + core_device = Volume(Size.from_MiB(100)) + cache = Cache.start_on_device( + cache_device, cache_mode=CacheMode.WT, cache_line_size=cls + ) + core = Core.using_device(core_device) + cache.add_core(core) + + test_ioclass_id = 1 + pinned_ioclass_id = 2 + pinned_ioclass_max_occupancy = 10 + + cache.configure_partition( + part_id=test_ioclass_id, + name="default_ioclass", + min_size=0, + max_size=100, + priority=1, + ) + cache.configure_partition( + part_id=pinned_ioclass_id, + name="pinned_ioclass", + min_size=0, + max_size=pinned_ioclass_max_occupancy, + priority=-1, + ) + + cache.set_seq_cut_off_policy(SeqCutOffPolicy.NEVER) + + cache_size = cache.get_stats()["conf"]["size"] + + data = Data(4096) + + # Populate cache with data + for i in range(cache_size.blocks_4k): + send_io(core, data, i * 4096, test_ioclass_id) + + part_current_size = CacheLines( + cache.get_partition_info(part_id=test_ioclass_id)["_curr_size"], cls + ) + assert isclose( + part_current_size.blocks_4k, cache_size.blocks_4k, abs_tol=Size(cls).blocks_4k + ), "Failed to populate the default partition" + + # Repart - force overflow of second partition occupancy limit + pinned_double_size = ceil( + (cache_size.blocks_4k * pinned_ioclass_max_occupancy * 2) / 100 + ) + for i in range(pinned_double_size): + send_io(core, data, i * 4096, pinned_ioclass_id) + + part_current_size = CacheLines( + cache.get_partition_info(part_id=pinned_ioclass_id)["_curr_size"], cls + ) + assert isclose( + part_current_size.blocks_4k, pinned_double_size, abs_tol=Size(cls).blocks_4k + ), "Occupancy of pinned ioclass doesn't match expected value" + + # Trigger IO to the default ioclass - force eviction from overlown ioclass + for i in range(cache_size.blocks_4k): + send_io(core, data, (cache_size.blocks_4k + i) * 4096, test_ioclass_id) + + part_current_size = CacheLines( + cache.get_partition_info(part_id=pinned_ioclass_id)["_curr_size"], cls + ) + assert isclose( + part_current_size.blocks_4k, + ceil(cache_size.blocks_4k * 0.1), + abs_tol=Size(cls).blocks_4k, + ), "Overflown part has not been evicted" + + +def send_io(exported_obj: Core, data: Data, addr: int = 0, target_ioclass: int = 0): io = exported_obj.new_io( exported_obj.cache.get_default_queue(), - addr, data.size, IoDir.WRITE, 0, 0 + addr, + data.size, + IoDir.WRITE, + target_ioclass, + 0, ) io.set_data(data)