Merge pull request #255 from imjfckm/pp-tests
Add promotion policies functional tests
This commit is contained in:
commit
0391fc17b7
@ -77,7 +77,10 @@ struct ocf_cache_info {
|
|||||||
/*!< Eviction policy selected */
|
/*!< Eviction policy selected */
|
||||||
|
|
||||||
ocf_cleaning_t cleaning_policy;
|
ocf_cleaning_t cleaning_policy;
|
||||||
/*!< Cleaning policy selected (alru/nop) */
|
/*!< Cleaning policy selected */
|
||||||
|
|
||||||
|
ocf_promotion_t promotion_policy;
|
||||||
|
/*!< Promotion policy selected */
|
||||||
|
|
||||||
ocf_cache_line_size_t cache_line_size;
|
ocf_cache_line_size_t cache_line_size;
|
||||||
/*!< Cache line size in KiB */
|
/*!< Cache line size in KiB */
|
||||||
|
@ -171,6 +171,7 @@ int ocf_cache_get_info(ocf_cache_t cache, struct ocf_cache_info *info)
|
|||||||
|
|
||||||
info->eviction_policy = cache->conf_meta->eviction_policy_type;
|
info->eviction_policy = cache->conf_meta->eviction_policy_type;
|
||||||
info->cleaning_policy = cache->conf_meta->cleaning_policy_type;
|
info->cleaning_policy = cache->conf_meta->cleaning_policy_type;
|
||||||
|
info->promotion_policy = cache->conf_meta->promotion_policy_type;
|
||||||
info->metadata_footprint = ocf_cache_is_device_attached(cache) ?
|
info->metadata_footprint = ocf_cache_is_device_attached(cache) ?
|
||||||
ocf_metadata_size_of(cache) : 0;
|
ocf_metadata_size_of(cache) : 0;
|
||||||
info->cache_line_size = ocf_line_size(cache);
|
info->cache_line_size = ocf_line_size(cache);
|
||||||
|
@ -183,7 +183,7 @@ bool nhit_req_should_promote(ocf_promotion_policy_t policy,
|
|||||||
ocf_metadata_get_cachelines_count(policy->owner) -
|
ocf_metadata_get_cachelines_count(policy->owner) -
|
||||||
ocf_freelist_num_free(policy->owner->freelist);
|
ocf_freelist_num_free(policy->owner->freelist);
|
||||||
|
|
||||||
if (occupied_cachelines > env_atomic64_read(&ctx->trigger_threshold))
|
if (occupied_cachelines < env_atomic64_read(&ctx->trigger_threshold))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for (i = 0, core_line = req->core_line_first;
|
for (i = 0, core_line = req->core_line_first;
|
||||||
@ -196,9 +196,8 @@ bool nhit_req_should_promote(ocf_promotion_policy_t policy,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We don't want to reject even partially
|
/* We don't want to reject even partially hit requests - this way we
|
||||||
* hit requests - this way we could trigger passthrough and invalidation.
|
* could trigger passthrough and invalidation. Let's let it in! */
|
||||||
* Let's let it in! */
|
return result || ocf_engine_mapped_count(req);
|
||||||
return result || req->info.hit_no;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@ ocf_error_t ocf_promotion_set_policy(ocf_promotion_policy_t policy,
|
|||||||
ocf_promotion_policies[prev_policy].deinit(policy);
|
ocf_promotion_policies[prev_policy].deinit(policy);
|
||||||
|
|
||||||
cache->conf_meta->promotion_policy_type = type;
|
cache->conf_meta->promotion_policy_type = type;
|
||||||
|
policy->type = type;
|
||||||
|
|
||||||
if (ocf_promotion_policies[type].init)
|
if (ocf_promotion_policies[type].init)
|
||||||
result = ocf_promotion_policies[type].init(cache, policy);
|
result = ocf_promotion_policies[type].init(cache, policy);
|
||||||
|
@ -100,6 +100,11 @@ class PromotionPolicy(IntEnum):
|
|||||||
DEFAULT = ALWAYS
|
DEFAULT = ALWAYS
|
||||||
|
|
||||||
|
|
||||||
|
class NhitParams(IntEnum):
|
||||||
|
INSERTION_THRESHOLD = 0
|
||||||
|
TRIGGER_THRESHOLD = 1
|
||||||
|
|
||||||
|
|
||||||
class CleaningPolicy(IntEnum):
|
class CleaningPolicy(IntEnum):
|
||||||
NOP = 0
|
NOP = 0
|
||||||
ALRU = 1
|
ALRU = 1
|
||||||
@ -251,6 +256,21 @@ class Cache:
|
|||||||
if status:
|
if status:
|
||||||
raise OcfError("Error setting promotion policy", status)
|
raise OcfError("Error setting promotion policy", status)
|
||||||
|
|
||||||
|
def get_promotion_policy_param(self, param_id):
|
||||||
|
self.read_lock()
|
||||||
|
|
||||||
|
param_value = c_uint64()
|
||||||
|
|
||||||
|
status = self.owner.lib.ocf_mngt_cache_promotion_get_param(
|
||||||
|
self.cache_handle, param_id, byref(param_value)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.read_unlock()
|
||||||
|
if status:
|
||||||
|
raise OcfError("Error getting promotion policy parameter", status)
|
||||||
|
|
||||||
|
return param_value
|
||||||
|
|
||||||
def set_promotion_policy_param(self, param_id, param_value):
|
def set_promotion_policy_param(self, param_id, param_value):
|
||||||
self.write_lock()
|
self.write_lock()
|
||||||
|
|
||||||
@ -489,6 +509,7 @@ class Cache:
|
|||||||
"state": cache_info.state,
|
"state": cache_info.state,
|
||||||
"eviction_policy": EvictionPolicy(cache_info.eviction_policy),
|
"eviction_policy": EvictionPolicy(cache_info.eviction_policy),
|
||||||
"cleaning_policy": CleaningPolicy(cache_info.cleaning_policy),
|
"cleaning_policy": CleaningPolicy(cache_info.cleaning_policy),
|
||||||
|
"promotion_policy": PromotionPolicy(cache_info.promotion_policy),
|
||||||
"cache_line_size": line_size,
|
"cache_line_size": line_size,
|
||||||
"flushed": CacheLines(cache_info.flushed, line_size),
|
"flushed": CacheLines(cache_info.flushed, line_size),
|
||||||
"core_count": cache_info.core_count,
|
"core_count": cache_info.core_count,
|
||||||
|
@ -54,6 +54,21 @@ class OcfCompletion:
|
|||||||
management API.
|
management API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class CompletionResult:
|
||||||
|
def __init__(self, completion_args):
|
||||||
|
self.completion_args = {
|
||||||
|
x[0]: i for i, x in enumerate(completion_args)
|
||||||
|
}
|
||||||
|
self.results = None
|
||||||
|
self.arg_types = [x[1] for x in completion_args]
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
try:
|
||||||
|
position = self.completion_args[key]
|
||||||
|
return self.results[position]
|
||||||
|
except KeyError:
|
||||||
|
raise KeyError(f"No completion argument {key} specified")
|
||||||
|
|
||||||
def __init__(self, completion_args: list):
|
def __init__(self, completion_args: list):
|
||||||
"""
|
"""
|
||||||
Provide ctypes arg list, and optionally index of status argument in
|
Provide ctypes arg list, and optionally index of status argument in
|
||||||
@ -63,19 +78,14 @@ class OcfCompletion:
|
|||||||
for OCF completion function
|
for OCF completion function
|
||||||
"""
|
"""
|
||||||
self.e = Event()
|
self.e = Event()
|
||||||
self.completion_args = completion_args
|
self.results = OcfCompletion.CompletionResult(completion_args)
|
||||||
self._as_parameter_ = self.callback
|
self._as_parameter_ = self.callback
|
||||||
self.results = None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def callback(self):
|
def callback(self):
|
||||||
arg_types = list(list(zip(*self.completion_args))[1])
|
@CFUNCTYPE(c_void_p, *self.results.arg_types)
|
||||||
|
|
||||||
@CFUNCTYPE(c_void_p, *arg_types)
|
|
||||||
def complete(*args):
|
def complete(*args):
|
||||||
self.results = {}
|
self.results.results = args
|
||||||
for i, arg in enumerate(args):
|
|
||||||
self.results[self.completion_args[i][0]] = arg
|
|
||||||
self.e.set()
|
self.e.set()
|
||||||
|
|
||||||
return complete
|
return complete
|
||||||
|
@ -30,6 +30,7 @@ class CacheInfo(Structure):
|
|||||||
("state", c_uint8),
|
("state", c_uint8),
|
||||||
("eviction_policy", c_uint32),
|
("eviction_policy", c_uint32),
|
||||||
("cleaning_policy", c_uint32),
|
("cleaning_policy", c_uint32),
|
||||||
|
("promotion_policy", c_uint32),
|
||||||
("cache_line_size", c_uint64),
|
("cache_line_size", c_uint64),
|
||||||
("flushed", c_uint32),
|
("flushed", c_uint32),
|
||||||
("core_count", c_uint32),
|
("core_count", c_uint32),
|
||||||
|
@ -7,7 +7,7 @@ from ctypes import c_uint64, c_uint32, Structure
|
|||||||
|
|
||||||
|
|
||||||
class _Stat(Structure):
|
class _Stat(Structure):
|
||||||
_fields_ = [("value", c_uint64), ("permil", c_uint64)]
|
_fields_ = [("value", c_uint64), ("fraction", c_uint64)]
|
||||||
|
|
||||||
|
|
||||||
class OcfStatsReq(Structure):
|
class OcfStatsReq(Structure):
|
||||||
|
@ -6,8 +6,15 @@
|
|||||||
from ctypes import string_at
|
from ctypes import string_at
|
||||||
|
|
||||||
|
|
||||||
def print_buffer(buf, length, offset=0, width=16, ignore=0,
|
def print_buffer(
|
||||||
stop_after_count_ignored=0, print_fcn=print):
|
buf,
|
||||||
|
length,
|
||||||
|
offset=0,
|
||||||
|
width=16,
|
||||||
|
ignore=0,
|
||||||
|
stop_after_count_ignored=0,
|
||||||
|
print_fcn=print,
|
||||||
|
):
|
||||||
end = int(offset) + int(length)
|
end = int(offset) + int(length)
|
||||||
offset = int(offset)
|
offset = int(offset)
|
||||||
ignored_lines = 0
|
ignored_lines = 0
|
||||||
@ -20,15 +27,25 @@ def print_buffer(buf, length, offset=0, width=16, ignore=0,
|
|||||||
byteline = ""
|
byteline = ""
|
||||||
asciiline = ""
|
asciiline = ""
|
||||||
if not any(x != ignore for x in cur_line):
|
if not any(x != ignore for x in cur_line):
|
||||||
if stop_after_count_ignored and ignored_lines > stop_after_count_ignored:
|
if (
|
||||||
print_fcn("<{} bytes of '0x{:02X}' encountered, stopping>".
|
stop_after_count_ignored
|
||||||
format(stop_after_count_ignored * width, ignore))
|
and ignored_lines > stop_after_count_ignored
|
||||||
|
):
|
||||||
|
print_fcn(
|
||||||
|
"<{} bytes of '0x{:02X}' encountered, stopping>".format(
|
||||||
|
stop_after_count_ignored * width, ignore
|
||||||
|
)
|
||||||
|
)
|
||||||
return
|
return
|
||||||
ignored_lines += 1
|
ignored_lines += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if ignored_lines:
|
if ignored_lines:
|
||||||
print_fcn("<{} of '0x{:02X}' bytes omitted>".format(ignored_lines * width, ignore))
|
print_fcn(
|
||||||
|
"<{} of '0x{:02X}' bytes omitted>".format(
|
||||||
|
ignored_lines * width, ignore
|
||||||
|
)
|
||||||
|
)
|
||||||
ignored_lines = 0
|
ignored_lines = 0
|
||||||
|
|
||||||
for byte in cur_line:
|
for byte in cur_line:
|
||||||
@ -58,9 +75,12 @@ class Size:
|
|||||||
|
|
||||||
def __init__(self, b: int, sector_aligned: bool = False):
|
def __init__(self, b: int, sector_aligned: bool = False):
|
||||||
if sector_aligned:
|
if sector_aligned:
|
||||||
self.bytes = ((b + self._SECTOR_SIZE - 1) // self._SECTOR_SIZE) * self._SECTOR_SIZE
|
self.bytes = int(
|
||||||
|
((b + self._SECTOR_SIZE - 1) // self._SECTOR_SIZE)
|
||||||
|
* self._SECTOR_SIZE
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.bytes = b
|
self.bytes = int(b)
|
||||||
|
|
||||||
def __int__(self):
|
def __int__(self):
|
||||||
return self.bytes
|
return self.bytes
|
||||||
|
331
tests/functional/tests/engine/test_pp.py
Normal file
331
tests/functional/tests/engine/test_pp.py
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
#
|
||||||
|
# Copyright(c) 2019 Intel Corporation
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||||
|
#
|
||||||
|
|
||||||
|
from ctypes import c_int, cast, c_void_p
|
||||||
|
from enum import IntEnum
|
||||||
|
import pytest
|
||||||
|
import math
|
||||||
|
|
||||||
|
from pyocf.types.cache import (
|
||||||
|
Cache,
|
||||||
|
CacheMode,
|
||||||
|
PromotionPolicy,
|
||||||
|
NhitParams,
|
||||||
|
CacheLineSize,
|
||||||
|
)
|
||||||
|
from pyocf.types.core import Core
|
||||||
|
from pyocf.types.volume import Volume
|
||||||
|
from pyocf.types.data import Data
|
||||||
|
from pyocf.types.io import IoDir
|
||||||
|
from pyocf.utils import Size
|
||||||
|
from pyocf.types.shared import OcfCompletion
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("promotion_policy", PromotionPolicy)
|
||||||
|
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_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
|
||||||
|
"""
|
||||||
|
|
||||||
|
io_error = False
|
||||||
|
# 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 fill_cache(cache, fill_ratio):
|
||||||
|
"""
|
||||||
|
Helper to fill cache from LBA 0.
|
||||||
|
TODO:
|
||||||
|
* make it generic and share across all tests
|
||||||
|
* reasonable error handling
|
||||||
|
"""
|
||||||
|
|
||||||
|
cache_lines = cache.get_stats()["conf"]["size"]
|
||||||
|
|
||||||
|
bytes_to_fill = cache_lines.bytes * fill_ratio
|
||||||
|
max_io_size = cache.device.get_max_io_size().bytes
|
||||||
|
|
||||||
|
ios_to_issue = math.floor(bytes_to_fill / max_io_size)
|
||||||
|
|
||||||
|
core = cache.cores[0]
|
||||||
|
completions = []
|
||||||
|
for i in range(ios_to_issue):
|
||||||
|
comp = OcfCompletion([("error", c_int)])
|
||||||
|
write_data = Data(max_io_size)
|
||||||
|
io = core.new_io(
|
||||||
|
cache.get_default_queue(),
|
||||||
|
i * max_io_size,
|
||||||
|
write_data.size,
|
||||||
|
IoDir.WRITE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
io.set_data(write_data)
|
||||||
|
io.callback = comp.callback
|
||||||
|
completions += [comp]
|
||||||
|
io.submit()
|
||||||
|
|
||||||
|
if bytes_to_fill % max_io_size:
|
||||||
|
comp = OcfCompletion([("error", c_int)])
|
||||||
|
write_data = Data(
|
||||||
|
Size.from_B(bytes_to_fill % max_io_size, sector_aligned=True)
|
||||||
|
)
|
||||||
|
io = core.new_io(
|
||||||
|
cache.get_default_queue(),
|
||||||
|
ios_to_issue * max_io_size,
|
||||||
|
write_data.size,
|
||||||
|
IoDir.WRITE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
io.set_data(write_data)
|
||||||
|
io.callback = comp.callback
|
||||||
|
completions += [comp]
|
||||||
|
io.submit()
|
||||||
|
|
||||||
|
for c in completions:
|
||||||
|
c.wait()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("fill_percentage", [0, 1, 50, 99])
|
||||||
|
@pytest.mark.parametrize("insertion_threshold", [2, 8])
|
||||||
|
@pytest.mark.parametrize("io_dir", IoDir)
|
||||||
|
def test_promoted_after_hits_various_thresholds(
|
||||||
|
pyocf_ctx, io_dir, 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(
|
||||||
|
NhitParams.TRIGGER_THRESHOLD, fill_percentage
|
||||||
|
)
|
||||||
|
cache.set_promotion_policy_param(
|
||||||
|
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,
|
||||||
|
io_dir,
|
||||||
|
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, io_dir, 0, 0
|
||||||
|
)
|
||||||
|
io.set_data(write_data)
|
||||||
|
io.callback = comp.callback
|
||||||
|
io.submit()
|
||||||
|
|
||||||
|
c.wait()
|
||||||
|
|
||||||
|
stats = cache.get_stats()
|
||||||
|
assert (
|
||||||
|
threshold_reached_occupancy
|
||||||
|
== cache.get_stats()["usage"]["occupancy"]["value"] - 1
|
||||||
|
), "Previous request should be promoted and occupancy should rise"
|
||||||
|
|
||||||
|
|
||||||
|
def test_partial_hit_promotion(pyocf_ctx):
|
||||||
|
"""
|
||||||
|
Check if NHIT promotion policy doesn't prevent partial hits from getting
|
||||||
|
promoted to cache
|
||||||
|
|
||||||
|
1. Create core/cache pair with promotion policy ALWAYS
|
||||||
|
2. Issue one-sector IO to cache to insert partially valid cache line
|
||||||
|
3. Set NHIT promotion policy with trigger=0 (always triggered) and high
|
||||||
|
insertion threshold
|
||||||
|
4. Issue a request containing partially valid cache line and next cache line
|
||||||
|
* occupancy should rise - partially hit request should bypass nhit criteria
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 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
|
||||||
|
comp = OcfCompletion([("error", c_int)])
|
||||||
|
write_data = Data(Size.from_sector(1))
|
||||||
|
io = core.new_io(
|
||||||
|
cache.get_default_queue(), 0, write_data.size, IoDir.READ, 0, 0
|
||||||
|
)
|
||||||
|
io.set_data(write_data)
|
||||||
|
io.callback = comp.callback
|
||||||
|
io.submit()
|
||||||
|
|
||||||
|
comp.wait()
|
||||||
|
|
||||||
|
stats = cache.get_stats()
|
||||||
|
cache_lines = stats["conf"]["size"]
|
||||||
|
assert stats["usage"]["occupancy"]["value"] == 1
|
||||||
|
|
||||||
|
# Step 3
|
||||||
|
cache.set_promotion_policy(PromotionPolicy.NHIT)
|
||||||
|
cache.set_promotion_policy_param(NhitParams.TRIGGER_THRESHOLD, 0)
|
||||||
|
cache.set_promotion_policy_param(NhitParams.INSERTION_THRESHOLD, 100)
|
||||||
|
|
||||||
|
# Step 4
|
||||||
|
comp = OcfCompletion([("error", c_int)])
|
||||||
|
write_data = Data(2 * cache_lines.line_size)
|
||||||
|
io = core.new_io(
|
||||||
|
cache.get_default_queue(), 0, write_data.size, IoDir.WRITE, 0, 0
|
||||||
|
)
|
||||||
|
io.set_data(write_data)
|
||||||
|
io.callback = comp.callback
|
||||||
|
io.submit()
|
||||||
|
comp.wait()
|
||||||
|
|
||||||
|
stats = cache.get_stats()
|
||||||
|
assert (
|
||||||
|
stats["usage"]["occupancy"]["value"] == 2
|
||||||
|
), "Second cache line should be mapped"
|
Loading…
Reference in New Issue
Block a user