Merge pull request #824 from mmichal10/partial-hit-tests
Partial hit tests
This commit is contained in:
commit
eb44557aed
@ -22,6 +22,11 @@ from .data import Data
|
|||||||
from .shared import OcfCompletion
|
from .shared import OcfCompletion
|
||||||
|
|
||||||
|
|
||||||
|
class WriteMode(IntEnum):
|
||||||
|
ZERO_PAD = 0
|
||||||
|
READ_MODIFY_WRITE = 1
|
||||||
|
|
||||||
|
|
||||||
class IoDir(IntEnum):
|
class IoDir(IntEnum):
|
||||||
READ = 0
|
READ = 0
|
||||||
WRITE = 1
|
WRITE = 1
|
||||||
@ -120,7 +125,7 @@ class Sync:
|
|||||||
self.io = io
|
self.io = io
|
||||||
|
|
||||||
def sync_submit(self, submit_method):
|
def sync_submit(self, submit_method):
|
||||||
if getattr(self.io, 'callback', None):
|
if getattr(self.io, "callback", None):
|
||||||
raise Exception("completion callback is already set")
|
raise Exception("completion callback is already set")
|
||||||
cmpl = OcfCompletion([("err", c_int)])
|
cmpl = OcfCompletion([("err", c_int)])
|
||||||
self.io.callback = cmpl.callback
|
self.io.callback = cmpl.callback
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#
|
#
|
||||||
# Copyright(c) 2019-2022 Intel Corporation
|
# Copyright(c) 2019-2022 Intel Corporation
|
||||||
|
# Copyright(c) 2024 Huawei Technologies
|
||||||
# SPDX-License-Identifier: BSD-3-Clause
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
#
|
#
|
||||||
|
|
||||||
@ -24,16 +25,18 @@ from hashlib import md5
|
|||||||
import weakref
|
import weakref
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
import warnings
|
import warnings
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
from .io import Io, IoOps, IoDir
|
from .io import Io, IoOps, IoDir, WriteMode, Sync
|
||||||
from .queue import Queue
|
from .queue import Queue
|
||||||
from .shared import OcfErrorCode, Uuid
|
from .shared import OcfErrorCode, Uuid
|
||||||
from ..ocf import OcfLib
|
from ..ocf import OcfLib
|
||||||
from ..utils import print_buffer, Size as S
|
from ..utils import print_buffer, Size as S
|
||||||
from .data import Data
|
from .data import Data, DataSeek
|
||||||
from .queue import Queue
|
from .queue import Queue
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class IoFlags(IntEnum):
|
class IoFlags(IntEnum):
|
||||||
FLUSH = 1
|
FLUSH = 1
|
||||||
|
|
||||||
@ -341,6 +344,66 @@ class Volume:
|
|||||||
)
|
)
|
||||||
return Io.from_pointer(io)
|
return Io.from_pointer(io)
|
||||||
|
|
||||||
|
def sync_io(
|
||||||
|
self,
|
||||||
|
queue,
|
||||||
|
address: int,
|
||||||
|
data: Data,
|
||||||
|
direction: IoDir,
|
||||||
|
io_class=0,
|
||||||
|
flags=0,
|
||||||
|
submit_func=Sync.submit,
|
||||||
|
):
|
||||||
|
assert address % 512 == 0
|
||||||
|
assert data.size % 512 == 0
|
||||||
|
|
||||||
|
io = self.new_io(queue, address, data.size, direction, io_class, flags)
|
||||||
|
io.set_data(data)
|
||||||
|
completion = submit_func(Sync(io))
|
||||||
|
|
||||||
|
assert int(completion.results["err"]) == 0
|
||||||
|
|
||||||
|
def write_sync_4k(
|
||||||
|
self,
|
||||||
|
queue: Queue,
|
||||||
|
address: int,
|
||||||
|
data: Union[bytes, Data],
|
||||||
|
mode: WriteMode,
|
||||||
|
io_class=0,
|
||||||
|
flags=0,
|
||||||
|
):
|
||||||
|
if mode not in list(WriteMode):
|
||||||
|
raise ValueError(f"illegal write mode: {mode}")
|
||||||
|
|
||||||
|
size = len(data)
|
||||||
|
|
||||||
|
address_4k = (address // 4096) * 4096
|
||||||
|
|
||||||
|
end_address_4k = ((address + size + 4095) // 4096) * 4096
|
||||||
|
size_4k = end_address_4k - address_4k
|
||||||
|
|
||||||
|
write_data = Data(size_4k)
|
||||||
|
|
||||||
|
if mode == WriteMode.ZERO_PAD:
|
||||||
|
write_data.zero(size_4k)
|
||||||
|
elif mode == WriteMode.READ_MODIFY_WRITE:
|
||||||
|
self.sync_io(queue, address_4k, write_data, IoDir.READ)
|
||||||
|
|
||||||
|
write_data.seek(DataSeek.BEGIN, address - address_4k)
|
||||||
|
write_data.write(data, size)
|
||||||
|
|
||||||
|
self.sync_io(queue, address_4k, write_data, IoDir.WRITE, io_class, flags)
|
||||||
|
|
||||||
|
def read_sync(self, queue: Queue, address: int, size: int, io_class=0, flags=0) -> bytes:
|
||||||
|
read_data = Data(size)
|
||||||
|
self.sync_io(queue, address, read_data, IoDir.READ, io_class, flags)
|
||||||
|
|
||||||
|
data = bytes(size)
|
||||||
|
read_data.seek(DataSeek.BEGIN, 0)
|
||||||
|
read_data.read(data, size)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class RamVolume(Volume):
|
class RamVolume(Volume):
|
||||||
props = None
|
props = None
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
#
|
#
|
||||||
# Copyright(c) 2022 Intel Corporation
|
# Copyright(c) 2022 Intel Corporation
|
||||||
|
# Copyright(c) 2024 Huawei Technologies
|
||||||
# SPDX-License-Identifier: BSD-3-Clause
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
#
|
#
|
||||||
|
|
||||||
from .core import Core
|
|
||||||
from .volume_exp_obj import OcfInternalVolume
|
from .volume_exp_obj import OcfInternalVolume
|
||||||
from .io import IoDir
|
from .queue import Queue
|
||||||
from .volume import Volume
|
from .io import Sync, IoDir
|
||||||
|
from .data import Data
|
||||||
|
|
||||||
|
|
||||||
class CoreVolume(OcfInternalVolume):
|
class CoreVolume(OcfInternalVolume):
|
||||||
@ -20,3 +21,16 @@ class CoreVolume(OcfInternalVolume):
|
|||||||
|
|
||||||
def md5(self):
|
def md5(self):
|
||||||
return self._exp_obj_md5(4096)
|
return self._exp_obj_md5(4096)
|
||||||
|
|
||||||
|
def sync_io(
|
||||||
|
self,
|
||||||
|
queue: Queue,
|
||||||
|
address: int,
|
||||||
|
data: Data,
|
||||||
|
direction: IoDir,
|
||||||
|
io_class=0,
|
||||||
|
flags=0,
|
||||||
|
submit_func=Sync.submit,
|
||||||
|
):
|
||||||
|
super().sync_io(queue, address, data, direction, io_class, flags, submit_func)
|
||||||
|
self.core.cache.settle()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#
|
#
|
||||||
# Copyright(c) 2019-2022 Intel Corporation
|
# Copyright(c) 2019-2022 Intel Corporation
|
||||||
|
# Copyright(c) 2024 Huawei Technologies
|
||||||
# SPDX-License-Identifier: BSD-3-Clause
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
#
|
#
|
||||||
|
|
||||||
|
169
tests/functional/tests/engine/test_partial_hit.py
Normal file
169
tests/functional/tests/engine/test_partial_hit.py
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#
|
||||||
|
# Copyright(c) 2024 Huawei Technologies
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
from pyocf.types.data import Data, DataSeek
|
||||||
|
from pyocf.types.cache import Cache, CacheMode
|
||||||
|
from pyocf.types.core import Core
|
||||||
|
from pyocf.types.shared import CacheLineSize
|
||||||
|
from pyocf.types.volume import RamVolume, Volume
|
||||||
|
from pyocf.types.volume_core import CoreVolume
|
||||||
|
from pyocf.utils import Size
|
||||||
|
from pyocf.types.io import IoDir
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("cacheline_size", CacheLineSize)
|
||||||
|
@pytest.mark.parametrize("cache_mode", CacheMode)
|
||||||
|
def test_partial_hit_write(pyocf_ctx, cacheline_size, cache_mode):
|
||||||
|
cache_device = RamVolume(Size.from_MiB(50))
|
||||||
|
core_device = RamVolume(Size.from_MiB(50))
|
||||||
|
|
||||||
|
cache = Cache.start_on_device(
|
||||||
|
cache_device, cache_line_size=cacheline_size, cache_mode=cache_mode
|
||||||
|
)
|
||||||
|
core = Core.using_device(core_device)
|
||||||
|
|
||||||
|
queue = cache.get_default_queue()
|
||||||
|
|
||||||
|
cache.add_core(core)
|
||||||
|
core_volume = CoreVolume(core)
|
||||||
|
core_volume.open()
|
||||||
|
|
||||||
|
# Fill core with data
|
||||||
|
CL = cache.cache_line_size
|
||||||
|
data = Data(CL // 2)
|
||||||
|
data.seek(DataSeek.BEGIN, 0)
|
||||||
|
data.write(b"A\x00\x00\x00\x00", 5)
|
||||||
|
core_device.sync_io(queue, 0, data, IoDir.WRITE)
|
||||||
|
core_device.sync_io(queue, CL // 2, data, IoDir.WRITE)
|
||||||
|
|
||||||
|
# Write 0.5 CL
|
||||||
|
data.seek(DataSeek.BEGIN, 0)
|
||||||
|
data.write(b"B\x00\x00\x00\x00", 5)
|
||||||
|
core_volume.sync_io(queue, 0, data, IoDir.WRITE)
|
||||||
|
|
||||||
|
data1 = core_volume.read_sync(queue, 0, CL)
|
||||||
|
|
||||||
|
assert chr(data1[0]) == "B"
|
||||||
|
assert chr(data1[CL // 2]) == "A"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("cacheline_size", CacheLineSize)
|
||||||
|
@pytest.mark.parametrize("cache_mode", CacheMode)
|
||||||
|
def test_partial_hit_read(pyocf_ctx, cacheline_size, cache_mode):
|
||||||
|
cache_device = RamVolume(Size.from_MiB(50))
|
||||||
|
core_device = RamVolume(Size.from_MiB(50))
|
||||||
|
|
||||||
|
cache = Cache.start_on_device(
|
||||||
|
cache_device, cache_line_size=cacheline_size, cache_mode=cache_mode
|
||||||
|
)
|
||||||
|
core = Core.using_device(core_device)
|
||||||
|
|
||||||
|
queue = cache.get_default_queue()
|
||||||
|
|
||||||
|
cache.add_core(core)
|
||||||
|
core_volume = CoreVolume(core)
|
||||||
|
core_volume.open()
|
||||||
|
|
||||||
|
# Fill core with data
|
||||||
|
CL = cache.cache_line_size
|
||||||
|
data = Data(CL // 2)
|
||||||
|
data.seek(DataSeek.BEGIN, 0)
|
||||||
|
data.write(b"A\x00\x00\x00\x00", 5)
|
||||||
|
core_device.sync_io(queue, 0, data, IoDir.WRITE)
|
||||||
|
core_device.sync_io(queue, CL // 2, data, IoDir.WRITE)
|
||||||
|
|
||||||
|
data_read = Data(CL // 2)
|
||||||
|
core_volume.sync_io(queue, 0, data_read, IoDir.READ)
|
||||||
|
|
||||||
|
data1 = core_volume.read_sync(queue, 0, CL)
|
||||||
|
|
||||||
|
assert chr(data1[0]) == "A"
|
||||||
|
assert chr(data1[CL // 2]) == "A"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("cacheline_size", CacheLineSize)
|
||||||
|
@pytest.mark.parametrize("cache_mode", [CacheMode.WB, CacheMode.WO])
|
||||||
|
def test_read_partial_hit_partial_invalidate_dirty(pyocf_ctx, cacheline_size, cache_mode):
|
||||||
|
cache_device = RamVolume(Size.from_MiB(50))
|
||||||
|
core_device = RamVolume(Size.from_MiB(50))
|
||||||
|
|
||||||
|
cache = Cache.start_on_device(
|
||||||
|
cache_device, cache_line_size=cacheline_size, cache_mode=cache_mode
|
||||||
|
)
|
||||||
|
core = Core.using_device(core_device)
|
||||||
|
|
||||||
|
queue = cache.get_default_queue()
|
||||||
|
|
||||||
|
cache.add_core(core)
|
||||||
|
core_volume = CoreVolume(core)
|
||||||
|
core_volume.open()
|
||||||
|
|
||||||
|
CL = cache.cache_line_size
|
||||||
|
data = Data(CL)
|
||||||
|
data.seek(DataSeek.BEGIN, 0)
|
||||||
|
data.write(b"A" * CL, CL)
|
||||||
|
core_volume.sync_io(queue, 0, data, IoDir.WRITE)
|
||||||
|
|
||||||
|
data.seek(DataSeek.BEGIN, 0)
|
||||||
|
data.write(b"B" * 512, 512)
|
||||||
|
core_volume.sync_io(queue, 512, data, IoDir.WRITE)
|
||||||
|
|
||||||
|
data1 = core_volume.read_sync(queue, 0, CL)
|
||||||
|
|
||||||
|
assert chr(data1[0]) == "A"
|
||||||
|
assert chr(data1[512]) == "B"
|
||||||
|
assert chr(data1[1023]) == "B"
|
||||||
|
assert chr(data1[1024]) == "A"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("cacheline_size", CacheLineSize)
|
||||||
|
def test_partial_hit_backfill(pyocf_ctx, cacheline_size):
|
||||||
|
cache_device = RamVolume(Size.from_MiB(50))
|
||||||
|
core_device = RamVolume(Size.from_MiB(50))
|
||||||
|
|
||||||
|
cache = Cache.start_on_device(cache_device, cache_line_size=cacheline_size)
|
||||||
|
core = Core.using_device(core_device)
|
||||||
|
|
||||||
|
queue = cache.get_default_queue()
|
||||||
|
|
||||||
|
cache.add_core(core)
|
||||||
|
core_volume = CoreVolume(core)
|
||||||
|
core_volume.open()
|
||||||
|
|
||||||
|
cache_device.reset_stats()
|
||||||
|
core_device.reset_stats()
|
||||||
|
|
||||||
|
CL = cache.cache_line_size
|
||||||
|
# Populate core backend volume
|
||||||
|
invalid_sectors = list(range(0, 6 * CL, Size._SECTOR_SIZE))
|
||||||
|
prefill_data = Data(Size._SECTOR_SIZE)
|
||||||
|
prefill_data.write(b"I\x00\x00\x00\x00", 5)
|
||||||
|
for addr in invalid_sectors:
|
||||||
|
core_device.sync_io(queue, addr, prefill_data, IoDir.WRITE)
|
||||||
|
|
||||||
|
stats = cache_device.get_stats()
|
||||||
|
assert stats[IoDir.WRITE] == 0
|
||||||
|
|
||||||
|
# Write data to the core
|
||||||
|
core_data_addr = 2 * CL + CL // 2
|
||||||
|
core_data_size = CL
|
||||||
|
valid_sectors = list(range(core_data_addr, core_data_addr + core_data_size, Size._SECTOR_SIZE))
|
||||||
|
valid_data = Data(core_data_size)
|
||||||
|
for addr in range(0, CL, Size._SECTOR_SIZE):
|
||||||
|
valid_data.seek(DataSeek.BEGIN, addr)
|
||||||
|
valid_data.write(b"C\x00\x00\x00\x00", 5)
|
||||||
|
core_volume.sync_io(queue, core_data_addr, valid_data, IoDir.WRITE)
|
||||||
|
|
||||||
|
invalid_sectors = [s for s in invalid_sectors if s not in valid_sectors]
|
||||||
|
|
||||||
|
read_data = core_volume.read_sync(queue, 0, 6 * CL)
|
||||||
|
for addr in invalid_sectors:
|
||||||
|
assert chr(read_data[addr]) == "I", f"data offset {addr}"
|
||||||
|
|
||||||
|
for addr in valid_sectors:
|
||||||
|
assert chr(read_data[addr]) == "C", f"data offset {addr}"
|
Loading…
Reference in New Issue
Block a user