diff options
Diffstat (limited to 'src/spdk/module/bdev/ocf')
-rw-r--r-- | src/spdk/module/bdev/ocf/Makefile | 52 | ||||
-rw-r--r-- | src/spdk/module/bdev/ocf/ctx.c | 565 | ||||
-rw-r--r-- | src/spdk/module/bdev/ocf/ctx.h | 65 | ||||
-rw-r--r-- | src/spdk/module/bdev/ocf/data.c | 122 | ||||
-rw-r--r-- | src/spdk/module/bdev/ocf/data.h | 57 | ||||
-rw-r--r-- | src/spdk/module/bdev/ocf/stats.c | 109 | ||||
-rw-r--r-- | src/spdk/module/bdev/ocf/stats.h | 51 | ||||
-rw-r--r-- | src/spdk/module/bdev/ocf/utils.c | 136 | ||||
-rw-r--r-- | src/spdk/module/bdev/ocf/utils.h | 67 | ||||
-rw-r--r-- | src/spdk/module/bdev/ocf/vbdev_ocf.c | 1775 | ||||
-rw-r--r-- | src/spdk/module/bdev/ocf/vbdev_ocf.h | 210 | ||||
-rw-r--r-- | src/spdk/module/bdev/ocf/vbdev_ocf_rpc.c | 362 | ||||
-rw-r--r-- | src/spdk/module/bdev/ocf/volume.c | 441 | ||||
-rw-r--r-- | src/spdk/module/bdev/ocf/volume.h | 63 |
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 |