diff options
Diffstat (limited to 'src/spdk/ocf/example/simple')
-rw-r--r-- | src/spdk/ocf/example/simple/Makefile | 37 | ||||
-rw-r--r-- | src/spdk/ocf/example/simple/src/ctx.c | 303 | ||||
-rw-r--r-- | src/spdk/ocf/example/simple/src/ctx.h | 19 | ||||
-rw-r--r-- | src/spdk/ocf/example/simple/src/data.h | 14 | ||||
-rw-r--r-- | src/spdk/ocf/example/simple/src/main.c | 380 | ||||
-rw-r--r-- | src/spdk/ocf/example/simple/src/volume.c | 168 | ||||
-rw-r--r-- | src/spdk/ocf/example/simple/src/volume.h | 27 |
7 files changed, 948 insertions, 0 deletions
diff --git a/src/spdk/ocf/example/simple/Makefile b/src/spdk/ocf/example/simple/Makefile new file mode 100644 index 000000000..f19cae0c5 --- /dev/null +++ b/src/spdk/ocf/example/simple/Makefile @@ -0,0 +1,37 @@ +# +# Copyright(c) 2019 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause-Clear +# + +OCFDIR=../../ +SRCDIR=src/ +INCDIR=include/ + +SRC=$(shell find ${SRCDIR} -name \*.c) +OBJS = $(patsubst %.c, %.o, $(SRC)) +PROGRAM=simple + +CC = gcc +CFLAGS = -g -Wall -I${INCDIR} -I${SRCDIR}/ocf/env/ +LDFLAGS = -lm -lz -pthread + +all: sync + $(MAKE) $(PROGRAM) + +$(PROGRAM): $(OBJS) + $(CC) -o $@ $^ $(LDFLAGS) + +sync: + @$(MAKE) -C ${OCFDIR} inc O=$(PWD) + @$(MAKE) -C ${OCFDIR} src O=$(PWD) + @$(MAKE) -C ${OCFDIR} env O=$(PWD) OCF_ENV=posix + +clean: + @rm -rf $(PROGRAM) $(OBJS) + +distclean: + @rm -rf $(PROGRAM) $(OBJS) + @rm -rf src/ocf + @rm -rf include/ocf + +.PHONY: all clean diff --git a/src/spdk/ocf/example/simple/src/ctx.c b/src/spdk/ocf/example/simple/src/ctx.c new file mode 100644 index 000000000..420ea63b0 --- /dev/null +++ b/src/spdk/ocf/example/simple/src/ctx.c @@ -0,0 +1,303 @@ +/* + * Copyright(c) 2019 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +#include <execinfo.h> +#include <ocf/ocf.h> +#include "ocf_env.h" +#include "data.h" +#include "volume.h" +#include "ctx.h" + +#define PAGE_SIZE 4096 + +/* + * Allocate structure representing data for io operations. + */ +ctx_data_t *ctx_data_alloc(uint32_t pages) +{ + struct volume_data *data; + + data = malloc(sizeof(*data)); + data->ptr = malloc(pages * PAGE_SIZE); + data->offset = 0; + + return data; +} + +/* + * Free data structure. + */ +void ctx_data_free(ctx_data_t *ctx_data) +{ + struct volume_data *data = ctx_data; + + if (!data) + return; + + free(data->ptr); + free(data); +} + +/* + * This function is supposed to set protection of data pages against swapping. + * Can be non-implemented if not needed. + */ +static int ctx_data_mlock(ctx_data_t *ctx_data) +{ + return 0; +} + +/* + * Stop protecting data pages against swapping. + */ +static void ctx_data_munlock(ctx_data_t *ctx_data) +{ +} + +/* + * Read data into flat memory buffer. + */ +static uint32_t ctx_data_read(void *dst, ctx_data_t *src, uint32_t size) +{ + struct volume_data *data = src; + + memcpy(dst, data->ptr + data->offset, size); + + return size; +} + +/* + * Write data from flat memory buffer. + */ +static uint32_t ctx_data_write(ctx_data_t *dst, const void *src, uint32_t size) +{ + struct volume_data *data = dst; + + memcpy(data->ptr + data->offset, src, size); + + return size; +} + +/* + * Fill data with zeros. + */ +static uint32_t ctx_data_zero(ctx_data_t *dst, uint32_t size) +{ + struct volume_data *data = dst; + + memset(data->ptr + data->offset, 0, size); + + return size; +} + +/* + * Perform seek operation on data. + */ +static uint32_t ctx_data_seek(ctx_data_t *dst, ctx_data_seek_t seek, + uint32_t offset) +{ + struct volume_data *data = dst; + + switch (seek) { + case ctx_data_seek_begin: + data->offset = offset; + break; + case ctx_data_seek_current: + data->offset += offset; + break; + } + + return offset; +} + +/* + * Copy data from one structure to another. + */ +static uint64_t ctx_data_copy(ctx_data_t *dst, ctx_data_t *src, + uint64_t to, uint64_t from, uint64_t bytes) +{ + struct volume_data *data_dst = dst; + struct volume_data *data_src = src; + + memcpy(data_dst->ptr + to, data_src->ptr + from, bytes); + + return bytes; +} + +/* + * Perform secure erase of data (e.g. fill pages with zeros). + * Can be left non-implemented if not needed. + */ +static void ctx_data_secure_erase(ctx_data_t *ctx_data) +{ +} + +/* + * Initialize cleaner thread. Cleaner thread is left non-implemented, + * to keep this example as simple as possible. + */ +static int ctx_cleaner_init(ocf_cleaner_t c) +{ + return 0; +} + +/* + * Kick cleaner thread. Cleaner thread is left non-implemented, + * to keep this example as simple as possible. + */ +static void ctx_cleaner_kick(ocf_cleaner_t c) +{ +} + +/* + * Stop cleaner thread. Cleaner thread is left non-implemented, to keep + * this example as simple as possible. + */ +static void ctx_cleaner_stop(ocf_cleaner_t c) +{ +} + +/* + * Initialize metadata updater thread. Metadata updater thread is left + * non-implemented to keep this example as simple as possible. + */ +static int ctx_metadata_updater_init(ocf_metadata_updater_t mu) +{ + return 0; +} + +/* + * Kick metadata updater thread. Metadata updater thread is left + * non-implemented to keep this example as simple as possible. + */ +static void ctx_metadata_updater_kick(ocf_metadata_updater_t mu) +{ + ocf_metadata_updater_run(mu); +} + +/* + * Stop metadata updater thread. Metadata updater thread is left + * non-implemented to keep this example as simple as possible. + */ +static void ctx_metadata_updater_stop(ocf_metadata_updater_t mu) +{ +} + +/* + * Function prividing interface for printing to log used by OCF internals. + * It can handle differently messages at varous log levels. + */ +static int ctx_logger_print(ocf_logger_t logger, ocf_logger_lvl_t lvl, + const char *fmt, va_list args) +{ + FILE *lfile = stdout; + + if (lvl > log_info) + return 0; + + if (lvl <= log_warn) + lfile = stderr; + + return vfprintf(lfile, fmt, args); +} + +#define CTX_LOG_TRACE_DEPTH 16 + +/* + * Function prividing interface for printing current stack. Used for debugging, + * and for providing additional information in log in case of errors. + */ +static int ctx_logger_dump_stack(ocf_logger_t logger) +{ + void *trace[CTX_LOG_TRACE_DEPTH]; + char **messages = NULL; + int i, size; + + size = backtrace(trace, CTX_LOG_TRACE_DEPTH); + messages = backtrace_symbols(trace, size); + printf("[stack trace]>>>\n"); + for (i = 0; i < size; ++i) + printf("%s\n", messages[i]); + printf("<<<[stack trace]\n"); + free(messages); + + return 0; +} + +/* + * This structure describes context config, containing simple context info + * and pointers to ops callbacks. Ops are splitted into few categories: + * - data ops, providing context specific data handing interface, + * - cleaner ops, providing interface to start and stop clener thread, + * - metadata updater ops, providing interface for starting, stoping + * and kicking metadata updater thread. + * - logger ops, providing interface for text message logging + */ +static const struct ocf_ctx_config ctx_cfg = { + .name = "OCF Example", + .ops = { + .data = { + .alloc = ctx_data_alloc, + .free = ctx_data_free, + .mlock = ctx_data_mlock, + .munlock = ctx_data_munlock, + .read = ctx_data_read, + .write = ctx_data_write, + .zero = ctx_data_zero, + .seek = ctx_data_seek, + .copy = ctx_data_copy, + .secure_erase = ctx_data_secure_erase, + }, + + .cleaner = { + .init = ctx_cleaner_init, + .kick = ctx_cleaner_kick, + .stop = ctx_cleaner_stop, + }, + + .metadata_updater = { + .init = ctx_metadata_updater_init, + .kick = ctx_metadata_updater_kick, + .stop = ctx_metadata_updater_stop, + }, + + .logger = { + .print = ctx_logger_print, + .dump_stack = ctx_logger_dump_stack, + }, + }, +}; + + +/* + * Function initializing context. Prepares context, sets logger and + * registers volume type. + */ +int ctx_init(ocf_ctx_t *ctx) +{ + int ret; + + ret = ocf_ctx_create(ctx, &ctx_cfg); + if (ret) + return ret; + + ret = volume_init(*ctx); + if (ret) { + ocf_ctx_put(*ctx); + return ret; + } + + return 0; +} + +/* + * Function cleaning up context. Unregisters volume type and + * deinitializes context. + */ +void ctx_cleanup(ocf_ctx_t ctx) +{ + volume_cleanup(ctx); + ocf_ctx_put(ctx); +} diff --git a/src/spdk/ocf/example/simple/src/ctx.h b/src/spdk/ocf/example/simple/src/ctx.h new file mode 100644 index 000000000..6f0360679 --- /dev/null +++ b/src/spdk/ocf/example/simple/src/ctx.h @@ -0,0 +1,19 @@ +/* + * Copyright(c) 2019 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +#ifndef __CTX_H__ +#define __CTX_H__ + +#include <ocf/ocf.h> + +#define VOL_TYPE 1 + +ctx_data_t *ctx_data_alloc(uint32_t pages); +void ctx_data_free(ctx_data_t *ctx_data); + +int ctx_init(ocf_ctx_t *ocf_ctx); +void ctx_cleanup(ocf_ctx_t ctx); + +#endif diff --git a/src/spdk/ocf/example/simple/src/data.h b/src/spdk/ocf/example/simple/src/data.h new file mode 100644 index 000000000..bbef4ec66 --- /dev/null +++ b/src/spdk/ocf/example/simple/src/data.h @@ -0,0 +1,14 @@ +/* + * Copyright(c) 2019 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +#ifndef __DATA_H__ +#define __DATA_H__ + +struct volume_data { + void *ptr; + int offset; +}; + +#endif diff --git a/src/spdk/ocf/example/simple/src/main.c b/src/spdk/ocf/example/simple/src/main.c new file mode 100644 index 000000000..ef059fdd7 --- /dev/null +++ b/src/spdk/ocf/example/simple/src/main.c @@ -0,0 +1,380 @@ +/* + * Copyright(c) 2019 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ocf/ocf.h> +#include "data.h" +#include "ctx.h" + +/* + * Cache private data. Used to share information between async contexts. + */ +struct cache_priv { + ocf_queue_t mngt_queue; + ocf_queue_t io_queue; +}; + +/* + * Helper function for error handling. + */ +void error(char *msg) +{ + printf("ERROR: %s", msg); + exit(1); +} + +/* + * Trigger queue asynchronously. Made synchronous for simplicity. + * Notice that it makes all asynchronous calls synchronous, because + * asynchronism in OCF is achieved mostly by using queues. + */ +static inline void queue_kick_async(ocf_queue_t q) +{ + ocf_queue_run(q); +} + +/* + * Trigger queue synchronously. May be implemented as asynchronous as well, + * but in some environments kicking queue synchronously may reduce latency, + * so to take advantage of such situations OCF call synchronous variant of + * queue kick callback where possible. + */ +static void queue_kick_sync(ocf_queue_t q) +{ + ocf_queue_run(q); +} + +/* + * Stop queue thread. To keep this example simple we handle queues + * synchronously, thus it's left non-implemented. + */ +static void queue_stop(ocf_queue_t q) +{ +} + +/* + * Queue ops providing interface for running queue thread in both synchronous + * and asynchronous way. The stop() operation in called just before queue is + * being destroyed. + */ +const struct ocf_queue_ops queue_ops = { + .kick_sync = queue_kick_sync, + .kick = queue_kick_async, + .stop = queue_stop, +}; + +/* + * Simple completion context. As lots of OCF API functions work asynchronously + * and call completion callback when job is done, we need some structure to + * share program state with completion callback. In this case we have single + * variable pointer to propagate error code. + */ +struct simple_context { + int *error; +}; + +/* + * Basic asynchronous completion callback. Just propagate error code. + */ +static void simple_complete(ocf_cache_t cache, void *priv, int error) +{ + struct simple_context *context= priv; + + *context->error = error; +} + +/* + * Function starting cache and attaching cache device. + */ +int initialize_cache(ocf_ctx_t ctx, ocf_cache_t *cache) +{ + struct ocf_mngt_cache_config cache_cfg = { .name = "cache1" }; + struct ocf_mngt_cache_device_config device_cfg = { }; + struct cache_priv *cache_priv; + struct simple_context context; + int ret; + + /* + * Asynchronous callbacks will assign error code to ret. That + * way we have always the same variable holding last error code. + */ + context.error = &ret; + + /* Cache configuration */ + ocf_mngt_cache_config_set_default(&cache_cfg); + cache_cfg.metadata_volatile = true; + + /* Cache deivce (volume) configuration */ + ocf_mngt_cache_device_config_set_default(&device_cfg); + device_cfg.volume_type = VOL_TYPE; + ret = ocf_uuid_set_str(&device_cfg.uuid, "cache"); + if (ret) + return ret; + + /* + * 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; + + /* Start cache */ + ret = ocf_mngt_cache_start(ctx, cache, &cache_cfg); + if (ret) + goto err_priv; + + /* Assing cache priv structure to cache. */ + ocf_cache_set_priv(*cache, cache_priv); + + /* + * Create management queue. It will be used for performing various + * asynchronous management operations, such as attaching cache volume + * or adding core object. + */ + ret = ocf_queue_create(*cache, &cache_priv->mngt_queue, &queue_ops); + if (ret) { + ocf_mngt_cache_stop(*cache, simple_complete, &context); + goto err_priv; + } + + /* + * Assign management queue to cache. This has to be done before any + * other management operation. Management queue is treated specially, + * and it may not be used for submitting IO requests. It also will not + * be put on the cache stop - we have to put it manually at the end. + */ + ocf_mngt_cache_set_mngt_queue(*cache, cache_priv->mngt_queue); + + /* Create queue which will be used for IO submission. */ + ret = ocf_queue_create(*cache, &cache_priv->io_queue, &queue_ops); + if (ret) + goto err_cache; + + /* Attach volume to cache */ + ocf_mngt_cache_attach(*cache, &device_cfg, simple_complete, &context); + if (ret) + goto err_cache; + + return 0; + +err_cache: + ocf_mngt_cache_stop(*cache, simple_complete, &context); + ocf_queue_put(cache_priv->mngt_queue); +err_priv: + free(cache_priv); + return ret; +} + +/* + * Add core completion callback context. We need this to propagate error code + * and handle to freshly initialized core object. + */ +struct add_core_context { + ocf_core_t *core; + int *error; +}; + +/* Add core complete callback. Just rewrite args to context structure. */ +static void add_core_complete(ocf_cache_t cache, ocf_core_t core, + void *priv, int error) +{ + struct add_core_context *context = priv; + + *context->core = core; + *context->error = error; +} + +/* + * Function adding cache to core. + */ +int initialize_core(ocf_cache_t cache, ocf_core_t *core) +{ + struct ocf_mngt_core_config core_cfg = { }; + struct add_core_context context; + int ret; + + /* + * Asynchronous callback will assign core handle to core, + * and to error code to ret. + */ + context.core = core; + context.error = &ret; + + /* Core configuration */ + ocf_mngt_core_config_set_default(&core_cfg); + strcpy(core_cfg.name, "core1"); + core_cfg.volume_type = VOL_TYPE; + ret = ocf_uuid_set_str(&core_cfg.uuid, "core"); + if (ret) + return ret; + + /* Add core to cache */ + ocf_mngt_cache_add_core(cache, &core_cfg, add_core_complete, &context); + + return ret; +} + +/* + * Callback function called when write completes. + */ +void complete_write(struct ocf_io *io, int error) +{ + struct volume_data *data = ocf_io_get_data(io); + + printf("WRITE COMPLETE: (error: %d)\n", error); + + /* Free data buffer and io */ + ctx_data_free(data); + ocf_io_put(io); +} + +/* + * Callback function called when read completes. + */ +void complete_read(struct ocf_io *io, int error) +{ + struct volume_data *data = ocf_io_get_data(io); + + printf("WRITE COMPLETE (error: %d)\n", error); + printf("DATA: \"%s\"\n", (char *)data->ptr); + + /* Free data buffer and io */ + ctx_data_free(data); + ocf_io_put(io); +} + +/* + * Wrapper function for io submition. + */ +int submit_io(ocf_core_t core, struct volume_data *data, + uint64_t addr, uint64_t len, int dir, ocf_end_io_t cmpl) +{ + ocf_cache_t cache = ocf_core_get_cache(core); + struct cache_priv *cache_priv = ocf_cache_get_priv(cache); + struct ocf_io *io; + + /* Allocate new io */ + io = ocf_core_new_io(core, cache_priv->io_queue, addr, len, dir, 0, 0); + if (!io) + return -ENOMEM; + + /* Assign data to io */ + ocf_io_set_data(io, data, 0); + /* Setup completion function */ + ocf_io_set_cmpl(io, NULL, NULL, cmpl); + /* Submit io */ + ocf_core_submit_io(io); + + return 0; +} + +/* + * This function simulates actual business logic. + * + * It performs following steps: + * 1. Allocate data buffer for write and write it with example data. + * 2. Allocate new io, configure it for write, setup completion callback + * and perform write to the core. + * 3. Wait for write io completion (write is handled synchronosly, so no + * actual wait is needed, but in real life we would need to use some + * synchronization to be sure, that completion function has been already + * called). Alternatively we could issue read io from write completion + * callback. + * 4. Allocate data buffer for read. + * 5. Allocate new io, configure it for read, setup completion callback + * and perform read from the core, from the same address where data + * was previously written. + * 6. Print example data in read completion callback. + * + * Data buffers and ios are freed in completion callbacks, so there is no + * need to handle freeing in this function. + */ +void perform_workload(ocf_core_t core) +{ + struct volume_data *data1, *data2; + + /* Allocate data buffer and fill it with example data */ + data1 = ctx_data_alloc(1); + if (!data1) + error("Unable to allocate data1\n"); + strcpy(data1->ptr, "This is some test data"); + /* Prepare and submit write IO to the core */ + submit_io(core, data1, 0, 512, OCF_WRITE, complete_write); + /* After write completes, complete_write() callback will be called. */ + + /* + * Here we would need to wait until write completes to be sure, that + * performing read we retrive written data. + */ + + /* Allocate data buffer for read */ + data2 = ctx_data_alloc(1); + if (!data2) + error("Unable to allocate data2\n"); + /* Prepare and submit read IO to the core */ + submit_io(core, data2, 0, 512, OCF_READ, complete_read); + /* After read completes, complete_read() callback will be called, + * where we print our example data to stdout. + */ +} + +static void remove_core_complete(void *priv, int error) +{ + struct simple_context *context = priv; + + *context->error = error; +} + +int main(int argc, char *argv[]) +{ + struct cache_priv *cache_priv; + struct simple_context context; + ocf_ctx_t ctx; + ocf_cache_t cache1; + ocf_core_t core1; + int ret; + + context.error = &ret; + + /* Initialize OCF context */ + if (ctx_init(&ctx)) + error("Unable to initialize context\n"); + + /* Start cache */ + if (initialize_cache(ctx, &cache1)) + error("Unable to start cache\n"); + + /* Add core */ + if (initialize_core(cache1, &core1)) + error("Unable to add core\n"); + + /* Do some actual io operations */ + perform_workload(core1); + + /* Remove core from cache */ + ocf_mngt_cache_remove_core(core1, remove_core_complete, &context); + if (ret) + error("Unable to remove core\n"); + + /* Stop cache */ + ocf_mngt_cache_stop(cache1, simple_complete, &context); + if (ret) + error("Unable to stop cache\n"); + + cache_priv = ocf_cache_get_priv(cache1); + + /* Put the management queue */ + ocf_queue_put(cache_priv->mngt_queue); + + free(cache_priv); + + /* Deinitialize context */ + ctx_cleanup(ctx); + + return 0; +} diff --git a/src/spdk/ocf/example/simple/src/volume.c b/src/spdk/ocf/example/simple/src/volume.c new file mode 100644 index 000000000..1aae692bb --- /dev/null +++ b/src/spdk/ocf/example/simple/src/volume.c @@ -0,0 +1,168 @@ +/* + * Copyright(c) 2019 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +#include <ocf/ocf.h> +#include "volume.h" +#include "data.h" +#include "ctx.h" + +#define VOL_SIZE 200*1024*1024 + +/* + * In open() function we store uuid data as volume name (for debug messages) + * and allocate 200 MiB of memory to simulate backend storage device. + */ +static int volume_open(ocf_volume_t volume, void *volume_params) +{ + const struct ocf_volume_uuid *uuid = ocf_volume_get_uuid(volume); + struct myvolume *myvolume = ocf_volume_get_priv(volume); + + myvolume->name = ocf_uuid_to_str(uuid); + myvolume->mem = malloc(VOL_SIZE); + + printf("VOL OPEN: (name: %s)\n", myvolume->name); + + return 0; +} + +/* + * In close() function we just free memory allocated in open(). + */ +static void volume_close(ocf_volume_t volume) +{ + struct myvolume *myvolume = ocf_volume_get_priv(volume); + + printf("VOL CLOSE: (name: %s)\n", myvolume->name); + free(myvolume->mem); +} + +/* + * In submit_io() function we simulate read or write to backend storage device + * by doing memcpy() to or from previously allocated memory buffer. + */ +static void volume_submit_io(struct ocf_io *io) +{ + struct volume_data *data; + struct myvolume *myvolume; + + data = ocf_io_get_data(io); + myvolume = ocf_volume_get_priv(ocf_io_get_volume(io)); + + if (io->dir == OCF_WRITE) { + memcpy(myvolume->mem + io->addr, + data->ptr + data->offset, io->bytes); + } else { + memcpy(data->ptr + data->offset, + myvolume->mem + io->addr, io->bytes); + } + + printf("VOL: (name: %s), IO: (dir: %s, addr: %ld, bytes: %d)\n", + myvolume->name, io->dir == OCF_READ ? "read" : "write", + io->addr, io->bytes); + + io->end(io, 0); +} + +/* + * We don't need to implement submit_flush(). Just complete io with success. + */ +static void volume_submit_flush(struct ocf_io *io) +{ + io->end(io, 0); +} + +/* + * We don't need to implement submit_discard(). Just complete io with success. + */ +static void volume_submit_discard(struct ocf_io *io) +{ + io->end(io, 0); +} + +/* + * Let's set maximum io size to 128 KiB. + */ +static unsigned int volume_get_max_io_size(ocf_volume_t volume) +{ + return 128 * 1024; +} + +/* + * Return volume size. + */ +static uint64_t volume_get_length(ocf_volume_t volume) +{ + return VOL_SIZE; +} + +/* + * In set_data() we just assing data and offset to io. + */ +static int myvolume_io_set_data(struct ocf_io *io, ctx_data_t *data, + uint32_t offset) +{ + struct myvolume_io *myvolume_io = ocf_io_get_priv(io); + + myvolume_io->data = data; + myvolume_io->offset = offset; + + return 0; +} + +/* + * In get_data() return data stored in io. + */ +static ctx_data_t *myvolume_io_get_data(struct ocf_io *io) +{ + struct myvolume_io *myvolume_io = ocf_io_get_priv(io); + + return myvolume_io->data; +} + +/* + * This structure contains volume properties. It describes volume + * type, which can be later instantiated as backend storage for cache + * or core. + */ +const struct ocf_volume_properties volume_properties = { + .name = "Example volume", + .io_priv_size = sizeof(struct myvolume_io), + .volume_priv_size = sizeof(struct myvolume), + .caps = { + .atomic_writes = 0, + }, + .ops = { + .open = volume_open, + .close = volume_close, + .submit_io = volume_submit_io, + .submit_flush = volume_submit_flush, + .submit_discard = volume_submit_discard, + .get_max_io_size = volume_get_max_io_size, + .get_length = volume_get_length, + }, + .io_ops = { + .set_data = myvolume_io_set_data, + .get_data = myvolume_io_get_data, + }, +}; + +/* + * This function registers volume type in OCF context. + * It should be called just after context initialization. + */ +int volume_init(ocf_ctx_t ocf_ctx) +{ + return ocf_ctx_register_volume_type(ocf_ctx, VOL_TYPE, + &volume_properties); +} + +/* + * This function unregisters volume type in OCF context. + * It should be called just before context cleanup. + */ +void volume_cleanup(ocf_ctx_t ocf_ctx) +{ + ocf_ctx_unregister_volume_type(ocf_ctx, VOL_TYPE); +} diff --git a/src/spdk/ocf/example/simple/src/volume.h b/src/spdk/ocf/example/simple/src/volume.h new file mode 100644 index 000000000..83314b66b --- /dev/null +++ b/src/spdk/ocf/example/simple/src/volume.h @@ -0,0 +1,27 @@ +/* + * Copyright(c) 2019 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +#ifndef __VOLUME_H__ +#define __VOLUME_H__ + +#include <ocf/ocf.h> +#include "ocf_env.h" +#include "ctx.h" +#include "data.h" + +struct myvolume_io { + struct volume_data *data; + uint32_t offset; +}; + +struct myvolume { + uint8_t *mem; + const char *name; +}; + +int volume_init(ocf_ctx_t ocf_ctx); +void volume_cleanup(ocf_ctx_t ocf_ctx); + +#endif |