summaryrefslogtreecommitdiffstats
path: root/src/spdk/module/bdev/ocf
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdk/module/bdev/ocf')
-rw-r--r--src/spdk/module/bdev/ocf/Makefile52
-rw-r--r--src/spdk/module/bdev/ocf/ctx.c565
-rw-r--r--src/spdk/module/bdev/ocf/ctx.h65
-rw-r--r--src/spdk/module/bdev/ocf/data.c122
-rw-r--r--src/spdk/module/bdev/ocf/data.h57
-rw-r--r--src/spdk/module/bdev/ocf/stats.c109
-rw-r--r--src/spdk/module/bdev/ocf/stats.h51
-rw-r--r--src/spdk/module/bdev/ocf/utils.c136
-rw-r--r--src/spdk/module/bdev/ocf/utils.h67
-rw-r--r--src/spdk/module/bdev/ocf/vbdev_ocf.c1775
-rw-r--r--src/spdk/module/bdev/ocf/vbdev_ocf.h210
-rw-r--r--src/spdk/module/bdev/ocf/vbdev_ocf_rpc.c362
-rw-r--r--src/spdk/module/bdev/ocf/volume.c441
-rw-r--r--src/spdk/module/bdev/ocf/volume.h63
14 files changed, 4075 insertions, 0 deletions
diff --git a/src/spdk/module/bdev/ocf/Makefile b/src/spdk/module/bdev/ocf/Makefile
new file mode 100644
index 000000000..b931de106
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/Makefile
@@ -0,0 +1,52 @@
+#
+# BSD LICENSE
+#
+# Copyright (c) Intel Corporation.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../..)
+
+include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
+
+SO_VER := 2
+SO_MINOR := 0
+
+CFLAGS += $(ENV_CFLAGS) -I$(SPDK_ROOT_DIR)/lib/env_ocf -I$(SPDK_ROOT_DIR)/lib/env_ocf/include
+C_SRCS = $(shell ls *.c)
+
+LIBNAME := bdev_ocf
+
+SPDK_MAP_FILE = $(SPDK_ROOT_DIR)/mk/spdk_blank.map
+
+include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk
+
+OCF_ENV := $(call spdk_lib_list_to_static_libs,ocfenv)
+
+$(LIB) : $(OCF_ENV)
diff --git a/src/spdk/module/bdev/ocf/ctx.c b/src/spdk/module/bdev/ocf/ctx.c
new file mode 100644
index 000000000..5bf4c8fee
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/ctx.c
@@ -0,0 +1,565 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <ocf/ocf.h>
+#include <execinfo.h>
+
+#include "spdk/env.h"
+#include "spdk_internal/log.h"
+
+#include "ctx.h"
+#include "ocf_env.h"
+#include "data.h"
+
+ocf_ctx_t vbdev_ocf_ctx;
+
+static ctx_data_t *
+vbdev_ocf_ctx_data_alloc(uint32_t pages)
+{
+ struct bdev_ocf_data *data;
+ void *buf;
+ uint32_t sz;
+
+ data = vbdev_ocf_data_alloc(1);
+
+ sz = pages * PAGE_SIZE;
+ buf = spdk_malloc(sz, PAGE_SIZE, NULL,
+ SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ vbdev_ocf_iovs_add(data, buf, sz);
+
+ data->size = sz;
+
+ return data;
+}
+
+static void
+vbdev_ocf_ctx_data_free(ctx_data_t *ctx_data)
+{
+ struct bdev_ocf_data *data = ctx_data;
+ int i;
+
+ if (!data) {
+ return;
+ }
+
+ for (i = 0; i < data->iovcnt; i++) {
+ spdk_free(data->iovs[i].iov_base);
+ }
+
+ vbdev_ocf_data_free(data);
+}
+
+static int
+vbdev_ocf_ctx_data_mlock(ctx_data_t *ctx_data)
+{
+ /* TODO [mlock]: add mlock option */
+ return 0;
+}
+
+static void
+vbdev_ocf_ctx_data_munlock(ctx_data_t *ctx_data)
+{
+ /* TODO [mlock]: add mlock option */
+}
+
+static size_t
+iovec_flatten(struct iovec *iov, size_t iovcnt, void *buf, size_t size, size_t offset)
+{
+ size_t i, len, done = 0;
+
+ for (i = 0; i < iovcnt; i++) {
+ if (offset >= iov[i].iov_len) {
+ offset -= iov[i].iov_len;
+ continue;
+ }
+
+ if (iov[i].iov_base == NULL) {
+ continue;
+ }
+
+ if (done >= size) {
+ break;
+ }
+
+ len = MIN(size - done, iov[i].iov_len - offset);
+ memcpy(buf, iov[i].iov_base + offset, len);
+ buf += len;
+ done += len;
+ offset = 0;
+ }
+
+ return done;
+}
+
+static uint32_t
+vbdev_ocf_ctx_data_rd(void *dst, ctx_data_t *src, uint32_t size)
+{
+ struct bdev_ocf_data *s = src;
+ uint32_t size_local;
+
+ size_local = iovec_flatten(s->iovs, s->iovcnt, dst, size, s->seek);
+ s->seek += size_local;
+
+ return size_local;
+}
+
+static size_t
+buf_to_iovec(const void *buf, size_t size, struct iovec *iov, size_t iovcnt, size_t offset)
+{
+ size_t i, len, done = 0;
+
+ for (i = 0; i < iovcnt; i++) {
+ if (offset >= iov[i].iov_len) {
+ offset -= iov[i].iov_len;
+ continue;
+ }
+
+ if (iov[i].iov_base == NULL) {
+ continue;
+ }
+
+ if (done >= size) {
+ break;
+ }
+
+ len = MIN(size - done, iov[i].iov_len - offset);
+ memcpy(iov[i].iov_base + offset, buf, len);
+ buf += len;
+ done += len;
+ offset = 0;
+ }
+
+ return done;
+}
+
+static uint32_t
+vbdev_ocf_ctx_data_wr(ctx_data_t *dst, const void *src, uint32_t size)
+{
+ struct bdev_ocf_data *d = dst;
+ uint32_t size_local;
+
+ size_local = buf_to_iovec(src, size, d->iovs, d->iovcnt, d->seek);
+ d->seek += size_local;
+
+ return size_local;
+}
+
+static size_t
+iovset(struct iovec *iov, size_t iovcnt, int byte, size_t size, size_t offset)
+{
+ size_t i, len, done = 0;
+
+ for (i = 0; i < iovcnt; i++) {
+ if (offset >= iov[i].iov_len) {
+ offset -= iov[i].iov_len;
+ continue;
+ }
+
+ if (iov[i].iov_base == NULL) {
+ continue;
+ }
+
+ if (done >= size) {
+ break;
+ }
+
+ len = MIN(size - done, iov[i].iov_len - offset);
+ memset(iov[i].iov_base + offset, byte, len);
+ done += len;
+ offset = 0;
+ }
+
+ return done;
+}
+
+static uint32_t
+vbdev_ocf_ctx_data_zero(ctx_data_t *dst, uint32_t size)
+{
+ struct bdev_ocf_data *d = dst;
+ uint32_t size_local;
+
+ size_local = iovset(d->iovs, d->iovcnt, 0, size, d->seek);
+ d->seek += size_local;
+
+ return size_local;
+}
+
+static uint32_t
+vbdev_ocf_ctx_data_seek(ctx_data_t *dst, ctx_data_seek_t seek, uint32_t offset)
+{
+ struct bdev_ocf_data *d = dst;
+ uint32_t off = 0;
+
+ switch (seek) {
+ case ctx_data_seek_begin:
+ off = MIN(offset, d->size);
+ d->seek = off;
+ break;
+ case ctx_data_seek_current:
+ off = MIN(offset, d->size - d->seek);
+ d->seek += off;
+ break;
+ }
+
+ return off;
+}
+
+static uint64_t
+vbdev_ocf_ctx_data_cpy(ctx_data_t *dst, ctx_data_t *src, uint64_t to,
+ uint64_t from, uint64_t bytes)
+{
+ struct bdev_ocf_data *s = src;
+ struct bdev_ocf_data *d = dst;
+ uint32_t it_iov = 0;
+ uint32_t it_off = 0;
+ uint32_t n, sz;
+
+ bytes = MIN(bytes, s->size - from);
+ bytes = MIN(bytes, d->size - to);
+ sz = bytes;
+
+ while (from || bytes) {
+ if (s->iovs[it_iov].iov_len == it_off) {
+ it_iov++;
+ it_off = 0;
+ continue;
+ }
+
+ if (from) {
+ n = MIN(from, s->iovs[it_iov].iov_len);
+ from -= n;
+ } else {
+ n = MIN(bytes, s->iovs[it_iov].iov_len);
+ buf_to_iovec(s->iovs[it_iov].iov_base + it_off, n, d->iovs, d->iovcnt, to);
+ bytes -= n;
+ to += n;
+ }
+
+ it_off += n;
+ }
+
+ return sz;
+}
+
+static void
+vbdev_ocf_ctx_data_secure_erase(ctx_data_t *ctx_data)
+{
+ struct bdev_ocf_data *data = ctx_data;
+ struct iovec *iovs = data->iovs;
+ int i;
+
+ for (i = 0; i < data->iovcnt; i++) {
+ if (env_memset(iovs[i].iov_base, iovs[i].iov_len, 0)) {
+ assert(false);
+ }
+ }
+}
+
+int vbdev_ocf_queue_create(ocf_cache_t cache, ocf_queue_t *queue, const struct ocf_queue_ops *ops)
+{
+ int rc;
+ struct vbdev_ocf_cache_ctx *ctx = ocf_cache_get_priv(cache);
+
+ pthread_mutex_lock(&ctx->lock);
+ rc = ocf_queue_create(cache, queue, ops);
+ pthread_mutex_unlock(&ctx->lock);
+ return rc;
+}
+
+void vbdev_ocf_queue_put(ocf_queue_t queue)
+{
+ ocf_cache_t cache = ocf_queue_get_cache(queue);
+ struct vbdev_ocf_cache_ctx *ctx = ocf_cache_get_priv(cache);
+
+ pthread_mutex_lock(&ctx->lock);
+ ocf_queue_put(queue);
+ pthread_mutex_unlock(&ctx->lock);
+}
+
+void vbdev_ocf_cache_ctx_put(struct vbdev_ocf_cache_ctx *ctx)
+{
+ if (env_atomic_dec_return(&ctx->refcnt) == 0) {
+ pthread_mutex_destroy(&ctx->lock);
+ free(ctx);
+ }
+}
+
+void vbdev_ocf_cache_ctx_get(struct vbdev_ocf_cache_ctx *ctx)
+{
+ env_atomic_inc(&ctx->refcnt);
+}
+
+struct cleaner_priv {
+ struct spdk_poller *poller;
+ ocf_queue_t queue;
+ uint64_t next_run;
+};
+
+static int
+cleaner_poll(void *arg)
+{
+ ocf_cleaner_t cleaner = arg;
+ struct cleaner_priv *priv = ocf_cleaner_get_priv(cleaner);
+ uint32_t iono = ocf_queue_pending_io(priv->queue);
+ int i, max = spdk_min(32, iono);
+
+ for (i = 0; i < max; i++) {
+ ocf_queue_run_single(priv->queue);
+ }
+
+ if (spdk_get_ticks() >= priv->next_run) {
+ ocf_cleaner_run(cleaner, priv->queue);
+ return SPDK_POLLER_BUSY;
+ }
+
+ if (iono > 0) {
+ return SPDK_POLLER_BUSY;
+ } else {
+ return SPDK_POLLER_IDLE;
+ }
+}
+
+static void
+cleaner_cmpl(ocf_cleaner_t c, uint32_t interval)
+{
+ struct cleaner_priv *priv = ocf_cleaner_get_priv(c);
+
+ priv->next_run = spdk_get_ticks() + ((interval * spdk_get_ticks_hz()) / 1000);
+}
+
+static void
+cleaner_queue_kick(ocf_queue_t q)
+{
+}
+
+static void
+cleaner_queue_stop(ocf_queue_t q)
+{
+ struct cleaner_priv *cpriv = ocf_queue_get_priv(q);
+
+ if (cpriv) {
+ spdk_poller_unregister(&cpriv->poller);
+ free(cpriv);
+ }
+}
+
+const struct ocf_queue_ops cleaner_queue_ops = {
+ .kick_sync = cleaner_queue_kick,
+ .kick = cleaner_queue_kick,
+ .stop = cleaner_queue_stop,
+};
+
+static int
+vbdev_ocf_ctx_cleaner_init(ocf_cleaner_t c)
+{
+ int rc;
+ struct cleaner_priv *priv = calloc(1, sizeof(*priv));
+ ocf_cache_t cache = ocf_cleaner_get_cache(c);
+ struct vbdev_ocf_cache_ctx *cctx = ocf_cache_get_priv(cache);
+
+ if (priv == NULL) {
+ return -ENOMEM;
+ }
+
+ rc = vbdev_ocf_queue_create(cache, &priv->queue, &cleaner_queue_ops);
+ if (rc) {
+ free(priv);
+ return rc;
+ }
+
+ ocf_queue_set_priv(priv->queue, priv);
+
+ cctx->cleaner_queue = priv->queue;
+
+ ocf_cleaner_set_cmpl(c, cleaner_cmpl);
+ ocf_cleaner_set_priv(c, priv);
+
+ return 0;
+}
+
+static void
+vbdev_ocf_ctx_cleaner_stop(ocf_cleaner_t c)
+{
+ struct cleaner_priv *priv = ocf_cleaner_get_priv(c);
+
+ vbdev_ocf_queue_put(priv->queue);
+}
+
+static void
+vbdev_ocf_ctx_cleaner_kick(ocf_cleaner_t cleaner)
+{
+ struct cleaner_priv *priv = ocf_cleaner_get_priv(cleaner);
+
+ if (priv->poller) {
+ return;
+ }
+
+ /* We start cleaner poller at the same thread where cache was created
+ * TODO: allow user to specify core at which cleaner should run */
+ priv->poller = SPDK_POLLER_REGISTER(cleaner_poll, cleaner, 0);
+}
+
+static void
+vbdev_ocf_md_kick(void *ctx)
+{
+ ocf_metadata_updater_t mu = ctx;
+ ocf_cache_t cache = ocf_metadata_updater_get_cache(mu);
+
+ if (ocf_cache_is_running(cache)) {
+ ocf_metadata_updater_run(mu);
+ }
+}
+
+static int
+vbdev_ocf_volume_updater_init(ocf_metadata_updater_t mu)
+{
+ struct spdk_thread *md_thread = spdk_get_thread();
+
+ ocf_metadata_updater_set_priv(mu, md_thread);
+
+ return 0;
+}
+
+static void
+vbdev_ocf_volume_updater_stop(ocf_metadata_updater_t mu)
+{
+
+}
+
+static void
+vbdev_ocf_volume_updater_kick(ocf_metadata_updater_t mu)
+{
+ struct spdk_thread *md_thread = ocf_metadata_updater_get_priv(mu);
+
+ /* We need to send message to updater thread because
+ * kick can happen from any thread */
+ spdk_thread_send_msg(md_thread, vbdev_ocf_md_kick, mu);
+}
+
+/* This function is main way by which OCF communicates with user
+ * We don't want to use SPDK_LOG here because debugging information that is
+ * associated with every print message is not helpful in callback that only prints info
+ * while the real source is somewhere in OCF code */
+static int
+vbdev_ocf_ctx_log_printf(ocf_logger_t logger, ocf_logger_lvl_t lvl,
+ const char *fmt, va_list args)
+{
+ int spdk_lvl;
+
+ switch (lvl) {
+ case log_emerg:
+ case log_alert:
+ case log_crit:
+ case log_err:
+ spdk_lvl = SPDK_LOG_ERROR;
+ break;
+
+ case log_warn:
+ spdk_lvl = SPDK_LOG_WARN;
+ break;
+
+ case log_notice:
+ spdk_lvl = SPDK_LOG_NOTICE;
+ break;
+
+ case log_info:
+ case log_debug:
+ default:
+ spdk_lvl = SPDK_LOG_INFO;
+ }
+
+ spdk_vlog(spdk_lvl, NULL, -1, NULL, fmt, args);
+ return 0;
+}
+
+static const struct ocf_ctx_config vbdev_ocf_ctx_cfg = {
+ .name = "OCF SPDK",
+
+ .ops = {
+ .data = {
+ .alloc = vbdev_ocf_ctx_data_alloc,
+ .free = vbdev_ocf_ctx_data_free,
+ .mlock = vbdev_ocf_ctx_data_mlock,
+ .munlock = vbdev_ocf_ctx_data_munlock,
+ .read = vbdev_ocf_ctx_data_rd,
+ .write = vbdev_ocf_ctx_data_wr,
+ .zero = vbdev_ocf_ctx_data_zero,
+ .seek = vbdev_ocf_ctx_data_seek,
+ .copy = vbdev_ocf_ctx_data_cpy,
+ .secure_erase = vbdev_ocf_ctx_data_secure_erase,
+ },
+
+ .metadata_updater = {
+ .init = vbdev_ocf_volume_updater_init,
+ .stop = vbdev_ocf_volume_updater_stop,
+ .kick = vbdev_ocf_volume_updater_kick,
+ },
+
+ .cleaner = {
+ .init = vbdev_ocf_ctx_cleaner_init,
+ .stop = vbdev_ocf_ctx_cleaner_stop,
+ .kick = vbdev_ocf_ctx_cleaner_kick,
+ },
+
+ .logger = {
+ .print = vbdev_ocf_ctx_log_printf,
+ .dump_stack = NULL,
+ },
+
+ },
+};
+
+int
+vbdev_ocf_ctx_init(void)
+{
+ int ret;
+
+ ret = ocf_ctx_create(&vbdev_ocf_ctx, &vbdev_ocf_ctx_cfg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+void
+vbdev_ocf_ctx_cleanup(void)
+{
+ ocf_ctx_put(vbdev_ocf_ctx);
+ vbdev_ocf_ctx = NULL;
+}
+
+SPDK_LOG_REGISTER_COMPONENT("ocf_ocfctx", SPDK_LOG_OCFCTX)
diff --git a/src/spdk/module/bdev/ocf/ctx.h b/src/spdk/module/bdev/ocf/ctx.h
new file mode 100644
index 000000000..446ac8d8f
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/ctx.h
@@ -0,0 +1,65 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef VBDEV_OCF_CTX_H
+#define VBDEV_OCF_CTX_H
+
+#include <ocf/ocf.h>
+#include "spdk/thread.h"
+
+extern ocf_ctx_t vbdev_ocf_ctx;
+
+#define OCF_WRITE_FLUSH 11
+
+#define SPDK_OBJECT 1
+
+/* Context of cache instance */
+struct vbdev_ocf_cache_ctx {
+ ocf_queue_t mngt_queue;
+ ocf_queue_t cleaner_queue;
+ pthread_mutex_t lock;
+ env_atomic refcnt;
+};
+
+void vbdev_ocf_cache_ctx_put(struct vbdev_ocf_cache_ctx *ctx);
+void vbdev_ocf_cache_ctx_get(struct vbdev_ocf_cache_ctx *ctx);
+
+int vbdev_ocf_ctx_init(void);
+void vbdev_ocf_ctx_cleanup(void);
+
+/* Thread safe queue creation and deletion
+ * These are wrappers for original ocf_queue_create() and ocf_queue_put() */
+int vbdev_ocf_queue_create(ocf_cache_t cache, ocf_queue_t *queue, const struct ocf_queue_ops *ops);
+void vbdev_ocf_queue_put(ocf_queue_t queue);
+
+#endif
diff --git a/src/spdk/module/bdev/ocf/data.c b/src/spdk/module/bdev/ocf/data.c
new file mode 100644
index 000000000..981c793f5
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/data.c
@@ -0,0 +1,122 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ocf/ocf.h>
+#include "spdk/bdev.h"
+#include "data.h"
+
+struct bdev_ocf_data *
+vbdev_ocf_data_alloc(uint32_t iovcnt)
+{
+ struct bdev_ocf_data *data;
+
+ data = env_malloc(sizeof(*data), ENV_MEM_NOIO);
+ if (!data) {
+ return NULL;
+ }
+
+ data->seek = 0;
+
+ if (iovcnt) {
+ data->iovs = env_malloc(sizeof(*data->iovs) * iovcnt, ENV_MEM_NOIO);
+ if (!data->iovs) {
+ env_free(data);
+ return NULL;
+ }
+ }
+
+ data->iovcnt = 0;
+ data->iovalloc = iovcnt;
+
+ return data;
+}
+
+void
+vbdev_ocf_data_free(struct bdev_ocf_data *data)
+{
+ if (!data) {
+ return;
+ }
+
+ if (data->iovalloc != 0) {
+ env_free(data->iovs);
+ }
+
+ env_free(data);
+}
+
+void
+vbdev_ocf_iovs_add(struct bdev_ocf_data *data, void *base, size_t len)
+{
+ assert(NULL != data);
+ assert(data->iovalloc != -1);
+
+ if (data->iovcnt == data->iovalloc) {
+ /* TODO: Realloc iovs */
+ SPDK_ERRLOG("IOV error\n");
+ }
+
+ data->iovs[data->iovcnt].iov_base = base;
+ data->iovs[data->iovcnt].iov_len = len;
+ data->iovcnt++;
+}
+
+struct bdev_ocf_data *
+vbdev_ocf_data_from_spdk_io(struct spdk_bdev_io *bdev_io)
+{
+ struct bdev_ocf_data *data;
+
+ if (bdev_io == NULL) {
+ return NULL;
+ }
+
+ switch (bdev_io->type) {
+ case SPDK_BDEV_IO_TYPE_WRITE:
+ case SPDK_BDEV_IO_TYPE_READ:
+ assert(bdev_io->u.bdev.iovs);
+ break;
+ case SPDK_BDEV_IO_TYPE_FLUSH:
+ case SPDK_BDEV_IO_TYPE_UNMAP:
+ break;
+ default:
+ SPDK_ERRLOG("Unsupported IO type %d\n", bdev_io->type);
+ return NULL;
+ }
+
+ data = (struct bdev_ocf_data *)bdev_io->driver_ctx;
+ data->iovs = bdev_io->u.bdev.iovs;
+ data->iovcnt = bdev_io->u.bdev.iovcnt;
+ data->size = bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen;
+
+ return data;
+}
diff --git a/src/spdk/module/bdev/ocf/data.h b/src/spdk/module/bdev/ocf/data.h
new file mode 100644
index 000000000..7ed5adcef
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/data.h
@@ -0,0 +1,57 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef VBDEV_OCF_DATA_H
+#define VBDEV_OCF_DATA_H
+
+#include "spdk/bdev_module.h"
+
+struct bdev_ocf_data {
+ struct iovec *iovs;
+ int iovcnt;
+ int iovalloc;
+ uint32_t size;
+ uint32_t seek;
+};
+
+struct bdev_ocf_data *vbdev_ocf_data_from_spdk_io(struct spdk_bdev_io *bdev_io);
+
+struct bdev_ocf_data *vbdev_ocf_data_alloc(uint32_t nvecs);
+
+void vbdev_ocf_data_free(struct bdev_ocf_data *data);
+
+struct bdev_ocf_data *vbdev_ocf_data_from_iov(struct iovec *iovs);
+
+void vbdev_ocf_iovs_add(struct bdev_ocf_data *data, void *base, size_t len);
+
+#endif
diff --git a/src/spdk/module/bdev/ocf/stats.c b/src/spdk/module/bdev/ocf/stats.c
new file mode 100644
index 000000000..164da7d2e
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/stats.c
@@ -0,0 +1,109 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ctx.h"
+#include "stats.h"
+
+int
+vbdev_ocf_stats_get(ocf_cache_t cache, char *core_name, struct vbdev_ocf_stats *stats)
+{
+ int status;
+ ocf_core_t core;
+
+ status = ocf_core_get_by_name(cache, core_name, strlen(core_name), &core);
+ if (status) {
+ return status;
+ }
+
+ return ocf_stats_collect_core(core, &stats->usage, &stats->reqs, &stats->blocks, &stats->errors);
+}
+
+#define WJSON_STAT(w, stats, group, field, units) \
+ spdk_json_write_named_object_begin(w, #field); \
+ spdk_json_write_named_uint64(w, "count", stats->group.field.value); \
+ spdk_json_write_named_string_fmt(w, "percentage", "%lu.%lu", \
+ stats->group.field.fraction / 100, stats->group.field.fraction % 100); \
+ spdk_json_write_named_string(w, "units", units); \
+ spdk_json_write_object_end(w);
+
+void
+vbdev_ocf_stats_write_json(struct spdk_json_write_ctx *w, struct vbdev_ocf_stats *stats)
+{
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_object_begin(w, "usage");
+ WJSON_STAT(w, stats, usage, occupancy, "4KiB blocks");
+ WJSON_STAT(w, stats, usage, free, "4KiB blocks");
+ WJSON_STAT(w, stats, usage, clean, "4KiB blocks");
+ WJSON_STAT(w, stats, usage, dirty, "4KiB blocks");
+ spdk_json_write_object_end(w);
+
+ spdk_json_write_named_object_begin(w, "requests");
+ WJSON_STAT(w, stats, reqs, rd_hits, "Requests");
+ WJSON_STAT(w, stats, reqs, rd_partial_misses, "Requests");
+ WJSON_STAT(w, stats, reqs, rd_full_misses, "Requests");
+ WJSON_STAT(w, stats, reqs, rd_total, "Requests");
+ WJSON_STAT(w, stats, reqs, wr_hits, "Requests");
+ WJSON_STAT(w, stats, reqs, wr_partial_misses, "Requests");
+ WJSON_STAT(w, stats, reqs, wr_full_misses, "Requests");
+ WJSON_STAT(w, stats, reqs, wr_total, "Requests");
+ WJSON_STAT(w, stats, reqs, rd_pt, "Requests");
+ WJSON_STAT(w, stats, reqs, wr_pt, "Requests");
+ WJSON_STAT(w, stats, reqs, serviced, "Requests");
+ WJSON_STAT(w, stats, reqs, total, "Requests");
+ spdk_json_write_object_end(w);
+
+ spdk_json_write_named_object_begin(w, "blocks");
+ WJSON_STAT(w, stats, blocks, core_volume_rd, "4KiB blocks");
+ WJSON_STAT(w, stats, blocks, core_volume_wr, "4KiB blocks");
+ WJSON_STAT(w, stats, blocks, core_volume_total, "4KiB blocks");
+ WJSON_STAT(w, stats, blocks, cache_volume_rd, "4KiB blocks");
+ WJSON_STAT(w, stats, blocks, cache_volume_wr, "4KiB blocks");
+ WJSON_STAT(w, stats, blocks, cache_volume_total, "4KiB blocks");
+ WJSON_STAT(w, stats, blocks, volume_rd, "4KiB blocks");
+ WJSON_STAT(w, stats, blocks, volume_wr, "4KiB blocks");
+ WJSON_STAT(w, stats, blocks, volume_total, "4KiB blocks");
+ spdk_json_write_object_end(w);
+
+ spdk_json_write_named_object_begin(w, "errors");
+ WJSON_STAT(w, stats, errors, core_volume_rd, "Requests");
+ WJSON_STAT(w, stats, errors, core_volume_wr, "Requests");
+ WJSON_STAT(w, stats, errors, core_volume_total, "Requests");
+ WJSON_STAT(w, stats, errors, cache_volume_rd, "Requests");
+ WJSON_STAT(w, stats, errors, cache_volume_wr, "Requests");
+ WJSON_STAT(w, stats, errors, cache_volume_total, "Requests");
+ WJSON_STAT(w, stats, errors, total, "Requests");
+ spdk_json_write_object_end(w);
+
+ spdk_json_write_object_end(w);
+}
diff --git a/src/spdk/module/bdev/ocf/stats.h b/src/spdk/module/bdev/ocf/stats.h
new file mode 100644
index 000000000..b377c67f5
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/stats.h
@@ -0,0 +1,51 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef VBDEV_OCF_STATS_H
+#define VBDEV_OCF_STATS_H
+
+#include "spdk/json.h"
+#include <ocf/ocf.h>
+
+struct vbdev_ocf_stats {
+ struct ocf_stats_usage usage;
+ struct ocf_stats_requests reqs;
+ struct ocf_stats_blocks blocks;
+ struct ocf_stats_errors errors;
+};
+
+int vbdev_ocf_stats_get(ocf_cache_t cache, char *core_name, struct vbdev_ocf_stats *stats);
+
+void vbdev_ocf_stats_write_json(struct spdk_json_write_ctx *w, struct vbdev_ocf_stats *stats);
+
+#endif
diff --git a/src/spdk/module/bdev/ocf/utils.c b/src/spdk/module/bdev/ocf/utils.c
new file mode 100644
index 000000000..3a1df3c9e
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/utils.c
@@ -0,0 +1,136 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+
+#include "utils.h"
+#include "vbdev_ocf.h"
+
+static char *cache_modes[ocf_cache_mode_max] = {
+ [ocf_cache_mode_wt] = "wt",
+ [ocf_cache_mode_wb] = "wb",
+ [ocf_cache_mode_wa] = "wa",
+ [ocf_cache_mode_pt] = "pt",
+ [ocf_cache_mode_wi] = "wi",
+ [ocf_cache_mode_wo] = "wo",
+};
+
+ocf_cache_mode_t
+ocf_get_cache_mode(const char *cache_mode)
+{
+ int i;
+
+ for (i = 0; i < ocf_cache_mode_max; i++) {
+ if (strcmp(cache_mode, cache_modes[i]) == 0) {
+ return i;
+ }
+ }
+
+ return ocf_cache_mode_none;
+}
+
+const char *
+ocf_get_cache_modename(ocf_cache_mode_t mode)
+{
+ if (mode > ocf_cache_mode_none && mode < ocf_cache_mode_max) {
+ return cache_modes[mode];
+ } else {
+ return NULL;
+ }
+}
+
+int
+vbdev_ocf_mngt_start(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_fn *path,
+ vbdev_ocf_mngt_callback cb, void *cb_arg)
+{
+ if (vbdev->mngt_ctx.current_step) {
+ return -EBUSY;
+ }
+
+ memset(&vbdev->mngt_ctx, 0, sizeof(vbdev->mngt_ctx));
+
+ vbdev->mngt_ctx.current_step = path;
+ vbdev->mngt_ctx.cb = cb;
+ vbdev->mngt_ctx.cb_arg = cb_arg;
+
+ (*vbdev->mngt_ctx.current_step)(vbdev);
+
+ return 0;
+}
+
+void
+vbdev_ocf_mngt_stop(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_fn *rollback_path, int status)
+{
+ if (status) {
+ vbdev->mngt_ctx.status = status;
+ }
+
+ if (vbdev->mngt_ctx.status && rollback_path) {
+ vbdev->mngt_ctx.poller_fn = NULL;
+ vbdev->mngt_ctx.current_step = rollback_path;
+ (*vbdev->mngt_ctx.current_step)(vbdev);
+ return;
+ }
+
+ if (vbdev->mngt_ctx.cb) {
+ vbdev->mngt_ctx.cb(vbdev->mngt_ctx.status, vbdev, vbdev->mngt_ctx.cb_arg);
+ }
+
+ memset(&vbdev->mngt_ctx, 0, sizeof(vbdev->mngt_ctx));
+}
+
+void
+vbdev_ocf_mngt_continue(struct vbdev_ocf *vbdev, int status)
+{
+ if (vbdev->mngt_ctx.current_step == NULL) {
+ return;
+ }
+
+ assert((*vbdev->mngt_ctx.current_step) != NULL);
+
+ vbdev->mngt_ctx.status = status;
+
+ vbdev->mngt_ctx.current_step++;
+ if (*vbdev->mngt_ctx.current_step) {
+ (*vbdev->mngt_ctx.current_step)(vbdev);
+ return;
+ }
+
+ vbdev_ocf_mngt_stop(vbdev, NULL, 0);
+}
+
+int
+vbdev_ocf_mngt_get_status(struct vbdev_ocf *vbdev)
+{
+ return vbdev->mngt_ctx.status;
+}
diff --git a/src/spdk/module/bdev/ocf/utils.h b/src/spdk/module/bdev/ocf/utils.h
new file mode 100644
index 000000000..73bf6c93a
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/utils.h
@@ -0,0 +1,67 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef VBDEV_OCF_UTILS_H
+#define VBDEV_OCF_UTILS_H
+
+#include <ocf/ocf.h>
+#include "vbdev_ocf.h"
+
+ocf_cache_mode_t ocf_get_cache_mode(const char *cache_mode);
+const char *ocf_get_cache_modename(ocf_cache_mode_t mode);
+
+/* Initiate management operation
+ * Receives NULL terminated array of functions (path)
+ * and callback (cb)
+ * and callback argument (cb_arg)
+ * This function may fail with ENOMEM or EBUSY */
+int vbdev_ocf_mngt_start(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_fn *path,
+ vbdev_ocf_mngt_callback cb, void *cb_arg);
+
+/* Continue execution with polling operation (fn)
+ * fn must invoke vbdev_ocf_mngt_continue() to stop polling
+ * Poller has default timeout of 5 seconds */
+void vbdev_ocf_mngt_poll(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_fn fn);
+
+/* Continue execution with next function that is on path
+ * If next function is NULL, finish management operation and invoke callback */
+void vbdev_ocf_mngt_continue(struct vbdev_ocf *vbdev, int status);
+
+/* Stop the execution, if status is non zero set it,
+ * if rollback function is not null invoke rollback
+ * else invoke callback with last status returned */
+void vbdev_ocf_mngt_stop(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_fn *rollback_path, int status);
+
+/* Get status */
+int vbdev_ocf_mngt_get_status(struct vbdev_ocf *vbdev);
+#endif
diff --git a/src/spdk/module/bdev/ocf/vbdev_ocf.c b/src/spdk/module/bdev/ocf/vbdev_ocf.c
new file mode 100644
index 000000000..4997772cd
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/vbdev_ocf.c
@@ -0,0 +1,1775 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ocf/ocf.h>
+#include <ocf/ocf_types.h>
+#include <ocf/ocf_mngt.h>
+
+#include "ctx.h"
+#include "data.h"
+#include "volume.h"
+#include "utils.h"
+#include "vbdev_ocf.h"
+
+#include "spdk/bdev_module.h"
+#include "spdk/conf.h"
+#include "spdk/thread.h"
+#include "spdk/string.h"
+#include "spdk_internal/log.h"
+#include "spdk/cpuset.h"
+
+static struct spdk_bdev_module ocf_if;
+
+static TAILQ_HEAD(, vbdev_ocf) g_ocf_vbdev_head
+ = TAILQ_HEAD_INITIALIZER(g_ocf_vbdev_head);
+
+static TAILQ_HEAD(, examining_bdev) g_ocf_examining_bdevs_head
+ = TAILQ_HEAD_INITIALIZER(g_ocf_examining_bdevs_head);
+
+bool g_fini_started = false;
+
+/* Structure for keeping list of bdevs that are claimed but not used yet */
+struct examining_bdev {
+ struct spdk_bdev *bdev;
+ TAILQ_ENTRY(examining_bdev) tailq;
+};
+
+/* Add bdev to list of claimed */
+static void
+examine_start(struct spdk_bdev *bdev)
+{
+ struct examining_bdev *entry = malloc(sizeof(*entry));
+
+ assert(entry);
+ entry->bdev = bdev;
+ TAILQ_INSERT_TAIL(&g_ocf_examining_bdevs_head, entry, tailq);
+}
+
+/* Find bdev on list of claimed bdevs, then remove it,
+ * if it was the last one on list then report examine done */
+static void
+examine_done(int status, struct vbdev_ocf *vbdev, void *cb_arg)
+{
+ struct spdk_bdev *bdev = cb_arg;
+ struct examining_bdev *entry, *safe, *found = NULL;
+
+ TAILQ_FOREACH_SAFE(entry, &g_ocf_examining_bdevs_head, tailq, safe) {
+ if (entry->bdev == bdev) {
+ if (found) {
+ goto remove;
+ } else {
+ found = entry;
+ }
+ }
+ }
+
+ assert(found);
+ spdk_bdev_module_examine_done(&ocf_if);
+
+remove:
+ TAILQ_REMOVE(&g_ocf_examining_bdevs_head, found, tailq);
+ free(found);
+}
+
+/* Free allocated strings and structure itself
+ * Used at shutdown only */
+static void
+free_vbdev(struct vbdev_ocf *vbdev)
+{
+ if (!vbdev) {
+ return;
+ }
+
+ free(vbdev->name);
+ free(vbdev->cache.name);
+ free(vbdev->core.name);
+ free(vbdev);
+}
+
+/* Get existing cache base
+ * that is attached to other vbdev */
+static struct vbdev_ocf_base *
+get_other_cache_base(struct vbdev_ocf_base *base)
+{
+ struct vbdev_ocf *vbdev;
+
+ TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
+ if (&vbdev->cache == base || !vbdev->cache.attached) {
+ continue;
+ }
+ if (!strcmp(vbdev->cache.name, base->name)) {
+ return &vbdev->cache;
+ }
+ }
+
+ return NULL;
+}
+
+static bool
+is_ocf_cache_running(struct vbdev_ocf *vbdev)
+{
+ if (vbdev->cache.attached && vbdev->ocf_cache) {
+ return ocf_cache_is_running(vbdev->ocf_cache);
+ }
+ return false;
+}
+
+/* Get existing OCF cache instance
+ * that is started by other vbdev */
+static ocf_cache_t
+get_other_cache_instance(struct vbdev_ocf *vbdev)
+{
+ struct vbdev_ocf *cmp;
+
+ TAILQ_FOREACH(cmp, &g_ocf_vbdev_head, tailq) {
+ if (cmp->state.doing_finish || cmp == vbdev) {
+ continue;
+ }
+ if (strcmp(cmp->cache.name, vbdev->cache.name)) {
+ continue;
+ }
+ if (is_ocf_cache_running(cmp)) {
+ return cmp->ocf_cache;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+_remove_base_bdev(void *ctx)
+{
+ struct spdk_bdev_desc *desc = ctx;
+
+ spdk_bdev_close(desc);
+}
+
+/* Close and unclaim base bdev */
+static void
+remove_base_bdev(struct vbdev_ocf_base *base)
+{
+ if (base->attached) {
+ if (base->management_channel) {
+ spdk_put_io_channel(base->management_channel);
+ }
+
+ spdk_bdev_module_release_bdev(base->bdev);
+ /* Close the underlying bdev on its same opened thread. */
+ if (base->thread && base->thread != spdk_get_thread()) {
+ spdk_thread_send_msg(base->thread, _remove_base_bdev, base->desc);
+ } else {
+ spdk_bdev_close(base->desc);
+ }
+ base->attached = false;
+ }
+}
+
+/* Finish unregister operation */
+static void
+unregister_finish(struct vbdev_ocf *vbdev)
+{
+ spdk_bdev_destruct_done(&vbdev->exp_bdev, vbdev->state.stop_status);
+ ocf_mngt_cache_put(vbdev->ocf_cache);
+ vbdev_ocf_cache_ctx_put(vbdev->cache_ctx);
+ vbdev_ocf_mngt_continue(vbdev, 0);
+}
+
+static void
+close_core_bdev(struct vbdev_ocf *vbdev)
+{
+ remove_base_bdev(&vbdev->core);
+ vbdev_ocf_mngt_continue(vbdev, 0);
+}
+
+static void
+remove_core_cmpl(void *priv, int error)
+{
+ struct vbdev_ocf *vbdev = priv;
+
+ ocf_mngt_cache_unlock(vbdev->ocf_cache);
+ vbdev_ocf_mngt_continue(vbdev, error);
+}
+
+/* Try to lock cache, then remove core */
+static void
+remove_core_cache_lock_cmpl(ocf_cache_t cache, void *priv, int error)
+{
+ struct vbdev_ocf *vbdev = (struct vbdev_ocf *)priv;
+
+ if (error) {
+ SPDK_ERRLOG("Error %d, can not lock cache instance %s\n",
+ error, vbdev->name);
+ vbdev_ocf_mngt_continue(vbdev, error);
+ return;
+ }
+
+ ocf_mngt_cache_remove_core(vbdev->ocf_core, remove_core_cmpl, vbdev);
+}
+
+/* Detach core base */
+static void
+detach_core(struct vbdev_ocf *vbdev)
+{
+ if (is_ocf_cache_running(vbdev)) {
+ ocf_mngt_cache_lock(vbdev->ocf_cache, remove_core_cache_lock_cmpl, vbdev);
+ } else {
+ vbdev_ocf_mngt_continue(vbdev, 0);
+ }
+}
+
+static void
+close_cache_bdev(struct vbdev_ocf *vbdev)
+{
+ remove_base_bdev(&vbdev->cache);
+ vbdev_ocf_mngt_continue(vbdev, 0);
+}
+
+/* Detach cache base */
+static void
+detach_cache(struct vbdev_ocf *vbdev)
+{
+ vbdev->state.stop_status = vbdev->mngt_ctx.status;
+
+ /* If some other vbdev references this cache bdev,
+ * we detach this only by changing the flag, without actual close */
+ if (get_other_cache_base(&vbdev->cache)) {
+ vbdev->cache.attached = false;
+ }
+
+ vbdev_ocf_mngt_continue(vbdev, 0);
+}
+
+static void
+stop_vbdev_cmpl(ocf_cache_t cache, void *priv, int error)
+{
+ struct vbdev_ocf *vbdev = priv;
+
+ vbdev_ocf_queue_put(vbdev->cache_ctx->mngt_queue);
+ ocf_mngt_cache_unlock(cache);
+
+ vbdev_ocf_mngt_continue(vbdev, error);
+}
+
+/* Try to lock cache, then stop it */
+static void
+stop_vbdev_cache_lock_cmpl(ocf_cache_t cache, void *priv, int error)
+{
+ struct vbdev_ocf *vbdev = (struct vbdev_ocf *)priv;
+
+ if (error) {
+ SPDK_ERRLOG("Error %d, can not lock cache instance %s\n",
+ error, vbdev->name);
+ vbdev_ocf_mngt_continue(vbdev, error);
+ return;
+ }
+
+ ocf_mngt_cache_stop(vbdev->ocf_cache, stop_vbdev_cmpl, vbdev);
+}
+
+/* Stop OCF cache object
+ * vbdev_ocf is not operational after this */
+static void
+stop_vbdev(struct vbdev_ocf *vbdev)
+{
+ if (!is_ocf_cache_running(vbdev)) {
+ vbdev_ocf_mngt_continue(vbdev, 0);
+ return;
+ }
+
+ if (!g_fini_started && get_other_cache_instance(vbdev)) {
+ SPDK_NOTICELOG("Not stopping cache instance '%s'"
+ " because it is referenced by other OCF bdev\n",
+ vbdev->cache.name);
+ vbdev_ocf_mngt_continue(vbdev, 0);
+ return;
+ }
+
+ ocf_mngt_cache_lock(vbdev->ocf_cache, stop_vbdev_cache_lock_cmpl, vbdev);
+}
+
+static void
+flush_vbdev_cmpl(ocf_cache_t cache, void *priv, int error)
+{
+ struct vbdev_ocf *vbdev = priv;
+
+ ocf_mngt_cache_unlock(cache);
+ vbdev_ocf_mngt_continue(vbdev, error);
+}
+
+static void
+flush_vbdev_cache_lock_cmpl(ocf_cache_t cache, void *priv, int error)
+{
+ struct vbdev_ocf *vbdev = (struct vbdev_ocf *)priv;
+
+ if (error) {
+ SPDK_ERRLOG("Error %d, can not lock cache instance %s\n",
+ error, vbdev->name);
+ vbdev_ocf_mngt_continue(vbdev, error);
+ return;
+ }
+
+ ocf_mngt_cache_flush(vbdev->ocf_cache, flush_vbdev_cmpl, vbdev);
+}
+
+static void
+flush_vbdev(struct vbdev_ocf *vbdev)
+{
+ if (!is_ocf_cache_running(vbdev)) {
+ vbdev_ocf_mngt_continue(vbdev, -EINVAL);
+ return;
+ }
+
+ ocf_mngt_cache_lock(vbdev->ocf_cache, flush_vbdev_cache_lock_cmpl, vbdev);
+}
+
+/* Procedures called during dirty unregister */
+vbdev_ocf_mngt_fn unregister_path_dirty[] = {
+ flush_vbdev,
+ stop_vbdev,
+ detach_cache,
+ close_cache_bdev,
+ detach_core,
+ close_core_bdev,
+ unregister_finish,
+ NULL
+};
+
+/* Procedures called during clean unregister */
+vbdev_ocf_mngt_fn unregister_path_clean[] = {
+ flush_vbdev,
+ detach_core,
+ close_core_bdev,
+ stop_vbdev,
+ detach_cache,
+ close_cache_bdev,
+ unregister_finish,
+ NULL
+};
+
+/* Start asynchronous management operation using unregister_path */
+static void
+unregister_cb(void *opaque)
+{
+ struct vbdev_ocf *vbdev = opaque;
+ vbdev_ocf_mngt_fn *unregister_path;
+ int rc;
+
+ unregister_path = vbdev->state.doing_clean_delete ?
+ unregister_path_clean : unregister_path_dirty;
+
+ rc = vbdev_ocf_mngt_start(vbdev, unregister_path, NULL, NULL);
+ if (rc) {
+ SPDK_ERRLOG("Unable to unregister OCF bdev: %d\n", rc);
+ spdk_bdev_destruct_done(&vbdev->exp_bdev, rc);
+ }
+}
+
+/* Clean remove case - remove core and then cache, this order
+ * will remove instance permanently */
+static void
+_vbdev_ocf_destruct_clean(struct vbdev_ocf *vbdev)
+{
+ if (vbdev->core.attached) {
+ detach_core(vbdev);
+ close_core_bdev(vbdev);
+ }
+
+ if (vbdev->cache.attached) {
+ detach_cache(vbdev);
+ close_cache_bdev(vbdev);
+ }
+}
+
+/* Dirty shutdown/hot remove case - remove cache and then core, this order
+ * will allow us to recover this instance in the future */
+static void
+_vbdev_ocf_destruct_dirty(struct vbdev_ocf *vbdev)
+{
+ if (vbdev->cache.attached) {
+ detach_cache(vbdev);
+ close_cache_bdev(vbdev);
+ }
+
+ if (vbdev->core.attached) {
+ detach_core(vbdev);
+ close_core_bdev(vbdev);
+ }
+}
+
+/* Unregister io device with callback to unregister_cb
+ * This function is called during spdk_bdev_unregister */
+static int
+vbdev_ocf_destruct(void *opaque)
+{
+ struct vbdev_ocf *vbdev = opaque;
+
+ if (vbdev->state.doing_finish) {
+ return -EALREADY;
+ }
+
+ if (vbdev->state.starting && !vbdev->state.started) {
+ /* Prevent before detach cache/core during register path of
+ this bdev */
+ return -EBUSY;
+ }
+
+ vbdev->state.doing_finish = true;
+
+ if (vbdev->state.started) {
+ spdk_io_device_unregister(vbdev, unregister_cb);
+ /* Return 1 because unregister is delayed */
+ return 1;
+ }
+
+ if (vbdev->state.doing_clean_delete) {
+ _vbdev_ocf_destruct_clean(vbdev);
+ } else {
+ _vbdev_ocf_destruct_dirty(vbdev);
+ }
+
+ return 0;
+}
+
+/* Stop OCF cache and unregister SPDK bdev */
+int
+vbdev_ocf_delete(struct vbdev_ocf *vbdev, void (*cb)(void *, int), void *cb_arg)
+{
+ int rc = 0;
+
+ if (vbdev->state.started) {
+ spdk_bdev_unregister(&vbdev->exp_bdev, cb, cb_arg);
+ } else {
+ rc = vbdev_ocf_destruct(vbdev);
+ if (rc == 0 && cb) {
+ cb(cb_arg, 0);
+ }
+ }
+
+ return rc;
+}
+
+/* Remove cores permanently and then stop OCF cache and unregister SPDK bdev */
+int
+vbdev_ocf_delete_clean(struct vbdev_ocf *vbdev, void (*cb)(void *, int),
+ void *cb_arg)
+{
+ vbdev->state.doing_clean_delete = true;
+
+ return vbdev_ocf_delete(vbdev, cb, cb_arg);
+}
+
+
+/* If vbdev is online, return its object */
+struct vbdev_ocf *
+vbdev_ocf_get_by_name(const char *name)
+{
+ struct vbdev_ocf *vbdev;
+
+ if (name == NULL) {
+ assert(false);
+ return NULL;
+ }
+
+ TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
+ if (vbdev->name == NULL || vbdev->state.doing_finish) {
+ continue;
+ }
+ if (strcmp(vbdev->name, name) == 0) {
+ return vbdev;
+ }
+ }
+ return NULL;
+}
+
+/* Return matching base if parent vbdev is online */
+struct vbdev_ocf_base *
+vbdev_ocf_get_base_by_name(const char *name)
+{
+ struct vbdev_ocf *vbdev;
+
+ if (name == NULL) {
+ assert(false);
+ return NULL;
+ }
+
+ TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
+ if (vbdev->state.doing_finish) {
+ continue;
+ }
+
+ if (vbdev->cache.name && strcmp(vbdev->cache.name, name) == 0) {
+ return &vbdev->cache;
+ }
+ if (vbdev->core.name && strcmp(vbdev->core.name, name) == 0) {
+ return &vbdev->core;
+ }
+ }
+ return NULL;
+}
+
+/* Execute fn for each OCF device that is online or waits for base devices */
+void
+vbdev_ocf_foreach(vbdev_ocf_foreach_fn fn, void *ctx)
+{
+ struct vbdev_ocf *vbdev;
+
+ assert(fn != NULL);
+
+ TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
+ if (!vbdev->state.doing_finish) {
+ fn(vbdev, ctx);
+ }
+ }
+}
+
+/* Called from OCF when SPDK_IO is completed */
+static void
+vbdev_ocf_io_submit_cb(struct ocf_io *io, int error)
+{
+ struct spdk_bdev_io *bdev_io = io->priv1;
+
+ if (error == 0) {
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_SUCCESS);
+ } else if (error == -ENOMEM) {
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM);
+ } else {
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
+ }
+
+ ocf_io_put(io);
+}
+
+/* Configure io parameters and send it to OCF */
+static int
+io_submit_to_ocf(struct spdk_bdev_io *bdev_io, struct ocf_io *io)
+{
+ switch (bdev_io->type) {
+ case SPDK_BDEV_IO_TYPE_WRITE:
+ case SPDK_BDEV_IO_TYPE_READ:
+ ocf_core_submit_io(io);
+ return 0;
+ case SPDK_BDEV_IO_TYPE_FLUSH:
+ ocf_core_submit_flush(io);
+ return 0;
+ case SPDK_BDEV_IO_TYPE_UNMAP:
+ ocf_core_submit_discard(io);
+ return 0;
+ case SPDK_BDEV_IO_TYPE_RESET:
+ case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
+ default:
+ SPDK_ERRLOG("Unsupported IO type: %d\n", bdev_io->type);
+ return -EINVAL;
+ }
+}
+
+/* Submit SPDK-IO to OCF */
+static void
+io_handle(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+{
+ struct vbdev_ocf *vbdev = bdev_io->bdev->ctxt;
+ struct ocf_io *io = NULL;
+ struct bdev_ocf_data *data = NULL;
+ struct vbdev_ocf_qctx *qctx = spdk_io_channel_get_ctx(ch);
+ uint64_t len = bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen;
+ uint64_t offset = bdev_io->u.bdev.offset_blocks * bdev_io->bdev->blocklen;
+ int dir, flags = 0;
+ int err;
+
+ switch (bdev_io->type) {
+ case SPDK_BDEV_IO_TYPE_READ:
+ dir = OCF_READ;
+ break;
+ case SPDK_BDEV_IO_TYPE_WRITE:
+ dir = OCF_WRITE;
+ break;
+ case SPDK_BDEV_IO_TYPE_FLUSH:
+ dir = OCF_WRITE;
+ break;
+ case SPDK_BDEV_IO_TYPE_UNMAP:
+ dir = OCF_WRITE;
+ break;
+ default:
+ err = -EINVAL;
+ goto fail;
+ }
+
+ if (bdev_io->type == SPDK_BDEV_IO_TYPE_FLUSH) {
+ flags = OCF_WRITE_FLUSH;
+ }
+
+ io = ocf_core_new_io(vbdev->ocf_core, qctx->queue, offset, len, dir, 0, flags);
+ if (!io) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ data = vbdev_ocf_data_from_spdk_io(bdev_io);
+ if (!data) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ err = ocf_io_set_data(io, data, 0);
+ if (err) {
+ goto fail;
+ }
+
+ ocf_io_set_cmpl(io, bdev_io, NULL, vbdev_ocf_io_submit_cb);
+
+ err = io_submit_to_ocf(bdev_io, io);
+ if (err) {
+ goto fail;
+ }
+
+ return;
+
+fail:
+ if (io) {
+ ocf_io_put(io);
+ }
+
+ if (err == -ENOMEM) {
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM);
+ } else {
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
+ }
+}
+
+static void
+vbdev_ocf_get_buf_cb(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io,
+ bool success)
+{
+ if (!success) {
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
+ return;
+ }
+
+ io_handle(ch, bdev_io);
+}
+
+/* Called from bdev layer when an io to Cache vbdev is submitted */
+static void
+vbdev_ocf_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+{
+ switch (bdev_io->type) {
+ case SPDK_BDEV_IO_TYPE_READ:
+ /* User does not have to allocate io vectors for the request,
+ * so in case they are not allocated, we allocate them here */
+ spdk_bdev_io_get_buf(bdev_io, vbdev_ocf_get_buf_cb,
+ bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen);
+ break;
+ case SPDK_BDEV_IO_TYPE_WRITE:
+ case SPDK_BDEV_IO_TYPE_FLUSH:
+ case SPDK_BDEV_IO_TYPE_UNMAP:
+ io_handle(ch, bdev_io);
+ break;
+ case SPDK_BDEV_IO_TYPE_RESET:
+ case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
+ default:
+ SPDK_ERRLOG("Unknown I/O type %d\n", bdev_io->type);
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
+ break;
+ }
+}
+
+/* Called from bdev layer */
+static bool
+vbdev_ocf_io_type_supported(void *opaque, enum spdk_bdev_io_type io_type)
+{
+ struct vbdev_ocf *vbdev = opaque;
+
+ switch (io_type) {
+ case SPDK_BDEV_IO_TYPE_READ:
+ case SPDK_BDEV_IO_TYPE_WRITE:
+ case SPDK_BDEV_IO_TYPE_FLUSH:
+ case SPDK_BDEV_IO_TYPE_UNMAP:
+ return spdk_bdev_io_type_supported(vbdev->core.bdev, io_type);
+ case SPDK_BDEV_IO_TYPE_RESET:
+ case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
+ default:
+ return false;
+ }
+}
+
+/* Called from bdev layer */
+static struct spdk_io_channel *
+vbdev_ocf_get_io_channel(void *opaque)
+{
+ struct vbdev_ocf *bdev = opaque;
+
+ return spdk_get_io_channel(bdev);
+}
+
+static int
+vbdev_ocf_dump_info_json(void *opaque, struct spdk_json_write_ctx *w)
+{
+ struct vbdev_ocf *vbdev = opaque;
+
+ spdk_json_write_named_string(w, "cache_device", vbdev->cache.name);
+ spdk_json_write_named_string(w, "core_device", vbdev->core.name);
+
+ spdk_json_write_named_string(w, "mode",
+ ocf_get_cache_modename(ocf_cache_get_mode(vbdev->ocf_cache)));
+ spdk_json_write_named_uint32(w, "cache_line_size",
+ ocf_cache_get_line_size(vbdev->ocf_cache));
+ spdk_json_write_named_bool(w, "metadata_volatile",
+ vbdev->cfg.cache.metadata_volatile);
+
+ return 0;
+}
+
+static void
+vbdev_ocf_write_json_config(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w)
+{
+ struct vbdev_ocf *vbdev = bdev->ctxt;
+
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_string(w, "method", "bdev_ocf_create");
+
+ spdk_json_write_named_object_begin(w, "params");
+ spdk_json_write_named_string(w, "name", vbdev->name);
+ spdk_json_write_named_string(w, "mode",
+ ocf_get_cache_modename(ocf_cache_get_mode(vbdev->ocf_cache)));
+ spdk_json_write_named_string(w, "cache_bdev_name", vbdev->cache.name);
+ spdk_json_write_named_string(w, "core_bdev_name", vbdev->core.name);
+ spdk_json_write_object_end(w);
+
+ spdk_json_write_object_end(w);
+}
+
+/* Cache vbdev function table
+ * Used by bdev layer */
+static struct spdk_bdev_fn_table cache_dev_fn_table = {
+ .destruct = vbdev_ocf_destruct,
+ .io_type_supported = vbdev_ocf_io_type_supported,
+ .submit_request = vbdev_ocf_submit_request,
+ .get_io_channel = vbdev_ocf_get_io_channel,
+ .write_config_json = vbdev_ocf_write_json_config,
+ .dump_info_json = vbdev_ocf_dump_info_json,
+};
+
+/* Poller function for the OCF queue
+ * We execute OCF requests here synchronously */
+static int
+queue_poll(void *opaque)
+{
+ struct vbdev_ocf_qctx *qctx = opaque;
+ uint32_t iono = ocf_queue_pending_io(qctx->queue);
+ int i, max = spdk_min(32, iono);
+
+ for (i = 0; i < max; i++) {
+ ocf_queue_run_single(qctx->queue);
+ }
+
+ if (iono > 0) {
+ return SPDK_POLLER_BUSY;
+ } else {
+ return SPDK_POLLER_IDLE;
+ }
+}
+
+/* Called during ocf_submit_io, ocf_purge*
+ * and any other requests that need to submit io */
+static void
+vbdev_ocf_ctx_queue_kick(ocf_queue_t q)
+{
+}
+
+/* OCF queue deinitialization
+ * Called at ocf_cache_stop */
+static void
+vbdev_ocf_ctx_queue_stop(ocf_queue_t q)
+{
+ struct vbdev_ocf_qctx *qctx = ocf_queue_get_priv(q);
+
+ if (qctx) {
+ spdk_put_io_channel(qctx->cache_ch);
+ spdk_put_io_channel(qctx->core_ch);
+ spdk_poller_unregister(&qctx->poller);
+ if (qctx->allocated) {
+ free(qctx);
+ }
+ }
+}
+
+/* Queue ops is an interface for running queue thread
+ * stop() operation in called just before queue gets destroyed */
+const struct ocf_queue_ops queue_ops = {
+ .kick_sync = vbdev_ocf_ctx_queue_kick,
+ .kick = vbdev_ocf_ctx_queue_kick,
+ .stop = vbdev_ocf_ctx_queue_stop,
+};
+
+/* Called on cache vbdev creation at every thread
+ * We allocate OCF queues here and SPDK poller for it */
+static int
+io_device_create_cb(void *io_device, void *ctx_buf)
+{
+ struct vbdev_ocf *vbdev = io_device;
+ struct vbdev_ocf_qctx *qctx = ctx_buf;
+ int rc;
+
+ rc = vbdev_ocf_queue_create(vbdev->ocf_cache, &qctx->queue, &queue_ops);
+ if (rc) {
+ return rc;
+ }
+
+ ocf_queue_set_priv(qctx->queue, qctx);
+
+ qctx->vbdev = vbdev;
+ qctx->cache_ch = spdk_bdev_get_io_channel(vbdev->cache.desc);
+ qctx->core_ch = spdk_bdev_get_io_channel(vbdev->core.desc);
+ qctx->poller = SPDK_POLLER_REGISTER(queue_poll, qctx, 0);
+
+ return rc;
+}
+
+/* Called per thread
+ * Put OCF queue and relaunch poller with new context to finish pending requests */
+static void
+io_device_destroy_cb(void *io_device, void *ctx_buf)
+{
+ /* Making a copy of context to use it after io channel will be destroyed */
+ struct vbdev_ocf_qctx *copy = malloc(sizeof(*copy));
+ struct vbdev_ocf_qctx *qctx = ctx_buf;
+
+ if (copy) {
+ ocf_queue_set_priv(qctx->queue, copy);
+ memcpy(copy, qctx, sizeof(*copy));
+ spdk_poller_unregister(&qctx->poller);
+ copy->poller = SPDK_POLLER_REGISTER(queue_poll, copy, 0);
+ copy->allocated = true;
+ } else {
+ SPDK_ERRLOG("Unable to stop OCF queue properly: %s\n",
+ spdk_strerror(ENOMEM));
+ }
+
+ vbdev_ocf_queue_put(qctx->queue);
+}
+
+/* OCF management queue deinitialization */
+static void
+vbdev_ocf_ctx_mngt_queue_stop(ocf_queue_t q)
+{
+ struct spdk_poller *poller = ocf_queue_get_priv(q);
+
+ if (poller) {
+ spdk_poller_unregister(&poller);
+ }
+}
+
+static int
+mngt_queue_poll(void *opaque)
+{
+ ocf_queue_t q = opaque;
+ uint32_t iono = ocf_queue_pending_io(q);
+ int i, max = spdk_min(32, iono);
+
+ for (i = 0; i < max; i++) {
+ ocf_queue_run_single(q);
+ }
+
+ if (iono > 0) {
+ return SPDK_POLLER_BUSY;
+ } else {
+ return SPDK_POLLER_IDLE;
+ }
+}
+
+static void
+vbdev_ocf_ctx_mngt_queue_kick(ocf_queue_t q)
+{
+}
+
+/* Queue ops is an interface for running queue thread
+ * stop() operation in called just before queue gets destroyed */
+const struct ocf_queue_ops mngt_queue_ops = {
+ .kick_sync = NULL,
+ .kick = vbdev_ocf_ctx_mngt_queue_kick,
+ .stop = vbdev_ocf_ctx_mngt_queue_stop,
+};
+
+static void
+vbdev_ocf_mngt_exit(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_fn *rollback_path, int rc)
+{
+ vbdev->state.starting = false;
+ vbdev_ocf_mngt_stop(vbdev, rollback_path, rc);
+}
+
+/* Create exported spdk object */
+static void
+finish_register(struct vbdev_ocf *vbdev)
+{
+ int result;
+
+ /* Copy properties of the base bdev */
+ vbdev->exp_bdev.blocklen = vbdev->core.bdev->blocklen;
+ vbdev->exp_bdev.write_cache = vbdev->core.bdev->write_cache;
+ vbdev->exp_bdev.required_alignment = vbdev->core.bdev->required_alignment;
+
+ vbdev->exp_bdev.name = vbdev->name;
+ vbdev->exp_bdev.product_name = "SPDK OCF";
+
+ vbdev->exp_bdev.blockcnt = vbdev->core.bdev->blockcnt;
+ vbdev->exp_bdev.ctxt = vbdev;
+ vbdev->exp_bdev.fn_table = &cache_dev_fn_table;
+ vbdev->exp_bdev.module = &ocf_if;
+
+ /* Finally register vbdev in SPDK */
+ spdk_io_device_register(vbdev, io_device_create_cb, io_device_destroy_cb,
+ sizeof(struct vbdev_ocf_qctx), vbdev->name);
+ result = spdk_bdev_register(&vbdev->exp_bdev);
+ if (result) {
+ SPDK_ERRLOG("Could not register exposed bdev %s\n",
+ vbdev->name);
+ vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, result);
+ return;
+ } else {
+ vbdev->state.started = true;
+ }
+
+ vbdev_ocf_mngt_continue(vbdev, result);
+}
+
+static void
+add_core_cmpl(ocf_cache_t cache, ocf_core_t core, void *priv, int error)
+{
+ struct vbdev_ocf *vbdev = priv;
+
+ ocf_mngt_cache_unlock(cache);
+
+ if (error) {
+ SPDK_ERRLOG("Error %d, failed to add core device to cache instance %s,"
+ "starting rollback\n", error, vbdev->name);
+ vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, error);
+ return;
+ } else {
+ vbdev->ocf_core = core;
+ }
+
+ vbdev_ocf_mngt_continue(vbdev, error);
+}
+
+/* Try to lock cache, then add core */
+static void
+add_core_cache_lock_cmpl(ocf_cache_t cache, void *priv, int error)
+{
+ struct vbdev_ocf *vbdev = (struct vbdev_ocf *)priv;
+
+ if (error) {
+ SPDK_ERRLOG("Error %d, can not lock cache instance %s,"
+ "starting rollback\n", error, vbdev->name);
+ vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, error);
+ }
+ ocf_mngt_cache_add_core(vbdev->ocf_cache, &vbdev->cfg.core, add_core_cmpl, vbdev);
+}
+
+/* Add core for existing OCF cache instance */
+static void
+add_core(struct vbdev_ocf *vbdev)
+{
+ ocf_mngt_cache_lock(vbdev->ocf_cache, add_core_cache_lock_cmpl, vbdev);
+}
+
+static void
+start_cache_cmpl(ocf_cache_t cache, void *priv, int error)
+{
+ struct vbdev_ocf *vbdev = priv;
+
+ ocf_mngt_cache_unlock(cache);
+
+ if (error) {
+ SPDK_ERRLOG("Error %d during start cache %s, starting rollback\n",
+ error, vbdev->name);
+ vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, error);
+ return;
+ }
+
+ vbdev_ocf_mngt_continue(vbdev, error);
+}
+
+static int
+create_management_queue(struct vbdev_ocf *vbdev)
+{
+ struct spdk_poller *mngt_poller;
+ int rc;
+
+ rc = vbdev_ocf_queue_create(vbdev->ocf_cache, &vbdev->cache_ctx->mngt_queue, &mngt_queue_ops);
+ if (rc) {
+ SPDK_ERRLOG("Unable to create mngt_queue: %d\n", rc);
+ return rc;
+ }
+
+ mngt_poller = SPDK_POLLER_REGISTER(mngt_queue_poll, vbdev->cache_ctx->mngt_queue, 100);
+ if (mngt_poller == NULL) {
+ SPDK_ERRLOG("Unable to initiate mngt request: %s", spdk_strerror(ENOMEM));
+ return -ENOMEM;
+ }
+
+ ocf_queue_set_priv(vbdev->cache_ctx->mngt_queue, mngt_poller);
+ ocf_mngt_cache_set_mngt_queue(vbdev->ocf_cache, vbdev->cache_ctx->mngt_queue);
+
+ return 0;
+}
+
+/* Start OCF cache, attach caching device */
+static void
+start_cache(struct vbdev_ocf *vbdev)
+{
+ ocf_cache_t existing;
+ int rc;
+
+ if (is_ocf_cache_running(vbdev)) {
+ vbdev_ocf_mngt_stop(vbdev, NULL, -EALREADY);
+ return;
+ }
+
+ existing = get_other_cache_instance(vbdev);
+ if (existing) {
+ SPDK_NOTICELOG("OCF bdev %s connects to existing cache device %s\n",
+ vbdev->name, vbdev->cache.name);
+ vbdev->ocf_cache = existing;
+ ocf_mngt_cache_get(vbdev->ocf_cache);
+ vbdev->cache_ctx = ocf_cache_get_priv(existing);
+ vbdev_ocf_cache_ctx_get(vbdev->cache_ctx);
+ vbdev_ocf_mngt_continue(vbdev, 0);
+ return;
+ }
+
+ vbdev->cache_ctx = calloc(1, sizeof(struct vbdev_ocf_cache_ctx));
+ if (vbdev->cache_ctx == NULL) {
+ vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, -ENOMEM);
+ return;
+ }
+
+ vbdev_ocf_cache_ctx_get(vbdev->cache_ctx);
+ pthread_mutex_init(&vbdev->cache_ctx->lock, NULL);
+
+ rc = ocf_mngt_cache_start(vbdev_ocf_ctx, &vbdev->ocf_cache, &vbdev->cfg.cache);
+ if (rc) {
+ vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, rc);
+ return;
+ }
+ ocf_mngt_cache_get(vbdev->ocf_cache);
+
+ ocf_cache_set_priv(vbdev->ocf_cache, vbdev->cache_ctx);
+
+ rc = create_management_queue(vbdev);
+ if (rc) {
+ SPDK_ERRLOG("Unable to create mngt_queue: %d\n", rc);
+ vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, rc);
+ return;
+ }
+
+ if (vbdev->cfg.loadq) {
+ ocf_mngt_cache_load(vbdev->ocf_cache, &vbdev->cfg.device, start_cache_cmpl, vbdev);
+ } else {
+ ocf_mngt_cache_attach(vbdev->ocf_cache, &vbdev->cfg.device, start_cache_cmpl, vbdev);
+ }
+}
+
+/* Procedures called during register operation */
+vbdev_ocf_mngt_fn register_path[] = {
+ start_cache,
+ add_core,
+ finish_register,
+ NULL
+};
+
+/* Start cache instance and register OCF bdev */
+static void
+register_vbdev(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_callback cb, void *cb_arg)
+{
+ int rc;
+
+ if (!(vbdev->core.attached && vbdev->cache.attached) || vbdev->state.started) {
+ cb(-EPERM, vbdev, cb_arg);
+ return;
+ }
+
+ vbdev->state.starting = true;
+ rc = vbdev_ocf_mngt_start(vbdev, register_path, cb, cb_arg);
+ if (rc) {
+ cb(rc, vbdev, cb_arg);
+ }
+}
+
+/* Init OCF configuration options
+ * for core and cache devices */
+static void
+init_vbdev_config(struct vbdev_ocf *vbdev)
+{
+ struct vbdev_ocf_config *cfg = &vbdev->cfg;
+
+ snprintf(cfg->cache.name, sizeof(cfg->cache.name), "%s", vbdev->name);
+ snprintf(cfg->core.name, sizeof(cfg->core.name), "%s", vbdev->core.name);
+
+ /* TODO [metadata]: make configurable with persistent
+ * metadata support */
+ cfg->cache.metadata_volatile = false;
+
+ /* TODO [cache line size]: make cache line size configurable
+ * Using standard 4KiB for now */
+ cfg->cache.cache_line_size = ocf_cache_line_size_4;
+
+ /* This are suggested values that
+ * should be sufficient for most use cases */
+ cfg->cache.backfill.max_queue_size = 65536;
+ cfg->cache.backfill.queue_unblock_size = 60000;
+
+ /* TODO [cache line size] */
+ cfg->device.cache_line_size = ocf_cache_line_size_4;
+ cfg->device.force = true;
+ cfg->device.perform_test = false;
+ cfg->device.discard_on_start = false;
+
+ vbdev->cfg.cache.locked = true;
+
+ cfg->core.volume_type = SPDK_OBJECT;
+ cfg->device.volume_type = SPDK_OBJECT;
+
+ if (vbdev->cfg.loadq) {
+ /* When doing cache_load(), we need to set try_add to true,
+ * otherwise OCF will interpret this core as new
+ * instead of the inactive one */
+ vbdev->cfg.core.try_add = true;
+ }
+
+ /* Serialize bdev names in OCF UUID to interpret on future loads
+ * Core UUID is a triple of (core name, vbdev name, cache name)
+ * Cache UUID is cache bdev name */
+ cfg->device.uuid.size = strlen(vbdev->cache.name) + 1;
+ cfg->device.uuid.data = vbdev->cache.name;
+
+ snprintf(vbdev->uuid, VBDEV_OCF_MD_MAX_LEN, "%s %s %s",
+ vbdev->core.name, vbdev->name, vbdev->cache.name);
+ cfg->core.uuid.size = strlen(vbdev->uuid) + 1;
+ cfg->core.uuid.data = vbdev->uuid;
+ vbdev->uuid[strlen(vbdev->core.name)] = 0;
+ vbdev->uuid[strlen(vbdev->core.name) + 1 + strlen(vbdev->name)] = 0;
+}
+
+/* Allocate vbdev structure object and add it to the global list */
+static int
+init_vbdev(const char *vbdev_name,
+ const char *cache_mode_name,
+ const char *cache_name,
+ const char *core_name,
+ bool loadq)
+{
+ struct vbdev_ocf *vbdev;
+ int rc = 0;
+
+ if (spdk_bdev_get_by_name(vbdev_name) || vbdev_ocf_get_by_name(vbdev_name)) {
+ SPDK_ERRLOG("Device with name '%s' already exists\n", vbdev_name);
+ return -EPERM;
+ }
+
+ vbdev = calloc(1, sizeof(*vbdev));
+ if (!vbdev) {
+ goto error_mem;
+ }
+
+ vbdev->cache.parent = vbdev;
+ vbdev->core.parent = vbdev;
+ vbdev->cache.is_cache = true;
+ vbdev->core.is_cache = false;
+
+ if (cache_mode_name) {
+ vbdev->cfg.cache.cache_mode
+ = ocf_get_cache_mode(cache_mode_name);
+ } else if (!loadq) { /* In load path it is OK to pass NULL as cache mode */
+ SPDK_ERRLOG("No cache mode specified\n");
+ rc = -EINVAL;
+ goto error_free;
+ }
+ if (vbdev->cfg.cache.cache_mode < 0) {
+ SPDK_ERRLOG("Incorrect cache mode '%s'\n", cache_mode_name);
+ rc = -EINVAL;
+ goto error_free;
+ }
+
+ vbdev->name = strdup(vbdev_name);
+ if (!vbdev->name) {
+ goto error_mem;
+ }
+
+ vbdev->cache.name = strdup(cache_name);
+ if (!vbdev->cache.name) {
+ goto error_mem;
+ }
+
+ vbdev->core.name = strdup(core_name);
+ if (!vbdev->core.name) {
+ goto error_mem;
+ }
+
+ vbdev->cfg.loadq = loadq;
+ init_vbdev_config(vbdev);
+ TAILQ_INSERT_TAIL(&g_ocf_vbdev_head, vbdev, tailq);
+ return rc;
+
+error_mem:
+ rc = -ENOMEM;
+error_free:
+ free_vbdev(vbdev);
+ return rc;
+}
+
+/* Read configuration file at the start of SPDK application
+ * This adds vbdevs to global list if some mentioned in config */
+static int
+vbdev_ocf_init(void)
+{
+ const char *vbdev_name, *modename, *cache_name, *core_name;
+ struct spdk_conf_section *sp;
+ int status;
+
+ status = vbdev_ocf_ctx_init();
+ if (status) {
+ SPDK_ERRLOG("OCF ctx initialization failed with=%d\n", status);
+ return status;
+ }
+
+ status = vbdev_ocf_volume_init();
+ if (status) {
+ vbdev_ocf_ctx_cleanup();
+ SPDK_ERRLOG("OCF volume initialization failed with=%d\n", status);
+ return status;
+ }
+
+ sp = spdk_conf_find_section(NULL, "OCF");
+ if (sp == NULL) {
+ return 0;
+ }
+
+ for (int i = 0; ; i++) {
+ if (!spdk_conf_section_get_nval(sp, "OCF", i)) {
+ break;
+ }
+
+ vbdev_name = spdk_conf_section_get_nmval(sp, "OCF", i, 0);
+ if (!vbdev_name) {
+ SPDK_ERRLOG("No vbdev name specified\n");
+ continue;
+ }
+
+ modename = spdk_conf_section_get_nmval(sp, "OCF", i, 1);
+ if (!modename) {
+ SPDK_ERRLOG("No modename specified for OCF vbdev '%s'\n", vbdev_name);
+ continue;
+ }
+
+ cache_name = spdk_conf_section_get_nmval(sp, "OCF", i, 2);
+ if (!cache_name) {
+ SPDK_ERRLOG("No cache device specified for OCF vbdev '%s'\n", vbdev_name);
+ continue;
+ }
+
+ core_name = spdk_conf_section_get_nmval(sp, "OCF", i, 3);
+ if (!core_name) {
+ SPDK_ERRLOG("No core devices specified for OCF vbdev '%s'\n", vbdev_name);
+ continue;
+ }
+
+ status = init_vbdev(vbdev_name, modename, cache_name, core_name, false);
+ if (status) {
+ SPDK_ERRLOG("Config initialization failed with code: %d\n", status);
+ }
+ }
+
+ return status;
+}
+
+/* Called after application shutdown started
+ * Release memory of allocated structures here */
+static void
+vbdev_ocf_module_fini(void)
+{
+ struct vbdev_ocf *vbdev;
+
+ while ((vbdev = TAILQ_FIRST(&g_ocf_vbdev_head))) {
+ TAILQ_REMOVE(&g_ocf_vbdev_head, vbdev, tailq);
+ free_vbdev(vbdev);
+ }
+
+ vbdev_ocf_volume_cleanup();
+ vbdev_ocf_ctx_cleanup();
+}
+
+/* When base device gets unpluged this is called
+ * We will unregister cache vbdev here
+ * When cache device is removed, we delete every OCF bdev that used it */
+static void
+hotremove_cb(void *ctx)
+{
+ struct vbdev_ocf_base *base = ctx;
+ struct vbdev_ocf *vbdev;
+
+ if (!base->is_cache) {
+ if (base->parent->state.doing_finish) {
+ return;
+ }
+
+ SPDK_NOTICELOG("Deinitializing '%s' because its core device '%s' was removed\n",
+ base->parent->name, base->name);
+ vbdev_ocf_delete(base->parent, NULL, NULL);
+ return;
+ }
+
+ TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
+ if (vbdev->state.doing_finish) {
+ continue;
+ }
+ if (strcmp(base->name, vbdev->cache.name) == 0) {
+ SPDK_NOTICELOG("Deinitializing '%s' because"
+ " its cache device '%s' was removed\n",
+ vbdev->name, base->name);
+ vbdev_ocf_delete(vbdev, NULL, NULL);
+ }
+ }
+}
+
+/* Open base SPDK bdev and claim it */
+static int
+attach_base(struct vbdev_ocf_base *base)
+{
+ int status;
+
+ if (base->attached) {
+ return -EALREADY;
+ }
+
+ /* If base cache bdev was already opened by other vbdev,
+ * we just copy its descriptor here */
+ if (base->is_cache) {
+ struct vbdev_ocf_base *existing = get_other_cache_base(base);
+ if (existing) {
+ base->desc = existing->desc;
+ base->management_channel = existing->management_channel;
+ base->attached = true;
+ return 0;
+ }
+ }
+
+ status = spdk_bdev_open(base->bdev, true, hotremove_cb, base, &base->desc);
+ if (status) {
+ SPDK_ERRLOG("Unable to open device '%s' for writing\n", base->name);
+ return status;
+ }
+
+ status = spdk_bdev_module_claim_bdev(base->bdev, base->desc,
+ &ocf_if);
+ if (status) {
+ SPDK_ERRLOG("Unable to claim device '%s'\n", base->name);
+ spdk_bdev_close(base->desc);
+ return status;
+ }
+
+ base->management_channel = spdk_bdev_get_io_channel(base->desc);
+ if (!base->management_channel) {
+ SPDK_ERRLOG("Unable to get io channel '%s'\n", base->name);
+ spdk_bdev_module_release_bdev(base->bdev);
+ spdk_bdev_close(base->desc);
+ return -ENOMEM;
+ }
+
+ /* Save the thread where the base device is opened */
+ base->thread = spdk_get_thread();
+
+ base->attached = true;
+ return status;
+}
+
+/* Attach base bdevs */
+static int
+attach_base_bdevs(struct vbdev_ocf *vbdev,
+ struct spdk_bdev *cache_bdev,
+ struct spdk_bdev *core_bdev)
+{
+ int rc = 0;
+
+ if (cache_bdev) {
+ vbdev->cache.bdev = cache_bdev;
+ rc |= attach_base(&vbdev->cache);
+ }
+
+ if (core_bdev) {
+ vbdev->core.bdev = core_bdev;
+ rc |= attach_base(&vbdev->core);
+ }
+
+ return rc;
+}
+
+/* Init and then start vbdev if all base devices are present */
+void
+vbdev_ocf_construct(const char *vbdev_name,
+ const char *cache_mode_name,
+ const char *cache_name,
+ const char *core_name,
+ bool loadq,
+ void (*cb)(int, struct vbdev_ocf *, void *),
+ void *cb_arg)
+{
+ int rc;
+ struct spdk_bdev *cache_bdev = spdk_bdev_get_by_name(cache_name);
+ struct spdk_bdev *core_bdev = spdk_bdev_get_by_name(core_name);
+ struct vbdev_ocf *vbdev;
+
+ rc = init_vbdev(vbdev_name, cache_mode_name, cache_name, core_name, loadq);
+ if (rc) {
+ cb(rc, NULL, cb_arg);
+ return;
+ }
+
+ vbdev = vbdev_ocf_get_by_name(vbdev_name);
+ if (vbdev == NULL) {
+ cb(-ENODEV, NULL, cb_arg);
+ return;
+ }
+
+ if (cache_bdev == NULL) {
+ SPDK_NOTICELOG("OCF bdev '%s' is waiting for cache device '%s' to connect\n",
+ vbdev->name, cache_name);
+ }
+ if (core_bdev == NULL) {
+ SPDK_NOTICELOG("OCF bdev '%s' is waiting for core device '%s' to connect\n",
+ vbdev->name, core_name);
+ }
+
+ rc = attach_base_bdevs(vbdev, cache_bdev, core_bdev);
+ if (rc) {
+ cb(rc, vbdev, cb_arg);
+ return;
+ }
+
+ if (core_bdev && cache_bdev) {
+ register_vbdev(vbdev, cb, cb_arg);
+ } else {
+ cb(0, vbdev, cb_arg);
+ }
+}
+
+/* This called if new device is created in SPDK application
+ * If that device named as one of base bdevs of OCF vbdev,
+ * claim and open them */
+static void
+vbdev_ocf_examine(struct spdk_bdev *bdev)
+{
+ const char *bdev_name = spdk_bdev_get_name(bdev);
+ struct vbdev_ocf *vbdev;
+
+ TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
+ if (vbdev->state.doing_finish) {
+ continue;
+ }
+
+ if (!strcmp(bdev_name, vbdev->cache.name)) {
+ attach_base_bdevs(vbdev, bdev, NULL);
+ continue;
+ }
+ if (!strcmp(bdev_name, vbdev->core.name)) {
+ attach_base_bdevs(vbdev, NULL, bdev);
+ break;
+ }
+ }
+ spdk_bdev_module_examine_done(&ocf_if);
+}
+
+struct metadata_probe_ctx {
+ struct vbdev_ocf_base base;
+ ocf_volume_t volume;
+
+ struct ocf_volume_uuid *core_uuids;
+ unsigned int uuid_count;
+
+ int result;
+ int refcnt;
+};
+
+static void
+_examine_ctx_put(void *ctx)
+{
+ struct spdk_bdev_desc *desc = ctx;
+
+ spdk_bdev_close(desc);
+}
+
+static void
+examine_ctx_put(struct metadata_probe_ctx *ctx)
+{
+ unsigned int i;
+
+ ctx->refcnt--;
+ if (ctx->refcnt > 0) {
+ return;
+ }
+
+ if (ctx->result) {
+ SPDK_ERRLOG("OCF metadata probe for bdev '%s' failed with %d\n",
+ spdk_bdev_get_name(ctx->base.bdev), ctx->result);
+ }
+
+ if (ctx->base.desc) {
+ /* Close the underlying bdev on its same opened thread. */
+ if (ctx->base.thread && ctx->base.thread != spdk_get_thread()) {
+ spdk_thread_send_msg(ctx->base.thread, _examine_ctx_put, ctx->base.desc);
+ } else {
+ spdk_bdev_close(ctx->base.desc);
+ }
+ }
+
+ if (ctx->volume) {
+ ocf_volume_destroy(ctx->volume);
+ }
+
+ if (ctx->core_uuids) {
+ for (i = 0; i < ctx->uuid_count; i++) {
+ free(ctx->core_uuids[i].data);
+ }
+ }
+ free(ctx->core_uuids);
+
+ examine_done(ctx->result, NULL, ctx->base.bdev);
+ free(ctx);
+}
+
+static void
+metadata_probe_construct_cb(int rc, struct vbdev_ocf *vbdev, void *vctx)
+{
+ struct metadata_probe_ctx *ctx = vctx;
+
+ examine_ctx_put(ctx);
+}
+
+/* This is second callback for ocf_metadata_probe_cores()
+ * Here we create vbdev configurations based on UUIDs */
+static void
+metadata_probe_cores_construct(void *priv, int error, unsigned int num_cores)
+{
+ struct metadata_probe_ctx *ctx = priv;
+ const char *vbdev_name;
+ const char *core_name;
+ const char *cache_name;
+ unsigned int i;
+
+ if (error) {
+ ctx->result = error;
+ examine_ctx_put(ctx);
+ return;
+ }
+
+ for (i = 0; i < num_cores; i++) {
+ core_name = ocf_uuid_to_str(&ctx->core_uuids[i]);
+ vbdev_name = core_name + strlen(core_name) + 1;
+ cache_name = vbdev_name + strlen(vbdev_name) + 1;
+
+ if (strcmp(ctx->base.bdev->name, cache_name)) {
+ SPDK_NOTICELOG("OCF metadata found on %s belongs to bdev named '%s'\n",
+ ctx->base.bdev->name, cache_name);
+ }
+
+ ctx->refcnt++;
+ vbdev_ocf_construct(vbdev_name, NULL, cache_name, core_name, true,
+ metadata_probe_construct_cb, ctx);
+ }
+
+ examine_ctx_put(ctx);
+}
+
+/* This callback is called after OCF reads cores UUIDs from cache metadata
+ * Here we allocate memory for those UUIDs and call ocf_metadata_probe_cores() again */
+static void
+metadata_probe_cores_get_num(void *priv, int error, unsigned int num_cores)
+{
+ struct metadata_probe_ctx *ctx = priv;
+ unsigned int i;
+
+ if (error) {
+ ctx->result = error;
+ examine_ctx_put(ctx);
+ return;
+ }
+
+ ctx->uuid_count = num_cores;
+ ctx->core_uuids = calloc(num_cores, sizeof(struct ocf_volume_uuid));
+ if (!ctx->core_uuids) {
+ ctx->result = -ENOMEM;
+ examine_ctx_put(ctx);
+ return;
+ }
+
+ for (i = 0; i < ctx->uuid_count; i++) {
+ ctx->core_uuids[i].size = OCF_VOLUME_UUID_MAX_SIZE;
+ ctx->core_uuids[i].data = malloc(OCF_VOLUME_UUID_MAX_SIZE);
+ if (!ctx->core_uuids[i].data) {
+ ctx->result = -ENOMEM;
+ examine_ctx_put(ctx);
+ return;
+ }
+ }
+
+ ocf_metadata_probe_cores(vbdev_ocf_ctx, ctx->volume, ctx->core_uuids, ctx->uuid_count,
+ metadata_probe_cores_construct, ctx);
+}
+
+static void
+metadata_probe_cb(void *priv, int rc,
+ struct ocf_metadata_probe_status *status)
+{
+ struct metadata_probe_ctx *ctx = priv;
+
+ if (rc) {
+ /* -ENODATA means device does not have cache metadata on it */
+ if (rc != -OCF_ERR_NO_METADATA) {
+ ctx->result = rc;
+ }
+ examine_ctx_put(ctx);
+ return;
+ }
+
+ ocf_metadata_probe_cores(vbdev_ocf_ctx, ctx->volume, NULL, 0,
+ metadata_probe_cores_get_num, ctx);
+}
+
+/* This is called after vbdev_ocf_examine
+ * It allows to delay application initialization
+ * until all OCF bdevs get registered
+ * If vbdev has all of its base devices it starts asynchronously here
+ * We first check if bdev appears in configuration,
+ * if not we do metadata_probe() to create its configuration from bdev metadata */
+static void
+vbdev_ocf_examine_disk(struct spdk_bdev *bdev)
+{
+ const char *bdev_name = spdk_bdev_get_name(bdev);
+ struct vbdev_ocf *vbdev;
+ struct metadata_probe_ctx *ctx;
+ bool created_from_config = false;
+ int rc;
+
+ examine_start(bdev);
+
+ TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
+ if (vbdev->state.doing_finish || vbdev->state.started) {
+ continue;
+ }
+
+ if (!strcmp(bdev_name, vbdev->cache.name)) {
+ examine_start(bdev);
+ register_vbdev(vbdev, examine_done, bdev);
+ created_from_config = true;
+ continue;
+ }
+ if (!strcmp(bdev_name, vbdev->core.name)) {
+ examine_start(bdev);
+ register_vbdev(vbdev, examine_done, bdev);
+ examine_done(0, NULL, bdev);
+ return;
+ }
+ }
+
+ /* If devices is discovered during config we do not check for metadata */
+ if (created_from_config) {
+ examine_done(0, NULL, bdev);
+ return;
+ }
+
+ /* Metadata probe path
+ * We create temporary OCF volume and a temporary base structure
+ * to use them for ocf_metadata_probe() and for bottom adapter IOs
+ * Then we get UUIDs of core devices an create configurations based on them */
+ ctx = calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ examine_done(-ENOMEM, NULL, bdev);
+ return;
+ }
+
+ ctx->base.bdev = bdev;
+ ctx->refcnt = 1;
+
+ rc = spdk_bdev_open(ctx->base.bdev, true, NULL, NULL, &ctx->base.desc);
+ if (rc) {
+ ctx->result = rc;
+ examine_ctx_put(ctx);
+ return;
+ }
+
+ rc = ocf_ctx_volume_create(vbdev_ocf_ctx, &ctx->volume, NULL, SPDK_OBJECT);
+ if (rc) {
+ ctx->result = rc;
+ examine_ctx_put(ctx);
+ return;
+ }
+
+ rc = ocf_volume_open(ctx->volume, &ctx->base);
+ if (rc) {
+ ctx->result = rc;
+ examine_ctx_put(ctx);
+ return;
+ }
+
+ /* Save the thread where the base device is opened */
+ ctx->base.thread = spdk_get_thread();
+
+ ocf_metadata_probe(vbdev_ocf_ctx, ctx->volume, metadata_probe_cb, ctx);
+}
+
+static int
+vbdev_ocf_get_ctx_size(void)
+{
+ return sizeof(struct bdev_ocf_data);
+}
+
+static void
+fini_start(void)
+{
+ g_fini_started = true;
+}
+
+/* Module-global function table
+ * Does not relate to vbdev instances */
+static struct spdk_bdev_module ocf_if = {
+ .name = "ocf",
+ .module_init = vbdev_ocf_init,
+ .fini_start = fini_start,
+ .module_fini = vbdev_ocf_module_fini,
+ .config_text = NULL,
+ .get_ctx_size = vbdev_ocf_get_ctx_size,
+ .examine_config = vbdev_ocf_examine,
+ .examine_disk = vbdev_ocf_examine_disk,
+};
+SPDK_BDEV_MODULE_REGISTER(ocf, &ocf_if);
+
+SPDK_LOG_REGISTER_COMPONENT("vbdev_ocf", SPDK_TRACE_VBDEV_OCF)
diff --git a/src/spdk/module/bdev/ocf/vbdev_ocf.h b/src/spdk/module/bdev/ocf/vbdev_ocf.h
new file mode 100644
index 000000000..d0fd0b183
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/vbdev_ocf.h
@@ -0,0 +1,210 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SPDK_VBDEV_OCF_H
+#define SPDK_VBDEV_OCF_H
+
+#include <ocf/ocf.h>
+
+#include "spdk/bdev.h"
+#include "spdk/bdev_module.h"
+
+#define VBDEV_OCF_MD_MAX_LEN 4096
+
+struct vbdev_ocf;
+
+/* Context for OCF queue poller
+ * Used for mapping SPDK threads to OCF queues */
+struct vbdev_ocf_qctx {
+ /* OCF queue. Contains OCF requests */
+ struct ocf_queue *queue;
+ /* Poller for OCF queue. Runs OCF requests */
+ struct spdk_poller *poller;
+ /* Reference to parent vbdev */
+ struct vbdev_ocf *vbdev;
+ /* Base devices channels */
+ struct spdk_io_channel *cache_ch;
+ struct spdk_io_channel *core_ch;
+ /* If true, we have to free this context on queue stop */
+ bool allocated;
+ /* Link to per-bdev list of queue contexts */
+ TAILQ_ENTRY(vbdev_ocf_qctx) tailq;
+};
+
+/* Important states */
+struct vbdev_ocf_state {
+ /* From the moment when clean delete started */
+ bool doing_clean_delete;
+ /* From the moment when finish started */
+ bool doing_finish;
+ /* From the moment when reset IO recieved, until it is completed */
+ bool doing_reset;
+ /* From the moment when exp_bdev is registered */
+ bool started;
+ /* From the moment when register path started */
+ bool starting;
+ /* Status of last attempt for stopping this device */
+ int stop_status;
+};
+
+/*
+ * OCF cache configuration options
+ */
+struct vbdev_ocf_config {
+ /* Initial cache configuration */
+ struct ocf_mngt_cache_config cache;
+
+ /* Cache device config */
+ struct ocf_mngt_cache_device_config device;
+
+ /* Core initial config */
+ struct ocf_mngt_core_config core;
+
+ /* Load flag, if set to true, then we will try load cache instance from disk,
+ * otherwise we will create new cache on that disk */
+ bool loadq;
+};
+
+/* Types for management operations */
+typedef void (*vbdev_ocf_mngt_fn)(struct vbdev_ocf *);
+typedef void (*vbdev_ocf_mngt_callback)(int, struct vbdev_ocf *, void *);
+
+/* Context for asynchronous management operations
+ * Single management operation usually contains a list of sub procedures,
+ * this structure handles sharing between those sub procedures */
+struct vbdev_ocf_mngt_ctx {
+ /* Pointer to function that is currently being executed
+ * It gets incremented on each step until it dereferences to NULL */
+ vbdev_ocf_mngt_fn *current_step;
+
+ /* Function that gets invoked by poller on each iteration */
+ vbdev_ocf_mngt_fn poller_fn;
+ /* Poller timeout time stamp - when the poller should stop with error */
+ uint64_t timeout_ts;
+
+ /* Status of management operation */
+ int status;
+
+ /* External callback and its argument */
+ vbdev_ocf_mngt_callback cb;
+ void *cb_arg;
+};
+
+/* Base device info */
+struct vbdev_ocf_base {
+ /* OCF internal name */
+ char *name;
+
+ /* True if this is a caching device */
+ bool is_cache;
+
+ /* Connected SPDK block device */
+ struct spdk_bdev *bdev;
+
+ /* SPDK device io handle */
+ struct spdk_bdev_desc *desc;
+
+ /* True if SPDK bdev has been claimed and opened for writing */
+ bool attached;
+
+ /* Channel for cleaner operations */
+ struct spdk_io_channel *management_channel;
+
+ /* Reference to main vbdev */
+ struct vbdev_ocf *parent;
+
+ /* thread where base device is opened */
+ struct spdk_thread *thread;
+};
+
+/*
+ * The main information provider
+ * It's also registered as io_device
+ */
+struct vbdev_ocf {
+ /* Exposed unique name */
+ char *name;
+
+ /* Base bdevs */
+ struct vbdev_ocf_base cache;
+ struct vbdev_ocf_base core;
+
+ /* Base bdevs OCF objects */
+ ocf_cache_t ocf_cache;
+ ocf_core_t ocf_core;
+
+ /* Parameters */
+ struct vbdev_ocf_config cfg;
+ struct vbdev_ocf_state state;
+
+ /* Management context */
+ struct vbdev_ocf_mngt_ctx mngt_ctx;
+ /* Cache conext */
+ struct vbdev_ocf_cache_ctx *cache_ctx;
+
+ /* Exposed SPDK bdev. Registered in bdev layer */
+ struct spdk_bdev exp_bdev;
+
+ /* OCF uuid for core device of this vbdev */
+ char uuid[VBDEV_OCF_MD_MAX_LEN];
+
+ /* Link to global list of this type structures */
+ TAILQ_ENTRY(vbdev_ocf) tailq;
+};
+
+void vbdev_ocf_construct(
+ const char *vbdev_name,
+ const char *cache_mode_name,
+ const char *cache_name,
+ const char *core_name,
+ bool loadq,
+ void (*cb)(int, struct vbdev_ocf *, void *),
+ void *cb_arg);
+
+/* If vbdev is online, return its object */
+struct vbdev_ocf *vbdev_ocf_get_by_name(const char *name);
+
+/* Return matching base if parent vbdev is online */
+struct vbdev_ocf_base *vbdev_ocf_get_base_by_name(const char *name);
+
+/* Stop OCF cache and unregister SPDK bdev */
+int vbdev_ocf_delete(struct vbdev_ocf *vbdev, void (*cb)(void *, int), void *cb_arg);
+
+int vbdev_ocf_delete_clean(struct vbdev_ocf *vbdev, void (*cb)(void *, int), void *cb_arg);
+
+typedef void (*vbdev_ocf_foreach_fn)(struct vbdev_ocf *, void *);
+
+/* Execute fn for each OCF device that is online or waits for base devices */
+void vbdev_ocf_foreach(vbdev_ocf_foreach_fn fn, void *ctx);
+
+#endif
diff --git a/src/spdk/module/bdev/ocf/vbdev_ocf_rpc.c b/src/spdk/module/bdev/ocf/vbdev_ocf_rpc.c
new file mode 100644
index 000000000..89286fe23
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/vbdev_ocf_rpc.c
@@ -0,0 +1,362 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "vbdev_ocf.h"
+#include "stats.h"
+#include "spdk/log.h"
+#include "spdk/rpc.h"
+#include "spdk/string.h"
+
+/* Structure to hold the parameters for this RPC method. */
+struct rpc_bdev_ocf_create {
+ char *name; /* master vbdev */
+ char *mode; /* OCF mode (choose one) */
+ char *cache_bdev_name; /* sub bdev */
+ char *core_bdev_name; /* sub bdev */
+};
+
+static void
+free_rpc_bdev_ocf_create(struct rpc_bdev_ocf_create *r)
+{
+ free(r->name);
+ free(r->core_bdev_name);
+ free(r->cache_bdev_name);
+ free(r->mode);
+}
+
+/* Structure to decode the input parameters for this RPC method. */
+static const struct spdk_json_object_decoder rpc_bdev_ocf_create_decoders[] = {
+ {"name", offsetof(struct rpc_bdev_ocf_create, name), spdk_json_decode_string},
+ {"mode", offsetof(struct rpc_bdev_ocf_create, mode), spdk_json_decode_string},
+ {"cache_bdev_name", offsetof(struct rpc_bdev_ocf_create, cache_bdev_name), spdk_json_decode_string},
+ {"core_bdev_name", offsetof(struct rpc_bdev_ocf_create, core_bdev_name), spdk_json_decode_string},
+};
+
+static void
+construct_cb(int status, struct vbdev_ocf *vbdev, void *cb_arg)
+{
+ struct spdk_jsonrpc_request *request = cb_arg;
+ struct spdk_json_write_ctx *w;
+
+ if (status) {
+ spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
+ "Could not create OCF vbdev: %d",
+ status);
+ } else {
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_string(w, vbdev->name);
+ spdk_jsonrpc_end_result(request, w);
+ }
+}
+
+static void
+rpc_bdev_ocf_create(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_bdev_ocf_create req = {NULL};
+ int ret;
+
+ ret = spdk_json_decode_object(params, rpc_bdev_ocf_create_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_create_decoders),
+ &req);
+ if (ret) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ free_rpc_bdev_ocf_create(&req);
+ return;
+ }
+
+ vbdev_ocf_construct(req.name, req.mode, req.cache_bdev_name, req.core_bdev_name, false,
+ construct_cb, request);
+ free_rpc_bdev_ocf_create(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_create", rpc_bdev_ocf_create, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(bdev_ocf_create, construct_ocf_bdev)
+
+/* Structure to hold the parameters for this RPC method. */
+struct rpc_bdev_ocf_delete {
+ char *name; /* master vbdev name */
+};
+
+static void
+free_rpc_bdev_ocf_delete(struct rpc_bdev_ocf_delete *r)
+{
+ free(r->name);
+}
+
+/* Structure to decode the input parameters for this RPC method. */
+static const struct spdk_json_object_decoder rpc_bdev_ocf_delete_decoders[] = {
+ {"name", offsetof(struct rpc_bdev_ocf_delete, name), spdk_json_decode_string},
+};
+
+static void
+delete_cb(void *cb_arg, int status)
+{
+ struct spdk_jsonrpc_request *request = cb_arg;
+ struct spdk_json_write_ctx *w;
+
+ if (status) {
+ spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
+ "Could not delete OCF vbdev: %d",
+ status);
+ } else {
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ }
+}
+
+static void
+rpc_bdev_ocf_delete(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_bdev_ocf_delete req = {NULL};
+ struct vbdev_ocf *vbdev;
+ int status;
+
+ status = spdk_json_decode_object(params, rpc_bdev_ocf_delete_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_delete_decoders),
+ &req);
+ if (status) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ goto end;
+ }
+
+ vbdev = vbdev_ocf_get_by_name(req.name);
+ if (vbdev == NULL) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(ENODEV));
+ goto end;
+ }
+
+ status = vbdev_ocf_delete_clean(vbdev, delete_cb, request);
+ if (status) {
+ spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
+ "Could not delete OCF vbdev: %s",
+ spdk_strerror(-status));
+ goto end;
+ }
+
+end:
+ free_rpc_bdev_ocf_delete(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_delete", rpc_bdev_ocf_delete, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(bdev_ocf_delete, delete_ocf_bdev)
+
+/* Structure to hold the parameters for this RPC method. */
+struct rpc_bdev_ocf_get_stats {
+ char *name; /* master vbdev name */
+};
+
+static void
+free_rpc_bdev_ocf_get_stats(struct rpc_bdev_ocf_get_stats *r)
+{
+ free(r->name);
+}
+
+/* Structure to decode the input parameters for this RPC method. */
+static const struct spdk_json_object_decoder rpc_bdev_ocf_get_stats_decoders[] = {
+ {"name", offsetof(struct rpc_bdev_ocf_get_stats, name), spdk_json_decode_string},
+};
+
+struct get_ocf_stats_ctx {
+ struct spdk_jsonrpc_request *request;
+ char *core_name;
+};
+
+static void
+rpc_bdev_ocf_get_stats_cmpl(ocf_cache_t cache, void *priv, int error)
+{
+ struct get_ocf_stats_ctx *ctx = (struct get_ocf_stats_ctx *) priv;
+ struct spdk_json_write_ctx *w;
+ struct vbdev_ocf_stats stats;
+
+ if (error) {
+ goto end;
+ }
+
+ error = vbdev_ocf_stats_get(cache, ctx->core_name, &stats);
+
+ ocf_mngt_cache_read_unlock(cache);
+
+ if (error) {
+ goto end;
+ }
+
+ w = spdk_jsonrpc_begin_result(ctx->request);
+ vbdev_ocf_stats_write_json(w, &stats);
+ spdk_jsonrpc_end_result(ctx->request, w);
+
+end:
+ if (error) {
+ spdk_jsonrpc_send_error_response_fmt(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
+ "Could not get stats: %s",
+ spdk_strerror(-error));
+ }
+ free(ctx);
+}
+
+static void
+rpc_bdev_ocf_get_stats(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_bdev_ocf_get_stats req = {NULL};
+ struct vbdev_ocf *vbdev;
+ struct get_ocf_stats_ctx *ctx;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Not enough memory to process request");
+ goto end;
+ }
+
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_get_stats_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_get_stats_decoders),
+ &req)) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ free(ctx);
+ goto end;
+ }
+
+ vbdev = vbdev_ocf_get_by_name(req.name);
+ if (vbdev == NULL) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(ENODEV));
+ free(ctx);
+ goto end;
+ }
+
+ ctx->core_name = vbdev->core.name;
+ ctx->request = request;
+ ocf_mngt_cache_read_lock(vbdev->ocf_cache, rpc_bdev_ocf_get_stats_cmpl, ctx);
+
+end:
+ free_rpc_bdev_ocf_get_stats(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_get_stats", rpc_bdev_ocf_get_stats, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(bdev_ocf_get_stats, get_ocf_stats)
+
+/* Structure to hold the parameters for this RPC method. */
+struct rpc_bdev_ocf_get_bdevs {
+ char *name;
+};
+
+static void
+free_rpc_bdev_ocf_get_bdevs(struct rpc_bdev_ocf_get_bdevs *r)
+{
+ free(r->name);
+}
+
+/* Structure to decode the input parameters for this RPC method. */
+static const struct spdk_json_object_decoder rpc_bdev_ocf_get_bdevs_decoders[] = {
+ {"name", offsetof(struct rpc_bdev_ocf_get_bdevs, name), spdk_json_decode_string, true},
+};
+
+struct bdev_get_bdevs_ctx {
+ char *name;
+ struct spdk_json_write_ctx *w;
+};
+
+static void
+bdev_get_bdevs_fn(struct vbdev_ocf *vbdev, void *ctx)
+{
+ struct bdev_get_bdevs_ctx *cctx = ctx;
+ struct spdk_json_write_ctx *w = cctx->w;
+
+ if (cctx->name != NULL &&
+ strcmp(vbdev->name, cctx->name) &&
+ strcmp(vbdev->cache.name, cctx->name) &&
+ strcmp(vbdev->core.name, cctx->name)) {
+ return;
+ }
+
+ spdk_json_write_object_begin(w);
+ spdk_json_write_named_string(w, "name", vbdev->name);
+ spdk_json_write_named_bool(w, "started", vbdev->state.started);
+
+ spdk_json_write_named_object_begin(w, "cache");
+ spdk_json_write_named_string(w, "name", vbdev->cache.name);
+ spdk_json_write_named_bool(w, "attached", vbdev->cache.attached);
+ spdk_json_write_object_end(w);
+
+ spdk_json_write_named_object_begin(w, "core");
+ spdk_json_write_named_string(w, "name", vbdev->core.name);
+ spdk_json_write_named_bool(w, "attached", vbdev->core.attached);
+ spdk_json_write_object_end(w);
+
+ spdk_json_write_object_end(w);
+}
+
+static void
+rpc_bdev_ocf_get_bdevs(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct spdk_json_write_ctx *w;
+ struct rpc_bdev_ocf_get_bdevs req = {NULL};
+ struct bdev_get_bdevs_ctx cctx;
+
+ if (params && spdk_json_decode_object(params, rpc_bdev_ocf_get_bdevs_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_get_bdevs_decoders),
+ &req)) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ goto end;
+ }
+
+ if (req.name) {
+ if (!(vbdev_ocf_get_by_name(req.name) || vbdev_ocf_get_base_by_name(req.name))) {
+ spdk_jsonrpc_send_error_response(request,
+ SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ spdk_strerror(ENODEV));
+ goto end;
+ }
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+
+ cctx.name = req.name;
+ cctx.w = w;
+
+ spdk_json_write_array_begin(w);
+ vbdev_ocf_foreach(bdev_get_bdevs_fn, &cctx);
+ spdk_json_write_array_end(w);
+ spdk_jsonrpc_end_result(request, w);
+
+end:
+ free_rpc_bdev_ocf_get_bdevs(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_get_bdevs", rpc_bdev_ocf_get_bdevs, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(bdev_ocf_get_bdevs, get_ocf_bdevs)
diff --git a/src/spdk/module/bdev/ocf/volume.c b/src/spdk/module/bdev/ocf/volume.c
new file mode 100644
index 000000000..de683b852
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/volume.c
@@ -0,0 +1,441 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ocf/ocf.h>
+
+#include "spdk/bdev_module.h"
+#include "spdk/env.h"
+#include "spdk/thread.h"
+#include "spdk_internal/log.h"
+
+#include "data.h"
+#include "volume.h"
+#include "ctx.h"
+#include "vbdev_ocf.h"
+
+static int
+vbdev_ocf_volume_open(ocf_volume_t volume, void *opts)
+{
+ struct vbdev_ocf_base **priv = ocf_volume_get_priv(volume);
+ struct vbdev_ocf_base *base;
+
+ if (opts) {
+ base = opts;
+ } else {
+ base = vbdev_ocf_get_base_by_name(ocf_volume_get_uuid(volume)->data);
+ if (base == NULL) {
+ return -ENODEV;
+ }
+ }
+
+ *priv = base;
+
+ return 0;
+}
+
+static void
+vbdev_ocf_volume_close(ocf_volume_t volume)
+{
+}
+
+static uint64_t
+vbdev_ocf_volume_get_length(ocf_volume_t volume)
+{
+ struct vbdev_ocf_base *base = *((struct vbdev_ocf_base **)ocf_volume_get_priv(volume));
+ uint64_t len;
+
+ len = base->bdev->blocklen * base->bdev->blockcnt;
+
+ return len;
+}
+
+static int
+vbdev_ocf_volume_io_set_data(struct ocf_io *io, ctx_data_t *data,
+ uint32_t offset)
+{
+ struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io);
+
+ io_ctx->offset = offset;
+ io_ctx->data = data;
+
+ if (io_ctx->data && offset >= io_ctx->data->size) {
+ return -ENOBUFS;
+ }
+
+ return 0;
+}
+
+static ctx_data_t *
+vbdev_ocf_volume_io_get_data(struct ocf_io *io)
+{
+ return ocf_get_io_ctx(io)->data;
+}
+
+static void
+vbdev_ocf_volume_io_get(struct ocf_io *io)
+{
+ struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io);
+
+ io_ctx->ref++;
+}
+
+static void
+vbdev_ocf_volume_io_put(struct ocf_io *io)
+{
+ struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io);
+
+ if (--io_ctx->ref) {
+ return;
+ }
+}
+
+static int
+get_starting_vec(struct iovec *iovs, int iovcnt, int *offset)
+{
+ int i;
+ size_t off;
+
+ off = *offset;
+
+ for (i = 0; i < iovcnt; i++) {
+ if (off < iovs[i].iov_len) {
+ *offset = off;
+ return i;
+ }
+ off -= iovs[i].iov_len;
+ }
+
+ return -1;
+}
+
+static void
+initialize_cpy_vector(struct iovec *cpy_vec, int cpy_vec_len, struct iovec *orig_vec,
+ int orig_vec_len,
+ size_t offset, size_t bytes)
+{
+ void *curr_base;
+ int len, i;
+
+ i = 0;
+
+ while (bytes > 0) {
+ curr_base = orig_vec[i].iov_base + offset;
+ len = MIN(bytes, orig_vec[i].iov_len - offset);
+
+ cpy_vec[i].iov_base = curr_base;
+ cpy_vec[i].iov_len = len;
+
+ bytes -= len;
+ offset = 0;
+ i++;
+ }
+}
+
+static void
+vbdev_ocf_volume_submit_io_cb(struct spdk_bdev_io *bdev_io, bool success, void *opaque)
+{
+ struct ocf_io *io;
+ struct ocf_io_ctx *io_ctx;
+
+ assert(opaque);
+
+ io = opaque;
+ io_ctx = ocf_get_io_ctx(io);
+ assert(io_ctx != NULL);
+
+ if (!success) {
+ io_ctx->error |= 1;
+ }
+
+ if (io_ctx->iovs_allocated && bdev_io != NULL) {
+ env_free(bdev_io->u.bdev.iovs);
+ }
+
+ if (io_ctx->error) {
+ SPDK_DEBUGLOG(SPDK_TRACE_VBDEV_OCF_VOLUME,
+ "base returned error on io submission: %d\n", io_ctx->error);
+ }
+
+ if (io->io_queue == NULL && io_ctx->ch != NULL) {
+ spdk_put_io_channel(io_ctx->ch);
+ }
+
+ vbdev_ocf_volume_io_put(io);
+ if (bdev_io) {
+ spdk_bdev_free_io(bdev_io);
+ }
+
+ if (--io_ctx->rq_cnt == 0) {
+ io->end(io, io_ctx->error);
+ }
+}
+
+static int
+prepare_submit(struct ocf_io *io)
+{
+ struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io);
+ struct vbdev_ocf_qctx *qctx;
+ struct vbdev_ocf_base *base;
+ ocf_queue_t q = io->io_queue;
+ ocf_cache_t cache;
+ struct vbdev_ocf_cache_ctx *cctx;
+ int rc = 0;
+
+ io_ctx->rq_cnt++;
+ if (io_ctx->rq_cnt != 1) {
+ return 0;
+ }
+
+ vbdev_ocf_volume_io_get(io);
+ base = *((struct vbdev_ocf_base **)ocf_volume_get_priv(ocf_io_get_volume(io)));
+
+ if (io->io_queue == NULL) {
+ /* In case IO is initiated by OCF, queue is unknown
+ * so we have to get io channel ourselves */
+ io_ctx->ch = spdk_bdev_get_io_channel(base->desc);
+ if (io_ctx->ch == NULL) {
+ return -EPERM;
+ }
+ return 0;
+ }
+
+ cache = ocf_queue_get_cache(q);
+ cctx = ocf_cache_get_priv(cache);
+
+ if (q == cctx->cleaner_queue || q == cctx->mngt_queue) {
+ io_ctx->ch = base->management_channel;
+ return 0;
+ }
+
+ qctx = ocf_queue_get_priv(q);
+ if (qctx == NULL) {
+ return -EFAULT;
+ }
+
+ if (base->is_cache) {
+ io_ctx->ch = qctx->cache_ch;
+ } else {
+ io_ctx->ch = qctx->core_ch;
+ }
+
+ return rc;
+}
+
+static void
+vbdev_ocf_volume_submit_flush(struct ocf_io *io)
+{
+ struct vbdev_ocf_base *base =
+ *((struct vbdev_ocf_base **)
+ ocf_volume_get_priv(ocf_io_get_volume(io)));
+ struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io);
+ int status;
+
+ status = prepare_submit(io);
+ if (status) {
+ SPDK_ERRLOG("Preparing io failed with status=%d\n", status);
+ vbdev_ocf_volume_submit_io_cb(NULL, false, io);
+ return;
+ }
+
+ status = spdk_bdev_flush(
+ base->desc, io_ctx->ch,
+ io->addr, io->bytes,
+ vbdev_ocf_volume_submit_io_cb, io);
+ if (status) {
+ /* Since callback is not called, we need to do it manually to free io structures */
+ SPDK_ERRLOG("Submission failed with status=%d\n", status);
+ vbdev_ocf_volume_submit_io_cb(NULL, false, io);
+ }
+}
+
+static void
+vbdev_ocf_volume_submit_io(struct ocf_io *io)
+{
+ struct vbdev_ocf_base *base =
+ *((struct vbdev_ocf_base **)
+ ocf_volume_get_priv(ocf_io_get_volume(io)));
+ struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io);
+ struct iovec *iovs;
+ int iovcnt, status = 0, i, offset;
+ uint64_t addr, len;
+
+ if (io->flags == OCF_WRITE_FLUSH) {
+ vbdev_ocf_volume_submit_flush(io);
+ return;
+ }
+
+ status = prepare_submit(io);
+ if (status) {
+ SPDK_ERRLOG("Preparing io failed with status=%d\n", status);
+ vbdev_ocf_volume_submit_io_cb(NULL, false, io);
+ return;
+ }
+
+ /* IO fields */
+ addr = io->addr;
+ len = io->bytes;
+ offset = io_ctx->offset;
+
+ if (len < io_ctx->data->size) {
+ if (io_ctx->data->iovcnt == 1) {
+ if (io->dir == OCF_READ) {
+ status = spdk_bdev_read(base->desc, io_ctx->ch,
+ io_ctx->data->iovs[0].iov_base + offset, addr, len,
+ vbdev_ocf_volume_submit_io_cb, io);
+ } else if (io->dir == OCF_WRITE) {
+ status = spdk_bdev_write(base->desc, io_ctx->ch,
+ io_ctx->data->iovs[0].iov_base + offset, addr, len,
+ vbdev_ocf_volume_submit_io_cb, io);
+ }
+ goto end;
+ } else {
+ i = get_starting_vec(io_ctx->data->iovs, io_ctx->data->iovcnt, &offset);
+
+ if (i < 0) {
+ SPDK_ERRLOG("offset bigger than data size\n");
+ vbdev_ocf_volume_submit_io_cb(NULL, false, io);
+ return;
+ }
+
+ iovcnt = io_ctx->data->iovcnt - i;
+
+ io_ctx->iovs_allocated = true;
+ iovs = env_malloc(sizeof(*iovs) * iovcnt, ENV_MEM_NOIO);
+
+ if (!iovs) {
+ SPDK_ERRLOG("allocation failed\n");
+ vbdev_ocf_volume_submit_io_cb(NULL, false, io);
+ return;
+ }
+
+ initialize_cpy_vector(iovs, io_ctx->data->iovcnt, &io_ctx->data->iovs[i],
+ iovcnt, offset, len);
+ }
+ } else {
+ iovs = io_ctx->data->iovs;
+ iovcnt = io_ctx->data->iovcnt;
+ }
+
+ if (io->dir == OCF_READ) {
+ status = spdk_bdev_readv(base->desc, io_ctx->ch,
+ iovs, iovcnt, addr, len, vbdev_ocf_volume_submit_io_cb, io);
+ } else if (io->dir == OCF_WRITE) {
+ status = spdk_bdev_writev(base->desc, io_ctx->ch,
+ iovs, iovcnt, addr, len, vbdev_ocf_volume_submit_io_cb, io);
+ }
+
+end:
+ if (status) {
+ /* TODO [ENOMEM]: implement ENOMEM handling when submitting IO to base device */
+
+ /* Since callback is not called, we need to do it manually to free io structures */
+ SPDK_ERRLOG("submission failed with status=%d\n", status);
+ vbdev_ocf_volume_submit_io_cb(NULL, false, io);
+ }
+}
+
+static void
+vbdev_ocf_volume_submit_discard(struct ocf_io *io)
+{
+ struct vbdev_ocf_base *base =
+ *((struct vbdev_ocf_base **)
+ ocf_volume_get_priv(ocf_io_get_volume(io)));
+ struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io);
+ int status = 0;
+
+ status = prepare_submit(io);
+ if (status) {
+ SPDK_ERRLOG("Preparing io failed with status=%d\n", status);
+ vbdev_ocf_volume_submit_io_cb(NULL, false, io);
+ return;
+ }
+
+ status = spdk_bdev_unmap(
+ base->desc, io_ctx->ch,
+ io->addr, io->bytes,
+ vbdev_ocf_volume_submit_io_cb, io);
+ if (status) {
+ /* Since callback is not called, we need to do it manually to free io structures */
+ SPDK_ERRLOG("Submission failed with status=%d\n", status);
+ vbdev_ocf_volume_submit_io_cb(NULL, false, io);
+ }
+}
+
+static void
+vbdev_ocf_volume_submit_metadata(struct ocf_io *io)
+{
+ /* Implement with persistent metadata support */
+}
+
+static unsigned int
+vbdev_ocf_volume_get_max_io_size(ocf_volume_t volume)
+{
+ return 131072;
+}
+
+static struct ocf_volume_properties vbdev_volume_props = {
+ .name = "SPDK block device",
+ .io_priv_size = sizeof(struct ocf_io_ctx),
+ .volume_priv_size = sizeof(struct vbdev_ocf_base *),
+ .caps = {
+ .atomic_writes = 0 /* to enable need to have ops->submit_metadata */
+ },
+ .ops = {
+ .open = vbdev_ocf_volume_open,
+ .close = vbdev_ocf_volume_close,
+ .get_length = vbdev_ocf_volume_get_length,
+ .submit_io = vbdev_ocf_volume_submit_io,
+ .submit_discard = vbdev_ocf_volume_submit_discard,
+ .submit_flush = vbdev_ocf_volume_submit_flush,
+ .get_max_io_size = vbdev_ocf_volume_get_max_io_size,
+ .submit_metadata = vbdev_ocf_volume_submit_metadata,
+ },
+ .io_ops = {
+ .set_data = vbdev_ocf_volume_io_set_data,
+ .get_data = vbdev_ocf_volume_io_get_data,
+ },
+};
+
+int
+vbdev_ocf_volume_init(void)
+{
+ return ocf_ctx_register_volume_type(vbdev_ocf_ctx, SPDK_OBJECT, &vbdev_volume_props);
+}
+
+void
+vbdev_ocf_volume_cleanup(void)
+{
+ ocf_ctx_unregister_volume_type(vbdev_ocf_ctx, SPDK_OBJECT);
+}
+
+SPDK_LOG_REGISTER_COMPONENT("vbdev_ocf_volume", SPDK_TRACE_VBDEV_OCF_VOLUME)
diff --git a/src/spdk/module/bdev/ocf/volume.h b/src/spdk/module/bdev/ocf/volume.h
new file mode 100644
index 000000000..6ae7488b5
--- /dev/null
+++ b/src/spdk/module/bdev/ocf/volume.h
@@ -0,0 +1,63 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef VBDEV_OCF_DOBJ_H
+#define VBDEV_OCF_DOBJ_H
+
+#include <ocf/ocf.h>
+
+#include "ocf_env.h"
+#include "ctx.h"
+#include "data.h"
+
+/* ocf_io context
+ * It is initialized from io size and offset */
+struct ocf_io_ctx {
+ struct bdev_ocf_data *data;
+ struct spdk_io_channel *ch;
+ uint32_t offset;
+ int ref;
+ int rq_cnt;
+ int error;
+ bool iovs_allocated;
+};
+
+int vbdev_ocf_volume_init(void);
+void vbdev_ocf_volume_cleanup(void);
+
+static inline struct ocf_io_ctx *ocf_get_io_ctx(struct ocf_io *io)
+{
+ return ocf_io_get_priv(io);
+}
+
+#endif