From 53acfd7285fe372529f4daedff357a8d7c5e4f45 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 16 Feb 2026 14:11:42 +0100 Subject: [PATCH 01/28] checkpatch: Ignore MULTILINE_DEREFERENCE It is recurring pattern in OCF. Signed-off-by: Robert Baldyga --- .checkpatch.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/.checkpatch.conf b/.checkpatch.conf index 592e9e9a..34372c01 100644 --- a/.checkpatch.conf +++ b/.checkpatch.conf @@ -11,6 +11,7 @@ --ignore NEW_TYPEDEFS --ignore PREFER_DEFINED_ATTRIBUTE_MACRO --ignore REPEATED_WORD +--ignore MULTILINE_DEREFERENCE --exclude .github --exclude doc From 7c4289f367c99afd3e4e38d9fa7c73d568627fda Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Fri, 10 Nov 2023 17:55:16 +0100 Subject: [PATCH 02/28] lru: Get clines by id from user parts only Since the free_detached partition will have the same ID as the freelist (due to limited number of available IDs) requesting cache lines with part id set to PARTITION_FREELIST would end up with accessing unavailable (detached) cache lines. However, at the point of calling `lru_req_next_cline()` the freelist is empty anyway as the eviction algorithm makes sure that the whole cache is populated before trying to move cachelines between the user parts. Thus, it is safe to assume that all cache lines with part id == PARTITION_FREELIST are detached and this change doesn't modify the OCF's behavior. Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- src/ocf_lru.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/ocf_lru.c b/src/ocf_lru.c index a887b1c7..6f91505c 100644 --- a/src/ocf_lru.c +++ b/src/ocf_lru.c @@ -455,6 +455,7 @@ static inline bool _lru_iter_eviction_lock(struct ocf_lru_iter *iter, /* Get next clean cacheline from tail of lru lists. Caller must not hold any * lru list lock. + * - the function doesn't return cache lines from freelist * - returned cacheline is write locked * - returned cacheline has the corresponding metadata hash bucket write locked * - cacheline is moved to the head of destination partition lru list before @@ -474,9 +475,14 @@ static inline ocf_cache_line_t lru_req_next_cline(struct ocf_request *req, ocf_cache_line_t ret = end_marker; ocf_core_id_t t_core_id; uint64_t t_core_line; + ocf_part_id_t tmp_part_id; ocf_metadata_lru_lock(&cache->metadata.lock, curr_lru); + tmp_part_id = ocf_metadata_get_partition_id(cache, cline); + if (tmp_part_id == PARTITION_FREELIST) + goto lru_wr_unlock; + if (!ocf_cache_line_try_lock_wr(c, cline)) goto lru_wr_unlock; @@ -492,16 +498,13 @@ static inline ocf_cache_line_t lru_req_next_cline(struct ocf_request *req, if (metadata_test_dirty(cache, cline)) goto line_unlock_wr; - *src_part_id = ocf_metadata_get_partition_id(cache, cline); - if (*src_part_id != PARTITION_FREELIST) { - if (!_lru_trylock_hash(iter, t_core_id, t_core_line)) - goto line_unlock_wr; - *core_id = t_core_id; - *core_line = t_core_line; - part = &cache->user_parts[*src_part_id].part; - } else { - part = &cache->free; - } + *src_part_id = tmp_part_id; + + if (!_lru_trylock_hash(iter, t_core_id, t_core_line)) + goto line_unlock_wr; + *core_id = t_core_id; + *core_line = t_core_line; + part = &cache->user_parts[*src_part_id].part; if (dst_part->id != *src_part_id) { ocf_lru_repart_locked(cache, cline, part, dst_part); From 3a8aa75c4cdba8e839b959d2a7c1f7ee4d29dc38 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Tue, 8 Aug 2023 11:20:24 +0200 Subject: [PATCH 03/28] lru: Introduce free detached partition An additional partition to store cache lines that belonged to detached members of composite volume. Keeping track of such cache lines will allow to easily reuse them once the missing composite volume member will be readded. The unavaliable cache lines will be moved to free_detached part, however because of the limited number of available part_ids the unavailable cache lines will have assigned PARTITION_FREE as partition id in metadata. Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- src/metadata/metadata.c | 2 ++ src/metadata/metadata_partition.h | 4 +++- src/mngt/ocf_mngt_cache.c | 2 ++ src/ocf_cache.c | 6 +++++- src/ocf_cache_priv.h | 2 ++ src/ocf_part.h | 2 +- 6 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/metadata/metadata.c b/src/metadata/metadata.c index a3bac796..9fb69d77 100644 --- a/src/metadata/metadata.c +++ b/src/metadata/metadata.c @@ -605,6 +605,8 @@ static int ocf_metadata_init_fixed_size(struct ocf_cache *cache, &part_runtime_meta[i].runtime; } cache->free.runtime= &part_runtime_meta[PARTITION_FREELIST].runtime; + cache->free_detached.runtime = + &part_runtime_meta[PARTITION_FREE_DETACHED].runtime; /* Set core metadata */ core_meta_config = METADATA_MEM_POOL(ctrl, diff --git a/src/metadata/metadata_partition.h b/src/metadata/metadata_partition.h index e8ec0338..6793bfaa 100644 --- a/src/metadata/metadata_partition.h +++ b/src/metadata/metadata_partition.h @@ -1,6 +1,7 @@ /* * Copyright(c) 2012-2021 Intel Corporation * Copyright(c) 2024 Huawei Technologies Co., Ltd. + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,7 +12,8 @@ #define PARTITION_DEFAULT 0 #define PARTITION_UNSPECIFIED ((ocf_part_id_t)-1) -#define PARTITION_FREELIST OCF_USER_IO_CLASS_MAX + 1 +#define PARTITION_FREELIST (OCF_USER_IO_CLASS_MAX + 1) +#define PARTITION_FREE_DETACHED (OCF_USER_IO_CLASS_MAX + 2) #define PARTITION_SIZE_MIN 0 #define PARTITION_SIZE_MAX 100 diff --git a/src/mngt/ocf_mngt_cache.c b/src/mngt/ocf_mngt_cache.c index c7b7eac7..1d87cdc8 100644 --- a/src/mngt/ocf_mngt_cache.c +++ b/src/mngt/ocf_mngt_cache.c @@ -214,6 +214,7 @@ static void _init_parts_attached(ocf_pipeline_t pipeline, void *priv, ocf_lru_init(cache, &cache->user_parts[part_id].part); ocf_lru_init(cache, &cache->free); + ocf_lru_init(cache, &cache->free_detached); ocf_pipeline_next(pipeline); } @@ -269,6 +270,7 @@ static void __deinit_promotion_policy(ocf_cache_t cache) static void __init_free(ocf_cache_t cache) { cache->free.id = PARTITION_FREELIST; + cache->free_detached.id = PARTITION_FREELIST; } static void __init_cores(ocf_cache_t cache) diff --git a/src/ocf_cache.c b/src/ocf_cache.c index 8bf9a12b..1d900bdf 100644 --- a/src/ocf_cache.c +++ b/src/ocf_cache.c @@ -121,6 +121,7 @@ int ocf_cache_get_info(ocf_cache_t cache, struct ocf_cache_info *info) uint64_t core_dirty_since; uint32_t dirty_blocks_inactive = 0; uint32_t cache_occupancy_inactive = 0; + uint32_t detached_size = 0; ocf_core_t core; ocf_core_id_t core_id; @@ -136,10 +137,13 @@ int ocf_cache_get_info(ocf_cache_t cache, struct ocf_cache_info *info) info->attached = ocf_cache_is_device_attached(cache); info->standby_detached = ocf_cache_is_standby(cache) && env_refcnt_frozen(&cache->refcnt.metadata); + + detached_size = env_atomic_read( + &cache->free_detached.runtime->curr_size); if (info->attached && !info->standby_detached) { info->volume_type = ocf_ctx_get_volume_type_id(cache->owner, cache->device->volume.type); - info->size = ocf_cache_get_line_count(cache); + info->size = ocf_cache_get_line_count(cache) - detached_size; } info->state = cache->cache_state; info->cache_line_size = ocf_line_size(cache); diff --git a/src/ocf_cache_priv.h b/src/ocf_cache_priv.h index 49b856c0..e010e94a 100644 --- a/src/ocf_cache_priv.h +++ b/src/ocf_cache_priv.h @@ -1,6 +1,7 @@ /* * Copyright(c) 2012-2022 Intel Corporation * Copyright(c) 2024-2025 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -70,6 +71,7 @@ struct ocf_cache { struct ocf_user_part user_parts[OCF_USER_IO_CLASS_MAX + 1]; struct ocf_part free; + struct ocf_part free_detached; uint32_t fallback_pt_error_threshold; ocf_queue_t mngt_queue; diff --git a/src/ocf_part.h b/src/ocf_part.h index 0217eda6..c1b692bf 100644 --- a/src/ocf_part.h +++ b/src/ocf_part.h @@ -14,7 +14,7 @@ #include "ocf_space.h" #include "ocf_env_refcnt.h" -#define OCF_NUM_PARTITIONS OCF_USER_IO_CLASS_MAX + 2 +#define OCF_NUM_PARTITIONS (OCF_USER_IO_CLASS_MAX + 3) struct ocf_user_part_config { char name[OCF_IO_CLASS_NAME_MAX]; From 659d88dca341e8ebebee31bb8ce846b7f74f708d Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Tue, 8 Aug 2023 11:26:52 +0200 Subject: [PATCH 04/28] flush: Allow to flush a range of cache lines Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- src/mngt/ocf_mngt_flush.c | 40 +++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/mngt/ocf_mngt_flush.c b/src/mngt/ocf_mngt_flush.c index 450ea82e..f2473efd 100644 --- a/src/mngt/ocf_mngt_flush.c +++ b/src/mngt/ocf_mngt_flush.c @@ -49,6 +49,10 @@ struct ocf_mngt_cache_flush_context /* target core */ ocf_core_t core; + /* Flush only range of cache lines */ + ocf_cache_line_t begin; + ocf_cache_line_t end; + struct { bool lock : 1; bool freeze : 1; @@ -210,7 +214,8 @@ static int _ocf_mngt_get_sectors(ocf_cache_t cache, ocf_core_id_t core_id, } static int _ocf_mngt_get_flush_containers(ocf_cache_t cache, - struct flush_container **fctbl, uint32_t *fcnum) + struct flush_container **fctbl, uint32_t *fcnum, + ocf_cache_line_t begin, ocf_cache_line_t end) { struct flush_container *fc; struct flush_container *curr; @@ -223,6 +228,16 @@ static int _ocf_mngt_get_flush_containers(ocf_cache_t cache, uint32_t dirty_found = 0, dirty_total = 0, dirty_cores = 0; int ret = 0; + end = OCF_MIN(ocf_metadata_collision_table_entries(cache), end); + + if (begin > end) + return -OCF_ERR_INVAL; + + if (begin == end) { + ocf_cache_log(cache, log_warn, "Flush with 0 length\n"); + return 0; + } + ocf_metadata_start_exclusive_access(&cache->metadata.lock); core_count = cache->conf_meta->core_count; @@ -273,7 +288,11 @@ static int _ocf_mngt_get_flush_containers(ocf_cache_t cache, goto free_fc; } - for (line = 0; line < cache->device->collision_table_entries; line++) { + ocf_cache_log(cache, log_debug, + "Range of cache lines to flush %u - %u\n", + begin, end); + + for (line = begin; line < end; line++) { ocf_metadata_get_core_info(cache, line, &core_id, &core_line); if (metadata_test_valid_any(cache, line) && @@ -361,8 +380,7 @@ static void _ocf_mngt_flush_portion_end(void *private_data, int error) } } - if (env_atomic_read(&core->runtime_meta->dirty_clines) == 0 || - env_atomic_read(&fsc->error)) { + if (fc->iter == fc->count || env_atomic_read(&fsc->error)) { ocf_req_put(fc->req); fc->end(context); return; @@ -409,15 +427,10 @@ static void _ocf_mngt_flush_portion(struct flush_container *fc) fc->flush_portion = OCF_MAX(fc->flush_portion, OCF_MNG_FLUSH_MIN); curr_flush_portion_size = OCF_MIN(core_dirty_count, fc->flush_portion); + curr_flush_portion_size = OCF_MIN(curr_flush_portion_size, + fc->count - fc->iter); ENV_BUG_ON(curr_flush_portion_size == 0); - if (unlikely(curr_flush_portion_size + fc->iter > fc->count)) { - ocf_cache_log(cache, log_crit, "An invalid flush request. Flush" - " container size %u. Flushed cache lines %u. To" - " be flushed in the next iteration %u\n", - fc->count, fc->iter, curr_flush_portion_size); - ENV_BUG(); - } fc->iter += curr_flush_portion_size; @@ -585,7 +598,8 @@ static void _ocf_mngt_flush_all_cores( env_atomic_set(&cache->flush_in_progress, 1); /* Get all 'dirty' sectors for all cores */ - ret = _ocf_mngt_get_flush_containers(cache, &fctbl, &fcnum); + ret = _ocf_mngt_get_flush_containers(cache, &fctbl, &fcnum, + context->begin, context->end); if (ret) { ocf_cache_log(cache, log_err, "Flushing operation aborted, " "no memory\n"); @@ -723,6 +737,8 @@ void ocf_mngt_cache_flush(ocf_cache_t cache, context->priv = priv; context->cache = cache; context->op = flush_cache; + context->begin = 0; + context->end = ocf_metadata_collision_table_entries(cache); ocf_pipeline_next(context->pipeline); } From af874c43db3c221ee6663597619c6963bfd3e62b Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Tue, 8 Aug 2023 11:30:13 +0200 Subject: [PATCH 05/28] Add utility to detach range of cache lines Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- src/metadata/metadata_misc.c | 36 +++++++++++++ src/metadata/metadata_misc.h | 3 ++ src/mngt/ocf_mngt_cache.c | 1 + src/mngt/ocf_mngt_flush.c | 98 ++++++++++++++++++++++++++++++++-- src/mngt/ocf_mngt_flush_priv.h | 25 +++++++++ src/ocf_lru.c | 9 +++- src/ocf_lru.h | 4 +- src/utils/utils_cache_line.c | 56 +++++++++++++++++++ src/utils/utils_cache_line.h | 15 ++++++ 9 files changed, 241 insertions(+), 6 deletions(-) create mode 100644 src/mngt/ocf_mngt_flush_priv.h diff --git a/src/metadata/metadata_misc.c b/src/metadata/metadata_misc.c index 294fe28b..ac933ddf 100644 --- a/src/metadata/metadata_misc.c +++ b/src/metadata/metadata_misc.c @@ -1,5 +1,7 @@ /* * Copyright(c) 2012-2021 Intel Corporation + * Copyright(c) 2023 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -44,3 +46,37 @@ int ocf_metadata_sparse_range(struct ocf_cache *cache, int core_id, return ocf_metadata_actor(cache, PARTITION_UNSPECIFIED, core_id, start_byte, end_byte, ocf_metadata_sparse_cache_line); } + +int ocf_metadata_detach_cline_range(ocf_cache_t cache, ocf_cache_line_t begin, + ocf_cache_line_t end) +{ + uint32_t step = 0; + struct ocf_alock *c = ocf_cache_line_concurrency(cache); + ocf_cache_line_t cline; + + if (begin > end) + return -OCF_ERR_INVAL; + + if (end > cache->device->collision_table_entries) + return -OCF_ERR_INVAL; + + for (cline = begin; cline < end; ++cline) { + ENV_BUG_ON(ocf_cache_line_is_used(c, cline)); + + ocf_metadata_start_collision_shared_access(cache, cline); + + set_cache_line_unavailable(cache, 0, ocf_line_end_sector(cache), + cline); + + /* + * This is especially for removing inactive core + */ + metadata_clear_dirty(cache, cline); + + ocf_metadata_end_collision_shared_access(cache, cline); + + OCF_COND_RESCHED_DEFAULT(step); + } + + return 0; +} diff --git a/src/metadata/metadata_misc.h b/src/metadata/metadata_misc.h index 6fb40c42..434d16a2 100644 --- a/src/metadata/metadata_misc.h +++ b/src/metadata/metadata_misc.h @@ -40,4 +40,7 @@ void ocf_metadata_sparse_cache_line(struct ocf_cache *cache, int ocf_metadata_sparse_range(struct ocf_cache *cache, int core_id, uint64_t start_byte, uint64_t end_byte); +int ocf_metadata_detach_cline_range(ocf_cache_t cache, ocf_cache_line_t begin, + ocf_cache_line_t end); + #endif /* __METADATA_MISC_H__ */ diff --git a/src/mngt/ocf_mngt_cache.c b/src/mngt/ocf_mngt_cache.c index 1d87cdc8..878c7af4 100644 --- a/src/mngt/ocf_mngt_cache.c +++ b/src/mngt/ocf_mngt_cache.c @@ -10,6 +10,7 @@ #include "ocf_env_refcnt.h" #include "ocf_mngt_common.h" #include "ocf_mngt_core_priv.h" +#include "ocf_mngt_flush_priv.h" #include "../ocf_priv.h" #include "../ocf_core_priv.h" #include "../ocf_part.h" diff --git a/src/mngt/ocf_mngt_flush.c b/src/mngt/ocf_mngt_flush.c index f2473efd..68c91da5 100644 --- a/src/mngt/ocf_mngt_flush.c +++ b/src/mngt/ocf_mngt_flush.c @@ -7,6 +7,7 @@ #include "ocf/ocf.h" #include "ocf_env_refcnt.h" +#include "ocf_mngt_flush_priv.h" #include "ocf_mngt_common.h" #include "../ocf_priv.h" #include "../metadata/metadata.h" @@ -55,7 +56,8 @@ struct ocf_mngt_cache_flush_context struct { bool lock : 1; - bool freeze : 1; + bool dirty_freeze : 1; + bool metadata_freeze : 1; } flags; /* management operation identifier */ @@ -102,12 +104,23 @@ static void _ocf_mngt_begin_flush(ocf_pipeline_t pipeline, void *priv, context->flags.lock = true; env_refcnt_freeze(&cache->refcnt.dirty); - context->flags.freeze = true; + context->flags.dirty_freeze = true; ocf_mngt_continue_pipeline_on_zero_refcnt(&cache->refcnt.dirty, context->pipeline); } +static void _ocf_mngt_begin_cache_lines_invalidate(ocf_pipeline_t pipeline, + void *priv, ocf_pipeline_arg_t arg) +{ + struct ocf_mngt_cache_flush_context *context = priv; + struct env_refcnt *refcnt = &context->cache->refcnt.metadata; + + env_refcnt_freeze(refcnt); + context->flags.metadata_freeze = true; + ocf_mngt_continue_pipeline_on_zero_refcnt(refcnt, context->pipeline); +} + bool ocf_mngt_core_is_dirty(ocf_core_t core) { ocf_cache_t cache; @@ -657,7 +670,10 @@ static void _ocf_mngt_flush_finish(ocf_pipeline_t pipeline, void *priv, ocf_cache_t cache = context->cache; ocf_core_t core = context->core; - if (context->flags.freeze) + if (context->flags.metadata_freeze) + env_refcnt_unfreeze(&cache->refcnt.metadata); + + if (context->flags.dirty_freeze) env_refcnt_unfreeze(&cache->refcnt.dirty); if (context->flags.lock) @@ -851,6 +867,35 @@ static void _ocf_mngt_cache_invalidate(ocf_pipeline_t pipeline, void *priv, OCF_PL_NEXT_ON_SUCCESS_RET(context->pipeline, result); } +static void _ocf_mngt_cache_detach_cline_range(ocf_pipeline_t pipeline, + void *priv, ocf_pipeline_arg_t arg) +{ + struct ocf_mngt_cache_flush_context *context = priv; + ocf_cache_t cache = context->cache; + int result; + ocf_cache_line_t free_detached_before, free_detached_after; + + free_detached_before = + env_atomic_read(&cache->free_detached.runtime->curr_size); + + ocf_metadata_start_exclusive_access(&cache->metadata.lock); + result = ocf_metadata_detach_cline_range(cache, context->begin, + context->end); + ocf_metadata_end_exclusive_access(&cache->metadata.lock); + + free_detached_after = + env_atomic_read(&cache->free_detached.runtime->curr_size); + + if (unlikely(result && free_detached_before != free_detached_after)) { + ocf_cache_log(cache, log_err, "Error %d during detaching cache" + " lines %u - %u\n", result, context->begin, + context->end); + ENV_BUG(); + } + + OCF_PL_NEXT_ON_SUCCESS_RET(context->pipeline, result); +} + static struct ocf_pipeline_properties _ocf_mngt_cache_purge_pipeline_properties = { .priv_size = sizeof(struct ocf_mngt_cache_flush_context), @@ -902,6 +947,53 @@ void ocf_mngt_cache_purge(ocf_cache_t cache, ocf_pipeline_next(context->pipeline); } +static struct ocf_pipeline_properties +_ocf_mngt_cache_detach_cline_range_pipeline_props = { + .priv_size = sizeof(struct ocf_mngt_cache_flush_context), + .finish = _ocf_mngt_flush_finish, + .steps = { + OCF_PL_STEP(_ocf_mngt_begin_flush), + OCF_PL_STEP(_ocf_mngt_cache_flush), + OCF_PL_STEP(_ocf_mngt_begin_cache_lines_invalidate), + OCF_PL_STEP(_ocf_mngt_cache_detach_cline_range), + OCF_PL_STEP_TERMINATOR(), + }, +}; + +void ocf_mngt_cache_detach_cline_range(ocf_cache_t cache, + ocf_cache_line_t begin, ocf_cache_line_t end, + ocf_mngt_cache_purge_end_t cmpl, void *priv) +{ + ocf_pipeline_t pipeline; + int result = 0; + struct ocf_mngt_cache_flush_context *context; + + OCF_CHECK_NULL(cache); + + if (begin > end) + OCF_CMPL_RET(cache, priv, -OCF_ERR_INVAL); + + if (end > ocf_metadata_collision_table_entries(cache)) + OCF_CMPL_RET(cache, priv, -OCF_ERR_INVAL); + + result = ocf_pipeline_create(&pipeline, cache, + &_ocf_mngt_cache_detach_cline_range_pipeline_props); + if (result) + OCF_CMPL_RET(cache, priv, -OCF_ERR_NO_MEM); + + context = ocf_pipeline_get_priv(pipeline); + + context->begin = begin; + context->end = end; + context->pipeline = pipeline; + context->cmpl.purge_cache = cmpl; + context->priv = priv; + context->cache = cache; + context->op = purge_cache; + + ocf_pipeline_next(context->pipeline); +} + static struct ocf_pipeline_properties _ocf_mngt_core_purge_pipeline_properties = { .priv_size = sizeof(struct ocf_mngt_cache_flush_context), diff --git a/src/mngt/ocf_mngt_flush_priv.h b/src/mngt/ocf_mngt_flush_priv.h new file mode 100644 index 00000000..50887379 --- /dev/null +++ b/src/mngt/ocf_mngt_flush_priv.h @@ -0,0 +1,25 @@ +/* + * Copyright(c) 2023 Huawei Technologies Co., Ltd. + * Copyright(c) 2026 Unvertical + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __OCF_MNGT_FLUSH_PRIV_H__ +#define __OCF_MNGT_FLUSH_PRIV_H__ + +#include + +/** + * @brief Detach range of clines from a given cache + * + * @param[in] cache Cache handle + * @param[in] begin begin of the cache line range + * @param[in] end end of the cache line range + * @param[in] cmpl Completion callback + * @param[in] priv Completion callback context + */ +void ocf_mngt_cache_detach_cline_range(ocf_cache_t cache, + ocf_cache_line_t begin, ocf_cache_line_t end, + ocf_mngt_cache_purge_end_t cmpl, void *priv); + +#endif /* __OCF_MNGT_FLUSH_PRIV_H__ */ diff --git a/src/ocf_lru.c b/src/ocf_lru.c index 6f91505c..8e1221ff 100644 --- a/src/ocf_lru.c +++ b/src/ocf_lru.c @@ -202,16 +202,21 @@ static void remove_lru_list_nobalance(ocf_cache_t cache, node->hot = false; } -void ocf_lru_remove_locked(ocf_cache_t cache, struct ocf_lru_list *list, +static void ocf_lru_remove_locked(ocf_cache_t cache, struct ocf_lru_list *list, ocf_cache_line_t cline) { remove_lru_list_nobalance(cache, list, cline); balance_lru_list(cache, list); } -static void ocf_lru_set_hot(ocf_cache_t cache, struct ocf_lru_list *list, +void ocf_lru_detach(ocf_cache_t cache, struct ocf_part *part, ocf_cache_line_t cline) +{ + ocf_lru_repart(cache, cline, part, &cache->free_detached); +} +static void ocf_lru_set_hot(ocf_cache_t cache, struct ocf_lru_list *list, + ocf_cache_line_t cline) { remove_lru_list_nobalance(cache, list, cline); add_lru_head_nobalance(cache, list, cline); diff --git a/src/ocf_lru.h b/src/ocf_lru.h index 79fc5599..6085d86d 100644 --- a/src/ocf_lru.h +++ b/src/ocf_lru.h @@ -1,5 +1,7 @@ /* * Copyright(c) 2012-2021 Intel Corporation + * Copyright(c) 2023 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ #ifndef __EVICTION_LRU_H__ @@ -34,7 +36,7 @@ void ocf_lru_add_free(ocf_cache_t cache, ocf_cache_line_t cline); uint32_t ocf_lru_num_free(ocf_cache_t cache); struct ocf_lru_list *ocf_lru_get_list(struct ocf_part *part, uint32_t lru_idx, bool clean); -void ocf_lru_remove_locked(ocf_cache_t cache, struct ocf_lru_list *list, +void ocf_lru_detach(ocf_cache_t cache, struct ocf_part *part, ocf_cache_line_t cline); typedef void (*ocf_lru_populate_end_t)(void *priv, int error); diff --git a/src/utils/utils_cache_line.c b/src/utils/utils_cache_line.c index 86689660..18336350 100644 --- a/src/utils/utils_cache_line.c +++ b/src/utils/utils_cache_line.c @@ -1,6 +1,7 @@ /* * Copyright(c) 2012-2021 Intel Corporation * Copyright(c) 2024-2025 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -42,6 +43,49 @@ static void __set_cache_line_invalid(struct ocf_cache *cache, uint8_t start_bit, part_counters[part_id].cached_clines); } +static void __detach_cache_line(struct ocf_cache *cache, uint8_t start_bit, + uint8_t end_bit, ocf_cache_line_t line, + ocf_core_id_t core_id, ocf_part_id_t part_id) +{ + ocf_core_t core; + bool is_valid = true; + struct ocf_part *part; + + ENV_BUG_ON(part_id == PARTITION_FREELIST && core_id != OCF_CORE_MAX); + ENV_BUG_ON(part_id != PARTITION_FREELIST && core_id == OCF_CORE_MAX); + + if (part_id != PARTITION_FREELIST) + part = &cache->user_parts[part_id].part; + else + part = &cache->free; + + if (core_id == OCF_CORE_MAX) + goto delete_invalid; + + if (metadata_clear_valid_sec_changed(cache, line, start_bit, end_bit, + &is_valid)) { + /* + * Update the number of cached data for that core object + */ + core = ocf_cache_get_core(cache, core_id); + env_atomic_dec(&core->runtime_meta->cached_clines); + env_atomic_dec(&core->runtime_meta-> + part_counters[part_id].cached_clines); + } + + ENV_BUG_ON(ocf_cache_line_are_waiters( + ocf_cache_line_concurrency(cache), line)); + + if (!is_valid) + ocf_metadata_remove_cache_line(cache, line); +delete_invalid: + /* + * Even if the cache line was on the freelist, + * it must be set as unavailable. + */ + ocf_lru_detach(cache, part, line); +} + void set_cache_line_invalid(struct ocf_cache *cache, uint8_t start_bit, uint8_t end_bit, struct ocf_request *req, uint32_t map_idx) { @@ -61,6 +105,18 @@ void set_cache_line_invalid(struct ocf_cache *cache, uint8_t start_bit, end_bit); } +void set_cache_line_unavailable(struct ocf_cache *cache, uint8_t start_bit, + uint8_t end_bit, ocf_cache_line_t line) +{ + ocf_part_id_t part_id; + ocf_core_id_t core_id; + + ocf_metadata_get_core_and_part_id(cache, line, &core_id, &part_id); + + __detach_cache_line(cache, start_bit, end_bit, line, core_id, + part_id); +} + void set_cache_line_invalid_no_flush(struct ocf_cache *cache, uint8_t start_bit, uint8_t end_bit, ocf_cache_line_t line) { diff --git a/src/utils/utils_cache_line.h b/src/utils/utils_cache_line.h index 87f3501d..d507a243 100644 --- a/src/utils/utils_cache_line.h +++ b/src/utils/utils_cache_line.h @@ -1,6 +1,7 @@ /* * Copyright(c) 2012-2021 Intel Corporation * Copyright(c) 2024 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -60,6 +61,20 @@ static inline uint64_t ocf_lines_2_bytes(struct ocf_cache *cache, void set_cache_line_invalid(struct ocf_cache *cache, uint8_t start_bit, uint8_t end_bit, struct ocf_request *req, uint32_t map_idx); +/** + * @brief Set cache line invalid and unavailable + * + * @note Collision page must be locked by the caller (either exclusive access + * to collision table page OR write lock on metadata hash bucket combined with + * shared access to the collision page) + * + * @param cache Cache instance + * @param start_bit Start bit of cache line for which state will be set + * @param end_bit End bit of cache line for which state will be set + * @param line Cache line to be set as unavailable + */ +void set_cache_line_unavailable(struct ocf_cache *cache, uint8_t start_bit, + uint8_t end_bit, ocf_cache_line_t line); /** * @brief Set cache line invalid without flush From 3f6325f1a62a815bad35a8a3d7f5edba824ba0f4 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Wed, 6 Sep 2023 15:44:06 +0200 Subject: [PATCH 06/28] Add utlility to restore detached cache lines It allows to move free detached cache lines back into the freelist. Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- src/metadata/metadata_misc.c | 25 +++++++++++++++++++++++++ src/metadata/metadata_misc.h | 3 +++ src/mngt/ocf_mngt_flush.c | 25 +++++++++++++++++++++++++ src/mngt/ocf_mngt_flush_priv.h | 10 ++++++++++ src/ocf_lru.c | 5 +++++ src/ocf_lru.h | 1 + src/utils/utils_cache_line.c | 5 +++++ src/utils/utils_cache_line.h | 13 +++++++++++++ 8 files changed, 87 insertions(+) diff --git a/src/metadata/metadata_misc.c b/src/metadata/metadata_misc.c index ac933ddf..ef60083b 100644 --- a/src/metadata/metadata_misc.c +++ b/src/metadata/metadata_misc.c @@ -80,3 +80,28 @@ int ocf_metadata_detach_cline_range(ocf_cache_t cache, ocf_cache_line_t begin, return 0; } + +int ocf_metadata_restore_cline_range(ocf_cache_t cache, ocf_cache_line_t begin, + ocf_cache_line_t end) +{ + uint32_t step = 0; + ocf_cache_line_t cline; + + if (begin > end) + return -OCF_ERR_INVAL; + + if (end > cache->device->collision_table_entries) + return -OCF_ERR_INVAL; + + for (cline = begin; cline < end; ++cline) { + ocf_metadata_start_collision_shared_access(cache, cline); + + set_cache_line_available(cache, cline); + + ocf_metadata_end_collision_shared_access(cache, cline); + + OCF_COND_RESCHED_DEFAULT(step); + } + + return 0; +} diff --git a/src/metadata/metadata_misc.h b/src/metadata/metadata_misc.h index 434d16a2..383be958 100644 --- a/src/metadata/metadata_misc.h +++ b/src/metadata/metadata_misc.h @@ -43,4 +43,7 @@ int ocf_metadata_sparse_range(struct ocf_cache *cache, int core_id, int ocf_metadata_detach_cline_range(ocf_cache_t cache, ocf_cache_line_t begin, ocf_cache_line_t end); +int ocf_metadata_restore_cline_range(ocf_cache_t cache, ocf_cache_line_t begin, + ocf_cache_line_t end); + #endif /* __METADATA_MISC_H__ */ diff --git a/src/mngt/ocf_mngt_flush.c b/src/mngt/ocf_mngt_flush.c index 68c91da5..e96f86c6 100644 --- a/src/mngt/ocf_mngt_flush.c +++ b/src/mngt/ocf_mngt_flush.c @@ -896,6 +896,31 @@ static void _ocf_mngt_cache_detach_cline_range(ocf_pipeline_t pipeline, OCF_PL_NEXT_ON_SUCCESS_RET(context->pipeline, result); } +int ocf_mngt_cache_attach_cline_range(ocf_cache_t cache, + ocf_cache_line_t begin, ocf_cache_line_t end) +{ + int result; + ocf_cache_line_t free_detached_before, free_detached_after; + + free_detached_before = + env_atomic_read(&cache->free_detached.runtime->curr_size); + + ocf_metadata_start_exclusive_access(&cache->metadata.lock); + result = ocf_metadata_restore_cline_range(cache, begin, end); + ocf_metadata_end_exclusive_access(&cache->metadata.lock); + + free_detached_after = + env_atomic_read(&cache->free_detached.runtime->curr_size); + + if (unlikely(result && free_detached_before != free_detached_after)) { + ocf_cache_log(cache, log_err, "Error %d during restoring cache" + " lines %u - %u\n", result, begin, end); + ENV_BUG(); + } + + return result; +} + static struct ocf_pipeline_properties _ocf_mngt_cache_purge_pipeline_properties = { .priv_size = sizeof(struct ocf_mngt_cache_flush_context), diff --git a/src/mngt/ocf_mngt_flush_priv.h b/src/mngt/ocf_mngt_flush_priv.h index 50887379..4ab6ab69 100644 --- a/src/mngt/ocf_mngt_flush_priv.h +++ b/src/mngt/ocf_mngt_flush_priv.h @@ -22,4 +22,14 @@ void ocf_mngt_cache_detach_cline_range(ocf_cache_t cache, ocf_cache_line_t begin, ocf_cache_line_t end, ocf_mngt_cache_purge_end_t cmpl, void *priv); +/** + * @brief Attach range of cache lines from free_detached + * + * @param[in] cache Cache handle + * @param[in] first_cline Begin of the range + * @param[in] last_cline End of the range + */ +int ocf_mngt_cache_attach_cline_range(ocf_cache_t cache, + ocf_cache_line_t first_cline, ocf_cache_line_t last_cline); + #endif /* __OCF_MNGT_FLUSH_PRIV_H__ */ diff --git a/src/ocf_lru.c b/src/ocf_lru.c index 8e1221ff..7e7ad2cd 100644 --- a/src/ocf_lru.c +++ b/src/ocf_lru.c @@ -215,6 +215,11 @@ void ocf_lru_detach(ocf_cache_t cache, struct ocf_part *part, ocf_lru_repart(cache, cline, part, &cache->free_detached); } +void ocf_lru_restore(ocf_cache_t cache, ocf_cache_line_t cline) +{ + ocf_lru_repart(cache, cline, &cache->free_detached, &cache->free); +} + static void ocf_lru_set_hot(ocf_cache_t cache, struct ocf_lru_list *list, ocf_cache_line_t cline) { diff --git a/src/ocf_lru.h b/src/ocf_lru.h index 6085d86d..840461b7 100644 --- a/src/ocf_lru.h +++ b/src/ocf_lru.h @@ -38,6 +38,7 @@ struct ocf_lru_list *ocf_lru_get_list(struct ocf_part *part, uint32_t lru_idx, bool clean); void ocf_lru_detach(ocf_cache_t cache, struct ocf_part *part, ocf_cache_line_t cline); +void ocf_lru_restore(ocf_cache_t cache, ocf_cache_line_t cline); typedef void (*ocf_lru_populate_end_t)(void *priv, int error); diff --git a/src/utils/utils_cache_line.c b/src/utils/utils_cache_line.c index 18336350..bef402ad 100644 --- a/src/utils/utils_cache_line.c +++ b/src/utils/utils_cache_line.c @@ -105,6 +105,11 @@ void set_cache_line_invalid(struct ocf_cache *cache, uint8_t start_bit, end_bit); } +void set_cache_line_available(struct ocf_cache *cache, ocf_cache_line_t line) +{ + ocf_lru_restore(cache, line); +} + void set_cache_line_unavailable(struct ocf_cache *cache, uint8_t start_bit, uint8_t end_bit, ocf_cache_line_t line) { diff --git a/src/utils/utils_cache_line.h b/src/utils/utils_cache_line.h index d507a243..4ff8d0e0 100644 --- a/src/utils/utils_cache_line.h +++ b/src/utils/utils_cache_line.h @@ -76,6 +76,19 @@ void set_cache_line_invalid(struct ocf_cache *cache, uint8_t start_bit, void set_cache_line_unavailable(struct ocf_cache *cache, uint8_t start_bit, uint8_t end_bit, ocf_cache_line_t line); + +/** + * @brief Set cache line available and move it to the freelist + * + * @note Collision page must be locked by the caller (either exclusive access + * to collision table page OR write lock on metadata hash bucket combined with + * shared access to the collision page) + * + * @param cache Cache instance + * @param line Cache line to be set as available + */ +void set_cache_line_available(struct ocf_cache *cache, ocf_cache_line_t line); + /** * @brief Set cache line invalid without flush * From 2107f9553e1646be483e9db2d8ce661d68e571ab Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Tue, 22 Aug 2023 08:05:00 +0200 Subject: [PATCH 07/28] volume: Add 'composite' flag to capabilities Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- example/simple/src/volume.c | 2 ++ inc/ocf_volume.h | 12 ++++++++++++ src/ocf_cache.c | 1 + src/ocf_composite_volume.c | 2 ++ src/ocf_core.c | 2 ++ src/ocf_volume.c | 5 +++++ 6 files changed, 24 insertions(+) diff --git a/example/simple/src/volume.c b/example/simple/src/volume.c index 40fa852a..28596731 100644 --- a/example/simple/src/volume.c +++ b/example/simple/src/volume.c @@ -1,6 +1,7 @@ /* * Copyright(c) 2019-2022 Intel Corporation * Copyright(c) 2024 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -103,6 +104,7 @@ const struct ocf_volume_properties volume_properties = { .volume_priv_size = sizeof(struct myvolume), .caps = { .atomic_writes = 0, + .composite_volume = 0, }, .ops = { .open = volume_open, diff --git a/inc/ocf_volume.h b/inc/ocf_volume.h index a7426c32..0dd22fd8 100644 --- a/inc/ocf_volume.h +++ b/inc/ocf_volume.h @@ -40,6 +40,9 @@ struct ocf_volume_uuid { struct ocf_volume_caps { uint32_t atomic_writes : 1; /*!< Volume supports atomic writes */ + + uint32_t composite_volume : 1; + /*!< Volume may be composed of multiple sub-volumes */ }; /** @@ -337,6 +340,15 @@ ocf_cache_t ocf_volume_get_cache(ocf_volume_t volume); */ int ocf_volume_is_atomic(ocf_volume_t volume); +/** + * @brief Check if volume is composited of multiple sub-volumes + * + * @param[in] volume Volume + * + * @return True if volume is composite, otherwise false + */ +bool ocf_volume_is_composite(ocf_volume_t volume); + /** * @brief Allocate new io * diff --git a/src/ocf_cache.c b/src/ocf_cache.c index 1d900bdf..55f99e87 100644 --- a/src/ocf_cache.c +++ b/src/ocf_cache.c @@ -443,6 +443,7 @@ const struct ocf_volume_properties ocf_cache_volume_properties = { .volume_priv_size = sizeof(struct ocf_cache_volume), .caps = { .atomic_writes = 0, + .composite_volume = 0, }, .ops = { .submit_io = ocf_cache_volume_submit_io, diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index e86c20ad..26347a9c 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -1,6 +1,7 @@ /* * Copyright(c) 2022 Intel Corporation * Copyright(c) 2024 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -330,6 +331,7 @@ const struct ocf_volume_properties ocf_composite_volume_properties = { .volume_priv_size = sizeof(struct ocf_composite_volume), .caps = { .atomic_writes = 0, + .composite_volume = 1, }, .ops = { .forward_io = ocf_composite_forward_io, diff --git a/src/ocf_core.c b/src/ocf_core.c index 07486426..fa88f600 100644 --- a/src/ocf_core.c +++ b/src/ocf_core.c @@ -1,6 +1,7 @@ /* * Copyright(c) 2012-2021 Intel Corporation * Copyright(c) 2023-2025 Huawei Technologies Co., Ltd. + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -439,6 +440,7 @@ const struct ocf_volume_properties ocf_core_volume_properties = { .volume_priv_size = sizeof(struct ocf_core_volume), .caps = { .atomic_writes = 0, + .composite_volume = 0, }, .ops = { .submit_io = ocf_core_volume_submit_io, diff --git a/src/ocf_volume.c b/src/ocf_volume.c index a9f0d314..47380b29 100644 --- a/src/ocf_volume.c +++ b/src/ocf_volume.c @@ -289,6 +289,11 @@ int ocf_volume_is_atomic(ocf_volume_t volume) return volume->type->properties->caps.atomic_writes; } +bool ocf_volume_is_composite(ocf_volume_t volume) +{ + return volume->type->properties->caps.composite_volume; +} + ocf_io_t ocf_volume_new_io(ocf_volume_t volume, ocf_queue_t queue, uint64_t addr, uint32_t bytes, uint32_t dir, uint32_t io_class, uint64_t flags) From e95b168c3a8f6ed4c4dd6d56686f71b01d16267c Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Tue, 7 Nov 2023 21:00:53 +0100 Subject: [PATCH 08/28] Store ctx pointer in volume type Signed-off-by: Robert Baldyga --- src/ocf_ctx.c | 4 +++- src/ocf_volume.c | 3 ++- src/ocf_volume_priv.h | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ocf_ctx.c b/src/ocf_ctx.c index 75c47264..ec6db1eb 100644 --- a/src/ocf_ctx.c +++ b/src/ocf_ctx.c @@ -1,6 +1,7 @@ /* * Copyright(c) 2012-2022 Intel Corporation * Copyright(c) 2024 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -36,7 +37,8 @@ int ocf_ctx_register_volume_type_internal(ocf_ctx_t ctx, uint8_t type_id, goto err; } - ocf_volume_type_init(&ctx->volume_type[type_id], properties, extended); + ocf_volume_type_init(&ctx->volume_type[type_id], ctx, properties, + extended); if (!ctx->volume_type[type_id]) result = -OCF_ERR_INVAL; diff --git a/src/ocf_volume.c b/src/ocf_volume.c index 47380b29..056445aa 100644 --- a/src/ocf_volume.c +++ b/src/ocf_volume.c @@ -33,7 +33,7 @@ int ocf_uuid_set_str(ocf_uuid_t uuid, char *str) * Volume type */ -int ocf_volume_type_init(struct ocf_volume_type **type, +int ocf_volume_type_init(struct ocf_volume_type **type, ocf_ctx_t ctx, const struct ocf_volume_properties *properties, const struct ocf_volume_extended *extended) { @@ -65,6 +65,7 @@ int ocf_volume_type_init(struct ocf_volume_type **type, goto err; new_type->properties = properties; + new_type->owner = ctx; *type = new_type; diff --git a/src/ocf_volume_priv.h b/src/ocf_volume_priv.h index dea2afe8..45822e84 100644 --- a/src/ocf_volume_priv.h +++ b/src/ocf_volume_priv.h @@ -1,6 +1,7 @@ /* * Copyright(c) 2012-2021 Intel Corporation * Copyright(c) 2024-2025 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -19,6 +20,7 @@ struct ocf_volume_extended { struct ocf_volume_type { const struct ocf_volume_properties *properties; struct ocf_io_allocator allocator; + ocf_ctx_t owner; }; struct ocf_volume { @@ -39,7 +41,7 @@ struct ocf_volume { struct env_refcnt refcnt; } __attribute__((aligned(64))); -int ocf_volume_type_init(struct ocf_volume_type **type, +int ocf_volume_type_init(struct ocf_volume_type **type, ocf_ctx_t ctx, const struct ocf_volume_properties *properties, const struct ocf_volume_extended *extended); From ae5d83eb3b753e4d016d1796cad7b0d4da59d98f Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Tue, 7 Nov 2023 21:02:49 +0100 Subject: [PATCH 09/28] core: Introduce core volume uuid Signed-off-by: Robert Baldyga --- src/mngt/ocf_mngt_core.c | 17 ++++++++++++++--- src/ocf_core.c | 24 ++++++++++++++++++++---- src/ocf_core_priv.h | 6 ++++++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/mngt/ocf_mngt_core.c b/src/mngt/ocf_mngt_core.c index 8f460cf9..ad3b2982 100644 --- a/src/mngt/ocf_mngt_core.c +++ b/src/mngt/ocf_mngt_core.c @@ -225,18 +225,29 @@ int ocf_mngt_core_init_front_volume(ocf_core_t core) { ocf_cache_t cache = ocf_core_get_cache(core); ocf_volume_type_t type; + struct ocf_core_volume_uuid core_uuid = {0}; struct ocf_volume_uuid uuid = { - .data = core, - .size = sizeof(core), + .data = &core_uuid, + .size = sizeof(core_uuid), }; int ret; + ret = env_memcpy(&core_uuid.cache_name, OCF_CACHE_NAME_SIZE, + ocf_cache_get_name(cache), OCF_CACHE_NAME_SIZE); + if (ret) + return -OCF_ERR_INVAL; + + ret = env_memcpy(&core_uuid.core_name, OCF_CORE_NAME_SIZE, + ocf_core_get_name(core), OCF_CORE_NAME_SIZE); + if (ret) + return -OCF_ERR_INVAL; + type = ocf_ctx_get_volume_type_internal(cache->owner, OCF_VOLUME_TYPE_CORE); if (!type) return -OCF_ERR_INVAL; - ret = ocf_volume_init(&core->front_volume, type, &uuid, false); + ret = ocf_volume_init(&core->front_volume, type, &uuid, true); if (ret) return ret; diff --git a/src/ocf_core.c b/src/ocf_core.c index fa88f600..ffba9b82 100644 --- a/src/ocf_core.c +++ b/src/ocf_core.c @@ -57,9 +57,6 @@ int ocf_core_get_by_name(ocf_cache_t cache, const char *name, size_t name_len, ocf_core_t i_core; ocf_core_id_t i_core_id; - if (ocf_cache_is_standby(cache)) - return -OCF_ERR_CACHE_STANDBY; - for_each_core(cache, i_core, i_core_id) { if (!env_strncmp(ocf_core_get_name(i_core), OCF_CORE_NAME_SIZE, name, name_len)) { @@ -409,11 +406,30 @@ static void ocf_core_volume_submit_discard(ocf_io_t io) static int ocf_core_volume_open(ocf_volume_t volume, void *volume_params) { struct ocf_core_volume *core_volume = ocf_volume_get_priv(volume); + ocf_ctx_t ctx = ocf_volume_get_type(volume)->owner; const struct ocf_volume_uuid *uuid = ocf_volume_get_uuid(volume); - ocf_core_t core = (ocf_core_t)uuid->data; + const struct ocf_core_volume_uuid *core_uuid = + (struct ocf_core_volume_uuid *)uuid->data; + ocf_cache_t cache; + ocf_core_t core; + int result; + + result = ocf_mngt_cache_get_by_name(ctx, core_uuid->cache_name, + OCF_CACHE_NAME_SIZE, &cache); + if (result) + return result; + + result = ocf_core_get_by_name(cache, core_uuid->core_name, + OCF_CORE_NAME_SIZE, &core); + if (result) { + ocf_mngt_cache_put(cache); + return result; + } core_volume->core = core; + ocf_mngt_cache_put(cache); + return 0; } diff --git a/src/ocf_core_priv.h b/src/ocf_core_priv.h index 02b82d80..6f2ab7bd 100644 --- a/src/ocf_core_priv.h +++ b/src/ocf_core_priv.h @@ -1,6 +1,7 @@ /* * Copyright(c) 2012-2021 Intel Corporation * Copyright(c) 2024 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -77,6 +78,11 @@ struct ocf_core_meta_runtime { } part_counters[OCF_USER_IO_CLASS_MAX]; }; +struct ocf_core_volume_uuid { + char cache_name[OCF_CACHE_NAME_SIZE]; + char core_name[OCF_CORE_NAME_SIZE]; +}; + struct ocf_core { struct ocf_volume front_volume; struct ocf_volume volume; From 02f3de1a06459c4cc392370303d0d0e2252f8129 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 9 Feb 2026 21:47:23 +0100 Subject: [PATCH 10/28] composite_volume: Move OCF_COMPOSITE_VOLUME_MEMBERS_MAX to inc/ Show OCF_COMPOSITE_VOLUME_MEMBERS_MAX in OCF public API, as it may be useful in adapter implementation. Signed-off-by: Robert Baldyga --- inc/ocf_def.h | 17 +++++++++++++++++ src/ocf_composite_volume.c | 2 -- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/inc/ocf_def.h b/inc/ocf_def.h index 4ff2c510..929561f7 100644 --- a/inc/ocf_def.h +++ b/inc/ocf_def.h @@ -1,6 +1,7 @@ /* * Copyright(c) 2012-2022 Intel Corporation * Copyright(c) 2024 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -93,6 +94,22 @@ * @} */ +/** + * @name Composite volume defines + * @{ + */ +/* + * Maximum value of a composite volume member id + */ +#define OCF_COMPOSITE_VOLUME_MEMBERS_MAX 16 +/* + * Invalid value of composite volume member + */ +#define OCF_COMPOSITE_VOLUME_MEMBER_ID_INVALID OCF_COMPOSITE_VOLUME_MEMBERS_MAX +/** + * @} + */ + /** * @name Miscellaneous defines * @{ diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index 26347a9c..aadd4454 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -15,8 +15,6 @@ #include "ocf_request.h" #include "ocf_composite_volume_priv.h" -#define OCF_COMPOSITE_VOLUME_MEMBERS_MAX 16 - struct ocf_composite_volume { uint8_t members_cnt; struct { From 18f26816439ab6b28f14857e791f8be6cbb60398 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 16 Feb 2026 13:07:48 +0100 Subject: [PATCH 11/28] volume: Fix on_init() Call on_init() properly in situation when uuid==NULL. Signed-off-by: Robert Baldyga --- src/ocf_volume.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ocf_volume.c b/src/ocf_volume.c index 056445aa..92c55f19 100644 --- a/src/ocf_volume.c +++ b/src/ocf_volume.c @@ -121,7 +121,7 @@ int ocf_volume_init(ocf_volume_t volume, ocf_volume_type_t type, env_refcnt_freeze(&volume->refcnt); if (!uuid) - return 0; + goto on_init; volume->uuid_copy = uuid_copy; @@ -145,6 +145,7 @@ int ocf_volume_init(ocf_volume_t volume, ocf_volume_type_t type, volume->uuid.size = uuid->size; +on_init: if (volume->type->properties->ops.on_init) { ret = volume->type->properties->ops.on_init(volume); if (ret) From d87402b9d9daa00fafc2f8126092e42d23ecc329 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 16 Feb 2026 10:18:26 +0100 Subject: [PATCH 12/28] composite_volume: Move length and max io size calculation to add Those values are going to be needed for composite volume attach/detach operations, which could be performed before the volume is opened. Signed-off-by: Robert Baldyga --- src/ocf_composite_volume.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index aadd4454..c70d70be 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -268,19 +268,12 @@ static int ocf_composite_volume_open(ocf_volume_t cvolume, void *volume_params) struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); int result, i; - composite->length = 0; - composite->max_io_size = UINT_MAX; for (i = 0; i < composite->members_cnt; i++) { ocf_volume_t volume = &composite->member[i].volume; result = ocf_volume_open(volume, composite->member[i].volume_params); if (result) goto err; - - composite->length += ocf_volume_get_length(volume); - composite->end_addr[i] = composite->length; - composite->max_io_size = OCF_MIN(composite->max_io_size, - ocf_volume_get_max_io_size(volume)); } return 0; @@ -315,6 +308,16 @@ static uint64_t ocf_composite_volume_get_byte_length(ocf_volume_t cvolume) return composite->length; } +static int ocf_composite_volume_on_init(ocf_volume_t cvolume) +{ + struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); + + composite->length = 0; + composite->max_io_size = UINT_MAX; + + return 0; +} + static void ocf_composite_volume_on_deinit(ocf_volume_t cvolume) { struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); @@ -344,6 +347,7 @@ const struct ocf_volume_properties ocf_composite_volume_properties = { .get_max_io_size = ocf_composite_volume_get_max_io_size, .get_length = ocf_composite_volume_get_byte_length, + .on_init = ocf_composite_volume_on_init, .on_deinit = ocf_composite_volume_on_deinit, }, .deinit = NULL, @@ -388,6 +392,19 @@ int ocf_composite_volume_add(ocf_composite_volume_t cvolume, if (result) return result; + result = ocf_volume_open(volume, volume_params); + if (result) { + ocf_volume_deinit(volume); + return result; + } + + composite->length += ocf_volume_get_length(volume); + composite->end_addr[composite->members_cnt] = composite->length; + composite->max_io_size = OCF_MIN(composite->max_io_size, + ocf_volume_get_max_io_size(volume)); + + ocf_volume_close(volume); + composite->member[composite->members_cnt].volume_params = volume_params; composite->members_cnt++; From 9ae938fae390b36e563947606deff714c332c5a2 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Fri, 13 Feb 2026 17:29:41 +0100 Subject: [PATCH 13/28] volume: Add option to copy uuid on ocf_volume_set_uuid() Signed-off-by: Robert Baldyga --- src/mngt/ocf_mngt_core.c | 2 +- src/ocf_volume.c | 35 +++++++++++++++++++++++++++++++++-- src/ocf_volume_priv.h | 4 ++-- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/mngt/ocf_mngt_core.c b/src/mngt/ocf_mngt_core.c index ad3b2982..a5819e74 100644 --- a/src/mngt/ocf_mngt_core.c +++ b/src/mngt/ocf_mngt_core.c @@ -956,7 +956,7 @@ int ocf_mngt_core_set_uuid(ocf_core_t core, const struct ocf_volume_uuid *uuid) if (result) return result; - ocf_volume_set_uuid(&core->volume, &new_uuid); + result = ocf_volume_set_uuid(&core->volume, &new_uuid, false); return result; } diff --git a/src/ocf_volume.c b/src/ocf_volume.c index 92c55f19..49e69f07 100644 --- a/src/ocf_volume.c +++ b/src/ocf_volume.c @@ -263,15 +263,46 @@ const struct ocf_volume_uuid *ocf_volume_get_uuid(ocf_volume_t volume) return &volume->uuid; } -void ocf_volume_set_uuid(ocf_volume_t volume, const struct ocf_volume_uuid *uuid) +int ocf_volume_set_uuid(ocf_volume_t volume, + const struct ocf_volume_uuid *uuid, bool uuid_copy) { + void *data; + int ret; + OCF_CHECK_NULL(volume); if (volume->uuid_copy && volume->uuid.data) env_vfree(volume->uuid.data); - volume->uuid.data = uuid->data; + volume->uuid_copy = uuid_copy; + + if (uuid_copy) { + data = env_vmalloc(uuid->size); + if (!data) + return -OCF_ERR_NO_MEM; + + volume->uuid.data = data; + + ret = env_memcpy(data, uuid->size, uuid->data, uuid->size); + if (ret) { + ret = -OCF_ERR_INVAL; + goto err; + } + } else { + volume->uuid.data = uuid->data; + } + volume->uuid.size = uuid->size; + + return 0; + +err: + if (volume->uuid_copy && volume->uuid.data) + env_vfree(volume->uuid.data); + volume->uuid.data = NULL; + volume->uuid.size = 0; + + return ret; } void *ocf_volume_get_priv(ocf_volume_t volume) diff --git a/src/ocf_volume_priv.h b/src/ocf_volume_priv.h index 45822e84..e5513df4 100644 --- a/src/ocf_volume_priv.h +++ b/src/ocf_volume_priv.h @@ -49,8 +49,8 @@ void ocf_volume_type_deinit(struct ocf_volume_type *type); void ocf_volume_move(ocf_volume_t volume, ocf_volume_t from); -void ocf_volume_set_uuid(ocf_volume_t volume, - const struct ocf_volume_uuid *uuid); +int ocf_volume_set_uuid(ocf_volume_t volume, + const struct ocf_volume_uuid *uuid, bool uuid_copy); void ocf_volume_forward_io(ocf_volume_t volume, ocf_forward_token_t token, int dir, uint64_t addr, uint64_t bytes, uint64_t offset); From 1f8f9152e306d44163b3db28952caba9ff496e43 Mon Sep 17 00:00:00 2001 From: Avi Halaf Date: Wed, 31 Aug 2022 10:16:58 -0400 Subject: [PATCH 14/28] composite_volume: Enhance composite volume API Signed-off-by: Avi Halaf Signed-off-by: Robert Baldyga --- inc/ocf_composite_volume.h | 23 +++++++++++++++++++++++ src/ocf_composite_volume.c | 17 +++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/inc/ocf_composite_volume.h b/inc/ocf_composite_volume.h index 04cb45f3..1d250fdf 100644 --- a/inc/ocf_composite_volume.h +++ b/inc/ocf_composite_volume.h @@ -1,6 +1,7 @@ /* * Copyright(c) 2022 Intel Corporation * Copyright(c) 2024 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -70,4 +71,26 @@ typedef int (*ocf_composite_volume_member_visitor_t)(ocf_volume_t subvolume, int ocf_composite_volume_member_visit(ocf_composite_volume_t cvolume, ocf_composite_volume_member_visitor_t visitor, void *priv); +/** + * @brief Get subvolume by index in composite volume + * + * @param[in] cvolume composite volume handle + * @param[in] index subvolume index + * + * @return subvolume in composite volume + */ +ocf_volume_t ocf_composite_volume_get_subvolume_by_index( + ocf_composite_volume_t cvolume, int index); + +/** + * @brief Set composite volume UUID + * + * @param[in] cvolume Volume + * @param[in] uuid UUID + * + * @return 0 in case of success, error code otherwise + */ +int ocf_composite_volume_set_uuid(ocf_composite_volume_t cvolume, + struct ocf_volume_uuid *uuid); + #endif /* __OCF_COMPOSITE_VOLUME_H__ */ diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index c70d70be..e0296a45 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -426,3 +426,20 @@ int ocf_composite_volume_member_visit(ocf_composite_volume_t cvolume, return 0; } + +ocf_volume_t ocf_composite_volume_get_subvolume_by_index( + ocf_composite_volume_t cvolume, int index) +{ + struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); + + if (index >= 0 && index < composite->members_cnt) + return &composite->member[index].volume; + else + return NULL; +} + +int ocf_composite_volume_set_uuid(ocf_composite_volume_t cvolume, + struct ocf_volume_uuid *uuid) +{ + return ocf_volume_set_uuid(cvolume, uuid, true); +} From 924a9e7b58c33f520eb885382f7775cbf8bc53a6 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Mon, 21 Aug 2023 14:36:11 +0200 Subject: [PATCH 15/28] composite_volume: Add 'detached' field for subvolumes Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- src/ocf_composite_volume.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index e0296a45..43e00b94 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -16,8 +16,17 @@ #include "ocf_composite_volume_priv.h" struct ocf_composite_volume { + /* 'members_cnt' describes how many members were added at the + * composite initialization. Even if a member is detached, + * 'member_cnt' shall not be decremented. + */ uint8_t members_cnt; struct { + /* A member is 'detached' only if it was once added but has + * been detached. For members that were never added, this + * flag is irrelevant + */ + bool detached; struct ocf_volume volume; void *volume_params; } member[OCF_COMPOSITE_VOLUME_MEMBERS_MAX]; @@ -406,6 +415,7 @@ int ocf_composite_volume_add(ocf_composite_volume_t cvolume, ocf_volume_close(volume); composite->member[composite->members_cnt].volume_params = volume_params; + composite->member[composite->members_cnt].detached = false; composite->members_cnt++; return 0; From 761c1392fcbb8def4a71389f0318a424f41dbb06 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Mon, 20 Nov 2023 11:34:45 +0100 Subject: [PATCH 16/28] composite_volume: Move composite_volume_add() to volume ops Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- inc/ocf_composite_volume.h | 14 ----- inc/ocf_err.h | 4 ++ inc/ocf_volume.h | 27 ++++++++++ src/ocf_composite_volume.c | 73 +++++++++++++------------- src/ocf_volume.c | 15 ++++++ tests/functional/pyocf/types/shared.py | 2 + tests/functional/pyocf/types/volume.py | 9 ++++ 7 files changed, 94 insertions(+), 50 deletions(-) diff --git a/inc/ocf_composite_volume.h b/inc/ocf_composite_volume.h index 1d250fdf..c63f6700 100644 --- a/inc/ocf_composite_volume.h +++ b/inc/ocf_composite_volume.h @@ -42,20 +42,6 @@ int ocf_composite_volume_create(ocf_composite_volume_t *cvolume, ocf_ctx_t ctx); */ void ocf_composite_volume_destroy(ocf_composite_volume_t cvolume); -/** - * @brief Add subvolume to composite volume - * - * @param[in] cvolume composite volume handle - * @param[in] type type of added subvolume - * @param[in] uuid UUID of added subvolume - * @param[in] volume_params params to be passed to subvolume open - * - * @return Zero when success, othewise an error - */ -int ocf_composite_volume_add(ocf_composite_volume_t cvolume, - ocf_volume_type_t type, struct ocf_volume_uuid *uuid, - void *volume_params); - typedef int (*ocf_composite_volume_member_visitor_t)(ocf_volume_t subvolume, void *priv); diff --git a/inc/ocf_err.h b/inc/ocf_err.h index eeb867d8..750f5199 100644 --- a/inc/ocf_err.h +++ b/inc/ocf_err.h @@ -1,6 +1,7 @@ /* * Copyright(c) 2012-2021 Intel Corporation * Copyright(c) 2024 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -72,6 +73,9 @@ typedef enum { /** Core ID/name does not exist */ OCF_ERR_CORE_NOT_EXIST, + /** The volume is not a composite */ + OCF_ERR_NOT_COMPOSITE_VOLUME, + /** Cache ID/name already exists */ OCF_ERR_CACHE_EXIST, diff --git a/inc/ocf_volume.h b/inc/ocf_volume.h index 0dd22fd8..c6a506d4 100644 --- a/inc/ocf_volume.h +++ b/inc/ocf_volume.h @@ -214,6 +214,20 @@ struct ocf_volume_ops { * @return Maximum io size in bytes */ unsigned int (*get_max_io_size)(ocf_volume_t volume); + + /** + * @brief Add subvolume to composite volume + * + * @param[in] volume composite volume handle + * @param[in] type type of added subvolume + * @param[in] uuid UUID of added subvolume + * @param[in] volume_params params to be passed to subvolume open + * + * @return Zero when success, otherwise an error + */ + int (*composite_volume_add)(ocf_volume_t cvolume, + ocf_volume_type_t type, struct ocf_volume_uuid *uuid, + void *volume_params); }; /** @@ -414,6 +428,19 @@ void ocf_volume_close(ocf_volume_t volume); */ unsigned int ocf_volume_get_max_io_size(ocf_volume_t volume); +/** + * @brief Add subvolume to composite volume + * + * @param[in] volume composite volume handle + * @param[in] type type of added subvolume + * @param[in] uuid UUID of added subvolume + * @param[in] volume_params params to be passed to subvolume open + * + * @return Zero when success, otherwise an error + */ +int ocf_composite_volume_add(ocf_volume_t volume, ocf_volume_type_t type, + struct ocf_volume_uuid *uuid, void *volume_params); + /** * @brief Get volume length * diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index 43e00b94..84b5281c 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -336,6 +336,41 @@ static void ocf_composite_volume_on_deinit(ocf_volume_t cvolume) ocf_volume_deinit(&composite->member[i].volume); } +static int composite_volume_add(ocf_volume_t cvolume, ocf_volume_type_t type, + struct ocf_volume_uuid *uuid, void *volume_params) +{ + struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); + ocf_volume_t volume; + int result; + + if (composite->members_cnt >= OCF_COMPOSITE_VOLUME_MEMBERS_MAX) + return -OCF_ERR_INVAL; + + volume = &composite->member[composite->members_cnt].volume; + result = ocf_volume_init(volume, type, uuid, true); + if (result) + return result; + + result = ocf_volume_open(volume, volume_params); + if (result) { + ocf_volume_deinit(volume); + return result; + } + + composite->length += ocf_volume_get_length(volume); + composite->end_addr[composite->members_cnt] = composite->length; + composite->max_io_size = OCF_MIN(composite->max_io_size, + ocf_volume_get_max_io_size(volume)); + + ocf_volume_close(volume); + + composite->member[composite->members_cnt].volume_params = volume_params; + composite->member[composite->members_cnt].detached = false; + composite->members_cnt++; + + return 0; +} + const struct ocf_volume_properties ocf_composite_volume_properties = { .name = "OCF Composite", .volume_priv_size = sizeof(struct ocf_composite_volume), @@ -358,6 +393,8 @@ const struct ocf_volume_properties ocf_composite_volume_properties = { .on_init = ocf_composite_volume_on_init, .on_deinit = ocf_composite_volume_on_deinit, + + .composite_volume_add = composite_volume_add, }, .deinit = NULL, }; @@ -385,42 +422,6 @@ void ocf_composite_volume_destroy(ocf_composite_volume_t cvolume) ocf_volume_destroy(cvolume); } -int ocf_composite_volume_add(ocf_composite_volume_t cvolume, - ocf_volume_type_t type, struct ocf_volume_uuid *uuid, - void *volume_params) -{ - struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); - ocf_volume_t volume; - int result; - - if (composite->members_cnt >= OCF_COMPOSITE_VOLUME_MEMBERS_MAX) - return -OCF_ERR_INVAL; - - volume = &composite->member[composite->members_cnt].volume; - result = ocf_volume_init(volume, type, uuid, true); - if (result) - return result; - - result = ocf_volume_open(volume, volume_params); - if (result) { - ocf_volume_deinit(volume); - return result; - } - - composite->length += ocf_volume_get_length(volume); - composite->end_addr[composite->members_cnt] = composite->length; - composite->max_io_size = OCF_MIN(composite->max_io_size, - ocf_volume_get_max_io_size(volume)); - - ocf_volume_close(volume); - - composite->member[composite->members_cnt].volume_params = volume_params; - composite->member[composite->members_cnt].detached = false; - composite->members_cnt++; - - return 0; -} - int ocf_composite_volume_member_visit(ocf_composite_volume_t cvolume, ocf_composite_volume_member_visitor_t visitor, void *priv) { diff --git a/src/ocf_volume.c b/src/ocf_volume.c index 49e69f07..cc56f817 100644 --- a/src/ocf_volume.c +++ b/src/ocf_volume.c @@ -538,6 +538,21 @@ unsigned int ocf_volume_get_max_io_size(ocf_volume_t volume) return volume->type->properties->ops.get_max_io_size(volume); } +int ocf_composite_volume_add(ocf_volume_t volume, ocf_volume_type_t type, + struct ocf_volume_uuid *uuid, void *volume_params) +{ + if (!ocf_volume_is_composite(volume)) + return -OCF_ERR_NOT_COMPOSITE_VOLUME; + + ENV_BUG_ON(!volume->type->properties->ops.composite_volume_add); + + if (volume->opened) + return -OCF_ERR_NOT_OPEN_EXC; + + return volume->type->properties->ops.composite_volume_add(volume, type, + uuid, volume_params); +} + uint64_t ocf_volume_get_length(ocf_volume_t volume) { ENV_BUG_ON(!volume->type->properties->ops.get_length); diff --git a/tests/functional/pyocf/types/shared.py b/tests/functional/pyocf/types/shared.py index 4f1c5de5..3c7c51e9 100644 --- a/tests/functional/pyocf/types/shared.py +++ b/tests/functional/pyocf/types/shared.py @@ -1,6 +1,7 @@ # # Copyright(c) 2019-2022 Intel Corporation # Copyright(c) 2024 Huawei Technologies +# Copyright(c) 2026 Unvertical # SPDX-License-Identifier: BSD-3-Clause # @@ -32,6 +33,7 @@ class OcfErrorCode(IntEnum): OCF_ERR_START_CACHE_FAIL = auto() OCF_ERR_CACHE_NOT_EXIST = auto() OCF_ERR_CORE_NOT_EXIST = auto() + OCF_ERR_NOT_COMPOSITE_VOLUME = auto() OCF_ERR_CACHE_EXIST = auto() OCF_ERR_CORE_EXIST = auto() OCF_ERR_TOO_MANY_CORES = auto() diff --git a/tests/functional/pyocf/types/volume.py b/tests/functional/pyocf/types/volume.py index 82d2b605..eb38c88d 100644 --- a/tests/functional/pyocf/types/volume.py +++ b/tests/functional/pyocf/types/volume.py @@ -1,6 +1,7 @@ # # Copyright(c) 2019-2022 Intel Corporation # Copyright(c) 2024 Huawei Technologies +# Copyright(c) 2026 Unvertical # SPDX-License-Identifier: BSD-3-Clause # @@ -15,6 +16,7 @@ Structure, CFUNCTYPE, c_int, + c_uint8, c_uint, c_uint64, sizeof, @@ -63,6 +65,7 @@ class VolumeOps(Structure): CLOSE = CFUNCTYPE(None, c_void_p) GET_MAX_IO_SIZE = CFUNCTYPE(c_uint, c_void_p) GET_LENGTH = CFUNCTYPE(c_uint64, c_void_p) + COMPOSITE_VOLUME_ADD = CFUNCTYPE(c_int, c_void_p, c_uint8, c_void_p, c_void_p) _fields_ = [ ("_submit_io", SUBMIT_IO), @@ -82,6 +85,7 @@ class VolumeOps(Structure): ("_close", CLOSE), ("_get_length", GET_LENGTH), ("_get_max_io_size", GET_MAX_IO_SIZE), + ("_composite_volume_add", COMPOSITE_VOLUME_ADD), ] @@ -164,6 +168,10 @@ def _get_max_io_size(ref): def _get_length(ref): return Volume.get_instance(ref).get_length() + @VolumeOps.COMPOSITE_VOLUME_ADD + def _composite_volume_add(ref): + raise NotImplementedError + Volume._ops_[cls] = VolumeOps( _forward_io=_forward_io, _forward_flush=_forward_flush, @@ -174,6 +182,7 @@ def _get_length(ref): _get_length=_get_length, _on_init=_on_init, _on_deinit=_on_deinit, + _composite_volume_add=_composite_volume_add, ) return Volume._ops_[cls] From 541fa31300f929aa61bdb068c73780f7158abaf5 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Thu, 12 Oct 2023 08:54:17 +0200 Subject: [PATCH 17/28] composite_volume: Introduce iter macros Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- src/ocf_composite_volume.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index 84b5281c..22121d49 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -35,6 +35,21 @@ struct ocf_composite_volume { unsigned max_io_size; }; +#define for_each_composite_member(_composite, _id) \ + for ((_id) = 0; (_id) < (_composite)->members_cnt; (_id)++) + +#define for_each_composite_member_attached(_composite, _id) \ + for_each_composite_member((_composite), (_id)) \ + if ((_composite)->member[(_id)].detached == false) + +#define for_each_composite_member_detached(_composite, _id) \ + for_each_composite_member((_composite), (_id)) \ + if ((_composite)->member[(_id)].detached == true) + +#define for_each_composite_member_opened(_composite, _id) \ + for_each_composite_member_attached((_composite), (_id)) \ + if ((_composite)->member[(_id)].volume.opened == true) + static void ocf_composite_forward_io(ocf_volume_t cvolume, ocf_forward_token_t token, int dir, uint64_t addr, uint64_t bytes, uint64_t offset) @@ -88,8 +103,9 @@ static void ocf_composite_forward_flush(ocf_volume_t cvolume, struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); int i; - for (i = 0; i < composite->members_cnt; i++) + for_each_composite_member_opened(composite, i) { ocf_forward_flush(&composite->member[i].volume, token); + } /* Put io forward counter to account for the original forward */ ocf_forward_end(token, 0); @@ -275,9 +291,9 @@ static void ocf_composite_forward_io_simple(ocf_volume_t cvolume, static int ocf_composite_volume_open(ocf_volume_t cvolume, void *volume_params) { struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); - int result, i; + int result, i, j; - for (i = 0; i < composite->members_cnt; i++) { + for_each_composite_member_attached(composite, i) { ocf_volume_t volume = &composite->member[i].volume; result = ocf_volume_open(volume, composite->member[i].volume_params); @@ -288,8 +304,11 @@ static int ocf_composite_volume_open(ocf_volume_t cvolume, void *volume_params) return 0; err: - while (i--) - ocf_volume_close(&composite->member[i].volume); + for_each_composite_member_attached(composite, j) { + if (j >= i) + break; + ocf_volume_close(&composite->member[j].volume); + } return result; } @@ -299,7 +318,7 @@ static void ocf_composite_volume_close(ocf_volume_t cvolume) struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); int i; - for (i = 0; i < composite->members_cnt; i++) + for_each_composite_member_opened(composite, i) ocf_volume_close(&composite->member[i].volume); } @@ -429,7 +448,7 @@ int ocf_composite_volume_member_visit(ocf_composite_volume_t cvolume, int i; int res; - for (i = 0 ; i < composite->members_cnt; i++) { + for_each_composite_member_attached(composite, i) { res = visitor(&composite->member[i].volume, priv); if (res != 0) return res; From f21679be546d584ecb8199941f2c6d43c5b353f4 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Wed, 22 Nov 2023 18:20:42 +0100 Subject: [PATCH 18/28] composite_volume: Extend visitor capabilities Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- inc/ocf_composite_volume.h | 24 +++++++++++++++++++++--- src/ocf_composite_volume.c | 25 ++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/inc/ocf_composite_volume.h b/inc/ocf_composite_volume.h index c63f6700..39a6055e 100644 --- a/inc/ocf_composite_volume.h +++ b/inc/ocf_composite_volume.h @@ -42,8 +42,22 @@ int ocf_composite_volume_create(ocf_composite_volume_t *cvolume, ocf_ctx_t ctx); */ void ocf_composite_volume_destroy(ocf_composite_volume_t cvolume); +typedef enum { + ocf_composite_member_state_attached = 1, + /* If subvolume is opened it must be attached as well */ + ocf_composite_member_state_opened = 3, + ocf_composite_member_state_detached = 4, + ocf_composite_member_state_any = 7, +} ocf_composite_member_state_t; + +/** + * @param[in] subvolume Pointer to a subvolume + * @param[in] priv Priv + * @param[in] subvol_status flag with the info if current subvolume is opened, + * attached or detached + */ typedef int (*ocf_composite_volume_member_visitor_t)(ocf_volume_t subvolume, - void *priv); + void *priv, ocf_composite_member_state_t subvol_status); /** * @brief Call @visitor on every valid member of composite volume @@ -51,11 +65,15 @@ typedef int (*ocf_composite_volume_member_visitor_t)(ocf_volume_t subvolume, * @param[in] cvolume composite volume handle * @param[in] visitor function callback * @param[in] priv pointer to be passed to the callback + * @param[in] subvol_status info whether iterate over + * opened/attached/detached/all volumes * - * @return subvolume in composite volume + * @return 0 if the visitor function was called for all subvolumes, error code + * otherwise */ int ocf_composite_volume_member_visit(ocf_composite_volume_t cvolume, - ocf_composite_volume_member_visitor_t visitor, void *priv); + ocf_composite_volume_member_visitor_t visitor, void *priv, + ocf_composite_member_state_t subvolume_status); /** * @brief Get subvolume by index in composite volume diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index 22121d49..4146de9f 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -442,14 +442,33 @@ void ocf_composite_volume_destroy(ocf_composite_volume_t cvolume) } int ocf_composite_volume_member_visit(ocf_composite_volume_t cvolume, - ocf_composite_volume_member_visitor_t visitor, void *priv) + ocf_composite_volume_member_visitor_t visitor, void *priv, + ocf_composite_member_state_t svol_status) { struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); int i; int res; + ocf_composite_member_state_t s; - for_each_composite_member_attached(composite, i) { - res = visitor(&composite->member[i].volume, priv); + if (svol_status != ocf_composite_member_state_attached && + svol_status != ocf_composite_member_state_opened && + svol_status != ocf_composite_member_state_detached && + svol_status != ocf_composite_member_state_any) { + return -OCF_ERR_INVAL; + } + + for_each_composite_member(composite, i) { + if (composite->member[i].detached) + s = ocf_composite_member_state_detached; + else if (composite->member[i].volume.opened) + s = ocf_composite_member_state_opened; + else + s = ocf_composite_member_state_attached; + + if (!(s & svol_status)) + continue; + + res = visitor(&composite->member[i].volume, priv, s); if (res != 0) return res; } From ed48ac1bba8530d2910615e9894e177f6d3b7292 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Mon, 21 Aug 2023 15:13:10 +0200 Subject: [PATCH 19/28] composite_volume: Util to retrieve member volume id Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- inc/ocf_composite_volume.h | 11 +++++++++++ inc/ocf_err.h | 3 +++ src/ocf_composite_volume.c | 18 ++++++++++++++++++ tests/functional/pyocf/types/shared.py | 1 + 4 files changed, 33 insertions(+) diff --git a/inc/ocf_composite_volume.h b/inc/ocf_composite_volume.h index 39a6055e..8e6b9480 100644 --- a/inc/ocf_composite_volume.h +++ b/inc/ocf_composite_volume.h @@ -97,4 +97,15 @@ ocf_volume_t ocf_composite_volume_get_subvolume_by_index( int ocf_composite_volume_set_uuid(ocf_composite_volume_t cvolume, struct ocf_volume_uuid *uuid); +/** + * @brief Get volume's id by UUID + * + * @param[in] volume Volume + * @param[in] uuid UUID + * + * @return id of the matching subvolume or error if the volume wasn't found + */ +int ocf_composite_volume_get_id_from_uuid(ocf_composite_volume_t cvolume, + ocf_uuid_t target_uuid); + #endif /* __OCF_COMPOSITE_VOLUME_H__ */ diff --git a/inc/ocf_err.h b/inc/ocf_err.h index 750f5199..86827b86 100644 --- a/inc/ocf_err.h +++ b/inc/ocf_err.h @@ -73,6 +73,9 @@ typedef enum { /** Core ID/name does not exist */ OCF_ERR_CORE_NOT_EXIST, + /** Composite volume member ID does not exist */ + OCF_ERR_COMPOSITE_VOLUME_MEMBER_NOT_EXIST, + /** The volume is not a composite */ OCF_ERR_NOT_COMPOSITE_VOLUME, diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index 4146de9f..467f6364 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -492,3 +492,21 @@ int ocf_composite_volume_set_uuid(ocf_composite_volume_t cvolume, { return ocf_volume_set_uuid(cvolume, uuid, true); } + +int ocf_composite_volume_get_id_from_uuid(ocf_composite_volume_t cvolume, + ocf_uuid_t target_uuid) +{ + struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); + const struct ocf_volume_uuid *subvol_uuid; + int i; + + for_each_composite_member_attached(composite, i) { + subvol_uuid = ocf_volume_get_uuid(&composite->member[i].volume); + if (env_strncmp(subvol_uuid->data, subvol_uuid->size, + target_uuid->data, target_uuid->size) == 0) { + return i; + } + } + + return -OCF_ERR_COMPOSITE_VOLUME_MEMBER_NOT_EXIST; +} diff --git a/tests/functional/pyocf/types/shared.py b/tests/functional/pyocf/types/shared.py index 3c7c51e9..ee301547 100644 --- a/tests/functional/pyocf/types/shared.py +++ b/tests/functional/pyocf/types/shared.py @@ -33,6 +33,7 @@ class OcfErrorCode(IntEnum): OCF_ERR_START_CACHE_FAIL = auto() OCF_ERR_CACHE_NOT_EXIST = auto() OCF_ERR_CORE_NOT_EXIST = auto() + OCF_ERR_COMPOSITE_VOLUME_MEMBER_NOT_EXIST = auto() OCF_ERR_NOT_COMPOSITE_VOLUME = auto() OCF_ERR_CACHE_EXIST = auto() OCF_ERR_CORE_EXIST = auto() From 6e099f46479225fa285c59d075fb718cee1216a4 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Wed, 23 Aug 2023 17:32:23 +0200 Subject: [PATCH 20/28] composite_volume: Utility for getting range of subvolume addresses Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- inc/ocf_composite_volume.h | 14 ++++++++++++++ src/ocf_composite_volume.c | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/inc/ocf_composite_volume.h b/inc/ocf_composite_volume.h index 8e6b9480..ecb09be2 100644 --- a/inc/ocf_composite_volume.h +++ b/inc/ocf_composite_volume.h @@ -86,6 +86,20 @@ int ocf_composite_volume_member_visit(ocf_composite_volume_t cvolume, ocf_volume_t ocf_composite_volume_get_subvolume_by_index( ocf_composite_volume_t cvolume, int index); +/** + * @brief Get range of addresses from a subvolume + * + * @param[in] cvolume composite volume handle + * @param[in] subvolume_id subvolume index + * @param[out] begin_addr beginning of the address range + * @param[out] end_addr end of the address range + * + * @return 0 in case of success, error code otherwise + */ +int ocf_composite_volume_get_subvolume_addr_range( + ocf_composite_volume_t cvolume, uint8_t subvolume_id, + uint64_t *begin_addr, uint64_t *end_addr); + /** * @brief Set composite volume UUID * diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index 467f6364..1f1aaef6 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -487,6 +487,22 @@ ocf_volume_t ocf_composite_volume_get_subvolume_by_index( return NULL; } +int ocf_composite_volume_get_subvolume_addr_range( + ocf_composite_volume_t cvolume, uint8_t subvolume_id, + uint64_t *begin_addr, uint64_t *end_addr) +{ + struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); + + if (subvolume_id >= composite->members_cnt) + return -OCF_ERR_INVAL; + + *begin_addr = subvolume_id > 0 ? + composite->end_addr[subvolume_id - 1] : 0; + *end_addr = composite->end_addr[subvolume_id]; + + return 0; +} + int ocf_composite_volume_set_uuid(ocf_composite_volume_t cvolume, struct ocf_volume_uuid *uuid) { From ba83417dde9155f5bb5cbc40547a7a606328582c Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Wed, 6 Sep 2023 18:10:18 +0200 Subject: [PATCH 21/28] composite_volume: Introduce attach_member volume ops Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- inc/ocf_mngt.h | 17 +++++++++++++++ inc/ocf_volume.h | 29 ++++++++++++++++++++++++++ src/ocf_volume.c | 13 ++++++++++++ tests/functional/pyocf/types/volume.py | 7 +++++++ 4 files changed, 66 insertions(+) diff --git a/inc/ocf_mngt.h b/inc/ocf_mngt.h index 91686eb9..743f9abf 100644 --- a/inc/ocf_mngt.h +++ b/inc/ocf_mngt.h @@ -1,6 +1,7 @@ /* * Copyright(c) 2012-2022 Intel Corporation * Copyright(c) 2024-2025 Huawei Technologies + * Copyright(c) 2026 Unvertical * SPDX-License-Identifier: BSD-3-Clause */ @@ -513,6 +514,22 @@ typedef void (*ocf_mngt_cache_detach_end_t)(ocf_cache_t cache, void ocf_mngt_cache_detach(ocf_cache_t cache, ocf_mngt_cache_detach_end_t cmpl, void *priv); +/** + * @brief Add a new member to composite cache + * + * @param[in] cache Cache handle + * @param[in] vol_uuid UUID of the new volume to be added as a member + * of composite + * @param[in] tgt_id Target subvolume id + * @param[in] vol_type Type of the new volume + * @param[in] vol_params Params of the new volume + * @param[in] cmpl Completion callback + * @param[in] priv Completion callback context + */ +void ocf_mngt_cache_attach_composite(ocf_cache_t cache, ocf_uuid_t vol_uuid, + uint8_t tgt_id, ocf_volume_type_t vol_type, void *vol_params, + ocf_mngt_cache_detach_end_t cmpl, void *priv); + /** * @brief Completion callback of cache load operation * diff --git a/inc/ocf_volume.h b/inc/ocf_volume.h index c6a506d4..9226cbb5 100644 --- a/inc/ocf_volume.h +++ b/inc/ocf_volume.h @@ -228,6 +228,21 @@ struct ocf_volume_ops { int (*composite_volume_add)(ocf_volume_t cvolume, ocf_volume_type_t type, struct ocf_volume_uuid *uuid, void *volume_params); + + /** + * @brief Attach subvolume to composite volume + * + * @param[in] volume composite volume handle + * @param[in] uuid UUID of added subvolume + * @param[in] tgt_id Target subvolume id + * @param[in] type type of added subvolume + * @param[in] volume_params params to be passed to subvolume open + * + * @return Zero when success, otherwise an error + */ + int (*composite_volume_attach_member)(ocf_volume_t volume, + struct ocf_volume_uuid *uuid, uint8_t tgt_id, + ocf_volume_type_t type, void *volume_params); }; /** @@ -419,6 +434,20 @@ int ocf_volume_open(ocf_volume_t volume, void *volume_params); */ void ocf_volume_close(ocf_volume_t volume); +/** + * @brief Attach subvolume to composite volume + * + * @param[in] volume composite volume handle + * @param[in] tgt_id Target subvolume id + * @param[in] uuid UUID of added subvolume + * @param[in] type type of added subvolume + * @param[in] volume_params params to be passed to subvolume open + * + * @return Zero when success, otherwise an error + */ +int ocf_composite_volume_attach_member(ocf_volume_t volume, ocf_uuid_t uuid, + uint8_t tgt_id, ocf_volume_type_t vol_type, void *vol_params); + /** * @brief Get volume max io size * diff --git a/src/ocf_volume.c b/src/ocf_volume.c index cc56f817..04c9a0f2 100644 --- a/src/ocf_volume.c +++ b/src/ocf_volume.c @@ -553,6 +553,19 @@ int ocf_composite_volume_add(ocf_volume_t volume, ocf_volume_type_t type, uuid, volume_params); } +int ocf_composite_volume_attach_member(ocf_volume_t volume, ocf_uuid_t uuid, + uint8_t tgt_id, ocf_volume_type_t vol_type, void *vol_params) +{ + if (!ocf_volume_is_composite(volume)) + return -OCF_ERR_NOT_COMPOSITE_VOLUME; + + ENV_BUG_ON(!volume->type->properties->ops. + composite_volume_attach_member); + + return volume->type->properties->ops.composite_volume_attach_member( + volume, uuid, tgt_id, vol_type, vol_params); +} + uint64_t ocf_volume_get_length(ocf_volume_t volume) { ENV_BUG_ON(!volume->type->properties->ops.get_length); diff --git a/tests/functional/pyocf/types/volume.py b/tests/functional/pyocf/types/volume.py index eb38c88d..3069cf74 100644 --- a/tests/functional/pyocf/types/volume.py +++ b/tests/functional/pyocf/types/volume.py @@ -66,6 +66,7 @@ class VolumeOps(Structure): GET_MAX_IO_SIZE = CFUNCTYPE(c_uint, c_void_p) GET_LENGTH = CFUNCTYPE(c_uint64, c_void_p) COMPOSITE_VOLUME_ADD = CFUNCTYPE(c_int, c_void_p, c_uint8, c_void_p, c_void_p) + COMPOSITE_VOLUME_ATTACH_MEMBER = CFUNCTYPE(c_int, c_void_p, c_void_p, c_uint8, c_void_p) _fields_ = [ ("_submit_io", SUBMIT_IO), @@ -86,6 +87,7 @@ class VolumeOps(Structure): ("_get_length", GET_LENGTH), ("_get_max_io_size", GET_MAX_IO_SIZE), ("_composite_volume_add", COMPOSITE_VOLUME_ADD), + ("_composite_volume_attach_member", COMPOSITE_VOLUME_ATTACH_MEMBER), ] @@ -172,6 +174,10 @@ def _get_length(ref): def _composite_volume_add(ref): raise NotImplementedError + @VolumeOps.COMPOSITE_VOLUME_ATTACH_MEMBER + def _composite_volume_attach_member(ref): + raise NotImplementedError + Volume._ops_[cls] = VolumeOps( _forward_io=_forward_io, _forward_flush=_forward_flush, @@ -183,6 +189,7 @@ def _composite_volume_add(ref): _on_init=_on_init, _on_deinit=_on_deinit, _composite_volume_add=_composite_volume_add, + _composite_volume_attach_member=_composite_volume_attach_member, ) return Volume._ops_[cls] From 12d9ab620c906dcc477d78c5ab9cd168a04717f3 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Wed, 22 Nov 2023 07:06:47 +0100 Subject: [PATCH 22/28] composite_volume: Implement attach_member ops Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- inc/ocf_err.h | 12 ++++ src/ocf_composite_volume.c | 92 +++++++++++++++++++++++++ tests/functional/pyocf/types/cvolume.py | 50 +++++++++++++- tests/functional/pyocf/types/shared.py | 4 ++ 4 files changed, 157 insertions(+), 1 deletion(-) diff --git a/inc/ocf_err.h b/inc/ocf_err.h index 86827b86..a391bde8 100644 --- a/inc/ocf_err.h +++ b/inc/ocf_err.h @@ -79,6 +79,18 @@ typedef enum { /** The volume is not a composite */ OCF_ERR_NOT_COMPOSITE_VOLUME, + /** Invalid subvolume ID */ + OCF_ERR_COMPOSITE_INVALID_ID, + + /** The target subvolume isn't initialised */ + OCF_ERR_COMPOSITE_UNINITIALISED_VOLUME, + + /** The target subvolume is already attached */ + OCF_ERR_COMPOSITE_ATTACHED, + + /** The target subvolume has invalid size */ + OCF_ERR_COMPOSITE_INVALID_SIZE, + /** Cache ID/name already exists */ OCF_ERR_CACHE_EXIST, diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index 1f1aaef6..b6d826f5 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -322,6 +322,96 @@ static void ocf_composite_volume_close(ocf_volume_t cvolume) ocf_volume_close(&composite->member[i].volume); } +#define get_subvolume_length(_composite, _id) \ + (_id == 0 ? _composite->end_addr[_id] : \ + _composite->end_addr[_id] - _composite->end_addr[_id - 1]) + +static int composite_volume_attach_member(ocf_volume_t cvolume, + ocf_uuid_t uuid, uint8_t tgt_id, ocf_volume_type_t vol_type, + void *vol_params) +{ + struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); + ocf_ctx_t ctx = cvolume->type->owner; + struct ocf_volume new_vol = {}; + uint64_t new_vol_size, tgt_vol_size; + unsigned new_vol_max_io_size; + int ret; + + if (tgt_id >= OCF_COMPOSITE_VOLUME_MEMBERS_MAX) { + ocf_log(ctx, log_err, "Failed to attach subvolume to " + "the composite volume. Invalid subvolume " + "target id\n"); + return -OCF_ERR_COMPOSITE_INVALID_ID; + } + + if (tgt_id >= composite->members_cnt) { + ocf_log(ctx, log_err, "Failed to attach subvolume to " + "the composite volume. Can't attach to " + "uninitialized member\n"); + return -OCF_ERR_COMPOSITE_UNINITIALISED_VOLUME; + } + + if (!composite->member[tgt_id].detached) { + ocf_log(ctx, log_err, "Failed to attach subvolume to " + "the composite volume. The target member is " + "already attached\n"); + return -OCF_ERR_COMPOSITE_ATTACHED; + } + + ret = ocf_volume_init(&new_vol, vol_type, uuid, true); + if (ret) + return ret; + + ret = ocf_volume_open(&new_vol, vol_params); + if (ret) { + ocf_volume_deinit(&new_vol); + return ret; + } + + new_vol_size = ocf_volume_get_length(&new_vol); + + new_vol_max_io_size = ocf_volume_get_max_io_size(&new_vol); + + ocf_volume_close(&new_vol); + + tgt_vol_size = get_subvolume_length(composite, tgt_id); + + if (new_vol_size != tgt_vol_size) { + ocf_log(ctx, log_err, "Failed to attach subvolume to " + "the composite volume. The new subvolume must " + "be of size %"ENV_PRIu64" but " + "is %"ENV_PRIu64"\n", tgt_vol_size, + new_vol_size); + ocf_volume_deinit(&new_vol); + return -OCF_ERR_COMPOSITE_INVALID_SIZE; + } + + if (composite->max_io_size > new_vol_max_io_size) { + ocf_log(ctx, log_err, "Failed to attach subvolume to the " + "composite volume. The max io size can't be " + "smaller than composite's max io size\n"); + ocf_volume_deinit(&new_vol); + return -OCF_ERR_INVAL; + } + + ocf_volume_move(&composite->member[tgt_id].volume, &new_vol); + ocf_volume_deinit(&new_vol); + + if (cvolume->opened) { + ret = ocf_volume_open(&composite->member[tgt_id].volume, + vol_params); + if (ret) { + ocf_volume_deinit(&composite->member[tgt_id].volume); + return ret; + } + } + + composite->member[tgt_id].detached = false; + composite->member[tgt_id].volume_params = vol_params; + + return 0; +} + static unsigned int ocf_composite_volume_get_max_io_size(ocf_volume_t cvolume) { struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); @@ -411,6 +501,8 @@ const struct ocf_volume_properties ocf_composite_volume_properties = { .get_length = ocf_composite_volume_get_byte_length, .on_init = ocf_composite_volume_on_init, + .composite_volume_attach_member = + composite_volume_attach_member, .on_deinit = ocf_composite_volume_on_deinit, .composite_volume_add = composite_volume_add, diff --git a/tests/functional/pyocf/types/cvolume.py b/tests/functional/pyocf/types/cvolume.py index ea06110e..b60fd2e6 100644 --- a/tests/functional/pyocf/types/cvolume.py +++ b/tests/functional/pyocf/types/cvolume.py @@ -1,9 +1,12 @@ # # Copyright(c) 2022 Intel Corporation +# Copyright(c) 2023 Huawei Technologies +# Copyright(c) 2026 Unvertical # SPDX-License-Identifier: BSD-3-Clause # from ctypes import ( + c_uint8, c_int, c_uint32, c_uint64, @@ -18,7 +21,7 @@ from .ctx import OcfCtx from .io import Io, IoDir from .queue import Queue -from .shared import OcfError, Uuid +from .shared import OcfError, Uuid, OcfErrorCode from .volume_exp_obj import OcfInternalVolume @@ -55,6 +58,49 @@ def add(self, vol): if ret != 0: raise OcfError("Failed to add volume to a composite volume", ret) + def attach_member(self, vol, target_id): + uuid = Uuid( + _data=cast(create_string_buffer(vol.uuid.encode("ascii")), c_char_p), + _size=len(vol.uuid) + 1, + ) + + ocf_vol_type = self.ctx.ocf_volume_type[type(vol)] + ret = lib.ocf_composite_volume_attach_member( + self.cvol, + byref(uuid), + target_id, + ocf_vol_type, + c_void_p(), + ) + + if ret == 0: + return + elif ret == -OcfErrorCode.OCF_ERR_COMPOSITE_INVALID_ID: + raise OcfError( + "Attaching member to composite failed. Invalid ID.", + ret, + ) + elif ret == -OcfErrorCode.OCF_ERR_COMPOSITE_UNINITIALISED_VOLUME: + raise OcfError( + "Attaching member to composite failed. Uninitialised member.", + ret, + ) + elif ret == -OcfErrorCode.OCF_ERR_COMPOSITE_ATTACHED: + raise OcfError( + "Attaching member to composite failed. Member already attached", + ret, + ) + elif ret == -OcfErrorCode.OCF_ERR_COMPOSITE_INVALID_SIZE: + raise OcfError( + "Attaching member to composite failed. Invalid size.", + ret + ) + else: + raise OcfError( + "Attaching member to composite failed", + ret, + ) + def get_c_handle(self): return self.cvol.value @@ -80,6 +126,8 @@ def close(self): 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_composite_volume_attach_member.argtypes = [c_void_p, c_void_p, c_uint8, c_void_p, c_void_p] +lib.ocf_composite_volume_attach_member.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] diff --git a/tests/functional/pyocf/types/shared.py b/tests/functional/pyocf/types/shared.py index ee301547..2a356b1d 100644 --- a/tests/functional/pyocf/types/shared.py +++ b/tests/functional/pyocf/types/shared.py @@ -35,6 +35,10 @@ class OcfErrorCode(IntEnum): OCF_ERR_CORE_NOT_EXIST = auto() OCF_ERR_COMPOSITE_VOLUME_MEMBER_NOT_EXIST = auto() OCF_ERR_NOT_COMPOSITE_VOLUME = auto() + OCF_ERR_COMPOSITE_INVALID_ID = auto() + OCF_ERR_COMPOSITE_UNINITIALISED_VOLUME = auto() + OCF_ERR_COMPOSITE_ATTACHED = auto() + OCF_ERR_COMPOSITE_INVALID_SIZE = auto() OCF_ERR_CACHE_EXIST = auto() OCF_ERR_CORE_EXIST = auto() OCF_ERR_TOO_MANY_CORES = auto() From bab8edc7d680d4aabdc5392ae1f9716d37035264 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Sat, 7 Feb 2026 01:18:33 +0100 Subject: [PATCH 23/28] composite_volume: Enable attaching composite cache subvolumes Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- src/mngt/ocf_mngt_cache.c | 136 ++++++++++++++++++++++++++ tests/functional/pyocf/types/cache.py | 34 +++++++ 2 files changed, 170 insertions(+) diff --git a/src/mngt/ocf_mngt_cache.c b/src/mngt/ocf_mngt_cache.c index 878c7af4..906fc8c6 100644 --- a/src/mngt/ocf_mngt_cache.c +++ b/src/mngt/ocf_mngt_cache.c @@ -3936,3 +3936,139 @@ void ocf_mngt_cache_detach(ocf_cache_t cache, ocf_pipeline_next(pipeline); } + +struct ocf_mngt_composite_attach_context { + ocf_uuid_t uuid; + ocf_volume_type_t vol_type; + void *vol_params; + uint8_t tgt_id; + + ocf_cache_t cache; + ocf_mngt_cache_detach_end_t cmpl; + ocf_pipeline_t pipeline; + void *priv; +}; + +static void ocf_mngt_cache_attach_finish(ocf_pipeline_t pipeline, + void *priv, int error) +{ + struct ocf_mngt_composite_attach_context *context = priv; + ocf_cache_t cache = context->cache; + + if (error) { + ocf_cache_log(cache, log_err, + "Adding new device to composite volume failed\n"); + } else { + ocf_cache_log(cache, log_info, + "Successfully added new device to composite volume\n"); + } + + context->cmpl(cache, context->priv, error); + + ocf_pipeline_destroy(context->pipeline); +} + +static void ocf_mngt_cache_composite_add(ocf_pipeline_t pipeline, + void *priv, ocf_pipeline_arg_t arg) +{ + struct ocf_mngt_composite_attach_context *context = priv; + ocf_cache_t cache = context->cache; + int ret; + + ret = ocf_composite_volume_attach_member(ocf_cache_get_volume(cache), + context->uuid, context->tgt_id, context->vol_type, + context->vol_params); + + OCF_PL_NEXT_ON_SUCCESS_RET(pipeline, ret); +} + +static void ocf_mngt_cache_composite_restore_cache_lines( + ocf_pipeline_t pipeline, void *priv, ocf_pipeline_arg_t arg) +{ + struct ocf_mngt_composite_attach_context *context = priv; + ocf_cache_t cache = context->cache; + uint64_t begin_addr, end_addr; + ocf_cache_line_t begin_cline, end_cline; + int ret; + + ret = ocf_composite_volume_get_subvolume_addr_range( + ocf_cache_get_volume(cache), context->tgt_id, + &begin_addr, &end_addr); + ENV_BUG_ON(ret); + + if (cache->device->metadata_offset >= end_addr) + OCF_PL_NEXT_RET(pipeline); + + if (cache->device->metadata_offset > begin_addr) + begin_addr = 0; + else + begin_addr -= cache->device->metadata_offset; + + end_addr -= cache->device->metadata_offset; + + begin_cline = begin_addr / ocf_cache_get_line_size(cache); + end_cline = OCF_DIV_ROUND_UP(end_addr, ocf_cache_get_line_size(cache)); + + if (end_cline > ocf_metadata_collision_table_entries(cache)) + end_cline = ocf_metadata_collision_table_entries(cache); + + ocf_mngt_cache_attach_cline_range(cache, begin_cline, end_cline); + + ocf_pipeline_next(pipeline); +} + +struct ocf_pipeline_properties ocf_mngt_attach_composite_pipeline_properties = { + .priv_size = sizeof(struct ocf_mngt_composite_attach_context), + .finish = ocf_mngt_cache_attach_finish, + .steps = { + OCF_PL_STEP(ocf_mngt_cache_composite_add), + OCF_PL_STEP(ocf_mngt_cache_composite_restore_cache_lines), + OCF_PL_STEP_TERMINATOR(), + }, +}; + +void ocf_mngt_cache_attach_composite(ocf_cache_t cache, ocf_uuid_t vol_uuid, + uint8_t tgt_id, ocf_volume_type_t vol_type, void *vol_params, + ocf_mngt_cache_detach_end_t cmpl, void *priv) +{ + struct ocf_mngt_composite_attach_context *context; + ocf_pipeline_t pipeline; + int result; + ocf_volume_t cvol = ocf_cache_get_volume(cache); + + OCF_CHECK_NULL(cache); + OCF_CHECK_NULL(vol_uuid); + + if (!cache->metadata.is_volatile) + OCF_CMPL_RET(cache, priv, -OCF_ERR_INVAL); + + if (ocf_cache_is_standby(cache)) + OCF_CMPL_RET(cache, priv, -OCF_ERR_CACHE_STANDBY); + + if (!cache->mngt_queue) + OCF_CMPL_RET(cache, priv, -OCF_ERR_INVAL); + + if (!ocf_cache_is_device_attached(cache)) + OCF_CMPL_RET(cache, priv, -OCF_ERR_INVAL); + + if (!ocf_volume_is_composite(cvol)) + OCF_CMPL_RET(cache, priv, -OCF_ERR_NOT_COMPOSITE_VOLUME); + + result = ocf_pipeline_create(&pipeline, cache, + &ocf_mngt_attach_composite_pipeline_properties); + if (result) + OCF_CMPL_RET(cache, priv, -OCF_ERR_NO_MEM); + + context = ocf_pipeline_get_priv(pipeline); + + context->uuid = vol_uuid; + context->cmpl = cmpl; + context->priv = priv; + context->pipeline = pipeline; + context->cache = cache; + context->vol_type = vol_type; + context->vol_params = vol_params; + context->tgt_id = tgt_id; + + ocf_pipeline_next(pipeline); +} diff --git a/tests/functional/pyocf/types/cache.py b/tests/functional/pyocf/types/cache.py index b3822e67..e1949b93 100644 --- a/tests/functional/pyocf/types/cache.py +++ b/tests/functional/pyocf/types/cache.py @@ -1,6 +1,7 @@ # # Copyright(c) 2019-2022 Intel Corporation # Copyright(c) 2024 Huawei Technologies +# Copyright(c) 2026 Unvertical # SPDX-License-Identifier: BSD-3-Clause # @@ -569,6 +570,37 @@ def alloc_device_config(self, device, perform_test=True): def free_device_config(self, cfg): lib = OcfLib.getInstance().ocf_volume_destroy(cfg._volume) + def attach_composite_member(self, volume, tgt_subvol_id): + uuid = Uuid( + _data=cast(create_string_buffer(volume.uuid.encode("ascii")), c_char_p), + _size=len(volume.uuid) + 1, + ) + volume_type = self.owner.ocf_volume_type[type(volume)] + + self.write_lock() + + c = OcfCompletion([("cache", c_void_p), ("priv", c_void_p), ("error", c_int)]) + + lib.ocf_mngt_cache_attach_composite( + self.cache_handle, + byref(uuid), + tgt_subvol_id, + volume_type, + None, + c, + None, + ) + + c.wait() + + self.write_unlock() + + if c.results["error"]: + raise OcfError( + "Attaching composite cache member failed", + c.results["error"], + ) + def attach_device_async( self, device, @@ -1137,3 +1169,5 @@ def get_by_name(cache_name, owner=None): 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] +lib.ocf_mngt_cache_attach_composite.argtypes = [c_void_p, c_void_p, c_uint8, c_void_p, c_void_p, c_void_p, c_void_p] +lib.ocf_mngt_cache_attach_composite.restype = None From d454c5636536d4fc77c469bfac70d10ac68defea Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Wed, 23 Aug 2023 17:34:12 +0200 Subject: [PATCH 24/28] composite_volume: Introduce detach_member volume ops Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- inc/ocf_mngt.h | 12 ++++++++++++ inc/ocf_volume.h | 24 +++++++++++++++++++++++- src/ocf_volume.c | 13 +++++++++++++ tests/functional/pyocf/types/volume.py | 7 +++++++ 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/inc/ocf_mngt.h b/inc/ocf_mngt.h index 743f9abf..3c4e5450 100644 --- a/inc/ocf_mngt.h +++ b/inc/ocf_mngt.h @@ -530,6 +530,18 @@ void ocf_mngt_cache_attach_composite(ocf_cache_t cache, ocf_uuid_t vol_uuid, uint8_t tgt_id, ocf_volume_type_t vol_type, void *vol_params, ocf_mngt_cache_detach_end_t cmpl, void *priv); +/** + * @brief Detach a member of composite cache + * + * @param[in] cache Cache handle + * @param[in] cmpl Completion callback + * @param[in] target_uuid uuid of the target subvolume + * @param[in] priv Completion callback context + */ +void ocf_mngt_cache_detach_composite(ocf_cache_t cache, + ocf_mngt_cache_detach_end_t cmpl, ocf_uuid_t target_uuid, + void *priv); + /** * @brief Completion callback of cache load operation * diff --git a/inc/ocf_volume.h b/inc/ocf_volume.h index 9226cbb5..b0591d2a 100644 --- a/inc/ocf_volume.h +++ b/inc/ocf_volume.h @@ -243,6 +243,17 @@ struct ocf_volume_ops { int (*composite_volume_attach_member)(ocf_volume_t volume, struct ocf_volume_uuid *uuid, uint8_t tgt_id, ocf_volume_type_t type, void *volume_params); + + /** + * @brief Detach composite member volume + * + * @param[in] volume composite volume handle + * @param[in] subvolume_id volume to be detached + * + * @return Zero on success, otherwise error code + */ + int (*composite_volume_detach_member)(ocf_volume_t volume, + uint8_t subvolume_id); }; /** @@ -428,7 +439,7 @@ void ocf_volume_submit_discard(ocf_io_t io); int ocf_volume_open(ocf_volume_t volume, void *volume_params); /** - * @brief Get volume max io size + * @brief Close volume * * @param[in] volume Volume */ @@ -448,6 +459,17 @@ void ocf_volume_close(ocf_volume_t volume); int ocf_composite_volume_attach_member(ocf_volume_t volume, ocf_uuid_t uuid, uint8_t tgt_id, ocf_volume_type_t vol_type, void *vol_params); +/** + * @brief Detach composite member volume + * + * @param[in] volume composite volume handle + * @param[in] subvolume_id volume to be detached + * + * @return Zero when success, otherwise an error + */ +int ocf_composite_volume_detach_member(ocf_volume_t volume, + uint8_t subvolume_id); + /** * @brief Get volume max io size * diff --git a/src/ocf_volume.c b/src/ocf_volume.c index 04c9a0f2..17a7138e 100644 --- a/src/ocf_volume.c +++ b/src/ocf_volume.c @@ -566,6 +566,19 @@ int ocf_composite_volume_attach_member(ocf_volume_t volume, ocf_uuid_t uuid, volume, uuid, tgt_id, vol_type, vol_params); } +int ocf_composite_volume_detach_member(ocf_volume_t volume, + uint8_t subvolume_id) +{ + if (!ocf_volume_is_composite(volume)) + return -OCF_ERR_NOT_COMPOSITE_VOLUME; + + ENV_BUG_ON(!volume->type->properties->ops. + composite_volume_detach_member); + + return volume->type->properties->ops.composite_volume_detach_member( + volume, subvolume_id); +} + uint64_t ocf_volume_get_length(ocf_volume_t volume) { ENV_BUG_ON(!volume->type->properties->ops.get_length); diff --git a/tests/functional/pyocf/types/volume.py b/tests/functional/pyocf/types/volume.py index 3069cf74..8591f6e6 100644 --- a/tests/functional/pyocf/types/volume.py +++ b/tests/functional/pyocf/types/volume.py @@ -67,6 +67,7 @@ class VolumeOps(Structure): GET_LENGTH = CFUNCTYPE(c_uint64, c_void_p) COMPOSITE_VOLUME_ADD = CFUNCTYPE(c_int, c_void_p, c_uint8, c_void_p, c_void_p) COMPOSITE_VOLUME_ATTACH_MEMBER = CFUNCTYPE(c_int, c_void_p, c_void_p, c_uint8, c_void_p) + COMPOSITE_VOLUME_DETACH_MEMBER = CFUNCTYPE(c_int, c_void_p, c_uint8) _fields_ = [ ("_submit_io", SUBMIT_IO), @@ -88,6 +89,7 @@ class VolumeOps(Structure): ("_get_max_io_size", GET_MAX_IO_SIZE), ("_composite_volume_add", COMPOSITE_VOLUME_ADD), ("_composite_volume_attach_member", COMPOSITE_VOLUME_ATTACH_MEMBER), + ("_composite_volume_detach_member", COMPOSITE_VOLUME_DETACH_MEMBER), ] @@ -178,6 +180,10 @@ def _composite_volume_add(ref): def _composite_volume_attach_member(ref): raise NotImplementedError + @VolumeOps.COMPOSITE_VOLUME_DETACH_MEMBER + def _composite_volume_detach_member(ref): + raise NotImplementedError + Volume._ops_[cls] = VolumeOps( _forward_io=_forward_io, _forward_flush=_forward_flush, @@ -190,6 +196,7 @@ def _composite_volume_attach_member(ref): _on_deinit=_on_deinit, _composite_volume_add=_composite_volume_add, _composite_volume_attach_member=_composite_volume_attach_member, + _composite_volume_detach_member=_composite_volume_detach_member, ) return Volume._ops_[cls] From 7ae1fc99132771c89b50f91003e36b0b79ebd1c7 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Wed, 22 Nov 2023 06:56:25 +0100 Subject: [PATCH 25/28] composite_volume: Implement detach_member ops Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- inc/ocf_err.h | 3 ++ src/ocf_composite_volume.c | 37 +++++++++++++++++++++++++ tests/functional/pyocf/types/cvolume.py | 28 +++++++++++++++++++ tests/functional/pyocf/types/shared.py | 1 + 4 files changed, 69 insertions(+) diff --git a/inc/ocf_err.h b/inc/ocf_err.h index a391bde8..654327dd 100644 --- a/inc/ocf_err.h +++ b/inc/ocf_err.h @@ -88,6 +88,9 @@ typedef enum { /** The target subvolume is already attached */ OCF_ERR_COMPOSITE_ATTACHED, + /** The target subvolume is already detached */ + OCF_ERR_COMPOSITE_DETACHED, + /** The target subvolume has invalid size */ OCF_ERR_COMPOSITE_INVALID_SIZE, diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c index b6d826f5..f04069d5 100644 --- a/src/ocf_composite_volume.c +++ b/src/ocf_composite_volume.c @@ -412,6 +412,41 @@ static int composite_volume_attach_member(ocf_volume_t cvolume, return 0; } +static int composite_volume_detach_member(ocf_volume_t cvolume, + uint8_t subvolume_id) +{ + struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); + ocf_ctx_t ctx = cvolume->type->owner; + + if (subvolume_id >= OCF_COMPOSITE_VOLUME_MEMBERS_MAX) { + ocf_log(ctx, log_err, "Failed to detach subvolume from " + "the composite volume. Invalid subvolume " + "target id\n"); + return -OCF_ERR_COMPOSITE_INVALID_ID; + } + + if (subvolume_id >= composite->members_cnt) { + ocf_log(ctx, log_err, "Failed to detach subvolume from " + "the composite volume. Can't detach " + "uninitialized member\n"); + return -OCF_ERR_COMPOSITE_UNINITIALISED_VOLUME; + } + + if (composite->member[subvolume_id].detached) { + ocf_log(ctx, log_err, "Failed to detach subvolume from " + "the composite volume. The target member is " + "already detached\n"); + return -OCF_ERR_COMPOSITE_DETACHED; + } + + ocf_volume_close(&composite->member[subvolume_id].volume); + + composite->member[subvolume_id].detached = true; + composite->member[subvolume_id].volume_params = NULL; + + return 0; +} + static unsigned int ocf_composite_volume_get_max_io_size(ocf_volume_t cvolume) { struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); @@ -503,6 +538,8 @@ const struct ocf_volume_properties ocf_composite_volume_properties = { .on_init = ocf_composite_volume_on_init, .composite_volume_attach_member = composite_volume_attach_member, + .composite_volume_detach_member = + composite_volume_detach_member, .on_deinit = ocf_composite_volume_on_deinit, .composite_volume_add = composite_volume_add, diff --git a/tests/functional/pyocf/types/cvolume.py b/tests/functional/pyocf/types/cvolume.py index b60fd2e6..837a707d 100644 --- a/tests/functional/pyocf/types/cvolume.py +++ b/tests/functional/pyocf/types/cvolume.py @@ -58,6 +58,32 @@ def add(self, vol): if ret != 0: raise OcfError("Failed to add volume to a composite volume", ret) + def detach_member(self, member_id): + ret = lib.ocf_composite_volume_detach_member(self.cvol, member_id) + + if ret == 0: + return + elif ret == -OcfErrorCode.OCF_ERR_COMPOSITE_INVALID_ID: + raise OcfError( + "Detaching member from composite failed. Invalid ID.", + ret, + ) + elif ret == -OcfErrorCode.OCF_ERR_COMPOSITE_UNINITIALISED_VOLUME: + raise OcfError( + "Detaching member from composite failed. Uninitialised member.", + ret, + ) + elif ret == -OcfErrorCode.OCF_ERR_COMPOSITE_DETACHED: + raise OcfError( + "Detaching member from composite failed. Member already detached", + ret, + ) + else: + raise OcfError( + "Detaching member from composite failed", + ret, + ) + def attach_member(self, vol, target_id): uuid = Uuid( _data=cast(create_string_buffer(vol.uuid.encode("ascii")), c_char_p), @@ -126,6 +152,8 @@ def close(self): 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_composite_volume_detach_member.argtypes = [c_void_p, c_int] +lib.ocf_composite_volume_detach_member.restype = c_int lib.ocf_composite_volume_attach_member.argtypes = [c_void_p, c_void_p, c_uint8, c_void_p, c_void_p] lib.ocf_composite_volume_attach_member.restype = c_int lib.ocf_volume_open.restype = c_int diff --git a/tests/functional/pyocf/types/shared.py b/tests/functional/pyocf/types/shared.py index 2a356b1d..041de9d0 100644 --- a/tests/functional/pyocf/types/shared.py +++ b/tests/functional/pyocf/types/shared.py @@ -38,6 +38,7 @@ class OcfErrorCode(IntEnum): OCF_ERR_COMPOSITE_INVALID_ID = auto() OCF_ERR_COMPOSITE_UNINITIALISED_VOLUME = auto() OCF_ERR_COMPOSITE_ATTACHED = auto() + OCF_ERR_COMPOSITE_DETACHED = auto() OCF_ERR_COMPOSITE_INVALID_SIZE = auto() OCF_ERR_CACHE_EXIST = auto() OCF_ERR_CORE_EXIST = auto() From 07beec3676b3442151c20ddf2ce8cca0675745a2 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Wed, 22 Nov 2023 06:52:59 +0100 Subject: [PATCH 26/28] Enable detaching composite cache subvolumes Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- src/mngt/ocf_mngt_cache.c | 154 ++++++++++++++++++++++++++ tests/functional/pyocf/types/cache.py | 29 +++++ 2 files changed, 183 insertions(+) diff --git a/src/mngt/ocf_mngt_cache.c b/src/mngt/ocf_mngt_cache.c index 906fc8c6..beab576c 100644 --- a/src/mngt/ocf_mngt_cache.c +++ b/src/mngt/ocf_mngt_cache.c @@ -3801,6 +3801,14 @@ static void ocf_mngt_cache_detach_stop_cache_io(ocf_pipeline_t pipeline, ocf_mngt_continue_pipeline_on_zero_refcnt(refcnt, context->pipeline); } +static void ocf_mngt_cache_detach_composite_invalidate_cmpl(ocf_cache_t cache, + void *priv, int error) +{ + struct ocf_mngt_cache_unplug_context *context = priv; + + OCF_PL_NEXT_ON_SUCCESS_RET(context->pipeline, error); +} + static void ocf_mngt_cache_detach_stop_cleaner_io_finish(void *priv) { ocf_pipeline_t pipeline = priv; @@ -4072,3 +4080,149 @@ void ocf_mngt_cache_attach_composite(ocf_cache_t cache, ocf_uuid_t vol_uuid, ocf_pipeline_next(pipeline); } + +static void ocf_mngt_detach_composite_invalidate(ocf_pipeline_t pipeline, + void *priv, ocf_pipeline_arg_t arg) +{ + struct ocf_mngt_cache_unplug_context *context = priv; + ocf_cache_t cache = context->cache; + uint64_t begin_addr, end_addr; + ocf_cache_line_t begin_cline, end_cline; + int result; + + result = ocf_composite_volume_get_subvolume_addr_range( + ocf_cache_get_volume(cache), context->composite_vol_id, + &begin_addr, &end_addr); + if (result) + OCF_PL_FINISH_RET(pipeline, result); + + if (cache->device->metadata_offset >= end_addr) + OCF_PL_NEXT_RET(pipeline); + + if (cache->device->metadata_offset > begin_addr) + begin_addr = 0; + else + begin_addr -= cache->device->metadata_offset; + + end_addr -= cache->device->metadata_offset; + + begin_cline = begin_addr / ocf_cache_get_line_size(cache); + end_cline = OCF_DIV_ROUND_UP(end_addr, ocf_cache_get_line_size(cache)); + + if (end_cline > ocf_metadata_collision_table_entries(cache)) + end_cline = ocf_metadata_collision_table_entries(cache); + + ocf_mngt_cache_detach_cline_range(cache, begin_cline, end_cline, + ocf_mngt_cache_detach_composite_invalidate_cmpl, + context); +} + +static void ocf_mngt_detach_composite_close_volume(ocf_pipeline_t pipeline, + void *priv, ocf_pipeline_arg_t arg) +{ + struct ocf_mngt_cache_unplug_context *context = priv; + ocf_cache_t cache = context->cache; + ocf_volume_t composite = ocf_cache_get_volume(cache); + int ret = 0; + + ret = ocf_composite_volume_detach_member(composite, + context->composite_vol_id); + + OCF_PL_NEXT_ON_SUCCESS_RET(pipeline, ret); +} + +static void ocf_mngt_cache_detach_composite_finish(ocf_pipeline_t pipeline, + void *priv, int error) +{ + struct ocf_mngt_cache_unplug_context *context = priv; + ocf_cache_t cache = context->cache; + + env_refcnt_unfreeze(&cache->refcnt.dirty); + + if (!error) { + if (!context->cache_write_error) { + ocf_cache_log(cache, log_info, + "Device successfully detached\n"); + } else { + ocf_cache_log(cache, log_warn, + "Device detached with errors\n"); + } + } else { + ocf_cache_log(cache, log_err, + "Detaching device failed\n"); + } + + context->cmpl(cache, context->priv, + error ?: context->cache_write_error); + + ocf_pipeline_destroy(context->pipeline); +} + +struct ocf_pipeline_properties ocf_mngt_detach_composite_pipeline_properties = { + .priv_size = sizeof(struct ocf_mngt_cache_unplug_context), + .finish = ocf_mngt_cache_detach_composite_finish, + .steps = { + OCF_PL_STEP(ocf_mngt_detach_composite_invalidate), + OCF_PL_STEP(ocf_mngt_detach_composite_close_volume), + OCF_PL_STEP_TERMINATOR(), + }, +}; + +static void _ocf_mngt_begin_detach_complete(void *priv) +{ + struct ocf_mngt_cache_unplug_context *context = priv; + + ocf_pipeline_next(context->pipeline); +} + +void ocf_mngt_cache_detach_composite(ocf_cache_t cache, + ocf_mngt_cache_detach_end_t cmpl, ocf_uuid_t target_uuid, + void *priv) +{ + struct ocf_mngt_cache_unplug_context *context; + ocf_pipeline_t pipeline; + int result; + int target_vol_id; + ocf_volume_t cvol = ocf_cache_get_volume(cache); + + OCF_CHECK_NULL(cache); + + if (ocf_cache_is_standby(cache)) + OCF_CMPL_RET(cache, priv, -OCF_ERR_CACHE_STANDBY); + + if (!cache->mngt_queue) + OCF_CMPL_RET(cache, priv, -OCF_ERR_INVAL); + + if (!ocf_cache_is_device_attached(cache)) + OCF_CMPL_RET(cache, priv, -OCF_ERR_INVAL); + + if (!ocf_volume_is_composite(cvol)) + OCF_CMPL_RET(cache, priv, -OCF_ERR_NOT_COMPOSITE_VOLUME); + + if (!cache->metadata.is_volatile) + OCF_CMPL_RET(cache, priv, -OCF_ERR_INVAL); + + target_vol_id = ocf_composite_volume_get_id_from_uuid( + ocf_cache_get_volume(cache), target_uuid); + if (target_vol_id < 0) + OCF_CMPL_RET(cache, priv, target_vol_id); + + result = ocf_pipeline_create(&pipeline, cache, + &ocf_mngt_detach_composite_pipeline_properties); + if (result) + OCF_CMPL_RET(cache, priv, -OCF_ERR_NO_MEM); + + context = ocf_pipeline_get_priv(pipeline); + + context->cmpl = cmpl; + context->priv = priv; + context->pipeline = pipeline; + context->cache = cache; + context->composite_vol_id = target_vol_id; + + /* prevent dirty io */ + env_refcnt_freeze(&cache->refcnt.dirty); + + env_refcnt_register_zero_cb(&cache->refcnt.dirty, + _ocf_mngt_begin_detach_complete, context); +} diff --git a/tests/functional/pyocf/types/cache.py b/tests/functional/pyocf/types/cache.py index e1949b93..1e7fe7cc 100644 --- a/tests/functional/pyocf/types/cache.py +++ b/tests/functional/pyocf/types/cache.py @@ -601,6 +601,33 @@ def attach_composite_member(self, volume, tgt_subvol_id): c.results["error"], ) + def detach_composite_member(self, volume): + uuid = Uuid( + _data=cast(create_string_buffer(volume.uuid.encode("ascii")), c_char_p), + _size=len(volume.uuid) + 1, + ) + + self.write_lock() + + c = OcfCompletion([("cache", c_void_p), ("priv", c_void_p), ("error", c_int)]) + + lib.ocf_mngt_cache_detach_composite( + self.cache_handle, + c, + byref(uuid), + None, + ) + + c.wait() + + self.write_unlock() + + if c.results["error"]: + raise OcfError( + "Detaching composite cache member failed", + c.results["error"], + ) + def attach_device_async( self, device, @@ -1171,3 +1198,5 @@ def get_by_name(cache_name, owner=None): lib.ocf_volume_destroy.argtypes = [c_void_p] lib.ocf_mngt_cache_attach_composite.argtypes = [c_void_p, c_void_p, c_uint8, c_void_p, c_void_p, c_void_p, c_void_p] lib.ocf_mngt_cache_attach_composite.restype = None +lib.ocf_mngt_cache_detach_composite.argtypes = [c_void_p, c_void_p, c_void_p, c_void_p] +lib.ocf_mngt_cache_detach_composite.restype = None From 73c91d52f9ca6ea8470c580bc62e987d1415a7b7 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Fri, 20 Oct 2023 08:34:57 +0200 Subject: [PATCH 27/28] pyocf: test for reattaching composite members Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- .../tests/management/test_composite_volume.py | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/tests/functional/tests/management/test_composite_volume.py b/tests/functional/tests/management/test_composite_volume.py index b61d6040..b490d651 100644 --- a/tests/functional/tests/management/test_composite_volume.py +++ b/tests/functional/tests/management/test_composite_volume.py @@ -1,6 +1,7 @@ # # Copyright(c) 2022 Intel Corporation # Copyright(c) 2024 Huawei Technologies +# Copyright(c) 2026 Unvertical # SPDX-License-Identifier: BSD-3-Clause # @@ -46,6 +47,184 @@ def test_create_composite_volume(pyocf_ctx): cvol.destroy() +def test_composite_reattach_twice_neg(pyocf_ctx): + """ + title: Try to attach attached mebmer + description: | + Check that it is not possible to attach volume to attached member + """ + cvol = CVolume(pyocf_ctx) + vols = [RamVolume(S.from_MiB(2)), RamVolume(S.from_MiB(2)), RamVolume(S.from_MiB(2))] + cvol.add(vols[0]) + cvol.add(vols[1]) + cvol.add(vols[2]) + + cvol.open() + + new_vol = RamVolume(S.from_MiB(2)) + + with pytest.raises( + OcfError, + match="Attaching member to composite failed. Member already attached", + ): + cvol.attach_member(new_vol, 1) + + cvol.close() + + cvol.destroy() + + +def test_composite_attach_uninitialized_neg(pyocf_ctx): + """ + title: Try to attach uninitialized subvolume + description: | + Check that it is not possible to attach uninitalized composite member + """ + cvol = CVolume(pyocf_ctx) + vols = [RamVolume(S.from_MiB(2)), RamVolume(S.from_MiB(2)), RamVolume(S.from_MiB(2))] + cvol.add(vols[0]) + cvol.add(vols[1]) + cvol.add(vols[2]) + + cvol.open() + + cvol.detach_member(1) + + new_vol = RamVolume(S.from_MiB(2)) + + for subvol_id in range(3, 16): + with pytest.raises( + OcfError, + match="Attaching member to composite failed. Uninitialised member.", + ): + cvol.attach_member(new_vol, subvol_id) + + cvol.close() + + cvol.destroy() + + +@pytest.mark.parametrize("member_id", [0, 1, 2]) +def test_composite_dettach_member_twice_neg(pyocf_ctx, member_id): + """ + title: Try to reattach subvolume to complete composite volume + description: | + Check that it is not possible to attach member to composite volume without detaching + any member first + """ + cvol = CVolume(pyocf_ctx) + vols = [RamVolume(S.from_MiB(2)), RamVolume(S.from_MiB(2)), RamVolume(S.from_MiB(2))] + cvol.add(vols[0]) + cvol.add(vols[1]) + cvol.add(vols[2]) + + cvol.open() + + cvol.detach_member(member_id) + + with pytest.raises( + OcfError, + match="Detaching member from composite failed. Member already detached", + ): + cvol.detach_member(member_id) + + cvol.close() + + cvol.destroy() + +def test_composite_reattach_different_size_neg(pyocf_ctx): + """ + title: Try to reattach subvolumes of size different than original + description: | + Check that it is not possible to attach volume of different size than the originally + detach size + """ + cvol = CVolume(pyocf_ctx) + original_size = S.from_MiB(5) + vols = [RamVolume(original_size), RamVolume(S.from_MiB(2))] + invalid_vols = [ + RamVolume(S.from_MiB(1)), + RamVolume(original_size + S.from_B(1)), + RamVolume(original_size - S.from_B(1)), + RamVolume(original_size + S.from_B(512)), + RamVolume(original_size - S.from_B(512)), + RamVolume(original_size + S.from_B(4096)), + RamVolume(original_size - S.from_B(4096)), + ] + + cvol.add(vols[0]) + cvol.add(vols[1]) + + cvol.open() + + member_id = 0 + cvol.detach_member(member_id) + + for invalid_vol in invalid_vols: + with pytest.raises(OcfError, + match="Attaching member to composite failed. Invalid size.", + ): + cvol.attach_member(invalid_vol, member_id) + + cvol.close() + + cvol.destroy() + + +@pytest.mark.parametrize("member_id", [0, 1, 2]) +def test_composite_reattach_member(pyocf_ctx, member_id): + """ + title: Detach member from composite volume + description: | + Check that it is possible to detach member for composite volume + """ + cvol = CVolume(pyocf_ctx) + vols = [RamVolume(S.from_MiB(2)), RamVolume(S.from_MiB(2)), RamVolume(S.from_MiB(2))] + cvol.add(vols[0]) + cvol.add(vols[1]) + cvol.add(vols[2]) + + cvol.open() + + new_vol = RamVolume(S.from_MiB(2)) + + cvol.detach_member(member_id) + cvol.attach_member(new_vol, member_id) + + cvol.close() + + cvol.destroy() + + +def test_composite_reattach_members(pyocf_ctx): + """ + title: Detach multiple subvolumes from composite + description: | + Check that it is possible to reattach multiple members to composite volume + """ + cvol = CVolume(pyocf_ctx) + + vols = [RamVolume(S.from_MiB(2)), RamVolume(S.from_MiB(2)), RamVolume(S.from_MiB(2))] + cvol.add(vols[0]) + cvol.add(vols[1]) + cvol.add(vols[2]) + + cvol.open() + + cvol.detach_member(0) + cvol.detach_member(1) + cvol.detach_member(2) + + vols = [RamVolume(S.from_MiB(2)), RamVolume(S.from_MiB(2)), RamVolume(S.from_MiB(2))] + cvol.attach_member(vols[0], 0) + cvol.attach_member(vols[1], 1) + cvol.attach_member(vols[2], 2) + + cvol.close() + + cvol.destroy() + + def test_add_subvolumes_of_different_types(pyocf_ctx): """ title: Add subvolumes of different types. From aec49682a5de02c5458fa9fd0bef1209630f4d36 Mon Sep 17 00:00:00 2001 From: Michal Mielewczyk Date: Wed, 25 Oct 2023 09:49:14 +0200 Subject: [PATCH 28/28] pyocf: composite cache tests Signed-off-by: Michal Mielewczyk Signed-off-by: Robert Baldyga --- .../tests/management/test_composite_volume.py | 242 +++++++++++++++++- 1 file changed, 239 insertions(+), 3 deletions(-) diff --git a/tests/functional/tests/management/test_composite_volume.py b/tests/functional/tests/management/test_composite_volume.py index b490d651..31fd924f 100644 --- a/tests/functional/tests/management/test_composite_volume.py +++ b/tests/functional/tests/management/test_composite_volume.py @@ -7,19 +7,23 @@ import pytest import random +import time from ctypes import POINTER, c_int, cast, c_void_p -from datetime import datetime +from datetime import datetime, timedelta from threading import Event from collections import namedtuple from pyocf.ocf import OcfLib from pyocf.types.volume import RamVolume, ErrorDevice, TraceDevice, IoFlags from pyocf.types.cvolume import CVolume +from pyocf.types.volume_core import CoreVolume from pyocf.types.data import Data -from pyocf.types.io import IoDir -from pyocf.types.cache import Cache +from pyocf.types.io import IoDir, Sync +from pyocf.types.cache import Cache, CacheMode +from pyocf.types.core import Core from pyocf.types.shared import OcfError, OcfErrorCode, OcfCompletion from pyocf.utils import Size as S +from pyocf.rio import Rio, ReadWrite def test_create_composite_volume(pyocf_ctx): @@ -989,3 +993,235 @@ def test_load(pyocf_ctx): cache.stop() assert Cache.get_by_name("cache1", pyocf_ctx) != 0, "Try getting cache after stopping it" + + +@pytest.mark.parametrize("member_id", [0, 1, 2]) +def test_composite_detach_attach_cache_member(pyocf_ctx, member_id): + """ + title: Detach and attach composite cache member + description: | + Check that it is possible to detach subvolumes from running composite + cache instance + pass_criteria: + - Composite volume is created w/o an error + - Composite subvolume is detached w/o error + - Composite subvolume is attached w/o error + steps: + - Create composite volume + - Add 3 RamVolume instances as subvolumes. + - Start cache and attach it using composite volume instance. + - Detach one subvolume from the composite cache + - Attach the missing subvolume to the composite cache + """ + + cvol = CVolume(pyocf_ctx) + + composite_subvols = [ + RamVolume(S.from_MiB(40)), + RamVolume(S.from_MiB(40)), + RamVolume(S.from_MiB(40)) + ] + for subvol in composite_subvols: + cvol.add(subvol) + + cache = Cache(owner=pyocf_ctx).start_on_device(cvol, metadata_volatile=True) + + cache.detach_composite_member(composite_subvols[member_id]) + cache.attach_composite_member(composite_subvols[member_id], member_id) + + +def test_composite_ops_nonvolatile_metadata_neg(pyocf_ctx): + """ + title: Block composite attach and detach in non-volatile MD mode + description: | + Check if is blocked to attach and detach composite members in + non-volatile metadata mode + pass_criteria: + - Composite cache is created w/o an error + - Detaching composite volume fails + - Attaching composite volume fails + - OCF doesn't crash + """ + + cvol = CVolume(pyocf_ctx) + + composite_subvols = [ + RamVolume(S.from_MiB(40)), + RamVolume(S.from_MiB(40)), + RamVolume(S.from_MiB(40)) + ] + for subvol in composite_subvols: + cvol.add(subvol) + + cache = Cache(owner=pyocf_ctx).start_on_device(cvol, metadata_volatile=False) + + attached_vol = RamVolume(S.from_MiB(40)) + + with pytest.raises(OcfError, match="Detaching composite cache member failed"): + cache.detach_composite_member(composite_subvols[0]) + + with pytest.raises(OcfError, match="Attaching composite cache member failed"): + cache.attach_composite_member(attached_vol, 0) + + +@pytest.mark.parametrize("member_id", [0, 1, 2]) +def test_composite_stop_detached(pyocf_ctx, member_id): + """ + title: Detach and attach composite cache member + description: | + Check that it is possible to detach subvolumes from running composite + cache instance + pass_criteria: + - Composite volume is created w/o an error + - Composite subvolume is detached w/o error + - Cache is stopped w/o error + steps: + - Create composite volume + - Add 3 RamVolume instances as subvolumes. + - Start cache and attach it using composite volume instance. + - Detach one subvolume from the composite cache + - Attach the missing subvolume to the composite cache + """ + + cvol = CVolume(pyocf_ctx) + + composite_subvols = [ + RamVolume(S.from_MiB(40)), + RamVolume(S.from_MiB(40)), + RamVolume(S.from_MiB(40)) + ] + for subvol in composite_subvols: + cvol.add(subvol) + + cache = Cache(owner=pyocf_ctx).start_on_device(cvol, metadata_volatile=True) + + cache.detach_composite_member(composite_subvols[member_id]) + + cache.stop() + + +def test_composite_detach_dirty(pyocf_ctx): + """ + title: Detach and attach composite cache member + description: | + Check that it is possible to detach subvolumes from running composite + cache instance + pass_criteria: + - Composite volume is created w/o an error + - Composite subvolume is detached w/o error + - Cache is stopped w/o error + steps: + - Create composite volume + - Add 3 RamVolume instances as subvolumes. + - Start cache and attach it using composite volume instance. + - Detach one subvolume from the composite cache + - Attach the missing subvolume to the composite cache + """ + core_size = S.from_MiB(60) + core_dev = RamVolume(core_size) + cvol = CVolume(pyocf_ctx) + + core = Core.using_device(core_dev) + + composite_subvols = [ + RamVolume(S.from_MiB(20)), + RamVolume(S.from_MiB(20)), + ] + for subvol in composite_subvols: + cvol.add(subvol) + + cache = Cache(owner=pyocf_ctx).start_on_device( + cvol, + metadata_volatile=True, + cache_mode=CacheMode.WB + ) + + cache.add_core(core) + core_vol = CoreVolume(core) + core_vol.open() + queue = cache.get_default_queue() + + r = ( + Rio() + .target(core_vol) + .njobs(10) + .bs(S.from_KiB(4)) + .readwrite(ReadWrite.RANDWRITE) + .size(core_dev.size) + .time_based() + .time(timedelta(seconds=10)) + .qd(10) + .run_async([queue]) + ) + + time.sleep(3) + + cache.detach_composite_member(composite_subvols[0]) + + r.abort() + + cache.stop() + +@pytest.mark.parametrize("member_id", [0, 1, 2]) +def test_composite_detach_attach_read(pyocf_ctx, member_id): + """ + title: Detach and attach composite cache member + description: | + Check that it is possible to detach subvolumes from running composite + cache instance + pass_criteria: + - Composite volume is created w/o an errors + - Composite subvolume is detached w/o errors + - Composite subvolume is attached w/o errors + steps: + - Create composite volume + - Add 3 RamVolume instances as subvolumes. + - Start cache and attach it using composite volume instance. + - Add core bigger than cache + - Fill cache with data + - Get md5 of core backend volume + - Detach one subvolume from the composite cache + - Check if core's md5 matches the device's md5 + - Attach the missing subvolume to the composite cache + - Check if core's md5 matches the device's md5 + """ + core_size = S.from_MiB(105) + cvol = CVolume(pyocf_ctx) + core_dev = RamVolume(core_size) + + composite_subvols = [ + RamVolume(S.from_MiB(70)), + RamVolume(S.from_MiB(15)), + RamVolume(S.from_MiB(15)) + ] + for subvol in composite_subvols: + cvol.add(subvol) + + # TODO make the test work with non volatile metadata + cache = Cache(owner=pyocf_ctx).start_on_device(cvol, metadata_volatile=True) + core = Core.using_device(core_dev) + cache.add_core(core) + + cache_size_4k = cache.get_stats()['conf']['size'].blocks_4k + + queue = cache.get_default_queue() + core_vol = CoreVolume(core) + core_vol.open() + + io_size = S.from_KiB(4) + pattern = b'0123456789abcdef' + data_str = pattern * (io_size.B // len(pattern)) + for i in range(core_size//io_size): + write_io = core_vol.new_io(queue, i * io_size, io_size, IoDir.WRITE, 0, 0) + write_data = Data(io_size) + write_data.write(data_str, io_size) + write_io.set_data(write_data) + write_cmpl = Sync(write_io).submit() + assert int(write_cmpl.results["err"]) == 0 + + original_md5 = core_vol.md5() + cache.detach_composite_member(composite_subvols[member_id]) + assert core_vol.md5() == original_md5 + + cache.attach_composite_member(composite_subvols[member_id], member_id) + assert core_vol.md5() == original_md5