From f75ad04ec79ad58caa763c6d757447f012d1b481 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Wed, 11 Sep 2024 16:20:27 +0200 Subject: [PATCH 1/3] pyocf: Remove redundant imports Signed-off-by: Michal Mielewczyk --- tests/functional/pyocf/types/volume_core.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/functional/pyocf/types/volume_core.py b/tests/functional/pyocf/types/volume_core.py index 95b8b433..94596bb2 100644 --- a/tests/functional/pyocf/types/volume_core.py +++ b/tests/functional/pyocf/types/volume_core.py @@ -1,12 +1,10 @@ # # Copyright(c) 2022 Intel Corporation +# Copyright(c) 2024 Huawei Technologies # SPDX-License-Identifier: BSD-3-Clause # -from .core import Core from .volume_exp_obj import OcfInternalVolume -from .io import IoDir -from .volume import Volume class CoreVolume(OcfInternalVolume): From 97477d484f9021555664e20d46012fc4565f4d22 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Tue, 10 Sep 2024 14:56:54 +0200 Subject: [PATCH 2/3] pyocf: Helpers for sync IOs for Volume Signed-off-by: Sara Merzel Signed-off-by: Roel Apfelbaum Signed-off-by: Michal Mielewczyk --- tests/functional/pyocf/types/io.py | 7 ++- tests/functional/pyocf/types/volume.py | 67 ++++++++++++++++++++- tests/functional/pyocf/types/volume_core.py | 16 +++++ tests/functional/pyocf/utils.py | 1 + 4 files changed, 88 insertions(+), 3 deletions(-) diff --git a/tests/functional/pyocf/types/io.py b/tests/functional/pyocf/types/io.py index dbd0d406..46f6e930 100644 --- a/tests/functional/pyocf/types/io.py +++ b/tests/functional/pyocf/types/io.py @@ -22,6 +22,11 @@ from .shared import OcfCompletion +class WriteMode(IntEnum): + ZERO_PAD = 0 + READ_MODIFY_WRITE = 1 + + class IoDir(IntEnum): READ = 0 WRITE = 1 @@ -120,7 +125,7 @@ def __init__(self, io: Io) -> None: self.io = io 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") cmpl = OcfCompletion([("err", c_int)]) self.io.callback = cmpl.callback diff --git a/tests/functional/pyocf/types/volume.py b/tests/functional/pyocf/types/volume.py index d80d0f39..fccbd4e1 100644 --- a/tests/functional/pyocf/types/volume.py +++ b/tests/functional/pyocf/types/volume.py @@ -1,5 +1,6 @@ # # Copyright(c) 2019-2022 Intel Corporation +# Copyright(c) 2024 Huawei Technologies # SPDX-License-Identifier: BSD-3-Clause # @@ -24,16 +25,18 @@ import weakref from enum import IntEnum 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 .shared import OcfErrorCode, Uuid from ..ocf import OcfLib from ..utils import print_buffer, Size as S -from .data import Data +from .data import Data, DataSeek from .queue import Queue + class IoFlags(IntEnum): FLUSH = 1 @@ -341,6 +344,66 @@ def new_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): props = None diff --git a/tests/functional/pyocf/types/volume_core.py b/tests/functional/pyocf/types/volume_core.py index 94596bb2..7c1c402b 100644 --- a/tests/functional/pyocf/types/volume_core.py +++ b/tests/functional/pyocf/types/volume_core.py @@ -5,6 +5,9 @@ # from .volume_exp_obj import OcfInternalVolume +from .queue import Queue +from .io import Sync, IoDir +from .data import Data class CoreVolume(OcfInternalVolume): @@ -18,3 +21,16 @@ def get_c_handle(self): def md5(self): 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() diff --git a/tests/functional/pyocf/utils.py b/tests/functional/pyocf/utils.py index 5497e9e3..de4e2d13 100644 --- a/tests/functional/pyocf/utils.py +++ b/tests/functional/pyocf/utils.py @@ -1,5 +1,6 @@ # # Copyright(c) 2019-2022 Intel Corporation +# Copyright(c) 2024 Huawei Technologies # SPDX-License-Identifier: BSD-3-Clause # From 9a6fe8a49bee8fba101b28dd8933241fe5c29cab Mon Sep 17 00:00:00 2001 From: Sara Merzel Date: Mon, 14 Aug 2023 15:34:38 +0300 Subject: [PATCH 3/3] pyocf: Tests for partial hits Signed-off-by: Sara Merzel Signed-off-by: Roel Apfelbaum Signed-off-by: Michal Mielewczyk --- .../tests/engine/test_partial_hit.py | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 tests/functional/tests/engine/test_partial_hit.py diff --git a/tests/functional/tests/engine/test_partial_hit.py b/tests/functional/tests/engine/test_partial_hit.py new file mode 100644 index 00000000..4f331758 --- /dev/null +++ b/tests/functional/tests/engine/test_partial_hit.py @@ -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}"