diff --git a/doc/requirements/composite_volume b/doc/requirements/composite_volume new file mode 100644 index 0000000..b786fe3 --- /dev/null +++ b/doc/requirements/composite_volume @@ -0,0 +1,172 @@ +--- +group: composite_volume +--- + +The OCF composite volume provides functionality of composing mutiple OCF volume +instances into one bigger volume. The component volumes are linearly mapped into +address space of the composite volume (unlike RAID0, which uses interleaved +mapping a.k.a. striping). + + +-----------------------+---------------+-----------------------------+ + | volume A | volume B | volume C | + *-----------------------|---------------|-----------------------------+ + LBA | 0 | 1 | 2 | 3 | 4 | 5 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | 4 | 5 | + +---------------------------------------------------------------------+ + | composite volume | + +---------------------------------------------------------------------+ + LBA | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | + +---------------------------------------------------------------------+ + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Creation of composite volume instance +id: creation +--- + +It shall be possible to create an instance of a composite volume using a +constructor function. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Destruction of composite volume instance +id: destruction +--- + +It shall be possible to destroy an instance of a composite volume using +standard ocf_volume API. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Adding new component volume +id: adding_component_volume +--- + +If shall be possible to add new component volume to the composite volume using +dedicated function. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Instatiation of component volumes +id: instatiation_component_volumes +--- + +Adding component volume to composite volume shall automatically instatiate +component volume based on type, uuid and volume_params parameters. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Destrution of component volumes +id: destruction_component_volumes +--- + +Component volume instances shall be destroyed on composite volume destruction. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Adding component volumes of different types +id: component_volume_types +--- + +It shall be possible to add component volumes of different volume types to +single composite volume. The only requirement is that those types shall be +registered within the same ocf_ctx. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Maximum number of composite volumes +id: max_composite_volumes +--- + +It shall be possible to add up to 16 component volumes to a single composite +volume. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Volume API +id: volume_api +--- + +Composite volume shall conform to regular ocf_volume API. I.e. it shall be +possible to perform on it following volume operations: open, close, +get_max_io_size, get_length, submit_io, submit_flush and submit_discard. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Volume open +id: volume_open +--- + +Composite volume shall open all component volumes when it's being opened. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Volume close +id: volume_close +--- + +Composite volume shall close all component volumes when it's being closed. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Getting maximum io size +id: get_max_io_size +--- + +Composite volume shall report a maximum io size being the lowest maximum io +size among all the component volumes. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Getting volume length +id: get_length +--- + +Composite volume shall report a lenght being a sum of lenghs of all component +volumes. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Request passing +id: request_passing +--- + +Composite volume shall pass io, flush and discard request or its fragments to +component volumes based on their mapping. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: IO Flags passing +id: io_flags_passing +--- + +Composite volume shall pass all IO flags associated with request to all requests +issued to component volumes as part of fulfilling the master request. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: I/O request completion +id: io_request_completion +--- + +The I/O request shall be completed only when all sub-I/Os submitted to component +volumes are completed. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: I/O error handling +id: io_error_handling +--- + +If I/O error occures on any I/O request submitted to composite volume, that +error shall be passed to the completion of the original I/O request. If multiple +sub-I/Os reported errors, the first error that occured shall be passed to the +completion of the original I/O request. + +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +title: Attaching/loading the cache +id: cache_attach_load +--- + +It shall be possible to attach/load the cache using instance of a composite +volume. diff --git a/example/simple/src/main.c b/example/simple/src/main.c index 1a0b07f..cfc7ce2 100644 --- a/example/simple/src/main.c +++ b/example/simple/src/main.c @@ -72,6 +72,9 @@ int initialize_cache(ocf_ctx_t ctx, ocf_cache_t *cache) { struct ocf_mngt_cache_config cache_cfg = { .name = "cache1" }; struct ocf_mngt_cache_attach_config attach_cfg = { }; + ocf_volume_t volume; + ocf_volume_type_t type; + struct ocf_volume_uuid uuid; struct cache_priv *cache_priv; struct simple_context context; int ret; @@ -92,20 +95,28 @@ int initialize_cache(ocf_ctx_t ctx, ocf_cache_t *cache) cache_cfg.metadata_volatile = true; /* Cache deivce (volume) configuration */ - ocf_mngt_cache_attach_config_set_default(&attach_cfg); - attach_cfg.device.volume_type = VOL_TYPE; - ret = ocf_uuid_set_str(&attach_cfg.device.uuid, "cache"); + type = ocf_ctx_get_volume_type(ctx, VOL_TYPE); + ret = ocf_uuid_set_str(&uuid, "cache"); if (ret) goto err_sem; + ret = ocf_volume_create(&volume, type, &uuid); + if (ret) + goto err_sem; + + ocf_mngt_cache_attach_config_set_default(&attach_cfg); + attach_cfg.device.volume = volume; + /* * Allocate cache private structure. We can not initialize it * on stack, as it may be used in various async contexts * throughout the entire live span of cache object. */ cache_priv = malloc(sizeof(*cache_priv)); - if (!cache_priv) - return -ENOMEM; + if (!cache_priv) { + ret = -ENOMEM; + goto err_vol; + } /* Start cache */ ret = ocf_mngt_cache_start(ctx, cache, &cache_cfg, NULL); @@ -157,6 +168,8 @@ err_cache: ocf_queue_put(cache_priv->mngt_queue); err_priv: free(cache_priv); +err_vol: + ocf_volume_destroy(&volume); err_sem: sem_destroy(&context.sem); return ret; diff --git a/inc/ocf.h b/inc/ocf.h index bcd60fd..d52af71 100644 --- a/inc/ocf.h +++ b/inc/ocf.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2012-2021 Intel Corporation + * Copyright(c) 2012-2022 Intel Corporation * SPDX-License-Identifier: BSD-3-Clause */ @@ -18,6 +18,7 @@ #include "ocf_types.h" #include "ocf_io.h" #include "ocf_volume.h" +#include "ocf_composite_volume.h" #include "ocf_cache.h" #include "ocf_core.h" #include "ocf_queue.h" diff --git a/inc/ocf_composite_volume.h b/inc/ocf_composite_volume.h new file mode 100644 index 0000000..7ae5a3a --- /dev/null +++ b/inc/ocf_composite_volume.h @@ -0,0 +1,55 @@ +/* + * Copyright(c) 2022 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __OCF_COMPOSITE_VOLUME_H__ +#define __OCF_COMPOSITE_VOLUME_H__ + +/** + * @file + * @brief OCF composite volume API + */ + +#include "ocf_types.h" +#include "ocf_env_headers.h" +#include "ocf_err.h" +#include "ocf_volume.h" + +/** + * @brief handle to object designating composite volume + */ +typedef ocf_volume_t ocf_composite_volume_t; + +/** + * @brief Allocate and initialize composite volume + * + * @param[out] cvolume pointer to volume handle + * @param[in] ctx OCF context + * + * @return Zero when success, othewise an error + */ +int ocf_composite_volume_create(ocf_composite_volume_t *cvolume, ocf_ctx_t ctx); + +/** + * @brief Deinitialize and free composite volume + * + * @param[in] volume volume handle + */ +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); + +#endif /* __OCF_COMPOSITE_VOLUME_H__ */ diff --git a/inc/ocf_mngt.h b/inc/ocf_mngt.h index f8b5af2..97e6021 100644 --- a/inc/ocf_mngt.h +++ b/inc/ocf_mngt.h @@ -361,14 +361,17 @@ void ocf_mngt_cache_stop(ocf_cache_t cache, */ struct ocf_mngt_cache_device_config { /** - * @brief Cache volume UUID + * @brief Cache volume + * + * The volume ownership is moved to the context of operation that takes + * this config. Thus the volume is being effectively moved using + * ocf_volume_move(), leaving the original volume uninitialized. + * + * Note that if the original volume was instantiated using *_create + * function, it still needs to be destroyed using ocf_volume_destroy() + * to deallocate the memory. */ - struct ocf_volume_uuid uuid; - - /** - * @brief Cache volume type - */ - uint8_t volume_type; + ocf_volume_t volume; /** * @brief If set, cache features (like discard) are tested diff --git a/inc/ocf_volume.h b/inc/ocf_volume.h index 3d71e70..f6b26f6 100644 --- a/inc/ocf_volume.h +++ b/inc/ocf_volume.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2012-2021 Intel Corporation + * Copyright(c) 2012-2022 Intel Corporation * SPDX-License-Identifier: BSD-3-Clause */ @@ -82,6 +82,24 @@ struct ocf_volume_ops { */ void (*submit_write_zeroes)(struct ocf_io *io); + /** + * @brief Volume initialization callback, called when volume object + * is being initialized + * + * @param[in] volume Volume + * + * @return Zero on success, otherwise error code + */ + int (*on_init)(ocf_volume_t volume); + + /** + * @brief Volume deinitialization callback, called when volume object + * is being deinitialized + * + * @param[in] volume Volume + */ + void (*on_deinit)(ocf_volume_t volume); + /** * @brief Open volume * diff --git a/src/mngt/ocf_mngt_cache.c b/src/mngt/ocf_mngt_cache.c index 50df9b2..53eb618 100644 --- a/src/mngt/ocf_mngt_cache.c +++ b/src/mngt/ocf_mngt_cache.c @@ -135,10 +135,6 @@ struct ocf_cache_attach_context { * load or recovery */ - bool uuid_copied : 1; - /*!< copied uuid data to context. - */ - bool metadata_frozen : 1; /*!< metadata reference counter frozen */ @@ -790,7 +786,6 @@ static void _ocf_mngt_attach_cache_device(ocf_pipeline_t pipeline, struct ocf_cache_attach_context *context = priv; struct ocf_mngt_cache_device_config *device_cfg = &context->cfg.device; ocf_cache_t cache = context->cache; - ocf_volume_type_t type; int ret; cache->device = env_vzalloc(sizeof(*cache->device)); @@ -799,20 +794,9 @@ static void _ocf_mngt_attach_cache_device(ocf_pipeline_t pipeline, context->flags.device_alloc = true; - /* Prepare UUID of cache volume */ - type = ocf_ctx_get_volume_type(cache->owner, device_cfg->volume_type); - if (!type) { - OCF_PL_FINISH_RET(pipeline, - -OCF_ERR_INVAL_VOLUME_TYPE); - } - - ret = ocf_volume_init(&cache->device->volume, type, - &device_cfg->uuid, true); - if (ret) - OCF_PL_FINISH_RET(pipeline, ret); - - cache->device->volume.cache = cache; + ocf_volume_move(&cache->device->volume, device_cfg->volume); context->flags.volume_inited = true; + cache->device->volume.cache = cache; /* * Open cache device, It has to be done first because metadata service @@ -1279,8 +1263,6 @@ uint64_t _ocf_mngt_calculate_ram_needed(ocf_cache_line_size_t line_size, int ocf_mngt_get_ram_needed(ocf_cache_t cache, struct ocf_mngt_cache_device_config *cfg, uint64_t *ram_needed) { - ocf_volume_t volume; - ocf_volume_type_t type; ocf_cache_line_size_t line_size; uint64_t volume_size; int result; @@ -1289,26 +1271,15 @@ int ocf_mngt_get_ram_needed(ocf_cache_t cache, OCF_CHECK_NULL(cfg); OCF_CHECK_NULL(ram_needed); - type = ocf_ctx_get_volume_type(cache->owner, cfg->volume_type); - if (!type) - return -OCF_ERR_INVAL_VOLUME_TYPE; - - result = ocf_volume_create(&volume, type, &cfg->uuid); + result = ocf_volume_open(cfg->volume, cfg->volume_params); if (result) return result; - result = ocf_volume_open(volume, cfg->volume_params); - if (result) { - ocf_volume_destroy(volume); - return result; - } - line_size = ocf_line_size(cache); - volume_size = ocf_volume_get_length(volume); + volume_size = ocf_volume_get_length(cfg->volume); *ram_needed = _ocf_mngt_calculate_ram_needed(line_size, volume_size); - ocf_volume_close(volume); - ocf_volume_destroy(volume); + ocf_volume_close(cfg->volume); return 0; } @@ -1479,34 +1450,6 @@ static void _ocf_mngt_init_attached_nonpersistent(ocf_pipeline_t pipeline, ocf_pipeline_next(pipeline); } -static void _ocf_mngt_copy_uuid_data(ocf_pipeline_t pipeline, - void *priv, ocf_pipeline_arg_t arg) -{ - struct ocf_cache_attach_context *context = priv; - struct ocf_mngt_cache_device_config *device_cfg = &context->cfg.device; - void *data; - int result; - - if (device_cfg->uuid.size == 0) - OCF_PL_NEXT_RET(pipeline); - - data = env_vmalloc(device_cfg->uuid.size); - if (!data) - OCF_PL_FINISH_RET(pipeline, -OCF_ERR_NO_MEM); - - result = env_memcpy(data, device_cfg->uuid.size, device_cfg->uuid.data, - device_cfg->uuid.size); - if (result) { - env_vfree(data); - OCF_PL_FINISH_RET(pipeline, -OCF_ERR_INVAL); - } - - device_cfg->uuid.data = data; - context->flags.uuid_copied = true; - - ocf_pipeline_next(pipeline); -} - static void _ocf_mngt_attach_check_ram(ocf_pipeline_t pipeline, void *priv, ocf_pipeline_arg_t arg) { @@ -1799,6 +1742,8 @@ static void _ocf_mngt_attach_handle_error( if (context->flags.volume_inited) ocf_volume_deinit(&cache->device->volume); + else + ocf_volume_deinit(context->cfg.device.volume); if (context->flags.front_volume_opened) ocf_volume_close(&cache->device->front_volume); @@ -1824,16 +1769,12 @@ static void _ocf_mngt_cache_attach_finish(ocf_pipeline_t pipeline, void *priv, int error) { struct ocf_cache_attach_context *context = priv; - struct ocf_mngt_cache_device_config *device_cfg = &context->cfg.device; if (error) _ocf_mngt_attach_handle_error(context); context->cmpl(context->cache, context->priv1, context->priv2, error); - if (context->flags.uuid_copied) - env_vfree(device_cfg->uuid.data); - ocf_pipeline_destroy(context->pipeline); } @@ -1841,7 +1782,6 @@ struct ocf_pipeline_properties _ocf_mngt_cache_attach_pipeline_properties = { .priv_size = sizeof(struct ocf_cache_attach_context), .finish = _ocf_mngt_cache_attach_finish, .steps = { - OCF_PL_STEP(_ocf_mngt_copy_uuid_data), OCF_PL_STEP(_ocf_mngt_init_attached_nonpersistent), OCF_PL_STEP(_ocf_mngt_attach_cache_device), OCF_PL_STEP(_ocf_mngt_init_properties), @@ -1868,7 +1808,6 @@ struct ocf_pipeline_properties _ocf_mngt_cache_load_pipeline_properties = { .priv_size = sizeof(struct ocf_cache_attach_context), .finish = _ocf_mngt_cache_attach_finish, .steps = { - OCF_PL_STEP(_ocf_mngt_copy_uuid_data), OCF_PL_STEP(_ocf_mngt_init_attached_nonpersistent), OCF_PL_STEP(_ocf_mngt_attach_cache_device), OCF_PL_STEP(_ocf_mngt_init_properties), @@ -2265,7 +2204,6 @@ struct ocf_pipeline_properties _ocf_mngt_cache_standby_attach_pipeline_propertie .priv_size = sizeof(struct ocf_cache_attach_context), .finish = _ocf_mngt_cache_attach_finish, .steps = { - OCF_PL_STEP(_ocf_mngt_copy_uuid_data), OCF_PL_STEP(_ocf_mngt_init_attached_nonpersistent), OCF_PL_STEP(_ocf_mngt_attach_cache_device), OCF_PL_STEP(_ocf_mngt_attach_read_properties), @@ -2292,7 +2230,6 @@ struct ocf_pipeline_properties _ocf_mngt_cache_standby_load_pipeline_properties .priv_size = sizeof(struct ocf_cache_attach_context), .finish = _ocf_mngt_cache_attach_finish, .steps = { - OCF_PL_STEP(_ocf_mngt_copy_uuid_data), OCF_PL_STEP(_ocf_mngt_init_attached_nonpersistent), OCF_PL_STEP(_ocf_mngt_attach_cache_device), OCF_PL_STEP(_ocf_mngt_init_cache_front_volume), @@ -2342,20 +2279,9 @@ static void _ocf_mngt_activate_set_cache_device(ocf_pipeline_t pipeline, struct ocf_cache_attach_context *context = priv; struct ocf_mngt_cache_device_config *device_cfg = &context->cfg.device; ocf_cache_t cache = context->cache; - ocf_volume_type_t type; int ret; - type = ocf_ctx_get_volume_type(cache->owner, device_cfg->volume_type); - if (!type) { - OCF_PL_FINISH_RET(pipeline, - -OCF_ERR_INVAL_VOLUME_TYPE); - } - - ret = ocf_volume_init(&cache->device->volume, type, - &device_cfg->uuid, true); - if (ret) - OCF_PL_FINISH_RET(pipeline, ret); - + ocf_volume_move(&cache->device->volume, device_cfg->volume); cache->device->volume.cache = cache; context->flags.volume_inited = true; @@ -2473,6 +2399,8 @@ static void _ocf_mngt_activate_handle_error( if (context->flags.volume_inited) ocf_volume_deinit(&cache->device->volume); + else + ocf_volume_deinit(context->cfg.device.volume); if (context->flags.volume_stored) ocf_volume_move(&cache->device->volume, &context->cache_volume); @@ -2485,7 +2413,6 @@ static void _ocf_mngt_cache_activate_finish(ocf_pipeline_t pipeline, void *priv, int error) { struct ocf_cache_attach_context *context = priv; - struct ocf_mngt_cache_device_config *device_cfg = &context->cfg.device; ocf_cache_t cache = context->cache; ocf_pipeline_t stop_pipeline; @@ -2510,12 +2437,9 @@ static void _ocf_mngt_cache_activate_finish(ocf_pipeline_t pipeline, ocf_volume_deinit(&context->cache_volume); } - out: context->cmpl(context->cache, context->priv1, context->priv2, error); - if (context->flags.uuid_copied) - env_vfree(device_cfg->uuid.data); ocf_pipeline_destroy(context->pipeline); } @@ -2523,7 +2447,6 @@ struct ocf_pipeline_properties _ocf_mngt_cache_activate_pipeline_properties = { .priv_size = sizeof(struct ocf_cache_attach_context), .finish = _ocf_mngt_cache_activate_finish, .steps = { - OCF_PL_STEP(_ocf_mngt_copy_uuid_data), OCF_PL_STEP(_ocf_mngt_activate_set_cache_device), OCF_PL_STEP(_ocf_mngt_activate_init_properties), OCF_PL_STEP(_ocf_mngt_activate_compare_superblock), @@ -2870,10 +2793,7 @@ static int _ocf_mngt_cache_validate_cfg(struct ocf_mngt_cache_config *cfg) static int _ocf_mngt_cache_validate_device_cfg( struct ocf_mngt_cache_device_config *device_cfg) { - if (!device_cfg->uuid.data) - return -OCF_ERR_INVAL; - - if (device_cfg->uuid.size > OCF_VOLUME_UUID_MAX_SIZE) + if (!device_cfg->volume) return -OCF_ERR_INVAL; return 0; diff --git a/src/ocf_composite_volume.c b/src/ocf_composite_volume.c new file mode 100644 index 0000000..5d7b95e --- /dev/null +++ b/src/ocf_composite_volume.c @@ -0,0 +1,387 @@ +/* + * Copyright(c) 2022 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "ocf/ocf.h" +#include "ocf_priv.h" +#include "ocf_core_priv.h" +#include "ocf_io_priv.h" +#include "metadata/metadata.h" +#include "engine/cache_engine.h" +#include "utils/utils_user_part.h" +#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 { + struct ocf_volume volume; + void *volume_params; + } member[OCF_COMPOSITE_VOLUME_MEMBERS_MAX]; + uint64_t end_addr[OCF_COMPOSITE_VOLUME_MEMBERS_MAX]; + uint64_t length; + unsigned max_io_size; +}; + +struct ocf_composite_volume_io { + struct ocf_io *member_io[OCF_COMPOSITE_VOLUME_MEMBERS_MAX]; + ctx_data_t *data; + uint8_t begin_member; + uint8_t end_member; + env_atomic remaining; + env_atomic error; +}; + +static void ocf_composite_volume_master_cmpl(struct ocf_io *master_io, + int error) +{ + struct ocf_composite_volume_io *cio = ocf_io_get_priv(master_io); + + env_atomic_cmpxchg(&cio->error, 0, error); + + if (env_atomic_dec_return(&cio->remaining)) + return; + + ocf_io_end(master_io, env_atomic_read(&cio->error)); +} + +static void ocf_composite_volume_io_cmpl(struct ocf_io *io, int error) +{ + struct ocf_io *master_io = io->priv1; + + ocf_composite_volume_master_cmpl(master_io, error); +} + +static void ocf_composite_volume_handle_io(struct ocf_io *master_io, + void (*hndl)(struct ocf_io *io)) +{ + struct ocf_composite_volume_io *cio = ocf_io_get_priv(master_io); + int i; + + env_atomic_set(&cio->remaining, + cio->end_member - cio->begin_member + 1); + env_atomic_set(&cio->error, 0); + + for (i = cio->begin_member; i < cio->end_member; i++) { + ocf_io_set_cmpl(cio->member_io[i], master_io, NULL, + ocf_composite_volume_io_cmpl); + + cio->member_io[i]->io_class = master_io->io_class; + cio->member_io[i]->flags = master_io->flags; + + hndl(cio->member_io[i]); + } + + ocf_composite_volume_master_cmpl(master_io, 0); +} + +static void ocf_composite_volume_submit_io(struct ocf_io *master_io) +{ + ocf_composite_volume_handle_io(master_io, ocf_volume_submit_io); +} + +static void ocf_composite_volume_submit_flush(struct ocf_io *master_io) +{ + ocf_composite_volume_handle_io(master_io, ocf_volume_submit_flush); +} + +static void ocf_composite_volume_submit_discard(struct ocf_io *master_io) +{ + ocf_composite_volume_handle_io(master_io, ocf_volume_submit_discard); +} + +/* *** VOLUME OPS *** */ + +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; + +err: + while (i--) + ocf_volume_close(&composite->member[i].volume); + + return result; +} + +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++) + ocf_volume_close(&composite->member[i].volume); +} + +static unsigned int ocf_composite_volume_get_max_io_size(ocf_volume_t cvolume) +{ + struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); + + return composite->max_io_size; +} + +static uint64_t ocf_composite_volume_get_byte_length(ocf_volume_t cvolume) +{ + struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); + + return composite->length; +} + +static void ocf_composite_volume_on_deinit(ocf_volume_t cvolume) +{ + struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); + int i; + + for (i = 0; i < composite->members_cnt; i++) + ocf_volume_deinit(&composite->member[i].volume); +} + +/* *** IO OPS *** */ + +static int ocf_composite_io_set_data(struct ocf_io *io, + ctx_data_t *data, uint32_t offset) +{ + ocf_volume_t cvolume = ocf_io_get_volume(io); + struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); + struct ocf_composite_volume_io *cio = ocf_io_get_priv(io); + uint64_t member_volume_start, member_data_offset; + int i, ret = 0; + + cio->data = data; + + for (i = cio->begin_member; i < cio->end_member; i++) { + /* Each member IO will have the same data set, but with + * different offset. First member will use bare offset set from + * caller, each subsequent member IO has to skip over parts + * "belonging" to previous members. */ + + if (i == cio->begin_member) { + member_data_offset = offset; + } else { + member_volume_start = composite->end_addr[i - 1]; + member_data_offset = member_volume_start - io->addr; + member_data_offset += offset; + } + + ret = ocf_io_set_data(cio->member_io[i], data, + member_data_offset); + if (ret) + break; + } + + return ret; +} + +static ctx_data_t *ocf_composite_io_get_data(struct ocf_io *io) +{ + struct ocf_composite_volume_io *cio = ocf_io_get_priv(io); + + return cio->data; +} + +const struct ocf_volume_properties ocf_composite_volume_properties = { + .name = "OCF Composite", + .io_priv_size = sizeof(struct ocf_composite_volume_io), + .volume_priv_size = sizeof(struct ocf_composite_volume), + .caps = { + .atomic_writes = 0, + }, + .ops = { + .submit_io = ocf_composite_volume_submit_io, + .submit_flush = ocf_composite_volume_submit_flush, + .submit_discard = ocf_composite_volume_submit_discard, + .submit_metadata = NULL, + + .open = ocf_composite_volume_open, + .close = ocf_composite_volume_close, + .get_max_io_size = ocf_composite_volume_get_max_io_size, + .get_length = ocf_composite_volume_get_byte_length, + + .on_deinit = ocf_composite_volume_on_deinit, + }, + .io_ops = { + .set_data = ocf_composite_io_set_data, + .get_data = ocf_composite_io_get_data, + }, + .deinit = NULL, +}; + +static int ocf_composite_io_allocator_init(ocf_io_allocator_t allocator, + uint32_t priv_size, const char *name) +{ + return ocf_io_allocator_default_init(allocator, priv_size, name); +} + +static void ocf_composite_io_allocator_deinit(ocf_io_allocator_t allocator) +{ + ocf_io_allocator_default_deinit(allocator); +} + +static void *ocf_composite_io_allocator_new(ocf_io_allocator_t allocator, + ocf_volume_t cvolume, ocf_queue_t queue, + uint64_t addr, uint32_t bytes, uint32_t dir) +{ + struct ocf_composite_volume *composite = ocf_volume_get_priv(cvolume); + struct ocf_composite_volume_io *cio; + struct ocf_io_internal *ioi; + uint64_t member_addr, member_bytes, cur_addr, cur_bytes; + int i; + + ioi = ocf_io_allocator_default_new(allocator, cvolume, queue, + addr, bytes, dir); + if (!ioi) + return NULL; + + cio = ocf_io_get_priv(&ioi->io); + + if (bytes == 0) { + /* Flush io - allocate io for each volume */ + for (i = 0; i < composite->members_cnt; i++) { + cio->member_io[i] = ocf_io_new(&composite->member[i].volume, + queue, 0, 0, dir, 0, 0); + if (!cio->member_io[i]) + goto err; + } + cio->begin_member = 0; + cio->end_member = composite->members_cnt; + + return ioi; + } + + for (i = 0; i < composite->members_cnt; i++) { + if (addr < composite->end_addr[i]) { + cio->begin_member = i; + break; + } + } + + cur_addr = addr; + cur_bytes = bytes; + + for (; i < composite->members_cnt; i++) { + member_addr = cur_addr - (i > 0 ? composite->end_addr[i-1] : 0); + member_bytes = + OCF_MIN(cur_addr + cur_bytes, composite->end_addr[i]) + - member_addr; + + cio->member_io[i] = ocf_io_new(&composite->member[i].volume, queue, + member_addr, member_bytes, dir, 0, 0); + if (!cio->member_io[i]) + goto err; + + cur_addr += member_bytes; + cur_bytes -= member_bytes; + + if (!cur_bytes) { + cio->end_member = i + 1; + break; + } + } + + ENV_BUG_ON(cur_bytes != 0); + + return ioi; + +err: + for (i = 0; i < composite->members_cnt; i++) { + if (cio->member_io[i]) + ocf_io_put(cio->member_io[i]); + } + + ocf_io_allocator_default_del(allocator, ioi); + + return NULL; +} + +static void ocf_composite_io_allocator_del(ocf_io_allocator_t allocator, void *obj) +{ + struct ocf_io_internal *ioi = obj; + struct ocf_composite_volume_io *cio = ocf_io_get_priv(&ioi->io); + int i; + + for (i = cio->begin_member; i < cio->end_member; i++) { + if (cio->member_io[i]) + ocf_io_put(cio->member_io[i]); + } + + ocf_io_allocator_default_del(allocator, ioi); +} + +const struct ocf_io_allocator_type ocf_composite_io_allocator_type = { + .ops = { + .allocator_init = ocf_composite_io_allocator_init, + .allocator_deinit = ocf_composite_io_allocator_deinit, + .allocator_new = ocf_composite_io_allocator_new, + .allocator_del = ocf_composite_io_allocator_del, + }, +}; + +const struct ocf_volume_extended ocf_composite_volume_extended = { + .allocator_type = &ocf_composite_io_allocator_type, +}; + +int ocf_composite_volume_type_init(ocf_ctx_t ctx) +{ + return ocf_ctx_register_volume_type_internal(ctx, + OCF_VOLUME_TYPE_COMPOSITE, + &ocf_composite_volume_properties, + &ocf_composite_volume_extended); +} + +int ocf_composite_volume_create(ocf_composite_volume_t *volume, ocf_ctx_t ctx) +{ + ocf_volume_type_t type; + + type = ocf_ctx_get_volume_type_internal(ctx, OCF_VOLUME_TYPE_COMPOSITE); + if (!type) + return -OCF_ERR_INVAL; + + return ocf_volume_create(volume, type, NULL); +} + +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; + + composite->member[composite->members_cnt].volume_params = volume_params; + composite->members_cnt++; + + return 0; +} diff --git a/src/ocf_composite_volume_priv.h b/src/ocf_composite_volume_priv.h new file mode 100644 index 0000000..b4ad4d5 --- /dev/null +++ b/src/ocf_composite_volume_priv.h @@ -0,0 +1,13 @@ +/* + * Copyright(c) 2022 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +#ifndef __OCF_COMPOSITE_VOLUME_PRIV_H__ +#define __OCF_COMPOSITE_VOLUME_PRIV_H__ + +#include "ocf/ocf.h" + +int ocf_composite_volume_type_init(ocf_ctx_t ctx); + +#endif /* __OCF_COMPOSITE_VOLUME_H__ */ diff --git a/src/ocf_ctx.c b/src/ocf_ctx.c index 51e7e4c..008e4d7 100644 --- a/src/ocf_ctx.c +++ b/src/ocf_ctx.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2012-2021 Intel Corporation + * Copyright(c) 2012-2022 Intel Corporation * SPDX-License-Identifier: BSD-3-Clause */ @@ -11,6 +11,7 @@ #include "ocf_logger_priv.h" #include "ocf_core_priv.h" #include "ocf_cache_priv.h" +#include "ocf_composite_volume_priv.h" #include "mngt/ocf_mngt_core_pool_priv.h" #include "metadata/metadata_io.h" @@ -220,12 +221,18 @@ int ocf_ctx_create(ocf_ctx_t *ctx, const struct ocf_ctx_config *cfg) if (ret) goto err_utils; + ret = ocf_composite_volume_type_init(ocf_ctx); + if (ret) + goto err_core_volume; + ocf_mngt_core_pool_init(ocf_ctx); *ctx = ocf_ctx; return 0; +err_core_volume: + ocf_ctx_unregister_volume_type(ocf_ctx, OCF_VOLUME_TYPE_CORE); err_utils: ocf_metadata_io_ctx_deinit(ocf_ctx); err_mio: diff --git a/src/ocf_ctx_priv.h b/src/ocf_ctx_priv.h index 1132bf6..3ce457f 100644 --- a/src/ocf_ctx_priv.h +++ b/src/ocf_ctx_priv.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2012-2021 Intel Corporation + * Copyright(c) 2012-2022 Intel Corporation * SPDX-License-Identifier: BSD-3-Clause */ @@ -12,13 +12,14 @@ #include "ocf_volume_priv.h" #define OCF_VOLUME_TYPE_CNT_USER 8 -#define OCF_VOLUME_TYPE_CNT_PRIV 2 +#define OCF_VOLUME_TYPE_CNT_PRIV 3 #define OCF_VOLUME_TYPE_MAX_USER OCF_VOLUME_TYPE_CNT_USER #define OCF_VOLUME_TYPE_MAX \ (OCF_VOLUME_TYPE_CNT_USER + OCF_VOLUME_TYPE_CNT_PRIV) #define OCF_VOLUME_TYPE_CORE (OCF_VOLUME_TYPE_MAX_USER + 0) #define OCF_VOLUME_TYPE_CACHE (OCF_VOLUME_TYPE_MAX_USER + 1) +#define OCF_VOLUME_TYPE_COMPOSITE (OCF_VOLUME_TYPE_MAX_USER + 2) /** * @brief OCF main control structure diff --git a/src/ocf_io.c b/src/ocf_io.c index 0610449..718ae24 100644 --- a/src/ocf_io.c +++ b/src/ocf_io.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2012-2021 Intel Corporation + * Copyright(c) 2012-2022 Intel Corporation * SPDX-License-Identifier: BSD-3-Clause */ @@ -31,7 +31,7 @@ #define OCF_IO_TOTAL(priv_size) \ (sizeof(struct ocf_io_internal) + priv_size) -static int ocf_io_allocator_default_init(ocf_io_allocator_t allocator, +int ocf_io_allocator_default_init(ocf_io_allocator_t allocator, uint32_t priv_size, const char *name) { allocator->priv = env_allocator_create(OCF_IO_TOTAL(priv_size), name, @@ -42,20 +42,20 @@ static int ocf_io_allocator_default_init(ocf_io_allocator_t allocator, return 0; } -static void ocf_io_allocator_default_deinit(ocf_io_allocator_t allocator) +void ocf_io_allocator_default_deinit(ocf_io_allocator_t allocator) { env_allocator_destroy(allocator->priv); allocator->priv = NULL; } -static void *ocf_io_allocator_default_new(ocf_io_allocator_t allocator, +void *ocf_io_allocator_default_new(ocf_io_allocator_t allocator, ocf_volume_t volume, ocf_queue_t queue, uint64_t addr, uint32_t bytes, uint32_t dir) { return env_allocator_new(allocator->priv); } -static void ocf_io_allocator_default_del(ocf_io_allocator_t allocator, void *obj) +void ocf_io_allocator_default_del(ocf_io_allocator_t allocator, void *obj) { env_allocator_del(allocator->priv, obj); } diff --git a/src/ocf_io_priv.h b/src/ocf_io_priv.h index 0e219f7..b26d5dd 100644 --- a/src/ocf_io_priv.h +++ b/src/ocf_io_priv.h @@ -1,5 +1,5 @@ /* - * Copyright(c) 2012-2021 Intel Corporation + * Copyright(c) 2012-2022 Intel Corporation * SPDX-License-Identifier: BSD-3-Clause */ @@ -27,6 +27,17 @@ static inline struct ocf_io_internal *ocf_io_get_internal(struct ocf_io* io) return container_of(io, struct ocf_io_internal, io); } +int ocf_io_allocator_default_init(ocf_io_allocator_t allocator, + uint32_t priv_size, const char *name); + +void ocf_io_allocator_default_deinit(ocf_io_allocator_t allocator); + +void *ocf_io_allocator_default_new(ocf_io_allocator_t allocator, + ocf_volume_t volume, ocf_queue_t queue, + uint64_t addr, uint32_t bytes, uint32_t dir); + +void ocf_io_allocator_default_del(ocf_io_allocator_t allocator, void *obj); + int ocf_io_allocator_init(ocf_io_allocator_t allocator, ocf_io_allocator_type_t type, uint32_t priv_size, const char *name); diff --git a/src/ocf_volume.c b/src/ocf_volume.c index b3ed1ea..f335ece 100644 --- a/src/ocf_volume.c +++ b/src/ocf_volume.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2012-2021 Intel Corporation + * Copyright(c) 2012-2022 Intel Corporation * SPDX-License-Identifier: BSD-3-Clause */ @@ -94,57 +94,73 @@ int ocf_volume_init(ocf_volume_t volume, ocf_volume_type_t type, return -OCF_ERR_INVAL; priv_size = type->properties->volume_priv_size; - - volume->opened = false; - volume->type = type; - volume->priv = env_zalloc(priv_size, ENV_MEM_NORMAL); if (!volume->priv) return -OCF_ERR_NO_MEM; + volume->opened = false; + volume->type = type; + + volume->uuid.size = 0; + volume->uuid.data = NULL; + volume->uuid_copy = false; + ocf_refcnt_init(&volume->refcnt); ocf_refcnt_freeze(&volume->refcnt); - if (!uuid) { - volume->uuid.size = 0; - volume->uuid.data = NULL; - volume->uuid_copy = false; + if (!uuid) return 0; - } volume->uuid_copy = uuid_copy; if (uuid_copy) { data = env_vmalloc(uuid->size); - if (!data) - goto err; - - ret = env_memcpy(data, uuid->size, uuid->data, uuid->size); - if (ret) { - env_vfree(data); + if (!data) { + ret = -OCF_ERR_NO_MEM; goto err; } 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; + if (volume->type->properties->ops.on_init) { + ret = volume->type->properties->ops.on_init(volume); + if (ret) + goto err; + } + return 0; err: ocf_refcnt_unfreeze(&volume->refcnt); env_free(volume->priv); - return -OCF_ERR_NO_MEM; + volume->priv = NULL; + 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_deinit(ocf_volume_t volume) { OCF_CHECK_NULL(volume); + if (volume->type && volume->type->properties->ops.on_deinit) + volume->type->properties->ops.on_deinit(volume); + env_free(volume->priv); + volume->priv = NULL; if (volume->uuid_copy && volume->uuid.data) { env_vfree(volume->uuid.data); diff --git a/tests/functional/pyocf/types/cache.py b/tests/functional/pyocf/types/cache.py index 544910f..307c68d 100644 --- a/tests/functional/pyocf/types/cache.py +++ b/tests/functional/pyocf/types/cache.py @@ -63,8 +63,7 @@ class CacheConfig(Structure): class CacheDeviceConfig(Structure): _fields_ = [ - ("_uuid", Uuid), - ("_volume_type", c_uint8), + ("_volume", c_void_p), ("_perform_test", c_bool), ("_volume_params", c_void_p), ] @@ -257,7 +256,7 @@ class Cache: raise OcfError("Failed to detach failover cache device", c.results["error"]) def standby_activate(self, device, open_cores=True): - device_cfg = Cache.generate_device_config(device) + device_cfg = self.generate_device_config(device) activate_cfg = CacheStandbyActivateConfig(_device=device_cfg, _open_cores=open_cores,) @@ -457,14 +456,24 @@ class Cache: if status: raise OcfError("Error adding partition to cache", status) - @staticmethod - def generate_device_config(device, perform_test=True): + def generate_device_config(self, device, perform_test=True): + uuid = Uuid( + _data=cast(create_string_buffer(device.uuid.encode("ascii")), c_char_p), + _size=len(device.uuid) + 1, + ) + volume = c_void_p() + + lib = OcfLib.getInstance() + result = lib.ocf_volume_create( + byref(volume), + self.owner.ocf_volume_type[type(device)], + byref(uuid) + ) + if result != 0: + raise OcfError("Cache volume initialization failed", result) + device_config = CacheDeviceConfig( - _uuid=Uuid( - _data=cast(create_string_buffer(device.uuid.encode("ascii")), c_char_p), - _size=len(device.uuid) + 1, - ), - _volume_type=device.type_id, + _volume=volume, _perform_test=perform_test, _volume_params=None, ) @@ -477,7 +486,7 @@ class Cache: self.device = device self.device_name = device.uuid - device_config = Cache.generate_device_config(device, perform_test=perform_test) + device_config = self.generate_device_config(device, perform_test=perform_test) attach_cfg = CacheAttachConfig( _device=device_config, @@ -505,7 +514,7 @@ class Cache: self.device = device self.device_name = device.uuid - device_config = Cache.generate_device_config(device, perform_test=False) + device_config = self.generate_device_config(device, perform_test=False) attach_cfg = CacheAttachConfig( _device=device_config, @@ -533,7 +542,7 @@ class Cache: self.device = device self.device_name = device.uuid - device_config = Cache.generate_device_config(device, perform_test=perform_test) + device_config = self.generate_device_config(device, perform_test=perform_test) attach_cfg = CacheAttachConfig( _device=device_config, @@ -570,7 +579,7 @@ class Cache: self.device = device self.device_name = device.uuid - device_config = Cache.generate_device_config(device) + device_config = self.generate_device_config(device) attach_cfg = CacheAttachConfig( _device=device_config, @@ -917,3 +926,5 @@ lib.ocf_mngt_add_partition_to_cache.argtypes = [ ] lib.ocf_mngt_cache_io_classes_configure.restype = c_int lib.ocf_mngt_cache_io_classes_configure.argtypes = [c_void_p, c_void_p] +lib.ocf_volume_create.restype = c_int +lib.ocf_volume_create.argtypes = [c_void_p, c_void_p, c_void_p] diff --git a/tests/functional/pyocf/types/ctx.py b/tests/functional/pyocf/types/ctx.py index 20a7883..dc3934f 100644 --- a/tests/functional/pyocf/types/ctx.py +++ b/tests/functional/pyocf/types/ctx.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: BSD-3-Clause # -from ctypes import c_void_p, Structure, c_char_p, cast, pointer, byref, c_int +from ctypes import c_void_p, Structure, c_char_p, cast, pointer, byref, c_int, c_uint8 import weakref from .logger import LoggerOps, Logger @@ -37,6 +37,7 @@ class OcfCtx: self.lib = lib self.volume_types_count = 1 self.volume_types = {} + self.ocf_volume_type = {} self.caches = [] self.cfg = OcfCtxCfg( @@ -77,6 +78,11 @@ class OcfCtx: if result != 0: raise OcfError("Volume type registration failed", result) + self.ocf_volume_type[volume_type] = self.lib.ocf_ctx_get_volume_type( + self.ctx_handle, + volume_type.type_id + ) + self.volume_types_count += 1 def unregister_volume_type(self, vol_type): @@ -86,6 +92,7 @@ class OcfCtx: self.lib.ocf_ctx_unregister_volume_type(self.ctx_handle, vol_type.type_id) del self.volume_types[vol_type.type_id] + del self.ocf_volume_type[vol_type] def cleanup_volume_types(self): for k, vol_type in list(self.volume_types.items()): @@ -108,3 +115,5 @@ class OcfCtx: lib = OcfLib.getInstance() lib.ocf_mngt_cache_get_by_name.argtypes = [c_void_p, c_void_p, c_void_p] lib.ocf_mngt_cache_get_by_name.restype = c_int +lib.ocf_ctx_get_volume_type.argtypes = [c_void_p, c_uint8] +lib.ocf_ctx_get_volume_type.restype = c_void_p diff --git a/tests/functional/pyocf/types/volume.py b/tests/functional/pyocf/types/volume.py index 5289472..f6124b1 100644 --- a/tests/functional/pyocf/types/volume.py +++ b/tests/functional/pyocf/types/volume.py @@ -47,7 +47,9 @@ class VolumeOps(Structure): SUBMIT_METADATA = CFUNCTYPE(None, c_void_p) SUBMIT_DISCARD = CFUNCTYPE(None, c_void_p) SUBMIT_WRITE_ZEROES = CFUNCTYPE(None, c_void_p) - OPEN = CFUNCTYPE(c_int, c_void_p) + ON_INIT = CFUNCTYPE(c_int, c_void_p) + ON_DEINIT = CFUNCTYPE(None, c_void_p) + OPEN = CFUNCTYPE(c_int, c_void_p, c_void_p) CLOSE = CFUNCTYPE(None, c_void_p) GET_MAX_IO_SIZE = CFUNCTYPE(c_uint, c_void_p) GET_LENGTH = CFUNCTYPE(c_uint64, c_void_p) @@ -58,6 +60,8 @@ class VolumeOps(Structure): ("_submit_metadata", SUBMIT_METADATA), ("_submit_discard", SUBMIT_DISCARD), ("_submit_write_zeroes", SUBMIT_WRITE_ZEROES), + ("_on_init", ON_INIT), + ("_on_deinit", ON_DEINIT), ("_open", OPEN), ("_close", CLOSE), ("_get_length", GET_LENGTH), @@ -124,8 +128,16 @@ class Volume: def _submit_write_zeroes(write_zeroes): raise NotImplementedError + @VolumeOps.ON_INIT + def _on_init(ref): + return 0 + + @VolumeOps.ON_DEINIT + def _on_deinit(ref): + return + @VolumeOps.OPEN - def _open(ref): + def _open(ref, params): uuid_ptr = cast(OcfLib.getInstance().ocf_volume_get_uuid(ref), POINTER(Uuid)) uuid = str(uuid_ptr.contents._data, encoding="ascii") try: @@ -161,6 +173,8 @@ class Volume: _close=_close, _get_max_io_size=_get_max_io_size, _get_length=_get_length, + _on_init=_on_init, + _on_deinit=_on_deinit, ) return Volume._ops_[cls] diff --git a/tests/functional/tests/engine/test_io_flags.py b/tests/functional/tests/engine/test_io_flags.py index d8efbe4..91bddd6 100644 --- a/tests/functional/tests/engine/test_io_flags.py +++ b/tests/functional/tests/engine/test_io_flags.py @@ -74,6 +74,8 @@ def test_io_flags(pyocf_ctx, cache_mode): data = bytes(block_size) + pyocf_ctx.register_volume_type(FlagsValVolume) + cache_device = FlagsValVolume(Size.from_MiB(50), flags) core_device = FlagsValVolume(Size.from_MiB(50), flags) diff --git a/tests/functional/tests/flush/test_flush_after_mngmt.py b/tests/functional/tests/flush/test_flush_after_mngmt.py index da80983..6719791 100644 --- a/tests/functional/tests/flush/test_flush_after_mngmt.py +++ b/tests/functional/tests/flush/test_flush_after_mngmt.py @@ -63,6 +63,8 @@ def test_flush_after_mngmt(pyocf_ctx): data = bytes(block_size) + pyocf_ctx.register_volume_type(FlushValVolume) + cache_device = FlushValVolume(Size.from_MiB(50)) core_device = FlushValVolume(Size.from_MiB(50)) diff --git a/tests/functional/tests/management/test_composite_volume.py b/tests/functional/tests/management/test_composite_volume.py new file mode 100644 index 0000000..eae42d3 --- /dev/null +++ b/tests/functional/tests/management/test_composite_volume.py @@ -0,0 +1,276 @@ +# +# Copyright(c) 2022 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# + +import pytest + + +@pytest.mark.skip(reason="not implemented") +def test_create_composite_volume(pyocf_ctx): + """ + title: Create composite volume. + description: | + Check that it is possible to create and destroy composite volume + object. + pass_criteria: + - Composite volume is created without an error. + - Subvolume is added without an error. + steps: + - Create composite volume + - Verify that no error occured + - Add RamVolume as a subvolume + - Verify that no error occured + - Destroy composite volume + requirements: + - composite_volume::creation + - composite_volume::adding_component_volume + """ + pass + + +@pytest.mark.skip(reason="not implemented") +def test_add_subvolumes_of_different_types(pyocf_ctx): + """ + title: Add subvolumes of different types. + description: | + Check that it is possible to add two subvolumes of different types to + composite volume. + pass_criteria: + - Composite volume is created without an error. + - Subvolumes are added without an error. + steps: + - Create composite volume + - Add RamVolume as a subvolume + - Verify that no error occured + - Add ErrorDevice as a subvolume + - Verify that no error occured + - Destroy composite volume + requirements: + - composite_volume::component_volume_types + """ + pass + + +@pytest.mark.skip(reason="not implemented") +def test_add_max_subvolumes(pyocf_ctx): + """ + title: Add maximum number of subvolumes. + description: | + Check that it is possible to add 16 subvolumes to composite volume. + pass_criteria: + - Composite volume is created without an error. + - Subvolumes are added without an error. + steps: + - Create composite volume + - Add 16 RamVolume instances as subvolumes + - Verify that no error occured + - Destroy composite volume + requirements: + - composite_volume::max_composite_volumes + """ + pass + + +@pytest.mark.skip(reason="not implemented") +def test_basic_volume_operations(pyocf_ctx): + """ + title: Perform basic volume operations. + description: | + Check that basic volume operations work on composite volume. + pass_criteria: + - Composite volume is created without an error. + - Subvolume is added without an error. + - Submit io, submit flush and submit discard operations work properly. + steps: + - Create composite volume + - Add mock volume as a subvolume + - Submit io to composite volume and check if it was propagated + - Submit flush to composite volume and check if it was propagated + - Submit discard to composite volume and check if it was propagated + - Destroy composite volume + requirements: + - composite_volume::volume_api + - composite_volume::io_request_passing + """ + pass + + +@pytest.mark.skip(reason="not implemented") +def test_io_propagation_basic(pyocf_ctx): + """ + title: Perform volume operations with multiple subvolumes. + description: | + Check that io operations are propagated properly to subvolumes. + pass_criteria: + - Composite volume is created without an error. + - Subvolumes are added without an error. + - Operations are propagated properly. + steps: + - Create composite volume + - Add 16 mock volumes as subvolumes + - Submit io to each subvolume address range + - Check if requests were propagated properly + - Submit flush to each subvolume address range + - Check if requests were propagated properly + - Submit discard to each subvolume address range + - Check if requests were propagated properly + - Destroy composite volume + requirements: + - composite_volume::volume_api + - composite_volume::io_request_passing + """ + pass + + +@pytest.mark.skip(reason="not implemented") +def test_io_propagation_cross_boundary(pyocf_ctx): + """ + title: Perform cross-subvolume operations. + description: | + Check that cross-subvolume operations are propagated properly. + pass_criteria: + - Composite volume is created without an error. + - Subvolumes are added without an error. + - Operations are propagated properly. + steps: + - Create composite volume + - Add 16 mock volumes as subvolumes + - Submit io that cross address range boundary between each subvolume + - Check if requests were propagated properly + - Submit flush that cross address range boundary between each subvolume + - Check if requests were propagated properly + - Submit discard that cross address range boundary between each subvolume + - Check if requests were propagated properly + - Destroy composite volume + requirements: + - composite_volume::io_request_passing + """ + pass + + +@pytest.mark.skip(reason="not implemented") +def test_io_propagation_multiple_subvolumes(pyocf_ctx): + """ + title: Perform multi-subvolume operations. + description: | + Check that multi-subvolume operations are propagated properly. + pass_criteria: + - Composite volume is created without an error. + - Subvolumes are added without an error. + - Operations are propagated properly. + steps: + - Create composite volume + - Add 16 mock volumes as subvolumes + - Submit series of ios that touch from 2 to 16 subvolumes + - Check if requests were propated properly + - Submit series of flushes that touch from 2 to 16 subvolumes + - Check if requests were propagated properly + - Submit series of discardss that touch from 2 to 16 subvolumes + - Check if requests were propagated properly + - Destroy composite volume + requirements: + - composite_volume::io_request_passing + """ + pass + + +@pytest.mark.skip(reason="not implemented") +def test_io_completion(pyocf_ctx): + """ + title: Composite volume completion order. + description: | + Check that composite volume waits for completions from all subvolumes. + pass_criteria: + - Composite volume is created without an error. + - Subvolumes are added without an error. + - Operations are completed only after all subvolumes operations complete. + steps: + - Create composite volume + - Add 16 mock volumes as subvolumes + - Submit series of ios that touch from 2 to 16 subvolumes + - Check if completions are called only after all subvolumes completed + - Submit series of flushes that touch from 2 to 16 subvolumes + - Check if completions are called only after all subvolumes completed + - Submit series of discardss that touch from 2 to 16 subvolumes + - Check if completions are called only after all subvolumes completed + - Destroy composite volume + requirements: + - composite_volume::io_request_completion + """ + pass + + +@pytest.mark.skip(reason="not implemented") +def test_io_completion(pyocf_ctx): + """ + title: Composite volume error propagation. + description: | + Check that composite volume propagates errors from subvolumes. + pass_criteria: + - Composite volume is created without an error. + - Subvolumes are added without an error. + - Errors from subvolumes are propagated to composite volume. + steps: + - Create composite volume + - Add 16 ErrorDevice instances as subvolumes + - Before each request arm one of ErrorDevices touched by this request + - Submit series of ios that touch from 2 to 16 subvolumes + - Check if errors were propagated properly + - Submit series of flushes that touch from 2 to 16 subvolumes + - Check if errors were propagated properly + - Submit series of discardss that touch from 2 to 16 subvolumes + - Check if errors were propagated properly + - Destroy composite volume + requirements: + - composite_volume::io_error_handling + """ + pass + + +@pytest.mark.skip(reason="not implemented") +def test_attach(pyocf_ctx): + """ + title: Attach composite volume. + description: | + Check that it is possible to attach composite volume + pass_criteria: + - Composite volume is created without an error. + - Subvolumes are added without an error. + - Cache attach succeeds. + steps: + - Create composite volume + - Add 16 RamVolume instances as subvolumes. + - Start cache and attach it using composite volume instance. + - Verify that cache was attached properly. + - Stop the cache. + - Verify that cache was stopped. + requirements: + - composite_volume::cache_attach_load + """ + pass + + +@pytest.mark.skip(reason="not implemented") +def test_load(pyocf_ctx): + """ + title: Load composite volume. + description: | + Check that it is possible to attach composite volume + pass_criteria: + - Composite volume is created without an error. + - Subvolumes are added without an error. + - Cache load succeeds. + steps: + - Create composite volume + - Add 16 RamVolume instances as subvolumes. + - Start cache and attach it using composite volume instance. + - Stop the cache. + - Start cache and load it using composite volume instance. + - Verify that cache was loaded properly. + - Stop the cache. + - Verify that cache was stopped. + requirements: + - composite_volume::cache_attach_load + """ + pass