Merge pull request #702 from robertbaldyga/v22.6-composite-volume
Introduce composite volume
This commit is contained in:
commit
b053f7925a
172
doc/requirements/composite_volume
Normal file
172
doc/requirements/composite_volume
Normal file
@ -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.
|
@ -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;
|
||||
|
@ -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"
|
||||
|
55
inc/ocf_composite_volume.h
Normal file
55
inc/ocf_composite_volume.h
Normal file
@ -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__ */
|
@ -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
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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;
|
||||
|
387
src/ocf_composite_volume.c
Normal file
387
src/ocf_composite_volume.c
Normal file
@ -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;
|
||||
}
|
13
src/ocf_composite_volume_priv.h
Normal file
13
src/ocf_composite_volume_priv.h
Normal file
@ -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__ */
|
@ -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:
|
||||
|
@ -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
|
||||
|
10
src/ocf_io.c
10
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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
ocf_refcnt_init(&volume->refcnt);
|
||||
ocf_refcnt_freeze(&volume->refcnt);
|
||||
volume->opened = false;
|
||||
volume->type = type;
|
||||
|
||||
if (!uuid) {
|
||||
volume->uuid.size = 0;
|
||||
volume->uuid.data = NULL;
|
||||
volume->uuid_copy = false;
|
||||
|
||||
ocf_refcnt_init(&volume->refcnt);
|
||||
ocf_refcnt_freeze(&volume->refcnt);
|
||||
|
||||
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);
|
||||
|
@ -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):
|
||||
device_config = CacheDeviceConfig(
|
||||
_uuid=Uuid(
|
||||
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_type=device.type_id,
|
||||
)
|
||||
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(
|
||||
_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]
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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))
|
||||
|
||||
|
276
tests/functional/tests/management/test_composite_volume.py
Normal file
276
tests/functional/tests/management/test_composite_volume.py
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user