def test_attach_different_size( pyocf_ctx, new_cache_size, mode: CacheMode, cls: CacheLineSize ): """Start cache and add partition with limited occupancy. Fill partition with data, attach cache with different size and trigger IO. Verify if occupancy thresold is respected with both original and new cache device. """ cache_device = Volume(Size.from_MiB(35)) core_device = Volume(Size.from_MiB(100)) cache = Cache.start_on_device(cache_device, cache_mode=mode, cache_line_size=cls) core = Core.using_device(core_device) cache.add_core(core) cache.configure_partition( part_id=1, name="test_part", min_size=0, max_size=50, priority=1 ) cache.set_seq_cut_off_policy(SeqCutOffPolicy.NEVER) cache_size = cache.get_stats()["conf"]["size"] block_size = 4096 data = bytes(block_size) for i in range(cache_size.blocks_4k): io_to_exp_obj(core, block_size * i, block_size, data, 0, IoDir.WRITE, 1, 0) part_current_size = CacheLines( cache.get_partition_info(part_id=1)["_curr_size"], cls ) assert part_current_size.blocks_4k == cache_size.blocks_4k * 0.5 cache.detach_device() new_cache_device = Volume(Size.from_MiB(new_cache_size)) cache.attach_device(new_cache_device, force=True) cache_size = cache.get_stats()["conf"]["size"] for i in range(cache_size.blocks_4k): io_to_exp_obj(core, block_size * i, block_size, data, 0, IoDir.WRITE, 1, 0) part_current_size = CacheLines( cache.get_partition_info(part_id=1)["_curr_size"], cls ) assert part_current_size.blocks_4k == cache_size.blocks_4k * 0.5
def test_start_stop_incrementally(pyocf_ctx): """Starting/stopping multiple caches incrementally. Check whether OCF behaves correctly when few caches at a time are in turns added and removed (#added > #removed) until their number reaches limit, and then proportions are reversed and number of caches gradually falls to 0. """ caches = [] caches_limit = 10 add = True run = True increase = True while run: if add: for i in range(0, randrange(3, 5) if increase else randrange(1, 3)): cache_device = Volume(Size.from_MiB(20)) cache_mode = CacheMode(randrange(0, len(CacheMode))) size = 4096 * 2**randrange(0, len(CacheLineSize)) cache_line_size = CacheLineSize(size) cache = Cache.start_on_device(cache_device, cache_mode=cache_mode, cache_line_size=cache_line_size) caches.append(cache) stats = cache.get_stats() assert stats["conf"]["cache_mode"] == cache_mode, "Cache mode" assert stats["conf"][ "cache_line_size"] == cache_line_size, "Cache line size" assert stats["conf"]["cache_id"] == len(caches), "Cache id" if len(caches) == caches_limit: increase = False else: for i in range(0, randrange(1, 3) if increase else randrange(3, 5)): if len(caches) == 0: run = False break cache = caches.pop() logger.info("Getting stats before stopping cache") stats = cache.get_stats() cache_id = stats["conf"]["cache_id"] cache.stop() assert get_cache_by_id( pyocf_ctx, cache_id) != 0, "Try getting cache after stopping it" add = not add
def test_start_read_first_and_check_mode(pyocf_ctx, mode: CacheMode, cls: CacheLineSize): """Starting cache in different modes with different cache line sizes. After start check proper cache mode behaviour, starting with read operation. """ cache_device = RamVolume(Size.from_MiB(50)) core_device = RamVolume(Size.from_MiB(5)) cache = Cache.start_on_device(cache_device, cache_mode=mode, cache_line_size=cls) core = Core.using_device(core_device) cache.add_core(core) front_vol = CoreVolume(core, open=True) bottom_vol = core.get_volume() queue = cache.get_default_queue() logger.info("[STAGE] Initial write to core device") test_data = Data.from_string("This is test data") io_to_core(bottom_vol, queue, test_data, Size.from_sector(1).B) cache_device.reset_stats() core_device.reset_stats() logger.info("[STAGE] Initial read from exported object") io_from_exported_object(front_vol, queue, test_data.size, Size.from_sector(1).B) check_stats_read_empty(core, mode, cls) logger.info("[STAGE] Write to exported object after initial read") cache_device.reset_stats() core_device.reset_stats() test_data = Data.from_string("Changed test data") io_to_core(front_vol, queue, test_data, Size.from_sector(1).B) check_stats_write_after_read(core, mode, cls, True) logger.info("[STAGE] Read from exported object after write") io_from_exported_object(front_vol, queue, test_data.size, Size.from_sector(1).B) check_stats_read_after_write(core, mode, cls) check_md5_sums(core, mode)
def test_neg_change_cache_mode(pyocf_ctx, cm, cls): """ Test whether it is possible to change cache mode to invalid value. :param pyocf_ctx: basic pyocf context fixture :param cm: cache mode we start with :param cls: cache line size we start with """ # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache.start_on_device(cache_device, cache_mode=cm, cache_line_size=cls) # Change cache mode to invalid one and check if failed for i in RandomGenerator(DefaultRanges.UINT32): if i in [item.value for item in CacheMode]: continue with pytest.raises(OcfError, match="Error changing cache mode"): cache.change_cache_mode(i)
def test_start_stop_noqueue(pyocf_ctx): # cache object just to construct cfg conveniently _cache = Cache(pyocf_ctx.ctx_handle) cache_handle = c_void_p() status = pyocf_ctx.lib.ocf_mngt_cache_start( pyocf_ctx.ctx_handle, byref(cache_handle), byref(_cache.cfg) ) assert not status, "Failed to start cache: {}".format(status) # stop without creating mngmt queue c = OcfCompletion( [("cache", c_void_p), ("priv", c_void_p), ("error", c_int)] ) pyocf_ctx.lib.ocf_mngt_cache_stop(cache_handle, c, None) c.wait() assert not c.results["error"], "Failed to stop cache: {}".format(c.results["error"])
def test_flush_after_mngmt(pyocf_ctx): """ Check whether underlying volumes volatile caches (VC) are flushed after management operation """ block_size = 4096 data = bytes(block_size) cache_device = FlushValVolume(Size.from_MiB(30)) core_device = FlushValVolume(Size.from_MiB(30)) # after start cache VC must be cleared cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WT) assert cache_device.flush_last # adding core must flush VC core = Core.using_device(core_device) cache.add_core(core) assert cache_device.flush_last # WT I/O to write data to core and cache VC io_to_exp_obj(core, block_size * 0, block_size, data, 0, IoDir.WRITE, 0) # WB I/O to produce dirty cachelines in CAS cache.change_cache_mode(CacheMode.WB) io_to_exp_obj(core, block_size * 1, block_size, data, 0, IoDir.WRITE, 0) # after cache flush VCs are expected to be cleared cache.flush() assert cache_device.flush_last assert core_device.flush_last # I/O to write data to cache device VC io_to_exp_obj(core, block_size * 0, block_size, data, 0, IoDir.WRITE, 0) # cache save must flush VC cache.save() assert cache_device.flush_last # I/O to write data to cache device VC io_to_exp_obj(core, block_size * 0, block_size, data, 0, IoDir.WRITE, 0) # cache stop must flush VC cache.stop() assert cache_device.flush_last
def test_neg_set_promotion_policy(pyocf_ctx, cm, cls): """ Test whether it is possible to set invalid param for promotion policy :param pyocf_ctx: basic pyocf context fixture :param cm: cache mode we start with :param cls: cache line size we start with :return: """ # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache.start_on_device(cache_device, cache_mode=cm, cache_line_size=cls) # Change to invalid promotion policy and check if failed for i in RandomGenerator(DefaultRanges.UINT32): if i in [item.value for item in PromotionPolicy]: continue with pytest.raises(OcfError, match="Error setting promotion policy"): cache.set_promotion_policy(i)
def test_neg_set_acp_param(pyocf_ctx, cm, cls): """ Test whether it is possible to set invalid param for acp cleaning policy :param pyocf_ctx: basic pyocf context fixture :param cm: cache mode we start with :param cls: cache line size we start with :return: """ # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache.start_on_device(cache_device, cache_mode=cm, cache_line_size=cls) # Change invalid acp param and check if failed for i in RandomGenerator(DefaultRanges.UINT32): if i in [item.value for item in AcpParams]: continue with pytest.raises(OcfError, match="Error setting cleaning policy param"): cache.set_cleaning_policy_param(CleaningPolicy.ALRU, i, 1)
def test_init_nhit(pyocf_ctx, promotion_policy): """ Check if starting cache with promotion policy is reflected in stats 1. Create core/cache pair with parametrized promotion policy 2. Get cache statistics * verify that promotion policy type is properly reflected in stats """ cache_device = Volume(Size.from_MiB(30)) core_device = Volume(Size.from_MiB(30)) cache = Cache.start_on_device(cache_device, promotion_policy=promotion_policy) core = Core.using_device(core_device) cache.add_core(core) assert cache.get_stats()["conf"]["promotion_policy"] == promotion_policy
def test_start_check_default(pyocf_ctx): """Test if default values are correct after start. """ cache_device = RamVolume(Size.from_MiB(50)) core_device = RamVolume(Size.from_MiB(10)) cache = Cache.start_on_device(cache_device) core = Core.using_device(core_device) cache.add_core(core) # Check if values are default stats = cache.get_stats() assert stats["conf"]["cleaning_policy"] == CleaningPolicy.DEFAULT assert stats["conf"]["cache_mode"] == CacheMode.DEFAULT assert stats["conf"]["cache_line_size"] == CacheLineSize.DEFAULT core_stats = core.get_stats() assert core_stats["seq_cutoff_policy"] == SeqCutOffPolicy.DEFAULT
def test_write_size_greater_than_cache(pyocf_ctx, mode: CacheMode, cls: CacheLineSize): """Test if eviction does not occur when IO greater than cache size is submitted. """ cache_device = Volume( Size.from_MiB(20)) # this gives about 1.375 MiB actual caching space core_device = Volume(Size.from_MiB(5)) cache = Cache.start_on_device(cache_device, cache_mode=mode, cache_line_size=cls) core_exported = Core.using_device(core_device) cache.add_core(core_exported) cache.set_seq_cut_off_policy(SeqCutOffPolicy.NEVER) valid_io_size = Size.from_KiB(512) test_data = Data(valid_io_size) send_io(core_exported, test_data) stats = core_exported.cache.get_stats() assert stats["usage"]["occupancy"]["value"] == (valid_io_size.B / Size.from_KiB(4).B),\ "Occupancy after first IO" prev_writes_to_core = stats["block"]["core_volume_wr"]["value"] # Anything below 5 MiB is a valid size (less than core device size) # Writing over 1.375 MiB in this case should go directly to core and shouldn't trigger eviction io_size_bigger_than_cache = Size.from_MiB(2) test_data = Data(io_size_bigger_than_cache) send_io(core_exported, test_data) stats = core_exported.cache.get_stats() # Writes from IO greater than cache size should go directly to core # Writes to core should equal the following: # Previous writes to core + size written + size cleaned (reads from cache) assert stats["block"]["core_volume_wr"]["value"] == \ stats["block"]["cache_volume_rd"]["value"] + \ prev_writes_to_core + io_size_bigger_than_cache.B / Size.from_KiB(4).B, \ "Writes to core after second IO" # Occupancy shouldn't change (no eviction) assert stats["usage"]["occupancy"]["value"] == (valid_io_size.B / Size.from_KiB(4).B),\ "Occupancy after second IO"
def test_30add_remove(pyocf_ctx): # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache.start_on_device(cache_device) # Create core device core_device = Volume(S.from_MiB(10)) core = Core.using_device(core_device) # Add and remove core device in a loop 100 times # Check statistics after every operation for i in range(0, 30): cache.add_core(core) stats = cache.get_stats() assert stats["conf"]["core_count"] == 1 cache.remove_core(core) stats = cache.get_stats() assert stats["conf"]["core_count"] == 0
def test_remove_dirty_no_flush(pyocf_ctx, cache_mode, cls): # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache.start_on_device(cache_device, cache_mode=cache_mode, cache_line_size=cls) # Create core device core_device = Volume(S.from_MiB(10)) core = Core.using_device(core_device) cache.add_core(core) # Prepare data core_size = core.get_stats()["size"] data = Data(core_size.B) _io_to_core(core, data) # Remove core from cache cache.remove_core(core)
def test_neg_set_cleaning_policy(pyocf_ctx, cm, cls): """ Test whether it is possible to change cleaning policy to invalid value :param pyocf_ctx: basic pyocf context fixture :param cm: cache mode we start with :param cls: cache line size we start with :return: """ # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache.start_on_device( cache_device, cache_mode=cm, cache_line_size=cls ) # Set cleaning policy to invalid one and check if failed for i in generate_random_numbers(c_uint32): if i in [item.value for item in CleaningPolicy]: continue with pytest.raises(OcfError, match="Error changing cleaning policy"): cache.set_cleaning_policy(i)
def test_removing_core(pyocf_ctx, cache_mode, cls): # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache.start_on_device(cache_device, cache_mode=cache_mode, cache_line_size=cls) # Create core device core_device = Volume(S.from_MiB(10)) core = Core.using_device(core_device) # Add core to cache cache.add_core(core) # Remove core from cache cache.remove_core(core) # Check statistics after removing core stats = cache.get_stats() assert stats["conf"]["core_count"] == 0
def test_add_remove_incrementally(pyocf_ctx, cache_mode, cls): # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache.start_on_device(cache_device, cache_mode=cache_mode, cache_line_size=cls) core_devices = [] core_amount = 5 # Create 5 core devices and add to cache for i in range(0, core_amount): core_device = Volume(S.from_MiB(10)) core = Core.using_device(core_device) core_devices.append(core) cache.add_core(core) # Check that core count is as expected stats = cache.get_stats() assert stats["conf"]["core_count"] == core_amount # Remove 3 cores cache.remove_core(core_devices[0]) cache.remove_core(core_devices[1]) cache.remove_core(core_devices[2]) # Add 2 cores and check if core count is as expected cache.add_core(core_devices[0]) cache.add_core(core_devices[1]) stats = cache.get_stats() assert stats["conf"]["core_count"] == core_amount - 1 # Remove 1 core and check if core count is as expected cache.remove_core(core_devices[1]) stats = cache.get_stats() assert stats["conf"]["core_count"] == core_amount - 2 # Add 2 cores and check if core count is as expected cache.add_core(core_devices[1]) cache.add_core(core_devices[2]) stats = cache.get_stats() assert stats["conf"]["core_count"] == core_amount
def test_change_cleaning_policy(pyocf_ctx, cm, cls): # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache.start_on_device( cache_device, cache_mode=cm, cache_line_size=cls ) # Check all possible cleaning policy switches for cp_from in CleaningPolicy: for cp_to in CleaningPolicy: cache.set_cleaning_policy(cp_from.value) # Check if cleaning policy is correct stats = cache.get_stats() assert stats["conf"]["cleaning_policy"] == cp_from.value cache.set_cleaning_policy(cp_to.value) # Check if cleaning policy is correct stats = cache.get_stats() assert stats["conf"]["cleaning_policy"] == cp_to.value
def test_start_cache_huge_device(pyocf_ctx_log_buffer, cls): """ Test whether we can start cache which would overflow ocf_cache_line_t type. pass_criteria: - Starting cache on device too big to handle should fail """ class HugeDevice(Volume): def get_length(self): return Size.from_B((cls * c_uint32(-1).value)) def submit_io(self, io): io.contents._end(io, 0) cache_device = HugeDevice(Size.from_MiB(20)) with pytest.raises(OcfError, match="OCF_ERR_START_CACHE_FAIL"): cache = Cache.start_on_device(cache_device, cache_line_size=cls, metadata_volatile=True) assert any( [line.find("exceeds maximum") > 0 for line in pyocf_ctx_log_buffer.get_lines()] ), "Expected to find log notifying that max size was exceeded"
def test_100_start_stop(pyocf_ctx): """Starting/stopping stress test. Check OCF behaviour when cache is started and stopped continuously """ for i in range(1, 101): cache_device = Volume(Size.from_MiB(20)) cache_mode = CacheMode(randrange(0, len(CacheMode))) size = 4096 * 2**randrange(0, len(CacheLineSize)) cache_line_size = CacheLineSize(size) cache = Cache.start_on_device( cache_device, cache_mode=cache_mode, cache_line_size=cache_line_size) stats = cache.get_stats() assert stats["conf"]["cache_mode"] == cache_mode, "Cache mode" assert stats["conf"]["cache_line_size"] == cache_line_size, "Cache line size" assert stats["conf"]["cache_id"] == 1, "Cache id" cache.stop() assert get_cache_by_id(pyocf_ctx, 1) != 0, "Try getting cache after stopping it"
def test_adding_core_twice(pyocf_ctx, cache_mode, cls): # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache.start_on_device(cache_device, cache_mode=cache_mode, cache_line_size=cls) # Create core device core_device = Volume(S.from_MiB(10)) core = Core.using_device(core_device) # Add core cache.add_core(core) # Check that it is not possible to add the same core again with pytest.raises(OcfError): cache.add_core(core) # Check that core count is still equal to one stats = cache.get_stats() assert stats["conf"]["core_count"] == 1
def test_core_change_seq_cut_off_policy(pyocf_ctx, cm, cls): # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache.start_on_device( cache_device, cache_mode=cm, cache_line_size=cls ) # Create 2 core devices core_device1 = Volume(S.from_MiB(10)) core1 = Core.using_device(core_device1, name="core1") core_device2 = Volume(S.from_MiB(10)) core2 = Core.using_device(core_device2, name="core2") # Add cores cache.add_core(core1) cache.add_core(core2) # Check all possible seq cut off policy switches for first core for seq_from in SeqCutOffPolicy: for seq_to in SeqCutOffPolicy: core1.set_seq_cut_off_policy(seq_from.value) # Check if seq cut off policy of the first core is correct stats = core1.get_stats() assert stats["seq_cutoff_policy"] == seq_from.value # Check if seq cut off policy of the second core did not change stats = core2.get_stats() assert stats["seq_cutoff_policy"] == SeqCutOffPolicy.DEFAULT core1.set_seq_cut_off_policy(seq_to.value) # Check if seq cut off policy of the first core is correct stats = core1.get_stats() assert stats["seq_cutoff_policy"] == seq_to.value # Check if seq cut off policy of the second core did not change stats = core2.get_stats() assert stats["seq_cutoff_policy"] == SeqCutOffPolicy.DEFAULT
def test_neg_set_ioclass_name_len(pyocf_ctx): """ Test whether it is possible to add ioclass with too long name :param pyocf_ctx: basic pyocf context fixture :return: """ # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WT, cache_line_size=CacheLineSize.LINE_4KiB) # Set invalid name and check if failed for name in RandomStringGenerator(len_range=Range(1025, 4096), count=10000): with pytest.raises(OcfError, match="Error adding partition to cache"): cache.configure_partition(part_id=1, name=name, max_size=100, priority=1) print(f"\n{name}")
def test_start_write_first_and_check_mode(pyocf_ctx, mode: CacheMode, cls: CacheLineSize): """Test starting cache in different modes with different cache line sizes. After start check proper cache mode behaviour, starting with write operation. """ cache_device = Volume(Size.from_MiB(40)) core_device = Volume(Size.from_MiB(10)) cache = Cache.start_on_device(cache_device, cache_mode=mode, cache_line_size=cls) core_exported = Core.using_device(core_device) cache.add_core(core_exported) logger.info("[STAGE] Initial write to exported object") cache_device.reset_stats() core_device.reset_stats() test_data = Data.from_string("This is test data") io_to_core(core_exported, test_data, Size.from_sector(1).B) check_stats_write_empty(core_exported, mode, cls) logger.info("[STAGE] Read from exported object after initial write") io_from_exported_object(core_exported, test_data.size, Size.from_sector(1).B) check_stats_read_after_write(core_exported, mode, cls, True) logger.info("[STAGE] Write to exported object after read") cache_device.reset_stats() core_device.reset_stats() test_data = Data.from_string("Changed test data") io_to_core(core_exported, test_data, Size.from_sector(1).B) check_stats_write_after_read(core_exported, mode, cls) check_md5_sums(core_exported, mode)
def test_neg_set_nhit_promotion_policy_param(pyocf_ctx, cm, cls): """ Test whether it is possible to set invalid promotion policy param id for nhit promotion policy :param pyocf_ctx: basic pyocf context fixture :param cm: cache mode we start with :param cls: cache line size we start with :return: """ # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache.start_on_device( cache_device, cache_mode=cm, cache_line_size=cls, promotion_policy=PromotionPolicy.NHIT, ) # Set invalid promotion policy param id and check if failed for i in RandomGenerator(DefaultRanges.UINT8): if i in [item.value for item in NhitParams]: continue with pytest.raises(OcfError, match="Error setting promotion policy parameter"): cache.set_promotion_policy_param(PromotionPolicy.NHIT, i, 1)
def test_stop(pyocf_ctx, mode: CacheMode, cls: CacheLineSize, with_flush: bool): """Stopping cache. Check if cache is stopped properly in different modes with or without preceding flush operation. """ cache_device = RamVolume(Size.from_MiB(50)) core_device = RamVolume(Size.from_MiB(5)) cache = Cache.start_on_device(cache_device, cache_mode=mode, cache_line_size=cls) core = Core.using_device(core_device) cache.add_core(core) front_vol = CoreVolume(core, open=True) queue = cache.get_default_queue() cls_no = 10 run_io_and_cache_data_if_possible(core, mode, cls, cls_no) stats = cache.get_stats() assert int(stats["conf"]["dirty"]) == (cls_no if mode.lazy_write() else 0),\ "Dirty data before MD5" md5_exported_core = front_vol.md5() if with_flush: cache.flush() cache.stop() if mode.lazy_write() and not with_flush: assert core_device.md5() != md5_exported_core, \ "MD5 check: core device vs exported object with dirty data" else: assert core_device.md5() == md5_exported_core, \ "MD5 check: core device vs exported object with clean data"
def test_simple_wt_write(pyocf_ctx): cache_device = RamVolume(S.from_MiB(50)) core_device = RamVolume(S.from_MiB(50)) cache = Cache.start_on_device(cache_device) core = Core.using_device(core_device) queue = cache.get_default_queue() cache.add_core(core) vol = CoreVolume(core, open=True) cache_device.reset_stats() core_device.reset_stats() r = Rio().target(vol).readwrite(ReadWrite.WRITE).size( S.from_sector(1)).run([queue]) assert cache_device.get_stats()[IoDir.WRITE] == 1 cache.settle() stats = cache.get_stats() assert stats["req"]["wr_full_misses"]["value"] == 1 assert stats["usage"]["occupancy"]["value"] == 1 assert vol.md5() == core_device.md5() cache.stop()
def test_neg_attach_cls(pyocf_ctx, cm, cls): """ Test whether it is possible to change cache line size to invalid value while attaching cache device :param pyocf_ctx: basic pyocf context fixture :param cm: cache mode we start with :param cls: cache line size we start with :return: """ # Start cache device cache_device = Volume(S.from_MiB(30)) cache = Cache(owner=cache_device.owner, cache_mode=cm, cache_line_size=cls) cache.start_cache() # Check whether it is possible to attach cache device with invalid cache line size for i in RandomGenerator(DefaultRanges.UINT64): if i in [item.value for item in CacheLineSize]: continue with pytest.raises(OcfError, match="Attaching cache device failed"): cache.attach_device(cache_device, cache_line_size=i)
def try_start_cache(**config): cache_device = Volume(Size.from_MiB(30)) cache = Cache.start_on_device(cache_device, **config) cache.stop()
def test_change_to_nhit_and_back_io_in_flight(pyocf_ctx): """ Try switching promotion policy during io, no io's should return with error 1. Create core/cache pair with promotion policy ALWAYS 2. Issue IOs without waiting for completion 3. Change promotion policy to NHIT 4. Wait for IO completions * no IOs should fail 5. Issue IOs without waiting for completion 6. Change promotion policy to ALWAYS 7. Wait for IO completions * no IOs should fail """ # Step 1 cache_device = Volume(Size.from_MiB(30)) core_device = Volume(Size.from_MiB(30)) cache = Cache.start_on_device(cache_device) core = Core.using_device(core_device) cache.add_core(core) # Step 2 completions = [] for i in range(2000): comp = OcfCompletion([("error", c_int)]) write_data = Data(4096) io = core.new_io(cache.get_default_queue(), i * 4096, write_data.size, IoDir.WRITE, 0, 0) completions += [comp] io.set_data(write_data) io.callback = comp.callback io.submit() # Step 3 cache.set_promotion_policy(PromotionPolicy.NHIT) # Step 4 for c in completions: c.wait() assert not c.results[ "error"], "No IO's should fail when turning NHIT policy on" # Step 5 completions = [] for i in range(2000): comp = OcfCompletion([("error", c_int)]) write_data = Data(4096) io = core.new_io(cache.get_default_queue(), i * 4096, write_data.size, IoDir.WRITE, 0, 0) completions += [comp] io.set_data(write_data) io.callback = comp.callback io.submit() # Step 6 cache.set_promotion_policy(PromotionPolicy.ALWAYS) # Step 7 for c in completions: c.wait() assert not c.results[ "error"], "No IO's should fail when turning NHIT policy off"
def test_promoted_after_hits_various_thresholds(pyocf_ctx, insertion_threshold, fill_percentage): """ Check promotion policy behavior with various set thresholds 1. Create core/cache pair with promotion policy NHIT 2. Set TRIGGER_THRESHOLD/INSERTION_THRESHOLD to predefined values 3. Fill cache from the beggining until occupancy reaches TRIGGER_THRESHOLD% 4. Issue INSERTION_THRESHOLD - 1 requests to core line not inserted to cache * occupancy should not change 5. Issue one request to LBA from step 4 * occupancy should rise by one cache line """ # Step 1 cache_device = Volume(Size.from_MiB(30)) core_device = Volume(Size.from_MiB(30)) cache = Cache.start_on_device(cache_device, promotion_policy=PromotionPolicy.NHIT) core = Core.using_device(core_device) cache.add_core(core) # Step 2 cache.set_promotion_policy_param(PromotionPolicy.NHIT, NhitParams.TRIGGER_THRESHOLD, fill_percentage) cache.set_promotion_policy_param(PromotionPolicy.NHIT, NhitParams.INSERTION_THRESHOLD, insertion_threshold) # Step 3 fill_cache(cache, fill_percentage / 100) stats = cache.get_stats() cache_lines = stats["conf"]["size"] assert stats["usage"]["occupancy"]["fraction"] // 10 == fill_percentage * 10 filled_occupancy = stats["usage"]["occupancy"]["value"] # Step 4 last_core_line = int(core_device.size) - cache_lines.line_size completions = [] for i in range(insertion_threshold - 1): comp = OcfCompletion([("error", c_int)]) write_data = Data(cache_lines.line_size) io = core.new_io( cache.get_default_queue(), last_core_line, write_data.size, IoDir.WRITE, 0, 0, ) completions += [comp] io.set_data(write_data) io.callback = comp.callback io.submit() for c in completions: c.wait() stats = cache.get_stats() threshold_reached_occupancy = stats["usage"]["occupancy"]["value"] assert threshold_reached_occupancy == filled_occupancy, ( "No insertion should occur while NHIT is triggered and core line ", "didn't reach INSERTION_THRESHOLD", ) # Step 5 comp = OcfCompletion([("error", c_int)]) write_data = Data(cache_lines.line_size) io = core.new_io(cache.get_default_queue(), last_core_line, write_data.size, IoDir.WRITE, 0, 0) io.set_data(write_data) io.callback = comp.callback io.submit() comp.wait() assert (threshold_reached_occupancy == cache.get_stats()["usage"]["occupancy"]["value"] - 1), "Previous request should be promoted and occupancy should rise"