From 0030ebdecc894246d36a72e3f10c9737f97c7087 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 8 Jun 2022 10:00:35 +0200 Subject: [PATCH 01/20] Handle already opened volume in volume open Volumes are now exposed in OCF API and we should gracefully handle attempt to open already opened volume (instead of ENV_BUG). Signed-off-by: Adam Rutkowski --- src/ocf_volume.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ocf_volume.c b/src/ocf_volume.c index f335ece..4ae8321 100644 --- a/src/ocf_volume.c +++ b/src/ocf_volume.c @@ -327,8 +327,10 @@ int ocf_volume_open(ocf_volume_t volume, void *volume_params) { int ret; + if (volume->opened) + return -OCF_ERR_NOT_OPEN_EXC; + ENV_BUG_ON(!volume->type->properties->ops.open); - ENV_BUG_ON(volume->opened); ret = volume->type->properties->ops.open(volume, volume_params); if (ret) @@ -352,7 +354,9 @@ void ocf_volume_close(ocf_volume_t volume) env_completion cmpl; ENV_BUG_ON(!volume->type->properties->ops.close); - ENV_BUG_ON(!volume->opened); + + if (!volume->opened) + return; env_completion_init(&cmpl); ocf_refcnt_freeze(&volume->refcnt); From 02db4de75b003af42b911de2544265e9642d0153 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 8 Jun 2022 21:16:18 +0200 Subject: [PATCH 02/20] Composite volume io calculations fix Signed-off-by: Adam Rutkowski --- src/ocf_composite_volume.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index 5d7b95e..d1530ec 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -282,7 +282,7 @@ static void *ocf_composite_io_allocator_new(ocf_io_allocator_t allocator, member_addr = cur_addr - (i > 0 ? composite->end_addr[i-1] : 0); member_bytes = OCF_MIN(cur_addr + cur_bytes, composite->end_addr[i]) - - member_addr; + - cur_addr; cio->member_io[i] = ocf_io_new(&composite->member[i].volume, queue, member_addr, member_bytes, dir, 0, 0); From 5a80237e74638a10bd1040001788d75960004f71 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 8 Jun 2022 15:13:40 +0200 Subject: [PATCH 03/20] expose composite volume type id in API Signed-off-by: Adam Rutkowski --- inc/ocf_composite_volume.h | 2 ++ src/ocf_ctx_priv.h | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/inc/ocf_composite_volume.h b/inc/ocf_composite_volume.h index 7ae5a3a..9c09eb4 100644 --- a/inc/ocf_composite_volume.h +++ b/inc/ocf_composite_volume.h @@ -16,6 +16,8 @@ #include "ocf_err.h" #include "ocf_volume.h" +#define OCF_VOLUME_TYPE_COMPOSITE 10 + /** * @brief handle to object designating composite volume */ diff --git a/src/ocf_ctx_priv.h b/src/ocf_ctx_priv.h index 3ce457f..c3f0882 100644 --- a/src/ocf_ctx_priv.h +++ b/src/ocf_ctx_priv.h @@ -8,6 +8,7 @@ #include "ocf_env.h" #include "ocf/ocf_ctx.h" +#include "ocf/ocf_composite_volume.h" #include "ocf_logger_priv.h" #include "ocf_volume_priv.h" @@ -19,7 +20,11 @@ #define OCF_VOLUME_TYPE_CORE (OCF_VOLUME_TYPE_MAX_USER + 0) #define OCF_VOLUME_TYPE_CACHE (OCF_VOLUME_TYPE_MAX_USER + 1) -#define OCF_VOLUME_TYPE_COMPOSITE (OCF_VOLUME_TYPE_MAX_USER + 2) +#define OCF_VOLUME_TYPE_COMPOSITE_PLACEHOLDER (OCF_VOLUME_TYPE_MAX_USER + 2) + +#if OCF_VOLUME_TYPE_COMPOSITE_PLACEHOLDER != OCF_VOLUME_TYPE_COMPOSITE +#error "composite volume id mismatch" +#endif /** * @brief OCF main control structure From a7f70687a97943a4f1ae7301541b5ec1a6307424 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Thu, 9 Jun 2022 10:54:06 +0200 Subject: [PATCH 04/20] fix deinitialization of moved composite volume After moving from a volume, it's priv is assigned to the new owner. Destroying the volume after moving from it must not attempt to use the priv, especially not to attempt to deinit member volumes in case of composite volume. Signed-off-by: Adam Rutkowski --- src/ocf_composite_volume.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index d1530ec..a6bf91f 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -152,6 +152,11 @@ static void ocf_composite_volume_on_deinit(ocf_volume_t cvolume) struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); int i; + /* priv can be NULL if this volume had been moved from. In this case + * it's the owner responsibility to deinit member volumes. */ + if (!composite) + return; + for (i = 0; i < composite->members_cnt; i++) ocf_volume_deinit(&composite->member[i].volume); } From 19dff9d4a2cd123122f2099d78d8470060b6cb32 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Thu, 9 Jun 2022 15:48:23 +0200 Subject: [PATCH 05/20] pyocf: fix standby I/O test Signed-off-by: Adam Rutkowski --- tests/functional/tests/failover/test_standby_io.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/tests/failover/test_standby_io.py b/tests/functional/tests/failover/test_standby_io.py index d02f804..329deec 100644 --- a/tests/functional/tests/failover/test_standby_io.py +++ b/tests/functional/tests/failover/test_standby_io.py @@ -34,10 +34,11 @@ def test_test_standby_io(pyocf_ctx, cacheline_size): cache.add_io_queue(f"io-queue-{i}") cache.standby_attach(cache_vol) + cache_vol = CacheVolume(cache, open=True) r = ( Rio() - .target(cache) + .target(cache_vol) .njobs(num_jobs) .readwrite(ReadWrite.RANDWRITE) .size(vol_size) From 03dbbd44718225185d757965a3e4603e7703de40 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 1 Jun 2022 23:13:50 +0200 Subject: [PATCH 06/20] pyocf: fix discard I/O tracing Signed-off-by: Adam Rutkowski --- tests/functional/pyocf/types/volume.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/functional/pyocf/types/volume.py b/tests/functional/pyocf/types/volume.py index f6124b1..1a8155d 100644 --- a/tests/functional/pyocf/types/volume.py +++ b/tests/functional/pyocf/types/volume.py @@ -518,15 +518,18 @@ class TraceDevice(Volume): if submit: self.vol.do_submit_flush(io) + def do_submit_discard(self, io): + submit = self._trace(io, TraceDevice.IoType.Discard) + + if submit: + self.vol.do_submit_discard(io) + def get_length(self): return self.vol.get_length() def get_max_io_size(self): return self.vol.get_max_io_size() - def do_submit_discard(self, discard): - return self.vol.do_submit_discard(discard) - def dump(self, offset=0, size=0, ignore=VOLUME_POISON, **kwargs): return self.vol.dump(offset, size, ignore=ignore, **kwargs) From 6b9719907a56317f9e23adfb0a63232b071c71cf Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 8 Jun 2022 15:14:18 +0200 Subject: [PATCH 07/20] pyocf: helper to get composite volume type id Signed-off-by: Adam Rutkowski --- tests/functional/pyocf/c/helpers/volume_type.c | 13 +++++++++++++ tests/functional/pyocf/helpers.py | 5 +++++ 2 files changed, 18 insertions(+) create mode 100644 tests/functional/pyocf/c/helpers/volume_type.c diff --git a/tests/functional/pyocf/c/helpers/volume_type.c b/tests/functional/pyocf/c/helpers/volume_type.c new file mode 100644 index 0000000..08815c4 --- /dev/null +++ b/tests/functional/pyocf/c/helpers/volume_type.c @@ -0,0 +1,13 @@ +/* + * Copyright(c) 2022-2022 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "ocf/ocf_composite_volume.h" + +int ocf_get_composite_volume_type_id_helper() +{ + return OCF_VOLUME_TYPE_COMPOSITE; +} + + diff --git a/tests/functional/pyocf/helpers.py b/tests/functional/pyocf/helpers.py index 96dd90f..d807c87 100644 --- a/tests/functional/pyocf/helpers.py +++ b/tests/functional/pyocf/helpers.py @@ -34,3 +34,8 @@ def get_metadata_segment_elem_size(cache, segment): def get_metadata_segment_is_flapped(cache, segment): lib = OcfLib.getInstance() return bool(lib.ocf_get_metadata_segment_is_flapped(cache, segment)) + + +def get_composite_volume_type_id(): + lib = OcfLib.getInstance() + return int(lib.ocf_get_composite_volume_type_id_helper()) From 59d1905139fb5ebf803b48a953384e4aae306ad1 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Thu, 9 Jun 2022 11:01:17 +0200 Subject: [PATCH 08/20] pyocf: function to register internal volume type id in ctx Signed-off-by: Adam Rutkowski --- tests/functional/pyocf/types/ctx.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/functional/pyocf/types/ctx.py b/tests/functional/pyocf/types/ctx.py index dc3934f..e6fa60a 100644 --- a/tests/functional/pyocf/types/ctx.py +++ b/tests/functional/pyocf/types/ctx.py @@ -66,7 +66,18 @@ class OcfCtx: return cls.default() + def register_internal_volume_type_id(self, volume_type, volume_type_id): + if volume_type_id in self.volume_types: + raise RuntimeError(f"volume type id {volume_type_id} already used") + self.volume_types[volume_type_id] = volume_type + volume_type.internal = True + def register_volume_type(self, volume_type): + if self.volume_types_count in self.volume_types: + raise RuntimeError( + f"volume type id slot already used by internal volume " + f"{self.volume_types[self.volume_types_count]}" + ) self.volume_types[self.volume_types_count] = volume_type volume_type.type_id = self.volume_types_count @@ -79,10 +90,11 @@ class OcfCtx: raise OcfError("Volume type registration failed", result) self.ocf_volume_type[volume_type] = self.lib.ocf_ctx_get_volume_type( - self.ctx_handle, - volume_type.type_id + self.ctx_handle, volume_type.type_id ) + volume_type.internal = False + self.volume_types_count += 1 def unregister_volume_type(self, vol_type): @@ -96,7 +108,7 @@ class OcfCtx: def cleanup_volume_types(self): for k, vol_type in list(self.volume_types.items()): - if vol_type: + if vol_type and not vol_type.internal: self.unregister_volume_type(vol_type) def stop_caches(self): From db2e4ecb68c25b82b6b9023dbe5c104fda34be84 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Mon, 6 Jun 2022 17:04:11 +0200 Subject: [PATCH 09/20] pyocf: rename static Volume::open() to s_open() .. to distinguish from exported object open() instance method Signed-off-by: Adam Rutkowski --- tests/functional/pyocf/types/volume.py | 4 ++-- tests/functional/pyocf/types/volume_core.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/functional/pyocf/types/volume.py b/tests/functional/pyocf/types/volume.py index 1a8155d..30eb174 100644 --- a/tests/functional/pyocf/types/volume.py +++ b/tests/functional/pyocf/types/volume.py @@ -147,7 +147,7 @@ class Volume: print("{}".format(Volume._uuid_)) return -1 - return Volume.open(ref, volume) + return Volume.s_open(ref, volume) @VolumeOps.CLOSE def _close(ref): @@ -180,7 +180,7 @@ class Volume: return Volume._ops_[cls] @staticmethod - def open(ref, volume): + def s_open(ref, volume): if volume.opened: return -OcfErrorCode.OCF_ERR_NOT_OPEN_EXC diff --git a/tests/functional/pyocf/types/volume_core.py b/tests/functional/pyocf/types/volume_core.py index 6381de1..1de5098 100644 --- a/tests/functional/pyocf/types/volume_core.py +++ b/tests/functional/pyocf/types/volume_core.py @@ -18,7 +18,7 @@ class CoreVolume(ExpObjVolume): self.open() def open(self): - return Volume.open(self.lib.ocf_core_get_front_volume(self.core.handle), self) + return Volume.s_open(self.lib.ocf_core_get_front_volume(self.core.handle), self) def md5(self): return self._exp_obj_md5(4096) From b4dd5dac00c952f468115916018c92b8b34b7fcf Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 8 Jun 2022 21:25:54 +0200 Subject: [PATCH 10/20] pyocf: methods to get ocf_volume_t from cache/core exp obj vol Signed-off-by: Adam Rutkowski --- tests/functional/pyocf/types/cache.py | 10 ++++++++-- tests/functional/pyocf/types/core.py | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/functional/pyocf/types/cache.py b/tests/functional/pyocf/types/cache.py index ab32f33..2d15a09 100644 --- a/tests/functional/pyocf/types/cache.py +++ b/tests/functional/pyocf/types/cache.py @@ -759,10 +759,16 @@ class Cache: self.cores.remove(core) def get_front_volume(self): - return Volume.get_instance(lib.ocf_cache_get_front_volume(self.cache_handle)) + return Volume.get_instance(self.get_c_front_volume()) + + def get_c_front_volume(self): + return lib.ocf_cache_get_front_volume(self.cache_handle) def get_volume(self): - return Volume.get_instance(lib.ocf_cache_get_volume(self.cache_handle)) + return Volume.get_instance(self.get_c_volume()) + + def get_c_volume(self): + return lib.ocf_cache_get_volume(self.cache_handle) def get_conf(self): cache_info = CacheInfo() diff --git a/tests/functional/pyocf/types/core.py b/tests/functional/pyocf/types/core.py index 7f5c93b..0ea11e4 100644 --- a/tests/functional/pyocf/types/core.py +++ b/tests/functional/pyocf/types/core.py @@ -97,10 +97,16 @@ class Core: return self.handle def get_front_volume(self): - return Volume.get_instance(lib.ocf_core_get_front_volume(self.handle)) + return Volume.get_instance(self.get_c_front_volume()) + + def get_c_front_volume(self): + return lib.ocf_core_get_front_volume(self.handle) def get_volume(self): - return Volume.get_instance(lib.ocf_core_get_volume(self.handle)) + return Volume.get_instance(self.get_c_volume()) + + def get_c_volume(self): + return lib.ocf_core_get_volume(self.handle) def get_default_queue(self): return self.cache.get_default_queue() From 8e1fe9a263a868fcc0849af373a2f5a0a23a6811 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Mon, 6 Jun 2022 17:10:33 +0200 Subject: [PATCH 11/20] pyocf: generic open() for exported object volumes Signed-off-by: Adam Rutkowski --- tests/functional/pyocf/types/volume_cache.py | 4 ++-- tests/functional/pyocf/types/volume_core.py | 4 ++-- tests/functional/pyocf/types/volume_exp_obj.py | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/functional/pyocf/types/volume_cache.py b/tests/functional/pyocf/types/volume_cache.py index 7c6199e..5865ec1 100644 --- a/tests/functional/pyocf/types/volume_cache.py +++ b/tests/functional/pyocf/types/volume_cache.py @@ -20,8 +20,8 @@ class CacheVolume(ExpObjVolume): if open: self.open() - def open(self): - return Volume.open(self.lib.ocf_cache_get_front_volume(self.cache.cache_handle), self) + def get_c_handle(self): + return self.cache.get_c_front_volume() def md5(self): out = self.cache.get_conf() diff --git a/tests/functional/pyocf/types/volume_core.py b/tests/functional/pyocf/types/volume_core.py index 1de5098..02a7695 100644 --- a/tests/functional/pyocf/types/volume_core.py +++ b/tests/functional/pyocf/types/volume_core.py @@ -17,8 +17,8 @@ class CoreVolume(ExpObjVolume): if open: self.open() - def open(self): - return Volume.s_open(self.lib.ocf_core_get_front_volume(self.core.handle), self) + def get_c_handle(self): + return self.core.get_c_front_volume() def md5(self): return self._exp_obj_md5(4096) diff --git a/tests/functional/pyocf/types/volume_exp_obj.py b/tests/functional/pyocf/types/volume_exp_obj.py index 984e5b3..9d92b5e 100644 --- a/tests/functional/pyocf/types/volume_exp_obj.py +++ b/tests/functional/pyocf/types/volume_exp_obj.py @@ -113,6 +113,10 @@ class ExpObjVolume(Volume): return read_buffer_all.md5() + def open(self): + handle = self.get_c_handle() + return Volume.s_open(handle, self) + lib = OcfLib.getInstance() lib.ocf_volume_get_max_io_size.argtypes = [c_void_p] From 6c9f558b624f713093b1e868ab48051de2d04cb6 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Mon, 6 Jun 2022 18:44:22 +0200 Subject: [PATCH 12/20] pyocf: manage volume.opened in s_open Signed-off-by: Adam Rutkowski --- tests/functional/pyocf/types/volume.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/functional/pyocf/types/volume.py b/tests/functional/pyocf/types/volume.py index 30eb174..d82ed1c 100644 --- a/tests/functional/pyocf/types/volume.py +++ b/tests/functional/pyocf/types/volume.py @@ -187,7 +187,11 @@ class Volume: Volume._instances_[ref] = volume volume.handle = ref - return volume.do_open() + ret = volume.do_open() + if ret == 0: + volume.opened = True + + return ret @classmethod def get_io_ops(cls): @@ -255,7 +259,6 @@ class Volume: self.opened = False def do_open(self): - self.opened = True return 0 def close(self): From bd202364446ac3fd4faa90a8b5d4fe5162350fad Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Mon, 6 Jun 2022 18:44:43 +0200 Subject: [PATCH 13/20] pyocf: add volume::close() Signed-off-by: Adam Rutkowski --- tests/functional/pyocf/types/volume.py | 18 ++++++++++++++---- tests/functional/pyocf/types/volume_exp_obj.py | 3 +++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/tests/functional/pyocf/types/volume.py b/tests/functional/pyocf/types/volume.py index d82ed1c..05cdfd9 100644 --- a/tests/functional/pyocf/types/volume.py +++ b/tests/functional/pyocf/types/volume.py @@ -152,8 +152,7 @@ class Volume: @VolumeOps.CLOSE def _close(ref): volume = Volume.get_instance(ref) - volume.close() - volume.opened = False + Volume.s_close(volume) @VolumeOps.GET_MAX_IO_SIZE def _get_max_io_size(ref): @@ -193,6 +192,17 @@ class Volume: return ret + @staticmethod + def s_close(volume): + if not volume.opened: + return + + volume.do_close() + volume.opened = False + + del Volume._instances_[volume.handle] + volume.handle = None + @classmethod def get_io_ops(cls): return IoOps(_set_data=cls._io_set_data, _get_data=cls._io_get_data) @@ -261,8 +271,8 @@ class Volume: def do_open(self): return 0 - def close(self): - self.opened = False + def do_close(self): + pass def get_length(self): raise NotImplementedError diff --git a/tests/functional/pyocf/types/volume_exp_obj.py b/tests/functional/pyocf/types/volume_exp_obj.py index 9d92b5e..6409a9b 100644 --- a/tests/functional/pyocf/types/volume_exp_obj.py +++ b/tests/functional/pyocf/types/volume_exp_obj.py @@ -117,6 +117,9 @@ class ExpObjVolume(Volume): handle = self.get_c_handle() return Volume.s_open(handle, self) + def close(self): + return Volume.s_close(self) + lib = OcfLib.getInstance() lib.ocf_volume_get_max_io_size.argtypes = [c_void_p] From 2976c77c4cd2b86ec0476c004d93e97859420e77 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 8 Jun 2022 21:42:03 +0200 Subject: [PATCH 14/20] pyocf: allow no queue in I/O allocation .. this is useful for testing I/O API without any cache Signed-off-by: Adam Rutkowski --- tests/functional/pyocf/types/volume.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/functional/pyocf/types/volume.py b/tests/functional/pyocf/types/volume.py index 05cdfd9..e67aef4 100644 --- a/tests/functional/pyocf/types/volume.py +++ b/tests/functional/pyocf/types/volume.py @@ -337,7 +337,13 @@ class Volume: ): lib = OcfLib.getInstance() io = lib.ocf_volume_new_io( - self.handle, queue.handle, addr, length, direction, io_class, flags + self.handle, + queue.handle if queue else c_void_p(), + addr, + length, + direction, + io_class, + flags, ) return Io.from_pointer(io) From eb3439666dca179ab36ad5f2050c3f58a42ba6a3 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Thu, 9 Jun 2022 13:22:05 +0200 Subject: [PATCH 15/20] pyocf: destroy ocf_volume_t after attaching cache device Signed-off-by: Adam Rutkowski --- tests/functional/pyocf/types/cache.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/functional/pyocf/types/cache.py b/tests/functional/pyocf/types/cache.py index 2d15a09..a6f9e41 100644 --- a/tests/functional/pyocf/types/cache.py +++ b/tests/functional/pyocf/types/cache.py @@ -299,7 +299,7 @@ class Cache: raise OcfError("Failed to detach failover cache device", c.results["error"]) def standby_activate(self, device, open_cores=True): - device_cfg = self.generate_device_config(device) + device_cfg = self.alloc_device_config(device) activate_cfg = CacheStandbyActivateConfig(_device=device_cfg, _open_cores=open_cores,) @@ -311,6 +311,8 @@ class Cache: c.wait() self.write_unlock() + self.free_device_config(device_cfg) + if c.results["error"]: raise OcfError("Failed to activate standby cache", c.results["error"]) @@ -499,7 +501,7 @@ class Cache: if status: raise OcfError("Error adding partition to cache", status) - def generate_device_config(self, device, perform_test=True): + def alloc_device_config(self, device, perform_test=True): uuid = Uuid( _data=cast(create_string_buffer(device.uuid.encode("ascii")), c_char_p), _size=len(device.uuid) + 1, @@ -523,13 +525,16 @@ class Cache: return device_config + def free_device_config(self, cfg): + lib = OcfLib.getInstance().ocf_volume_destroy(cfg._volume) + def attach_device( self, device, force=False, perform_test=False, cache_line_size=None, open_cores=False, ): self.device = device self.device_name = device.uuid - device_config = self.generate_device_config(device, perform_test=perform_test) + device_config = self.alloc_device_config(device, perform_test=perform_test) attach_cfg = CacheAttachConfig( _device=device_config, @@ -548,6 +553,8 @@ class Cache: self.write_unlock() + self.free_device_config(device_config) + if c.results["error"]: raise OcfError( f"Attaching cache device failed", c.results["error"], @@ -557,7 +564,7 @@ class Cache: self.device = device self.device_name = device.uuid - device_config = self.generate_device_config(device, perform_test=False) + device_config = self.alloc_device_config(device, perform_test=False) attach_cfg = CacheAttachConfig( _device=device_config, @@ -576,6 +583,8 @@ class Cache: self.write_unlock() + self.free_device_config(device_config) + if c.results["error"]: raise OcfError( f"Attaching to standby cache failed", c.results["error"], @@ -585,7 +594,7 @@ class Cache: self.device = device self.device_name = device.uuid - device_config = self.generate_device_config(device, perform_test=perform_test) + device_config = self.alloc_device_config(device, perform_test=perform_test) attach_cfg = CacheAttachConfig( _device=device_config, @@ -601,6 +610,8 @@ class Cache: c.wait() self.write_unlock() + self.free_device_config(device_config) + if c.results["error"]: raise OcfError("Loading standby cache device failed", c.results["error"]) @@ -622,7 +633,7 @@ class Cache: self.device = device self.device_name = device.uuid - device_config = self.generate_device_config(device) + device_config = self.alloc_device_config(device) attach_cfg = CacheAttachConfig( _device=device_config, @@ -638,6 +649,8 @@ class Cache: c.wait() self.write_unlock() + self.free_device_config(device_config) + if c.results["error"]: raise OcfError("Loading cache device failed", c.results["error"]) @@ -977,3 +990,4 @@ lib.ocf_mngt_cache_io_classes_configure.restype = c_int lib.ocf_mngt_cache_io_classes_configure.argtypes = [c_void_p, c_void_p] lib.ocf_volume_create.restype = c_int lib.ocf_volume_create.argtypes = [c_void_p, c_void_p, c_void_p] +lib.ocf_volume_destroy.argtypes = [c_void_p] From 6195967483070005399f5ae2604d235178bc4246 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 8 Jun 2022 21:24:27 +0200 Subject: [PATCH 16/20] pyocf: use device.handle for device config if available Signed-off-by: Adam Rutkowski --- tests/functional/pyocf/types/cache.py | 30 +++--- tests/functional/pyocf/types/volume.py | 1 + .../tests/management/test_start_stop.py | 97 ++++++++++++++++--- 3 files changed, 102 insertions(+), 26 deletions(-) diff --git a/tests/functional/pyocf/types/cache.py b/tests/functional/pyocf/types/cache.py index a6f9e41..cebaba2 100644 --- a/tests/functional/pyocf/types/cache.py +++ b/tests/functional/pyocf/types/cache.py @@ -502,20 +502,24 @@ class Cache: raise OcfError("Error adding partition to cache", status) def alloc_device_config(self, device, perform_test=True): - uuid = Uuid( - _data=cast(create_string_buffer(device.uuid.encode("ascii")), c_char_p), - _size=len(device.uuid) + 1, - ) - volume = c_void_p() + if not device.handle: + uuid = Uuid( + _data=cast(create_string_buffer(device.uuid.encode("ascii")), c_char_p), + _size=len(device.uuid) + 1, + ) + volume = c_void_p() - lib = OcfLib.getInstance() - result = lib.ocf_volume_create( - byref(volume), - self.owner.ocf_volume_type[type(device)], - byref(uuid) - ) - if result != 0: - raise OcfError("Cache volume initialization failed", result) + lib = OcfLib.getInstance() + result = lib.ocf_volume_create( + byref(volume), + self.owner.ocf_volume_type[type(device)], + byref(uuid) + ) + + if result != 0: + raise OcfError("Cache volume initialization failed", result) + else: + volume = device.handle device_config = CacheDeviceConfig( _volume=volume, diff --git a/tests/functional/pyocf/types/volume.py b/tests/functional/pyocf/types/volume.py index e67aef4..09d5224 100644 --- a/tests/functional/pyocf/types/volume.py +++ b/tests/functional/pyocf/types/volume.py @@ -267,6 +267,7 @@ class Volume: self.reset_stats() self.is_online = True self.opened = False + self.handle = None def do_open(self): return 0 diff --git a/tests/functional/tests/management/test_start_stop.py b/tests/functional/tests/management/test_start_stop.py index 539ad6e..81b5bf9 100644 --- a/tests/functional/tests/management/test_start_stop.py +++ b/tests/functional/tests/management/test_start_stop.py @@ -4,7 +4,15 @@ # import logging -from ctypes import c_int, c_void_p, byref, c_uint32 +from ctypes import ( + c_int, + c_void_p, + byref, + c_uint32, + cast, + create_string_buffer, + c_char_p, +) from random import randrange from itertools import count @@ -19,13 +27,22 @@ from pyocf.types.cache import ( CacheConfig, PromotionPolicy, Backfill, + CacheDeviceConfig, + CacheAttachConfig, ) from pyocf.types.core import Core from pyocf.types.ctx import OcfCtx from pyocf.types.data import Data from pyocf.types.io import IoDir from pyocf.types.queue import Queue -from pyocf.types.shared import OcfError, OcfCompletion, CacheLineSize, SeqCutOffPolicy +from pyocf.types.shared import ( + Uuid, + OcfError, + OcfErrorCode, + OcfCompletion, + CacheLineSize, + SeqCutOffPolicy, +) from pyocf.types.volume import Volume, RamVolume from pyocf.types.volume_core import CoreVolume from pyocf.utils import Size @@ -234,7 +251,7 @@ def test_start_stop_multiple(pyocf_ctx): cache_line_size = CacheLineSize(size) cache = Cache.start_on_device( - cache_device, name=cache_name, cache_mode=cache_mode, cache_line_size=cache_line_size + cache_device, name=cache_name, cache_mode=cache_mode, cache_line_size=cache_line_size, ) caches.append(cache) stats = cache.get_stats() @@ -264,7 +281,7 @@ def test_100_start_stop(pyocf_ctx): cache_line_size = CacheLineSize(size) cache = Cache.start_on_device( - cache_device, name=cache_name, cache_mode=cache_mode, cache_line_size=cache_line_size + cache_device, name=cache_name, cache_mode=cache_mode, cache_line_size=cache_line_size, ) stats = cache.get_stats() assert stats["conf"]["cache_mode"] == cache_mode, "Cache mode" @@ -378,18 +395,72 @@ def test_start_cache_huge_device(pyocf_ctx_log_buffer, cls): @pytest.mark.parametrize("cls", CacheLineSize) def test_start_cache_same_device(pyocf_ctx, mode, cls): """Adding two caches using the same cache device - Check that OCF does not allow for 2 caches using the same cache device to be started + Check that OCF does not allow for 2 caches using the same cache device to be started. + Low level OCF API is used for attach instead of Cache::attach_device as the latter operates + on pyocf Volume objects and this test requires explicit construction of two volumes with + identical UUID. Pyocf does not allow for two Volume objects with the same UUID, as these + represent a resource that should be uniquely identified by UUID. So we need to create + two distinct OCF volumes with identical UUID and pass them to OCF cache attach method. """ + _uuid = "cache_dev" - cache_device = RamVolume(Size.from_MiB(50)) - cache = Cache.start_on_device(cache_device, cache_mode=mode, cache_line_size=cls, name="cache1") - cache.get_stats() + cache_device = RamVolume(Size.from_MiB(50), uuid=_uuid) - with pytest.raises(OcfError, match="OCF_ERR_NOT_OPEN_EXC"): - cache = Cache.start_on_device( - cache_device, cache_mode=mode, cache_line_size=cls, name="cache2" - ) - cache.get_stats() + uuid = Uuid( + _data=cast(create_string_buffer(_uuid.encode("ascii")), c_char_p), _size=len(_uuid) + 1, + ) + + lib = OcfLib.getInstance() + + vol1 = c_void_p() + vol2 = c_void_p() + + result = lib.ocf_volume_create(byref(vol1), pyocf_ctx.ocf_volume_type[RamVolume], byref(uuid)) + assert result == 0 + result = lib.ocf_volume_create(byref(vol2), pyocf_ctx.ocf_volume_type[RamVolume], byref(uuid)) + assert result == 0 + + dev_cfg = CacheDeviceConfig(_volume=vol1, _perform_test=False, _volume_params=None) + + attach_cfg = CacheAttachConfig( + _device=dev_cfg, + _cache_line_size=cls, + _open_cores=True, + _force=False, + _discard_on_start=False, + ) + + # start first cache instance + cache1 = Cache(pyocf_ctx, cache_mode=mode, cache_line_size=cls, name="cache1") + cache1.start_cache() + cache1.write_lock() + c = OcfCompletion([("cache", c_void_p), ("priv", c_void_p), ("error", c_int)]) + lib.ocf_mngt_cache_attach(cache1.cache_handle, byref(attach_cfg), c, None) + c.wait() + cache1.write_unlock() + assert not c.results["error"] + + # attempt to start second cache instance on a volume with the same UUID + attach_cfg._device._volume = vol2 + cache2 = Cache(pyocf_ctx, cache_mode=mode, cache_line_size=cls, name="cache2") + cache2.start_cache() + cache2.write_lock() + c = OcfCompletion([("cache", c_void_p), ("priv", c_void_p), ("error", c_int)]) + lib.ocf_mngt_cache_attach(cache2.cache_handle, byref(attach_cfg), c, None) + c.wait() + cache2.write_unlock() + + assert c.results["error"] + error_code = OcfErrorCode(abs(c.results["error"])) + assert error_code == OcfErrorCode.OCF_ERR_NOT_OPEN_EXC + + cache1.stop() + cache2.stop() + + lib = OcfLib.getInstance().ocf_volume_destroy(vol1) + lib = OcfLib.getInstance().ocf_volume_destroy(vol2) + + del cache_device @pytest.mark.parametrize("mode", CacheMode) From 0f8183157d2d2f1d1ff939e6523e862448e2e44d Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Fri, 10 Jun 2022 11:38:54 +0200 Subject: [PATCH 17/20] pyocf: rename ExpObjVolume to OcfInternalVolume Signed-off-by: Adam Rutkowski --- tests/functional/pyocf/types/volume_cache.py | 4 ++-- tests/functional/pyocf/types/volume_core.py | 4 ++-- tests/functional/pyocf/types/volume_exp_obj.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/functional/pyocf/types/volume_cache.py b/tests/functional/pyocf/types/volume_cache.py index 5865ec1..5faa834 100644 --- a/tests/functional/pyocf/types/volume_cache.py +++ b/tests/functional/pyocf/types/volume_cache.py @@ -8,11 +8,11 @@ from ctypes import cast, POINTER from .cache import Cache from .io import Io from .io import IoDir -from .volume_exp_obj import ExpObjVolume +from .volume_exp_obj import OcfInternalVolume from .volume import Volume -class CacheVolume(ExpObjVolume): +class CacheVolume(OcfInternalVolume): def __init__(self, cache, open=False, uuid=None): super().__init__(cache, uuid) self.cache = cache diff --git a/tests/functional/pyocf/types/volume_core.py b/tests/functional/pyocf/types/volume_core.py index 02a7695..71c9999 100644 --- a/tests/functional/pyocf/types/volume_core.py +++ b/tests/functional/pyocf/types/volume_core.py @@ -4,12 +4,12 @@ # from .core import Core -from .volume_exp_obj import ExpObjVolume +from .volume_exp_obj import OcfInternalVolume from .io import IoDir from .volume import Volume -class CoreVolume(ExpObjVolume): +class CoreVolume(OcfInternalVolume): def __init__(self, core, open=False, uuid=None): super().__init__(core, uuid) self.core = core diff --git a/tests/functional/pyocf/types/volume_exp_obj.py b/tests/functional/pyocf/types/volume_exp_obj.py index 6409a9b..b2838ae 100644 --- a/tests/functional/pyocf/types/volume_exp_obj.py +++ b/tests/functional/pyocf/types/volume_exp_obj.py @@ -14,7 +14,7 @@ from pyocf.types.io import IoDir, Io from pyocf.types.shared import OcfCompletion -class ExpObjVolume(Volume): +class OcfInternalVolume(Volume): def __init__(self, parent, uuid=None): super().__init__(uuid) self.parent = parent From 8f2fac399e21ab6ca5294783da0d19f57e05ccba Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 1 Jun 2022 18:22:51 +0200 Subject: [PATCH 18/20] pyocf: composite volume class Signed-off-by: Adam Rutkowski --- tests/functional/pyocf/types/cvolume.py | 78 +++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/functional/pyocf/types/cvolume.py diff --git a/tests/functional/pyocf/types/cvolume.py b/tests/functional/pyocf/types/cvolume.py new file mode 100644 index 0000000..b55d8c7 --- /dev/null +++ b/tests/functional/pyocf/types/cvolume.py @@ -0,0 +1,78 @@ +# +# Copyright(c) 2022 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# + +from ctypes import ( + c_int, + c_uint32, + c_uint64, + c_void_p, + c_char_p, + byref, + cast, + create_string_buffer, +) + +from ..ocf import OcfLib +from .ctx import OcfCtx +from .io import Io, IoDir +from .queue import Queue +from .shared import OcfError, Uuid +from .volume_exp_obj import OcfInternalVolume + + +class CVolume(OcfInternalVolume): + def __init__(self, ctx): + super().__init__(None) + self.ctx = ctx + self.lib = ctx.lib + + self.cvol = c_void_p() + ret = lib.ocf_composite_volume_create(byref(self.cvol), self.ctx.ctx_handle) + + if ret != 0: + raise OcfError("Composite volume creation failed", ret) + + self.handle = self.cvol.value + + def destroy(self): + self.lib.ocf_composite_volume_destroy(self.cvol) + self.cvol = None + self.handle = 0 + + def add(self, vol): + uuid = Uuid( + _data=cast(create_string_buffer(vol.uuid.encode("ascii")), c_char_p), + _size=len(vol.uuid) + 1, + ) + + volume = c_void_p() + ocf_vol_type = self.ctx.ocf_volume_type[type(vol)] + + ret = self.lib.ocf_composite_volume_add(self.cvol, ocf_vol_type, byref(uuid), c_void_p()) + + if ret != 0: + raise OcfError("Failed to add volume to a composite volume", ret) + + def get_c_handle(self): + return self.cvol.value + + def do_open(self): + ret = self.lib.ocf_volume_open(self.cvol, c_void_p()) + if ret != 0: + raise OcfError("openning composite volume failed", ret) + + def do_close(self): + self.lib.ocf_volume_close(self.cvol) + + +lib = OcfLib.getInstance() +lib.ocf_composite_volume_create.restype = c_int +lib.ocf_composite_volume_create.argtypes = [c_void_p, c_void_p] +lib.ocf_composite_volume_destroy.argtypes = [c_void_p] +lib.ocf_composite_volume_add.argtypes = [c_void_p, c_void_p, c_void_p, c_void_p] +lib.ocf_composite_volume_add.restype = c_int +lib.ocf_volume_open.restype = c_int +lib.ocf_volume_open.argtypes = [c_void_p, c_void_p] +lib.ocf_volume_close.argtypes = [c_void_p] From 96c5ef1d016ed3a33b091aa7cb9cc77f9eea07cd Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Thu, 9 Jun 2022 11:01:41 +0200 Subject: [PATCH 19/20] pyocf: register composite volume type id in ctx Signed-off-by: Adam Rutkowski --- tests/functional/tests/conftest.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/functional/tests/conftest.py b/tests/functional/tests/conftest.py index 2570769..f61e187 100644 --- a/tests/functional/tests/conftest.py +++ b/tests/functional/tests/conftest.py @@ -14,7 +14,9 @@ from pyocf.types.volume import RamVolume, ErrorDevice from pyocf.types.volume_cache import CacheVolume from pyocf.types.volume_core import CoreVolume from pyocf.types.volume_replicated import ReplicatedVolume +from pyocf.types.cvolume import CVolume from pyocf.types.ctx import OcfCtx +from pyocf.helpers import get_composite_volume_type_id default_registered_volumes = [RamVolume, ErrorDevice, CacheVolume, CoreVolume, ReplicatedVolume] @@ -28,6 +30,7 @@ def pyocf_ctx(): c = OcfCtx.with_defaults(DefaultLogger(LogLevel.WARN)) for vol_type in default_registered_volumes: c.register_volume_type(vol_type) + c.register_internal_volume_type_id(CVolume, get_composite_volume_type_id()) yield c c.exit() gc.collect() @@ -39,6 +42,7 @@ def pyocf_ctx_log_buffer(): c = OcfCtx.with_defaults(logger) for vol_type in default_registered_volumes: c.register_volume_type(vol_type) + c.register_internal_volume_type_id(CVolume, get_composite_volume_type_id()) yield logger c.exit() gc.collect() @@ -51,6 +55,8 @@ def pyocf_2_ctx(): for vol_type in default_registered_volumes: c1.register_volume_type(vol_type) c2.register_volume_type(vol_type) + c1.register_internal_volume_type_id(CVolume, get_composite_volume_type_id()) + c2.register_internal_volume_type_id(CVolume, get_composite_volume_type_id()) yield [c1, c2] c1.exit() c2.exit() From ae735b443492bbbe0d3303dcb5edf26e7128856a Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 1 Jun 2022 23:15:29 +0200 Subject: [PATCH 20/20] pyocf: composite volume tests Signed-off-by: Adam Rutkowski --- .../tests/management/test_composite_volume.py | 106 ++++++++++++++++-- 1 file changed, 98 insertions(+), 8 deletions(-) diff --git a/tests/functional/tests/management/test_composite_volume.py b/tests/functional/tests/management/test_composite_volume.py index eae42d3..19ee523 100644 --- a/tests/functional/tests/management/test_composite_volume.py +++ b/tests/functional/tests/management/test_composite_volume.py @@ -4,9 +4,16 @@ # import pytest +from ctypes import c_int + +from pyocf.types.volume import RamVolume, ErrorDevice, TraceDevice, IoFlags +from pyocf.types.cvolume import CVolume +from pyocf.types.data import Data +from pyocf.types.io import IoDir +from pyocf.types.shared import OcfError, OcfCompletion +from pyocf.utils import Size as S -@pytest.mark.skip(reason="not implemented") def test_create_composite_volume(pyocf_ctx): """ title: Create composite volume. @@ -26,10 +33,12 @@ def test_create_composite_volume(pyocf_ctx): - composite_volume::creation - composite_volume::adding_component_volume """ - pass + cvol = CVolume(pyocf_ctx) + vol = RamVolume(S.from_MiB(1)) + cvol.add(vol) + cvol.destroy() -@pytest.mark.skip(reason="not implemented") def test_add_subvolumes_of_different_types(pyocf_ctx): """ title: Add subvolumes of different types. @@ -49,10 +58,16 @@ def test_add_subvolumes_of_different_types(pyocf_ctx): requirements: - composite_volume::component_volume_types """ - pass + vol1 = RamVolume(S.from_MiB(1)) + vol2_backend = RamVolume(S.from_MiB(1)) + vol2 = ErrorDevice(vol2_backend) + + cvol = CVolume(pyocf_ctx) + cvol.add(vol1) + cvol.add(vol2) + cvol.destroy() -@pytest.mark.skip(reason="not implemented") def test_add_max_subvolumes(pyocf_ctx): """ title: Add maximum number of subvolumes. @@ -69,10 +84,36 @@ def test_add_max_subvolumes(pyocf_ctx): requirements: - composite_volume::max_composite_volumes """ - pass + + cvol = CVolume(pyocf_ctx) + + for i in range(16): + vol = RamVolume(S.from_MiB(1)) + cvol.add(vol) + + vol = RamVolume(S.from_MiB(1)) + with pytest.raises(OcfError): + cvol.add(vol) + + cvol.destroy() + + +def _cvol_io(cvol, addr, size, func, flags=0): + io = cvol.new_io( + queue=None, addr=addr, length=size, direction=IoDir.WRITE, io_class=0, flags=flags, + ) + completion = OcfCompletion([("err", c_int)]) + io.callback = completion.callback + data = Data(byte_count=size) + io.set_data(data, 0) + + submit_fn = getattr(io, func) + submit_fn() + completion.wait() + + return int(completion.results["err"]) -@pytest.mark.skip(reason="not implemented") def test_basic_volume_operations(pyocf_ctx): """ title: Perform basic volume operations. @@ -93,7 +134,56 @@ def test_basic_volume_operations(pyocf_ctx): - composite_volume::volume_api - composite_volume::io_request_passing """ - pass + count = {"flush": 0, "discard": 0, "io": 0} + expected = {"flush": 0, "discard": 0, "io": 0} + + pyocf_ctx.register_volume_type(TraceDevice) + + addr = S.from_KiB(512).B + size = S.from_KiB(4).B + + def trace(vol, io, io_type): + if io_type == TraceDevice.IoType.Flush or int(io.contents._flags) & IoFlags.FLUSH: + count["flush"] += 1 + elif io_type == TraceDevice.IoType.Discard: + count["discard"] += 1 + else: + assert io_type == TraceDevice.IoType.Data + count["io"] += 1 + assert io.contents._dir == IoDir.WRITE + assert io.contents._addr == addr + assert io.contents._bytes == size + + return True + + backend = RamVolume(S.from_MiB(1)) + trace_dev = TraceDevice(backend, trace_fcn=trace) + + cvol = CVolume(pyocf_ctx) + + cvol.add(trace_dev) + cvol.open() + + # verify data properly propagated + ret = _cvol_io(cvol, addr, size, "submit") + assert ret == 0 + expected["io"] += 1 + assert expected == count + + # verify flush properly propagated + ret = _cvol_io(cvol, addr, size, "submit_flush", IoFlags.FLUSH) + assert ret == 0 + expected["flush"] += 1 + assert expected == count + + # verify discard properly propagated + ret = _cvol_io(cvol, addr, size, "submit_discard") + assert ret == 0 + expected["discard"] += 1 + assert expected == count + + cvol.close() + cvol.destroy() @pytest.mark.skip(reason="not implemented")