pyocf: Update tests after the API changes

Signed-off-by: Robert Baldyga <robert.baldyga@huawei.com>
Signed-off-by: Michal Mielewczyk <michal.mielewczyk@huawei.com>
This commit is contained in:
Robert Baldyga 2023-10-13 21:52:47 +02:00 committed by Michal Mielewczyk
parent c5741df0ed
commit be0ad8fe20
3 changed files with 129 additions and 103 deletions

View File

@ -88,19 +88,13 @@ class VolumeOps(Structure):
class VolumeProperties(Structure): class VolumeProperties(Structure):
_fields_ = [ _fields_ = [
("_name", c_char_p), ("_name", c_char_p),
("_io_priv_size", c_uint32),
("_volume_priv_size", c_uint32), ("_volume_priv_size", c_uint32),
("_caps", VolumeCaps), ("_caps", VolumeCaps),
("_io_ops", IoOps),
("_deinit", c_char_p), ("_deinit", c_char_p),
("_ops_", VolumeOps), ("_ops_", VolumeOps),
] ]
class VolumeIoPriv(Structure):
_fields_ = [("_data", c_void_p), ("_offset", c_uint64)]
VOLUME_POISON = 0x13 VOLUME_POISON = 0x13
@ -243,11 +237,9 @@ class Volume:
Volume._props_[cls] = VolumeProperties( Volume._props_[cls] = VolumeProperties(
_name=str(cls.__name__).encode("ascii"), _name=str(cls.__name__).encode("ascii"),
_io_priv_size=sizeof(VolumeIoPriv),
_volume_priv_size=0, _volume_priv_size=0,
_caps=VolumeCaps(_atomic_writes=0), _caps=VolumeCaps(_atomic_writes=0),
_ops_=cls.get_ops(), _ops_=cls.get_ops(),
_io_ops=cls.get_io_ops(),
_deinit=0, _deinit=0,
) )
return Volume._props_[cls] return Volume._props_[cls]
@ -267,22 +259,6 @@ class Volume:
def get_by_uuid(cls, uuid): def get_by_uuid(cls, uuid):
return cls._uuid_[uuid] return cls._uuid_[uuid]
@staticmethod
@IoOps.SET_DATA
def _io_set_data(io, data, offset):
io_priv = cast(OcfLib.getInstance().ocf_io_get_priv(io), POINTER(VolumeIoPriv))
data = Data.get_instance(data)
io_priv.contents._offset = offset
io_priv.contents._data = data.handle
return 0
@staticmethod
@IoOps.GET_DATA
def _io_get_data(io):
io_priv = cast(OcfLib.getInstance().ocf_io_get_priv(io), POINTER(VolumeIoPriv))
return io_priv.contents._data
def __init__(self, uuid=None): def __init__(self, uuid=None):
if uuid: if uuid:
if uuid in type(self)._uuid_: if uuid in type(self)._uuid_:
@ -499,8 +475,7 @@ class RamVolume(Volume):
return return
try: try:
io_priv = cast(OcfLib.getInstance().ocf_io_get_priv(io), POINTER(VolumeIoPriv)) offset = OcfLib.getInstance().ocf_io_get_offset(io)
offset = io_priv.contents._offset
if io.contents._dir == IoDir.WRITE: if io.contents._dir == IoDir.WRITE:
src_ptr = cast(OcfLib.getInstance().ocf_io_get_data(io), c_void_p) src_ptr = cast(OcfLib.getInstance().ocf_io_get_data(io), c_void_p)
@ -512,7 +487,6 @@ class RamVolume(Volume):
src = self.data_ptr + io.contents._addr src = self.data_ptr + io.contents._addr
memmove(dst, src, io.contents._bytes) memmove(dst, src, io.contents._bytes)
io_priv.contents._offset += io.contents._bytes
io.contents._end(io, 0) io.contents._end(io, 0)
except: # noqa E722 except: # noqa E722
@ -597,7 +571,6 @@ class ErrorDevice(Volume):
def should_forward_io(self, rw, addr): def should_forward_io(self, rw, addr):
if not self.armed: if not self.armed:
return True return True
direction = IoDir(rw) direction = IoDir(rw)
seq_no_match = ( seq_no_match = (
self.error_seq_no[direction] >= 0 self.error_seq_no[direction] >= 0
@ -633,7 +606,7 @@ class ErrorDevice(Volume):
else: else:
self.complete_submit_with_error(io) self.complete_submit_with_error(io)
def complete_forward_with_error(self, token, rw): def complete_forward_with_error(self, token, rw=IoDir.WRITE):
self.error = True self.error = True
direction = IoDir(rw) direction = IoDir(rw)
self.stats["errors"][direction] += 1 self.stats["errors"][direction] += 1
@ -646,16 +619,16 @@ class ErrorDevice(Volume):
self.complete_forward_with_error(token, rw) self.complete_forward_with_error(token, rw)
def do_forward_flush(self, token): def do_forward_flush(self, token):
if self.data_only or self.should_forward_io(0, 0): if self.data_only or self.should_forward_io(IoDir.WRITE, 0):
self.vol.do_forward_flush(token) self.vol.do_forward_flush(token)
else: else:
self.complete_forward_with_error(token, rw) self.complete_forward_with_error(token)
def do_forward_discard(self, token, addr, nbytes): def do_forward_discard(self, token, addr, nbytes):
if self.data_only or self.should_forward_io(0, addr): if self.data_only or self.should_forward_io(IoDir.WRITE, addr):
self.vol.do_forward_discard(token, addr, nbytes) self.vol.do_forward_discard(token, addr, nbytes)
else: else:
self.complete_forward_with_error(token, rw) self.complete_forward_with_error(token)
def arm(self): def arm(self):
self.armed = True self.armed = True
@ -812,7 +785,7 @@ class TraceDevice(Volume):
lib = OcfLib.getInstance() lib = OcfLib.getInstance()
lib.ocf_io_get_priv.restype = POINTER(VolumeIoPriv) lib.ocf_io_get_offset.restype = c_uint32
lib.ocf_io_get_volume.argtypes = [c_void_p] lib.ocf_io_get_volume.argtypes = [c_void_p]
lib.ocf_io_get_volume.restype = c_void_p lib.ocf_io_get_volume.restype = c_void_p
lib.ocf_io_get_data.argtypes = [c_void_p] lib.ocf_io_get_data.argtypes = [c_void_p]

View File

@ -4,10 +4,7 @@
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
# #
from ctypes import c_int, memmove, cast, c_void_p from ctypes import memmove, cast, c_void_p, c_uint64
from enum import IntEnum
from itertools import product
import random
import pytest import pytest
@ -18,6 +15,7 @@ from pyocf.types.volume_core import CoreVolume
from pyocf.types.data import Data from pyocf.types.data import Data
from pyocf.types.io import IoDir, Sync from pyocf.types.io import IoDir, Sync
from pyocf.utils import Size from pyocf.utils import Size
from pyocf.ocf import OcfLib
def __io(io, data): def __io(io, data):
@ -48,19 +46,19 @@ class FlagsValVolume(RamVolume):
self.fail = False self.fail = False
super().__init__(size) super().__init__(size)
def set_check(self, check): def set_check(self):
self.check = check self.check = True
self.fail = True
def submit_io(self, io): def do_forward_io(self, token, rw, addr, nbytes, offset):
if self.check: if self.check:
flags = io.contents._flags flags = lib.ocf_forward_get_flags(token)
if flags != self.flags: if flags == self.flags:
self.fail = True self.fail = False
super().submit_io(io) super().do_forward_io(token, rw, addr, nbytes, offset)
@pytest.mark.parametrize("cache_mode", CacheMode) def test_io_flags(pyocf_ctx):
def test_io_flags(pyocf_ctx, cache_mode):
""" """
Verify that I/O flags provided at the top volume interface Verify that I/O flags provided at the top volume interface
are propagated down to bottom volumes for all associated are propagated down to bottom volumes for all associated
@ -74,44 +72,54 @@ def test_io_flags(pyocf_ctx, cache_mode):
pyocf_ctx.register_volume_type(FlagsValVolume) pyocf_ctx.register_volume_type(FlagsValVolume)
cache_device = FlagsValVolume(Size.from_MiB(50), flags) cache_device = FlagsValVolume(Size.from_MiB(50), 0)
core_device = FlagsValVolume(Size.from_MiB(50), flags) core_device = FlagsValVolume(Size.from_MiB(50), flags)
cache = Cache.start_on_device(cache_device, cache_mode=cache_mode) cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB)
core = Core.using_device(core_device) core = Core.using_device(core_device)
cache.add_core(core) cache.add_core(core)
vol = CoreVolume(core) vol = CoreVolume(core)
cache_device.set_check(True) def set_check():
core_device.set_check(True) cache_device.set_check()
core_device.set_check()
# write miss # write miss
set_check()
io_to_exp_obj(vol, block_size * 0, block_size, data, 0, IoDir.WRITE, flags) io_to_exp_obj(vol, block_size * 0, block_size, data, 0, IoDir.WRITE, flags)
assert not cache_device.fail assert not cache_device.fail
assert not core_device.fail
# read miss # read miss
set_check()
io_to_exp_obj(vol, block_size * 1, block_size, data, 0, IoDir.READ, flags) io_to_exp_obj(vol, block_size * 1, block_size, data, 0, IoDir.READ, flags)
assert not cache_device.fail
assert not core_device.fail assert not core_device.fail
# "dirty" read hit # "dirty" read hit
set_check()
io_to_exp_obj(vol, block_size * 0, block_size, data, 0, IoDir.READ, flags) io_to_exp_obj(vol, block_size * 0, block_size, data, 0, IoDir.READ, flags)
assert not cache_device.fail assert not cache_device.fail
assert not core_device.fail
# "clean" read hit # "clean" read hit
set_check()
io_to_exp_obj(vol, block_size * 1, block_size, data, 0, IoDir.READ, flags) io_to_exp_obj(vol, block_size * 1, block_size, data, 0, IoDir.READ, flags)
assert not cache_device.fail assert not cache_device.fail
assert not core_device.fail
cache.change_cache_mode(CacheMode.WT)
# "dirty" write hit # "dirty" write hit
set_check()
io_to_exp_obj(vol, block_size * 0, block_size, data, 0, IoDir.WRITE, flags) io_to_exp_obj(vol, block_size * 0, block_size, data, 0, IoDir.WRITE, flags)
assert not cache_device.fail assert not cache_device.fail
assert not core_device.fail assert not core_device.fail
# "clean" write hit # "clean" write hit
set_check()
io_to_exp_obj(vol, block_size * 1, block_size, data, 0, IoDir.WRITE, flags) io_to_exp_obj(vol, block_size * 1, block_size, data, 0, IoDir.WRITE, flags)
assert not cache_device.fail assert not cache_device.fail
assert not core_device.fail assert not core_device.fail
lib = OcfLib.getInstance()
lib.ocf_forward_get_flags.argtypes = [c_uint64]
lib.ocf_forward_get_flags.restype = c_uint64

View File

@ -12,7 +12,7 @@ from threading import Event
from collections import namedtuple from collections import namedtuple
from pyocf.ocf import OcfLib from pyocf.ocf import OcfLib
from pyocf.types.volume import RamVolume, ErrorDevice, TraceDevice, IoFlags, VolumeIoPriv from pyocf.types.volume import RamVolume, ErrorDevice, TraceDevice, IoFlags
from pyocf.types.cvolume import CVolume from pyocf.types.cvolume import CVolume
from pyocf.types.data import Data from pyocf.types.data import Data
from pyocf.types.io import IoDir from pyocf.types.io import IoDir
@ -287,19 +287,25 @@ def test_io_propagation_basic(pyocf_ctx):
ret = cvol_submit_data_io(cvol, addr, io_size) ret = cvol_submit_data_io(cvol, addr, io_size)
assert ret == 0 assert ret == 0
ret = cvol_submit_flush_io(cvol, addr, io_size, IoFlags.FLUSH) ret = cvol_submit_flush_io(cvol, addr, io_size)
assert ret == 0 assert ret == 0
ret = cvol_submit_discard_io(cvol, addr, io_size) ret = cvol_submit_discard_io(cvol, addr, io_size)
assert ret == 0 assert ret == 0
for io_type in TraceDevice.IoType: ios = io_trace[vol][TraceDevice.IoType.Data]
ios = io_trace[vol][io_type] assert len(ios) == 1
assert len(ios) == 1 assert ios[0].dir == IoDir.WRITE
io = ios[0] assert ios[0].addr == addr.B - int(vol_begin[i])
assert io.dir == IoDir.WRITE assert ios[0].bytes == io_size.B
assert io.addr == addr.B - int(vol_begin[i])
assert io.bytes == io_size.B ios = io_trace[vol][TraceDevice.IoType.Flush]
assert len(ios) == i + 1
ios = io_trace[vol][TraceDevice.IoType.Discard]
assert len(ios) == 1
assert ios[0].addr == addr.B - int(vol_begin[i])
assert ios[0].bytes == io_size.B
cvol.close() cvol.close()
cvol.destroy() cvol.destroy()
@ -355,27 +361,36 @@ def test_io_propagation_cross_boundary(pyocf_ctx):
ret = cvol_submit_data_io(cvol, addr, io_size) ret = cvol_submit_data_io(cvol, addr, io_size)
assert ret == 0 assert ret == 0
ret = cvol_submit_flush_io(cvol, addr, io_size, IoFlags.FLUSH) ret = cvol_submit_flush_io(cvol, addr, io_size)
assert ret == 0 assert ret == 0
ret = cvol_submit_discard_io(cvol, addr, io_size) ret = cvol_submit_discard_io(cvol, addr, io_size)
assert ret == 0 assert ret == 0
for io_type in TraceDevice.IoType: ios1 = io_trace[vols[i]][TraceDevice.IoType.Data]
ios1 = io_trace[vols[i]][io_type] ios2 = io_trace[vols[i + 1]][TraceDevice.IoType.Data]
ios2 = io_trace[vols[i + 1]][io_type] assert len(ios1) == 1
assert ios1[0].dir == IoDir.WRITE
assert ios1[0].addr == int(vols[i].vol.size - (io_size / 2))
assert ios1[0].bytes == io_size.B / 2
assert len(ios2) == 1
assert ios2[0].dir == IoDir.WRITE
assert ios2[0].addr == 0
assert ios2[0].bytes == io_size.B / 2
assert len(ios1) == 1 ios1 = io_trace[vols[i]][TraceDevice.IoType.Flush]
io = ios1[0] ios2 = io_trace[vols[i + 1]][TraceDevice.IoType.Flush]
assert io.dir == IoDir.WRITE assert len(ios1) == 1
assert io.addr == int(vols[i].vol.size - (io_size / 2)) assert len(ios2) == 1
assert io.bytes == io_size.B / 2
assert len(ios2) == 1 ios1 = io_trace[vols[i]][TraceDevice.IoType.Discard]
io = ios2[0] ios2 = io_trace[vols[i + 1]][TraceDevice.IoType.Discard]
assert io.dir == IoDir.WRITE assert len(ios1) == 1
assert io.addr == 0 assert ios1[0].addr == int(vols[i].vol.size - (io_size / 2))
assert io.bytes == io_size.B / 2 assert ios1[0].bytes == io_size.B / 2
assert len(ios2) == 1
assert ios2[0].addr == 0
assert ios2[0].bytes == io_size.B / 2
cvol.close() cvol.close()
cvol.destroy() cvol.destroy()
@ -483,25 +498,51 @@ def test_io_propagation_multiple_subvolumes(pyocf_ctx, rand_seed):
ret = cvol_submit_data_io(cvol, addr, size) ret = cvol_submit_data_io(cvol, addr, size)
assert ret == 0 assert ret == 0
ret = cvol_submit_flush_io(cvol, addr, size, IoFlags.FLUSH) ret = cvol_submit_flush_io(cvol, addr, size)
assert ret == 0 assert ret == 0
ret = cvol_submit_discard_io(cvol, addr, size) ret = cvol_submit_discard_io(cvol, addr, size)
assert ret == 0 assert ret == 0
for vol in middle: for vol in middle:
for io in io_trace[vol].values(): ios = io_trace[vol][TraceDevice.IoType.Data]
assert len(io) == 1 assert len(ios) == 1
assert io[0].addr == 0 assert ios[0].addr == 0
assert io[0].bytes == int(vol.vol.size) assert ios[0].bytes == int(vol.vol.size)
for io in io_trace[first].values(): ios = io_trace[first][TraceDevice.IoType.Data]
assert io[0].addr == int(start_offset) assert len(ios) == 1
assert io[0].bytes == int(vol_size - start_offset) assert ios[0].addr == int(start_offset)
assert ios[0].bytes == int(vol_size - start_offset)
for io in io_trace[last].values(): ios = io_trace[last][TraceDevice.IoType.Data]
assert io[0].addr == 0 assert len(ios) == 1
assert io[0].bytes == int(end_offset) assert ios[0].addr == 0
assert ios[0].bytes == int(end_offset)
ios = io_trace[vol][TraceDevice.IoType.Flush]
assert len(ios) == 1
ios = io_trace[first][TraceDevice.IoType.Flush]
assert len(ios) == 1
ios = io_trace[last][TraceDevice.IoType.Flush]
assert len(ios) == 1
ios = io_trace[vol][TraceDevice.IoType.Discard]
assert len(ios) == 1
assert ios[0].addr == 0
assert ios[0].bytes == int(vol.vol.size)
ios = io_trace[first][TraceDevice.IoType.Discard]
assert len(ios) == 1
assert ios[0].addr == int(start_offset)
assert ios[0].bytes == int(vol_size - start_offset)
ios = io_trace[last][TraceDevice.IoType.Discard]
assert len(ios) == 1
assert ios[0].addr == 0
assert ios[0].bytes == int(end_offset)
cvol.close() cvol.close()
cvol.destroy() cvol.destroy()
@ -538,16 +579,16 @@ def test_io_completion(pyocf_ctx, rand_seed):
self.pending_ios = [] self.pending_ios = []
self.io_submitted = Event() self.io_submitted = Event()
def do_submit_io(self, io): def do_forward_io(self, token, rw, addr, nbytes, offset):
self.pending_ios.append(("io", io)) self.pending_ios.append(("io", token, rw, addr, nbytes, offset))
self.io_submitted.set() self.io_submitted.set()
def do_submit_flush(self, flush): def do_forward_flush(self, token):
self.pending_ios.append(("flush", flush)) self.pending_ios.append(("flush", token))
self.io_submitted.set() self.io_submitted.set()
def do_submit_discard(self, discard): def do_forward_discard(self, token, addr, nbytes):
self.pending_ios.append(("discard", discard)) self.pending_ios.append(("discard", token, addr, nbytes))
self.io_submitted.set() self.io_submitted.set()
def wait_submitted(self): def wait_submitted(self):
@ -558,13 +599,13 @@ def test_io_completion(pyocf_ctx, rand_seed):
if not self.pending_ios: if not self.pending_ios:
return False return False
io_type, io = self.pending_ios.pop() io_type, token, *params = self.pending_ios.pop()
if io_type == "io": if io_type == "io":
super().do_submit_io(io) super().do_forward_io(token, *params)
elif io_type == "flush": elif io_type == "flush":
super().do_submit_flush(io) super().do_forward_flush(token)
elif io_type == "discard": elif io_type == "discard":
super().do_submit_discard(io) super().do_forward_discard(token, *params)
else: else:
assert False assert False
@ -586,14 +627,18 @@ def test_io_completion(pyocf_ctx, rand_seed):
addr = vol_size / 2 addr = vol_size / 2
size = (subvol_count - 1) * vol_size size = (subvol_count - 1) * vol_size
for op, flags in [("submit", 0), ("submit_flush", IoFlags.FLUSH), ("submit_discard", 0)]: for op, cnt in [
("submit", subvol_count),
("submit_flush", len(vols)),
("submit_discard", subvol_count)
]:
io = cvol.new_io( io = cvol.new_io(
queue=None, queue=None,
addr=addr, addr=addr,
length=size, length=size,
direction=IoDir.WRITE, direction=IoDir.WRITE,
io_class=0, io_class=0,
flags=flags, flags=0,
) )
completion = OcfCompletion([("err", c_int)]) completion = OcfCompletion([("err", c_int)])
io.callback = completion.callback io.callback = completion.callback
@ -604,7 +649,7 @@ def test_io_completion(pyocf_ctx, rand_seed):
submit_fn = getattr(io, op) submit_fn = getattr(io, op)
submit_fn() submit_fn()
pending_vols = vols[:subvol_count] pending_vols = vols[:cnt]
for v in pending_vols: for v in pending_vols:
v.wait_submitted() v.wait_submitted()
@ -674,7 +719,7 @@ def test_io_error(pyocf_ctx, rand_seed):
assert ret == -OcfErrorCode.OCF_ERR_IO assert ret == -OcfErrorCode.OCF_ERR_IO
# verify flush properly propagated # verify flush properly propagated
ret = cvol_submit_flush_io(cvol, addr, size, IoFlags.FLUSH) ret = cvol_submit_flush_io(cvol, addr, size)
assert ret == -OcfErrorCode.OCF_ERR_IO assert ret == -OcfErrorCode.OCF_ERR_IO
# verdiscard discard properly propagated # verdiscard discard properly propagated