diff --git a/CONFIG b/CONFIG index 39f92448d..5a4c20edb 100644 --- a/CONFIG +++ b/CONFIG @@ -124,3 +124,8 @@ CONFIG_FTL=n # Build Intel IPSEC_MB library CONFIG_IPSEC_MB=n + +# Enable OCF module +CONFIG_OCF=n +CONFIG_OCF_PATH= +CONFIG_CUSTOMOCF=n diff --git a/configure b/configure index 08f5b33d8..492fe09c8 100755 --- a/configure +++ b/configure @@ -62,6 +62,10 @@ function usage() echo " No path required." echo " vtune Required to profile I/O under Intel VTune Amplifier XE." echo " example: /opt/intel/vtune_amplifier_xe_version" + echo " ocf Required to build OCF module." + echo " If argument is directory, interpret it as root of OCF repo" + echo " If argument is file, interpret it as compiled OCF lib" + echo " example: /usr/src/ocf/" echo "" echo "Environment variables:" echo "" @@ -269,6 +273,14 @@ for i in "$@"; do --without-ftl) CONFIG[FTL]=n ;; + --with-ocf=*) + CONFIG[OCF]=y + CONFIG[OCF_PATH]=$(readlink -f ${i#*=}) + ;; + --without-ocf) + CONFIG[OCF]=n + CONFIG[OCF_PATH]= + ;; --) break ;; @@ -390,6 +402,38 @@ if [[ "${CONFIG[REDUCE]}" = "y" ]]; then fi fi +if [[ "${CONFIG[OCF]}" = "y" ]]; then + if [ -z "${CONFIG[OCF_PATH]}" ]; then + echo "When OCF module is enabled, you must specify" + echo "the OCF directory or path to OCF library using --with-ocf=path" + exit 1 + fi + + # If OCF_PATH is a file, assume it is a library and use it to compile with + if [ -f ${CONFIG[OCF_PATH]} ]; then + CONFIG[CUSTOMOCF]=y + else + CONFIG[CUSTOMOCF]=n + fi + + # If OCF_PATH is not a library, we need to do sources export procedure using OCF Makefile + if [[ ${CONFIG[CUSTOMOCF]} = "n" ]]; then + echo "configuring OCF..." + rootdir=$(readlink -f $(dirname $0)) + + if pushd "${CONFIG[OCF_PATH]}" > /dev/null && \ + make inc O="$rootdir/lib/bdev/ocf/env/" && \ + make src O="$rootdir/lib/bdev/ocf/env/" CMD=cp 1>/dev/null && \ + popd > /dev/null + then + echo "done configuring OCF" + else + echo "Could not configure OCF" + exit 1 + fi + fi +fi + # We are now ready to generate final configuration. But first do sanity # check to see if all keys in CONFIG array have its reflection in CONFIG file. if [ $(egrep -c "^\s*CONFIG_[[:alnum:]_]+=" CONFIG) -ne ${#CONFIG[@]} ]; then diff --git a/lib/bdev/Makefile b/lib/bdev/Makefile index b019b3a36..36a08aff1 100644 --- a/lib/bdev/Makefile +++ b/lib/bdev/Makefile @@ -48,6 +48,12 @@ ifeq ($(CONFIG_CRYPTO),y) DIRS-y += crypto endif +ifeq ($(CONFIG_OCF), y) +DIRS-y += ocf +DIRS-y += ocf/env +DEPDIRS-ocf := ocf/env +endif + ifeq ($(OS),Linux) DIRS-y += aio DIRS-$(CONFIG_ISCSI_INITIATOR) += iscsi diff --git a/lib/bdev/ocf/Makefile b/lib/bdev/ocf/Makefile new file mode 100644 index 000000000..0468e9569 --- /dev/null +++ b/lib/bdev/ocf/Makefile @@ -0,0 +1,47 @@ +# +# 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 + +CFLAGS += $(ENV_CFLAGS) -I$(CURDIR)/env -I$(CURDIR)/env/include +C_SRCS = $(shell ls *.c) + +LIBNAME := bdev_ocf + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk + +SPDK_DEP_LIBS = $(call spdk_lib_list_to_static_libs,ocfenv) + +$(LIB) : $(SPDK_DEP_LIBS) diff --git a/lib/bdev/ocf/ctx.c b/lib/bdev/ocf/ctx.c new file mode 100644 index 000000000..52466cd15 --- /dev/null +++ b/lib/bdev/ocf/ctx.c @@ -0,0 +1,413 @@ +/*- + * 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 +#include + +#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_dma_malloc(sz, PAGE_SIZE, NULL); + 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_dma_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(off, d->size); + d->seek = off; + break; + case ctx_data_seek_current: + off = MIN(off, 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); + } + } +} + +/* OCF queue initialization procedure + * Called during ocf_cache_start */ +static int +vbdev_ocf_ctx_queue_init(ocf_queue_t q) +{ + return 0; +} + +/* 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) +{ +} + +static int +vbdev_ocf_ctx_cleaner_init(ocf_cleaner_t c) +{ + /* TODO [writeback]: implement with writeback mode support */ + return 0; +} + +static void +vbdev_ocf_ctx_cleaner_stop(ocf_cleaner_t c) +{ + /* TODO [writeback]: implement with writeback mode support */ +} + +static int vbdev_ocf_dobj_updater_init(ocf_metadata_updater_t mu) +{ + /* TODO [metadata]: implement with persistent metadata support */ + return 0; +} +static void vbdev_ocf_dobj_updater_stop(ocf_metadata_updater_t mu) +{ + /* TODO [metadata]: implement with persistent metadata support */ +} +static void vbdev_ocf_dobj_updater_kick(ocf_metadata_updater_t mu) +{ + /* TODO [metadata]: implement with persistent metadata support */ +} + +static const struct ocf_ctx_ops vbdev_ocf_ctx_ops = { + .name = "OCF SPDK", + + .data_alloc = vbdev_ocf_ctx_data_alloc, + .data_free = vbdev_ocf_ctx_data_free, + .data_mlock = vbdev_ocf_ctx_data_mlock, + .data_munlock = vbdev_ocf_ctx_data_munlock, + .data_rd = vbdev_ocf_ctx_data_rd, + .data_wr = vbdev_ocf_ctx_data_wr, + .data_zero = vbdev_ocf_ctx_data_zero, + .data_seek = vbdev_ocf_ctx_data_seek, + .data_cpy = vbdev_ocf_ctx_data_cpy, + .data_secure_erase = vbdev_ocf_ctx_data_secure_erase, + + .queue_init = vbdev_ocf_ctx_queue_init, + .queue_kick = vbdev_ocf_ctx_queue_kick, + .queue_stop = vbdev_ocf_ctx_queue_stop, + + .cleaner_init = vbdev_ocf_ctx_cleaner_init, + .cleaner_stop = vbdev_ocf_ctx_cleaner_stop, + + .metadata_updater_init = vbdev_ocf_dobj_updater_init, + .metadata_updater_stop = vbdev_ocf_dobj_updater_stop, + .metadata_updater_kick = vbdev_ocf_dobj_updater_kick, +}; + +/* 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(const struct ocf_logger *logger, + ocf_logger_lvl_t lvl, const char *fmt, va_list args) +{ + FILE *lfile = stdout; + + if (lvl > log_info) { + return 0; + } + + if (lvl <= log_warn) { + lfile = stderr; + } + + return vfprintf(lfile, fmt, args); +} + +static const struct ocf_logger logger = { + .printf = vbdev_ocf_ctx_log_printf, + .dump_stack = NULL, +}; + +int +vbdev_ocf_ctx_init(void) +{ + int ret; + + ret = ocf_ctx_init(&vbdev_ocf_ctx, &vbdev_ocf_ctx_ops); + if (ret < 0) { + return ret; + } + + ocf_ctx_set_logger(vbdev_ocf_ctx, &logger); + + return 0; +} + +void +vbdev_ocf_ctx_cleanup(void) +{ + ocf_ctx_exit(vbdev_ocf_ctx); + vbdev_ocf_ctx = NULL; +} + +SPDK_LOG_REGISTER_COMPONENT("ocf_ocfctx", SPDK_LOG_OCFCTX) diff --git a/lib/bdev/ocf/ctx.h b/lib/bdev/ocf/ctx.h new file mode 100644 index 000000000..23a7b2f7c --- /dev/null +++ b/lib/bdev/ocf/ctx.h @@ -0,0 +1,48 @@ +/*- + * 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 + +extern ocf_ctx_t vbdev_ocf_ctx; + +#define OCF_WRITE_FLUSH 11 + +#define SPDK_OBJECT 0 + +int vbdev_ocf_ctx_init(void); +void vbdev_ocf_ctx_cleanup(void); + +#endif diff --git a/lib/bdev/ocf/data.c b/lib/bdev/ocf/data.c new file mode 100644 index 000000000..981c793f5 --- /dev/null +++ b/lib/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 +#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/lib/bdev/ocf/data.h b/lib/bdev/ocf/data.h new file mode 100644 index 000000000..7ed5adcef --- /dev/null +++ b/lib/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/lib/bdev/ocf/dobj.c b/lib/bdev/ocf/dobj.c new file mode 100644 index 000000000..fbc0a99b6 --- /dev/null +++ b/lib/bdev/ocf/dobj.c @@ -0,0 +1,402 @@ +/*- + * 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 + +#include "spdk/bdev_module.h" +#include "spdk/env.h" +#include "spdk/io_channel.h" +#include "spdk_internal/log.h" + +#include "data.h" +#include "dobj.h" +#include "ctx.h" +#include "vbdev_ocf.h" + +static int +vbdev_ocf_dobj_open(ocf_data_obj_t obj) +{ + struct vbdev_ocf_base *base = vbdev_ocf_get_base_by_name(ocf_data_obj_get_uuid(obj)->data); + + if (base == NULL) { + assert(false); + return -EINVAL; + } + + ocf_data_obj_set_priv(obj, base); + + return 0; +} + +static void +vbdev_ocf_dobj_close(ocf_data_obj_t obj) +{ +} + +static uint64_t +vbdev_ocf_dobj_get_length(ocf_data_obj_t obj) +{ + struct vbdev_ocf_base *base = ocf_data_obj_get_priv(obj); + uint64_t len; + + len = base->bdev->blocklen * base->bdev->blockcnt; + + return len; +} + +static int +vbdev_ocf_dobj_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_dobj_io_get_data(struct ocf_io *io) +{ + return ocf_get_io_ctx(io)->data; +} + +static void +vbdev_ocf_dobj_io_get(struct ocf_io *io) +{ + struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io); + + io_ctx->ref++; +} + +static void +vbdev_ocf_dobj_io_put(struct ocf_io *io) +{ + struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io); + + if (--io_ctx->ref) { + return; + } + + ocf_data_obj_del_io(io); +} + +static const struct ocf_io_ops vbdev_ocf_dobj_io_ops = { + .set_data = vbdev_ocf_dobj_io_set_data, + .get_data = vbdev_ocf_dobj_io_get_data, + .get = vbdev_ocf_dobj_io_get, + .put = vbdev_ocf_dobj_io_put, +}; + +static struct ocf_io * +vbdev_ocf_dobj_new_io(ocf_data_obj_t obj) +{ + struct ocf_io *io; + struct ocf_io_ctx *io_ctx; + + io = ocf_data_obj_new_io(obj); + if (io == NULL) { + return NULL; + } + + io->ops = &vbdev_ocf_dobj_io_ops; + + io_ctx = ocf_get_io_ctx(io); + io_ctx->rq_cnt = 0; + io_ctx->ref = 1; + io_ctx->error = 0; + + return io; +} + +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_dobj_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->offset && bdev_io != NULL) { + switch (bdev_io->type) { + case SPDK_BDEV_IO_TYPE_READ: + case SPDK_BDEV_IO_TYPE_WRITE: + env_free(bdev_io->u.bdev.iovs); + break; + default: + assert(false); + break; + } + } + + if (io_ctx->error) { + SPDK_DEBUGLOG(SPDK_TRACE_VBDEV_OCF_DOBJ, + "base returned error on io submission: %d\n", io_ctx->error); + } + + if (io->io_queue == 0 && io_ctx->ch != NULL) { + spdk_put_io_channel(io_ctx->ch); + } + + vbdev_ocf_dobj_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_qcxt *qctx; + struct vbdev_ocf_base *base; + ocf_queue_t q; + int rc; + + io_ctx->rq_cnt++; + if (io_ctx->rq_cnt != 1) { + return 0; + } + + vbdev_ocf_dobj_io_get(io); + base = ocf_data_obj_get_priv(io->obj); + + if (io->io_queue == 0) { + /* In SPDK we never set queue id to 0 + * but OCF sometimes gives it to us (not a bug) + * In such cases we cannot determine on which queue we are now + * So to get io channel that is usually passed as queue context + * we have to reallocate it using global method */ + io_ctx->ch = spdk_bdev_get_io_channel(base->desc); + if (io_ctx->ch == NULL) { + return -EPERM; + } + return 0; + } + + rc = ocf_cache_get_queue(base->parent->ocf_cache, io->io_queue, &q); + if (rc) { + SPDK_ERRLOG("Could not get queue #%d\n", io->io_queue); + return rc; + } + + qctx = ocf_queue_get_priv(q); + if (base->is_cache) { + io_ctx->ch = qctx->cache_ch; + } else { + io_ctx->ch = qctx->core_ch; + } + + return rc; +} + +static void +vbdev_ocf_dobj_submit_flush(struct ocf_io *io) +{ +} + +static void +vbdev_ocf_dobj_submit_io(struct ocf_io *io) +{ + struct vbdev_ocf_base *base = ocf_data_obj_get_priv(io->obj); + 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_dobj_submit_flush(io); + return; + } + + prepare_submit(io); + + /* IO fields */ + addr = io->addr; + len = io->bytes; + offset = io_ctx->offset; + + if (offset) { + 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_dobj_submit_io_cb(NULL, false, io); + return; + } + + iovcnt = io_ctx->data->iovcnt - i; + + iovs = env_malloc(sizeof(*iovs) * iovcnt, ENV_MEM_NOIO); + + if (!iovs) { + SPDK_ERRLOG("allocation failed\n"); + vbdev_ocf_dobj_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_dobj_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_dobj_submit_io_cb, io); + } + + 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_dobj_submit_io_cb(NULL, false, io); + } +} + +static void +vbdev_ocf_dobj_submit_discard(struct ocf_io *io) +{ + /* TODO [unmap support] */ + io->end(io, 0); +} + +static void +vbdev_ocf_dobj_submit_metadata(struct ocf_io *io) +{ + /* Implement with persistent metadata support */ +} + +static unsigned int +vbdev_ocf_dobj_get_max_io_size(ocf_data_obj_t obj) +{ + return 256; +} + +static struct ocf_data_obj_properties vbdev_ocf_dobj_props = { + .name = "SPDK block device", + .io_context_size = sizeof(struct ocf_io_ctx), + .caps = { + .atomic_writes = 0 /* to enable need to have ops->submit_metadata */ + }, + .ops = { + .new_io = vbdev_ocf_dobj_new_io, + .open = vbdev_ocf_dobj_open, + .close = vbdev_ocf_dobj_close, + .get_length = vbdev_ocf_dobj_get_length, + .submit_io = vbdev_ocf_dobj_submit_io, + .submit_discard = vbdev_ocf_dobj_submit_discard, + .submit_flush = vbdev_ocf_dobj_submit_flush, + .get_max_io_size = vbdev_ocf_dobj_get_max_io_size, + .submit_metadata = vbdev_ocf_dobj_submit_metadata, + } +}; + +int +vbdev_ocf_dobj_init(void) +{ + return ocf_ctx_register_data_obj_type(vbdev_ocf_ctx, SPDK_OBJECT, &vbdev_ocf_dobj_props); +} + +void +vbdev_ocf_dobj_cleanup(void) +{ + ocf_ctx_unregister_data_obj_type(vbdev_ocf_ctx, SPDK_OBJECT); +} + +SPDK_LOG_REGISTER_COMPONENT("vbdev_ocf_dobj", SPDK_TRACE_VBDEV_OCF_DOBJ) diff --git a/lib/bdev/ocf/dobj.h b/lib/bdev/ocf/dobj.h new file mode 100644 index 000000000..c17d6128a --- /dev/null +++ b/lib/bdev/ocf/dobj.h @@ -0,0 +1,62 @@ +/*- + * 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 + +#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; +}; + +int vbdev_ocf_dobj_init(void); +void vbdev_ocf_dobj_cleanup(void); + +static inline struct ocf_io_ctx *ocf_get_io_ctx(struct ocf_io *io) +{ + return ocf_data_obj_get_data_from_io(io); +} + +#endif diff --git a/lib/bdev/ocf/env/.gitignore b/lib/bdev/ocf/env/.gitignore new file mode 100644 index 000000000..f5452c248 --- /dev/null +++ b/lib/bdev/ocf/env/.gitignore @@ -0,0 +1,2 @@ +src/ +include/ diff --git a/lib/bdev/ocf/env/Makefile b/lib/bdev/ocf/env/Makefile new file mode 100644 index 000000000..963019b11 --- /dev/null +++ b/lib/bdev/ocf/env/Makefile @@ -0,0 +1,75 @@ +# +# 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. +# + +# OCF requires users to build with their sources +# If SPDK is configured with OCF source directory, +# we export its files and then compile SPDK LIB with them +# Else if SPDK is configured with OCF precompiled library +# we just use it as SPDK lib by copying it to /build/lib/ + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..) +OCFDIR=$(CONFIG_OCF_DIR) + +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +LIBNAME := ocfenv + +ifeq ($(CONFIG_CUSTOMOCF),n) + +CFLAGS += $(ENV_CFLAGS) -I$(CURDIR) -I$(CURDIR)/include -w +C_SRCS = $(shell find -name \*.c) + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk + +else + +LIB = $(call spdk_lib_list_to_static_libs,$(LIBNAME)) + +all: $(LIB) + +$(LIB): + cp $(CONFIG_OCF_PATH) $(LIB) + +clean: + $(Q)rm -f $(LIB) +install: + +endif + +exportlib: $(LIB) + @ if [ -z $(O) ]; then echo "No output specified"; exit 1; fi + cp $(LIB) $(O) + +help: + @ echo "all Default" + @ echo "exportlib O= Default build to specified outpath" diff --git a/lib/bdev/ocf/env/ocf_env.c b/lib/bdev/ocf/env/ocf_env.c new file mode 100644 index 000000000..2b5b15727 --- /dev/null +++ b/lib/bdev/ocf/env/ocf_env.c @@ -0,0 +1,144 @@ +/*- + * 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_def.h" +#include "ocf_env.h" + +#include "spdk/crc32.h" +#include "spdk/env.h" +#include "spdk_internal/log.h" + +struct _env_allocator { + /* Memory pool ID unique name */ + char *name; + + /* Size of specific item of memory pool */ + uint32_t item_size; + + /* Number of currently allocated items in pool */ + env_atomic count; +}; + +struct _env_allocator_item { + uint32_t flags; + uint32_t cpu; + char data[]; +}; + +void * +env_allocator_new(env_allocator *allocator) +{ + struct _env_allocator_item *item = NULL; + + item = spdk_dma_zmalloc(allocator->item_size, 0, NULL); + + if (item) { + item->cpu = 0; + env_atomic_inc(&allocator->count); + } else { + return NULL; + } + + return &item->data; +} + +env_allocator * +env_allocator_create(uint32_t size, const char *name) +{ + size_t name_size; + env_allocator *allocator; + + allocator = spdk_dma_zmalloc(sizeof(*allocator), 0, NULL); + + allocator->item_size = size + sizeof(struct _env_allocator_item); + allocator->name = env_strdup(name, 0); + + return allocator; +} + +void +env_allocator_del(env_allocator *allocator, void *obj) +{ + struct _env_allocator_item *item = container_of(obj, struct _env_allocator_item, data); + + env_atomic_dec(&allocator->count); + + spdk_dma_free(item); +} + +void +env_allocator_destroy(env_allocator *allocator) +{ + if (allocator) { + if (env_atomic_read(&allocator->count)) { + SPDK_ERRLOG("Not all objects deallocated\n"); + assert(false); + } + + spdk_dma_free(allocator->name); + spdk_dma_free(allocator); + } +} + +uint32_t +env_allocator_item_count(env_allocator *allocator) +{ + return env_atomic_read(&allocator->count); +} + +/* *** COMPLETION *** */ + +void +env_completion_init(env_completion *completion) +{ + atomic_set(&completion->atom, 1); +} + +void +env_completion_wait(env_completion *completion) +{ + while (atomic_read(&completion->atom)); +} + +void +env_completion_complete(env_completion *completion) +{ + atomic_set(&completion->atom, 0); +} + +/* *** CRC *** */ + +uint32_t +env_crc32(uint32_t crc, uint8_t const *message, size_t len) +{ + return spdk_crc32_ieee_update(message, len, crc); +} diff --git a/lib/bdev/ocf/env/ocf_env.h b/lib/bdev/ocf/env/ocf_env.h new file mode 100644 index 000000000..c3c44a2fd --- /dev/null +++ b/lib/bdev/ocf/env/ocf_env.h @@ -0,0 +1,746 @@ +/*- + * 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 __LIBOCF_ENV_H__ +#define __LIBOCF_ENV_H__ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#ifndef __USE_GNU +#define __USE_GNU +#endif + +#include +#include + +#include "spdk/stdinc.h" +#include "spdk/likely.h" +#include "spdk/env.h" +#include "spdk/util.h" +#include "spdk_internal/log.h" + +#include "ocf_env_list.h" + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef uint64_t sector_t; + +#define __packed __attribute__((packed)) +#define __aligned(x) __attribute__((aligned(x))) + +/* linux sector 512-bytes */ +#define ENV_SECTOR_SHIFT 9 +#define ENV_SECTOR_SIZE (1<> ENV_SECTOR_SHIFT) + +/* *** MEMORY MANAGEMENT *** */ + +#define ENV_MEM_NORMAL 0 +#define ENV_MEM_NOIO 0 +#define ENV_MEM_ATOMIC 0 + +#define likely spdk_likely +#define unlikely spdk_unlikely + +#define min(x, y) MIN(x, y) +#ifndef MIN +#define MIN(x, y) spdk_min(x, y) +#endif + +#define ARRAY_SIZE(x) SPDK_COUNTOF(x) + +/* LOGGING */ +#define ENV_PRIu64 PRIu64 + +#define ENV_WARN(cond, fmt, args...) ({ \ + if (spdk_unlikely((uintptr_t)(cond))) \ + SPDK_NOTICELOG("WARNING" fmt, ##args); \ + }) + +#define ENV_WARN_ON(cond) ({ \ + if (spdk_unlikely((uintptr_t)(cond))) \ + SPDK_NOTICELOG("WARNING\n"); \ + }) + +#define ENV_BUG() ({ \ + SPDK_ERRLOG("BUG\n"); \ + assert(0); \ + abort(); \ + }) + +#define ENV_BUG_ON(cond) ({ \ + if (spdk_unlikely((uintptr_t)(cond))) { \ + SPDK_ERRLOG("BUG\n"); \ + assert(0); \ + abort(); \ + } \ + }) + +#define container_of(ptr, type, member) SPDK_CONTAINEROF(ptr, type, member) + +static inline void *env_malloc(size_t size, int flags) +{ + return spdk_dma_malloc(size, 0, NULL); +} + +static inline void *env_zalloc(size_t size, int flags) +{ + return spdk_dma_zmalloc(size, 0, NULL); +} + +static inline void env_free(const void *ptr) +{ + return spdk_dma_free((void *)ptr); +} + +static inline void *env_vmalloc(size_t size) +{ + return spdk_dma_malloc(size, 0, NULL); +} + +static inline void *env_vzalloc(size_t size) +{ + /* TODO: raw_ram init can request huge amount of memory to store + * hashtable in it. need to ensure that allocation succedds */ + return spdk_dma_zmalloc(size, 0, NULL); +} + +static inline void env_vfree(const void *ptr) +{ + return spdk_dma_free((void *)ptr); +} + +static inline uint64_t env_get_free_memory(void) +{ + /* TODO: do we need implementation for this function? */ + return sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES); +} + +/* *** ALLOCATOR *** */ + +#define OCF_ALLOCATOR_NAME_MAX 128 + +typedef struct _env_allocator env_allocator; + +env_allocator *env_allocator_create(uint32_t size, const char *name); + +void env_allocator_destroy(env_allocator *allocator); + +void *env_allocator_new(env_allocator *allocator); + +void env_allocator_del(env_allocator *allocator, void *item); + +uint32_t env_allocator_item_count(env_allocator *allocator); + +/* *** MUTEX *** */ + +typedef struct { + pthread_mutex_t m; +} env_mutex; + +static inline int env_mutex_init(env_mutex *mutex) +{ + return !!pthread_mutex_init(&mutex->m, NULL); +} + +static inline void env_mutex_lock(env_mutex *mutex) +{ + ENV_BUG_ON(pthread_mutex_lock(&mutex->m)); +} + +static inline int env_mutex_lock_interruptible(env_mutex *mutex) +{ + env_mutex_lock(mutex); + return 0; +} + +static inline int env_mutex_trylock(env_mutex *mutex) +{ + if (pthread_mutex_trylock(&mutex->m) == 0) { + return 1; + } + return 0; +} + +static inline void env_mutex_unlock(env_mutex *mutex) +{ + ENV_BUG_ON(pthread_mutex_unlock(&mutex->m)); +} + +static inline int env_mutex_is_locked(env_mutex *mutex) +{ + if (env_mutex_trylock(mutex)) { + env_mutex_unlock(mutex); + return 0; + } + + return 1; +} + +/* *** RECURSIVE MUTEX *** */ + +typedef env_mutex env_rmutex; + +static inline int env_rmutex_init(env_rmutex *rmutex) +{ + return env_mutex_init(rmutex); +} + +static inline void env_rmutex_lock(env_rmutex *rmutex) +{ + env_mutex_lock(rmutex); +} + +static inline int env_rmutex_lock_interruptible(env_rmutex *rmutex) +{ + return env_mutex_lock_interruptible(rmutex); +} + +static inline int env_rmutex_trylock(env_rmutex *rmutex) +{ + return env_mutex_trylock(rmutex); +} + +static inline void env_rmutex_unlock(env_rmutex *rmutex) +{ + env_mutex_unlock(rmutex); +} + +static inline int env_rmutex_is_locked(env_rmutex *rmutex) +{ + return env_mutex_is_locked(rmutex); +} + +/* *** RW SEMAPHORE *** */ +typedef struct { + pthread_rwlock_t lock; +} env_rwsem; + +static inline int env_rwsem_init(env_rwsem *s) +{ + return !!pthread_rwlock_init(&s->lock, NULL); +} + +static inline void env_rwsem_up_read(env_rwsem *s) +{ + ENV_BUG_ON(pthread_rwlock_unlock(&s->lock)); +} + +static inline void env_rwsem_down_read(env_rwsem *s) +{ + ENV_BUG_ON(pthread_rwlock_rdlock(&s->lock)); +} + +static inline int env_rwsem_down_read_trylock(env_rwsem *s) +{ + int result = pthread_rwlock_tryrdlock(&s->lock); + + if (result == 0) { + return 1; + } else { + return 0; + } +} + +static inline void env_rwsem_up_write(env_rwsem *s) +{ + ENV_BUG_ON(pthread_rwlock_unlock(&s->lock)); +} + +static inline void env_rwsem_down_write(env_rwsem *s) +{ + ENV_BUG_ON(pthread_rwlock_wrlock(&s->lock)); +} + +static inline int env_rwsem_down_write_trylock(env_rwsem *s) +{ + int result = pthread_rwlock_trywrlock(&s->lock); + + if (result == 0) { + return 1; + } else { + return 0; + } +} + +static inline int env_rwsem_is_locked(env_rwsem *s) +{ + if (env_rwsem_down_write_trylock(s)) { + env_rwsem_up_write(s); + return 1; + } + if (env_rwsem_down_read_trylock(s)) { + env_rwsem_up_read(s); + return 1; + } + + return 0; +} + +static inline int env_rwsem_down_read_interruptible(env_rwsem *s) +{ + return pthread_rwlock_rdlock(&s->lock); +} +static inline int env_rwsem_down_write_interruptible(env_rwsem *s) +{ + return pthread_rwlock_wrlock(&s->lock); +} + +/* *** ATOMIC VARIABLES *** */ + +typedef int env_atomic; + +typedef long env_atomic64; + +#ifndef atomic_read +#define atomic_read(ptr) (*(__typeof__(*ptr) *volatile) (ptr)) +#endif + +#ifndef atomic_set +#define atomic_set(ptr, i) ((*(__typeof__(*ptr) *volatile) (ptr)) = (i)) +#endif + +#define atomic_inc(ptr) ((void) __sync_fetch_and_add(ptr, 1)) +#define atomic_dec(ptr) ((void) __sync_fetch_and_add(ptr, -1)) +#define atomic_add(ptr, n) ((void) __sync_fetch_and_add(ptr, n)) +#define atomic_sub(ptr, n) ((void) __sync_fetch_and_sub(ptr, n)) + +#define atomic_cmpxchg __sync_val_compare_and_swap + +static inline int env_atomic_read(const env_atomic *a) +{ + return atomic_read(a); +} + +static inline void env_atomic_set(env_atomic *a, int i) +{ + atomic_set(a, i); +} + +static inline void env_atomic_add(int i, env_atomic *a) +{ + atomic_add(a, i); +} + +static inline void env_atomic_sub(int i, env_atomic *a) +{ + atomic_sub(a, i); +} + +static inline bool env_atomic_sub_and_test(int i, env_atomic *a) +{ + return __sync_sub_and_fetch(a, i) == 0; +} + +static inline void env_atomic_inc(env_atomic *a) +{ + atomic_inc(a); +} + +static inline void env_atomic_dec(env_atomic *a) +{ + atomic_dec(a); +} + +static inline bool env_atomic_dec_and_test(env_atomic *a) +{ + return __sync_sub_and_fetch(a, 1) == 0; +} + +static inline bool env_atomic_inc_and_test(env_atomic *a) +{ + return __sync_add_and_fetch(a, 1) == 0; +} + +static inline int env_atomic_add_return(int i, env_atomic *a) +{ + return __sync_add_and_fetch(a, i); +} + +static inline int env_atomic_sub_return(int i, env_atomic *a) +{ + return __sync_sub_and_fetch(a, i); +} + +static inline int env_atomic_inc_return(env_atomic *a) +{ + return env_atomic_add_return(1, a); +} + +static inline int env_atomic_dec_return(env_atomic *a) +{ + return env_atomic_sub_return(1, a); +} + +static inline int env_atomic_cmpxchg(env_atomic *a, int old, int new_value) +{ + return atomic_cmpxchg(a, old, new_value); +} + +static inline int env_atomic_add_unless(env_atomic *a, int i, int u) +{ + int c, old; + c = env_atomic_read(a); + for (;;) { + if (spdk_unlikely(c == (u))) { + break; + } + old = env_atomic_cmpxchg((a), c, c + (i)); + if (spdk_likely(old == c)) { + break; + } + c = old; + } + return c != (u); +} + +static inline long env_atomic64_read(const env_atomic64 *a) +{ + return atomic_read(a); +} + +static inline void env_atomic64_set(env_atomic64 *a, long i) +{ + atomic_set(a, i); +} + +static inline void env_atomic64_add(long i, env_atomic64 *a) +{ + atomic_add(a, i); +} + +static inline void env_atomic64_sub(long i, env_atomic64 *a) +{ + atomic_sub(a, i); +} + +static inline void env_atomic64_inc(env_atomic64 *a) +{ + atomic_inc(a); +} + +static inline void env_atomic64_dec(env_atomic64 *a) +{ + atomic_dec(a); +} + +static inline long env_atomic64_cmpxchg(env_atomic64 *a, long old, long new) +{ + return atomic_cmpxchg(a, old, new); +} + +/* *** COMPLETION *** */ +struct completion { + env_atomic atom; +}; + +typedef struct completion env_completion; + +void env_completion_init(env_completion *completion); +void env_completion_wait(env_completion *completion); +void env_completion_complete(env_completion *completion); + +/* *** SPIN LOCKS *** */ + +typedef env_mutex env_spinlock; + +static inline void env_spinlock_init(env_spinlock *l) +{ + env_mutex_init(l); +} + +static inline void env_spinlock_lock(env_spinlock *l) +{ + env_mutex_lock(l); +} + +static inline void env_spinlock_unlock(env_spinlock *l) +{ + env_mutex_unlock(l); +} + +static inline void env_spinlock_lock_irq(env_spinlock *l) +{ + env_spinlock_lock(l); +} + +static inline void env_spinlock_unlock_irq(env_spinlock *l) +{ + env_spinlock_unlock(l); +} + +static inline void env_spinlock_lock_irqsave(env_spinlock *l, int flags) +{ + env_spinlock_lock(l); + (void)flags; +} + +static inline void env_spinlock_unlock_irqrestore(env_spinlock *l, int flags) +{ + env_spinlock_unlock(l); + (void)flags; +} + +/* *** RW LOCKS *** */ + +typedef env_rwsem env_rwlock; + +static inline void env_rwlock_init(env_rwlock *l) +{ + env_rwsem_init(l); +} + +static inline void env_rwlock_read_lock(env_rwlock *l) +{ + env_rwsem_down_read(l); +} + +static inline void env_rwlock_read_unlock(env_rwlock *l) +{ + env_rwsem_up_read(l); +} + +static inline void env_rwlock_write_lock(env_rwlock *l) +{ + env_rwsem_down_write(l); +} + +static inline void env_rwlock_write_unlock(env_rwlock *l) +{ + env_rwsem_up_write(l); +} + +static inline void env_bit_set(int nr, volatile void *addr) +{ + char *byte = (char *)addr + (nr >> 3); + char mask = 1 << (nr & 7); + + __sync_or_and_fetch(byte, mask); +} + +static inline void env_bit_clear(int nr, volatile void *addr) +{ + char *byte = (char *)addr + (nr >> 3); + char mask = 1 << (nr & 7); + + mask = ~mask; + __sync_and_and_fetch(byte, mask); +} + +static inline bool env_bit_test(int nr, const volatile unsigned long *addr) +{ + const char *byte = (char *)addr + (nr >> 3); + char mask = 1 << (nr & 7); + + return !!(*byte & mask); +} + +/* *** WAITQUEUE *** */ + +typedef struct { + sem_t sem; +} env_waitqueue; + +static inline void env_waitqueue_init(env_waitqueue *w) +{ + sem_init(&w->sem, 0, 0); +} + +static inline void env_waitqueue_wake_up(env_waitqueue *w) +{ + sem_post(&w->sem); +} + +#define env_waitqueue_wait(w, condition) \ +({ \ + int __ret = 0; \ + if (!(condition)) \ + sem_wait(&w.sem); \ + __ret = __ret; \ +}) + +/* *** SCHEDULING *** */ + +/* CAS does not need this while in user-space */ +static inline void env_schedule(void) +{ +} + +#define env_cond_resched env_schedule + +static inline int env_in_interrupt(void) +{ + return 0; +} + +static inline uint64_t env_get_tick_count(void) +{ + return spdk_get_ticks(); +} + +static inline uint64_t env_ticks_to_secs(uint64_t j) +{ + return j / spdk_get_ticks_hz(); +} + +static inline uint64_t env_ticks_to_msecs(uint64_t j) +{ + return env_ticks_to_secs(j) * 1000; +} + +static inline uint64_t env_secs_to_ticks(uint64_t j) +{ + return j * spdk_get_ticks_hz(); +} + +/* *** STRING OPERATIONS *** */ + +/* 256 KB is sufficient amount of memory for OCF operations */ +#define ENV_MAX_MEM (256 * 1024) + +static inline int env_memset(void *dest, size_t len, uint8_t value) +{ + if (dest == NULL || len == 0) { + return 1; + } + + memset(dest, value, len); + return 0; +} + +static inline int env_memcpy(void *dest, size_t dmax, const void *src, size_t len) +{ + if (dest == NULL || src == NULL) { + return 1; + } + if (dmax == 0 || dmax > ENV_MAX_MEM) { + return 1; + } + if (len == 0 || len > dmax) { + return 1; + } + + memcpy(dest, src, len); + return 0; +} + +static inline int env_memcmp(const void *aptr, size_t dmax, const void *bptr, size_t len, + int *diff) +{ + if (diff == NULL || aptr == NULL || bptr == NULL) { + return 1; + } + if (dmax == 0 || dmax > ENV_MAX_MEM) { + return 1; + } + if (len == 0 || len > dmax) { + return 1; + } + + *diff = memcmp(aptr, bptr, len); + return 0; +} + +/* 4096 is sufficient max length for any OCF operation on string */ +#define ENV_MAX_STR (4 * 1024) + +static inline size_t env_strnlen(const char *src, size_t dmax) +{ + return strnlen(src, dmax); +} + +static inline int env_strncpy(char *dest, size_t dmax, const char *src, size_t len) +{ + if (dest == NULL || src == NULL) { + return 1; + } + if (dmax == 0 || dmax > ENV_MAX_STR) { + return 1; + } + if (len == 0 || len > dmax) { + return 1; + } + + strncpy(dest, src, len); + return 0; +} + +#define env_strncmp strncmp + +static inline char *env_strdup(const char *src, int flags) +{ + int len; + char *ret; + + if (src == NULL) { + return NULL; + } + + len = env_strnlen(src, ENV_MAX_STR) + 1; + ret = env_malloc(len, flags); + + if (env_strncpy(ret, ENV_MAX_STR, src, len)) { + return NULL; + } else { + return ret; + } +} + +/* *** SORTING *** */ + +static inline void env_sort(void *base, size_t num, size_t size, + int (*cmp_fn)(const void *, const void *), + void (*swap_fn)(void *, void *, int size)) +{ + qsort(base, num, size, cmp_fn); +} + +static inline void env_msleep(uint64_t n) +{ + usleep(n * 1000); +} + +static inline void env_touch_softlockup_wd(void) +{ +} + +/* *** CRC *** */ + +uint32_t env_crc32(uint32_t crc, uint8_t const *data, size_t len); + +#endif /* __OCF_ENV_H__ */ diff --git a/lib/bdev/ocf/env/ocf_env_headers.h b/lib/bdev/ocf/env/ocf_env_headers.h new file mode 100644 index 000000000..7fd40256a --- /dev/null +++ b/lib/bdev/ocf/env/ocf_env_headers.h @@ -0,0 +1,43 @@ +/*- + * 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 __OCF_ENV_HEADERS_H__ +#define __OCF_ENV_HEADERS_H__ + +#include "spdk/stdinc.h" + +#define OCF_VERSION_MAIN 3 +#define OCF_VERSION_MAJOR 8 +#define OCF_VERSION_MINOR 0 + +#endif /* __OCF_ENV_HEADERS_H__ */ diff --git a/lib/bdev/ocf/env/ocf_env_list.h b/lib/bdev/ocf/env/ocf_env_list.h new file mode 100644 index 000000000..e5f60d6c3 --- /dev/null +++ b/lib/bdev/ocf/env/ocf_env_list.h @@ -0,0 +1,185 @@ +/*- + * 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 __OCF_LIST_H__ +#define __OCF_LIST_H__ + +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/** + * List entry structure mimicking linux kernel based one. + */ +struct list_head { + struct list_head *next; + struct list_head *prev; +}; + +/** + * start an empty list + */ +#define INIT_LIST_HEAD(l) { (l)->prev = l; (l)->next = l; } + +/** + * Add item to list head. + * @param it list entry to be added + * @param l1 list main node (head) + */ +static inline void list_add(struct list_head *it, struct list_head *l1) +{ + it->prev = l1; + it->next = l1->next; + + l1->next->prev = it; + l1->next = it; +} + +/** + * Add item it to tail. + * @param it list entry to be added + * @param l1 list main node (head) + */ +static inline void list_add_tail(struct list_head *it, struct list_head *l1) +{ + it->prev = l1->prev; + it->next = l1; + + l1->prev->next = it; + l1->prev = it; +} + +/** + * check if a list is empty (return true) + */ +static inline int list_empty(struct list_head *it) +{ + return it->next == it; +} + +/** + * delete an entry from a list + */ +static inline void list_del(struct list_head *it) +{ + it->next->prev = it->prev; + it->prev->next = it->next; +} + +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + list_del(list); + list_add_tail(list, head); +} + +static inline void list_move(struct list_head *list, + struct list_head *head) +{ + list_del(list); + list_add(list, head); +} + +/** + * Extract an entry. + * @param list_head_i list head item, from which entry is extracted + * @param item_type type (struct) of list entry + * @param field_name name of list_head field within item_type + */ +#define list_entry(list_head_i, item_type, field_name) \ + (item_type *)(((void*)(list_head_i)) - offsetof(item_type, field_name)) + +#define list_first_entry(list_head_i, item_type, field_name) \ + list_entry((list_head_i)->next, item_type, field_name) + +/** + * @param iterator uninitialized list_head pointer, to be used as iterator + * @param plist list head (main node) + */ +#define list_for_each(iterator, plist) \ + for (iterator = (plist)->next; \ + (iterator)->next != (plist)->next; \ + iterator = (iterator)->next) + +/** + * Safe version of list_for_each which works even if entries are deleted during + * loop. + * @param iterator uninitialized list_head pointer, to be used as iterator + * @param q another uninitialized list_head, used as helper + * @param plist list head (main node) + */ +/* + * Algorithm handles situation, where q is deleted. + * consider in example 3 element list with header h: + * + * h -> 1 -> 2 -> 3 -> + *1. i q + * + *2. i q + * + *3. q i + */ +#define list_for_each_safe(iterator, q, plist) \ + for (iterator = (q = (plist)->next->next)->prev; \ + (q) != (plist)->next; \ + iterator = (q = (q)->next)->prev) + +#define _list_entry_helper(item, head, field_name) list_entry(head, typeof(*item), field_name) + +/** + * Iterate over list entries. + * @param list pointer to list item (iterator) + * @param plist pointer to list_head item + * @param field_name name of list_head field in list entry + */ +#define list_for_each_entry(item, plist, field_name) \ + for (item = _list_entry_helper(item, (plist)->next, field_name); \ + _list_entry_helper(item, (item)->field_name.next, field_name) !=\ + _list_entry_helper(item, (plist)->next, field_name); \ + item = _list_entry_helper(item, (item)->field_name.next, field_name)) + +/** + * Safe version of list_for_each_entry which works even if entries are deleted + * during loop. + * @param list pointer to list item (iterator) + * @param q another pointer to list item, used as helper + * @param plist pointer to list_head item + * @param field_name name of list_head field in list entry + */ +#define list_for_each_entry_safe(item, q, plist, field_name) \ + for (item = _list_entry_helper(item, (plist)->next, field_name), \ + q = _list_entry_helper(item, (item)->field_name.next, field_name); \ + _list_entry_helper(item, (item)->field_name.next, field_name) != \ + _list_entry_helper(item, (plist)->next, field_name); \ + item = q, q = _list_entry_helper(q, (q)->field_name.next, field_name)) + +#endif diff --git a/lib/bdev/ocf/utils.c b/lib/bdev/ocf/utils.c new file mode 100644 index 000000000..9f7307d50 --- /dev/null +++ b/lib/bdev/ocf/utils.c @@ -0,0 +1,68 @@ +/*- + * 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" + +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_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; + } +} diff --git a/lib/bdev/ocf/utils.h b/lib/bdev/ocf/utils.h new file mode 100644 index 000000000..c8966cb3e --- /dev/null +++ b/lib/bdev/ocf/utils.h @@ -0,0 +1,42 @@ +/*- + * 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_cache_mode_t ocf_get_cache_mode(const char *cache_mode); +const char *ocf_get_cache_modename(ocf_cache_mode_t mode); + +#endif diff --git a/lib/bdev/ocf/vbdev_ocf.c b/lib/bdev/ocf/vbdev_ocf.c new file mode 100644 index 000000000..6cbc36be2 --- /dev/null +++ b/lib/bdev/ocf/vbdev_ocf.c @@ -0,0 +1,911 @@ +/*- + * 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 +#include +#include + +#include "ctx.h" +#include "data.h" +#include "dobj.h" +#include "utils.h" +#include "vbdev_ocf.h" + +#include "spdk/bdev_module.h" +#include "spdk/conf.h" +#include "spdk/io_channel.h" +#include "spdk/string.h" +#include "spdk_internal/log.h" +#include "spdk/cpuset.h" + +static struct spdk_bdev_module ocf_if; + +/* Set number of OCF queues to maximum numbers of cores + * that SPDK supports, so we never run out of them */ +static int g_queues_count = SPDK_CPUSET_SIZE; + +static TAILQ_HEAD(, vbdev_ocf) g_ocf_vbdev_head + = TAILQ_HEAD_INITIALIZER(g_ocf_vbdev_head); + +/* Free allocated strings and structure itself + * Used at shutdown only */ +static void +free_vbdev(struct vbdev_ocf *vbdev) +{ + if (!vbdev) { + return; + } + + pthread_mutex_destroy(&vbdev->_lock); + free(vbdev->name); + free(vbdev->cache.name); + free(vbdev->core.name); + free(vbdev); +} + +/* Stop OCF cache object + * vbdev_ocf is not operational after this */ +static int +stop_vbdev(struct vbdev_ocf *vbdev) +{ + int rc; + + if (vbdev == NULL) { + return -EFAULT; + } + + if (vbdev->ocf_cache == NULL) { + return -EFAULT; + } + + if (!ocf_cache_is_running(vbdev->ocf_cache)) { + return -EINVAL; + } + + /* This function blocks execution until all OCF requests are finished + * But we don't have to worry about possible deadlocks because in + * supported modes (WT and PT) all OCF requests are finished before + * SPDK bdev io requests */ + rc = ocf_mngt_cache_stop(vbdev->ocf_cache); + if (rc) { + SPDK_ERRLOG("Could not stop cache for '%s'\n", vbdev->name); + return rc; + } + + return rc; +} + +/* Release SPDK and OCF objects associated with base */ +static int +remove_base(struct vbdev_ocf_base *base) +{ + int rc = 0; + + if (base == NULL) { + return -EFAULT; + } + + assert(base->attached); + + /* Release OCF-part */ + if (base->parent->ocf_cache && ocf_cache_is_running(base->parent->ocf_cache)) { + if (base->is_cache) { + rc = stop_vbdev(base->parent); + } else { + rc = ocf_mngt_cache_remove_core(base->parent->ocf_cache, base->id, false); + } + } + + /* Release SPDK-part */ + spdk_bdev_module_release_bdev(base->bdev); + spdk_bdev_close(base->desc); + + base->attached = false; + return rc; +} + +/* Free OCF resources, close base bdevs, unregister io device + * This function is called during spdk_bdev_unregister */ +static int +vbdev_ocf_destruct(void *opaque) +{ + struct vbdev_ocf *vbdev = opaque; + int status = 0; + + if (vbdev->state.doing_finish) { + return -EALREADY; + } + vbdev->state.doing_finish = true; + + if (vbdev->state.started) { + status = stop_vbdev(vbdev); + } + + if (vbdev->core.attached) { + remove_base(&vbdev->core); + } + if (vbdev->cache.attached) { + remove_base(&vbdev->cache); + } + + if (vbdev->state.started) { + spdk_io_device_unregister(vbdev, NULL); + } + + return status; +} + +/* Stop OCF cache and unregister SPDK bdev */ +int +vbdev_ocf_delete(struct vbdev_ocf *vbdev) +{ + int rc = 0; + + if (vbdev->state.started) { + spdk_bdev_unregister(&vbdev->exp_bdev, NULL, NULL); + } else { + rc = vbdev_ocf_destruct(vbdev); + } + + return rc; +} + +/* 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; +} + +/* 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) +{ + int dir; + 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; + + switch (bdev_io->type) { + case SPDK_BDEV_IO_TYPE_WRITE: + case SPDK_BDEV_IO_TYPE_READ: + dir = OCF_READ; + if (bdev_io->type == SPDK_BDEV_IO_TYPE_WRITE) { + dir = OCF_WRITE; + } + ocf_io_configure(io, offset, len, dir, 0, 0); + return ocf_submit_io(io); + case SPDK_BDEV_IO_TYPE_FLUSH: + case SPDK_BDEV_IO_TYPE_UNMAP: + 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; + struct bdev_ocf_data *data = NULL; + struct vbdev_ocf_qcxt *qctx = spdk_io_channel_get_ctx(ch); + int err; + + io = ocf_new_io(vbdev->ocf_core); + if (!io) { + err = -ENOMEM; + goto fail; + } + + ocf_io_set_queue(io, ocf_queue_get_id(qctx->queue)); + + 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); + } +} + +/* 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, io_handle, + bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen); + break; + case SPDK_BDEV_IO_TYPE_WRITE: + io_handle(ch, bdev_io); + break; + case SPDK_BDEV_IO_TYPE_UNMAP: + case SPDK_BDEV_IO_TYPE_FLUSH: + 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) +{ + switch (io_type) { + case SPDK_BDEV_IO_TYPE_READ: + case SPDK_BDEV_IO_TYPE_WRITE: + return true; + case SPDK_BDEV_IO_TYPE_UNMAP: + case SPDK_BDEV_IO_TYPE_FLUSH: + 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_config_info(void *opaque, struct spdk_json_write_ctx *w) +{ + return 0; +} + +/* 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, + .dump_info_json = vbdev_ocf_dump_config_info, +}; + +/* Start OCF cache, attach caching device */ +static int +start_cache(struct vbdev_ocf *vbdev) +{ + int rc; + + rc = ocf_mngt_cache_start(vbdev_ocf_ctx, &vbdev->ocf_cache, &vbdev->cfg.cache); + if (rc) { + SPDK_ERRLOG("Failed to start cache instance\n"); + return rc; + } + vbdev->cache.id = ocf_cache_get_id(vbdev->ocf_cache); + + rc = ocf_mngt_cache_attach(vbdev->ocf_cache, &vbdev->cfg.device); + if (rc) { + SPDK_ERRLOG("Failed to attach cache device\n"); + return rc; + } + + return 0; +} + +/* Add core for existing OCF cache instance */ +static int +add_core(struct vbdev_ocf *vbdev) +{ + int rc; + + rc = ocf_mngt_cache_add_core(vbdev->ocf_cache, &vbdev->ocf_core, &vbdev->cfg.core); + if (rc) { + SPDK_ERRLOG("Failed to add core device to cache instance\n"); + return rc; + } + + vbdev->core.id = ocf_core_get_id(vbdev->ocf_core); + + return 0; +} + +/* Poller function for the OCF queue + * We execute OCF requests here synchronously */ +static int queue_poll(void *opaque) +{ + struct vbdev_ocf_qcxt *qctx = opaque; + uint32_t iono = ocf_queue_pending_io(qctx->queue); + + ocf_queue_run(qctx->queue); + + if (iono > 0) { + return 1; + } else { + return 0; + } +} + +/* Find queue index that is not taken */ +static int +get_free_queue_id(struct vbdev_ocf *vbdev) +{ + struct vbdev_ocf_qcxt *qctx; + int i, tmp; + + for (i = 1; i < (int)vbdev->cfg.cache.io_queues; i++) { + tmp = i; + TAILQ_FOREACH(qctx, &vbdev->queues, tailq) { + tmp = ocf_queue_get_id(qctx->queue); + if (tmp == i) { + tmp = -1; + break; + } + } + if (tmp > 0) { + return i; + } + } + + return -1; +} + +/* Called on cache vbdev creation at every thread + * We determine on which OCF queue IOs from this thread will be running + * and allocate resources for that queue + * This is also where queue poller gets registered */ +static int +io_device_create_cb(void *io_device, void *ctx_buf) +{ + struct vbdev_ocf *vbdev = io_device; + struct vbdev_ocf_qcxt *qctx = ctx_buf; + int queue_id = 0, rc; + + /* Modifying state of vbdev->queues needs to be synchronous + * We use vbdev private lock to achive that */ + pthread_mutex_lock(&vbdev->_lock); + + queue_id = get_free_queue_id(vbdev); + + if (queue_id < 0) { + SPDK_ERRLOG("OCF queues count is too small, try to allocate more than %d\n", + vbdev->cfg.cache.io_queues); + rc = -EINVAL; + goto end; + } + + rc = ocf_cache_get_queue(vbdev->ocf_cache, queue_id, &qctx->queue); + if (rc) { + SPDK_ERRLOG("Could not get OCF queue #%d\n", queue_id); + goto end; + } + + 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); + + TAILQ_INSERT_TAIL(&vbdev->queues, qctx, tailq); + +end: + pthread_mutex_unlock(&vbdev->_lock); + return rc; +} + +/* Called per thread + * We free resources associated with OCF queue here + * and close base devices channels */ +static void +io_device_destroy_cb(void *io_device, void *ctx_buf) +{ + struct vbdev_ocf_qcxt *qctx = ctx_buf; + + spdk_put_io_channel(qctx->cache_ch); + spdk_put_io_channel(qctx->core_ch); + spdk_poller_unregister(&qctx->poller); + + pthread_mutex_lock(&qctx->vbdev->_lock); + TAILQ_REMOVE(&qctx->vbdev->queues, qctx, tailq); + pthread_mutex_unlock(&qctx->vbdev->_lock); +} + +/* Start OCF cache and register vbdev_ocf at bdev layer */ +static int +register_vbdev(struct vbdev_ocf *vbdev) +{ + int result; + + if (!vbdev->cache.attached || !vbdev->core.attached) { + return -EPERM; + } + + result = start_cache(vbdev); + if (result) { + SPDK_ERRLOG("Failed to start cache instance\n"); + return result; + } + + result = add_core(vbdev); + if (result) { + SPDK_ERRLOG("Failed to add core to cache instance\n"); + return result; + } + + /* Create exported spdk object */ + + /* 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_qcxt), vbdev->name); + result = spdk_bdev_register(&vbdev->exp_bdev); + if (result) { + SPDK_ERRLOG("Could not register exposed bdev\n"); + return result; + } + + vbdev->state.started = true; + + return result; +} + +/* 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; + + /* Id 0 means OCF decides the id */ + cfg->cache.id = 0; + cfg->cache.name = vbdev->name; + cfg->cache.name_size = strlen(vbdev->name) + 1; + + /* TODO [metadata]: make configurable with persistent + * metadata support */ + cfg->cache.metadata_volatile = true; + + /* 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; + + /* At this moment OCF queues count is static + * so we choose some value for it + * It has to be bigger than SPDK thread count */ + cfg->cache.io_queues = g_queues_count; + + /* TODO [cache line size] */ + cfg->device.cache_line_size = ocf_cache_line_size_4; + cfg->device.force = true; + cfg->device.min_free_ram = 0; + cfg->device.perform_test = false; + cfg->device.discard_on_start = false; + + cfg->core.data_obj_type = SPDK_OBJECT; + + cfg->device.uuid.size = strlen(vbdev->cache.name) + 1; + cfg->device.uuid.data = vbdev->cache.name; + cfg->core.uuid.size = strlen(vbdev->core.name) + 1; + cfg->core.uuid.data = vbdev->core.name; +} + +/* 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) +{ + 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", 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; + pthread_mutex_init(&vbdev->_lock, NULL); + TAILQ_INIT(&vbdev->queues); + + if (cache_mode_name) { + vbdev->cfg.cache.cache_mode + = ocf_get_cache_mode(cache_mode_name); + } else { + 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; + } + + 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_dobj_init(); + if (status) { + vbdev_ocf_ctx_cleanup(); + SPDK_ERRLOG("OCF dobj 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); + 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_dobj_cleanup(); + vbdev_ocf_ctx_cleanup(); +} + +/* Open base SPDK bdev and claim it */ +static int +attach_base(struct vbdev_ocf_base *base) +{ + int status; + + if (base->attached) { + return -EALREADY; + } + + status = spdk_bdev_open(base->bdev, true, NULL, NULL, &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->attached = true; + return status; +} + +/* Attach base bdevs + * If they attached, start vbdev + * otherwise wait for them to appear at examine */ +static int +create_from_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); + } + + if (rc == 0 && vbdev->core.attached && vbdev->cache.attached) { + rc = register_vbdev(vbdev); + } + + return rc; +} + +/* Init and then start vbdev if all base devices are present */ +int +vbdev_ocf_construct(const char *vbdev_name, + const char *cache_mode_name, + const char *cache_name, + const char *core_name) +{ + 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); + if (rc) { + return rc; + } + + vbdev = vbdev_ocf_get_by_name(vbdev_name); + if (vbdev == NULL) { + return -ENODEV; + } + + 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); + } + + return create_from_bdevs(vbdev, cache_bdev, core_bdev); +} + +/* This called if new device is created in SPDK application + * If that device named as one of base bdevs of cache_vbdev, + * attach them + * If last device attached here, vbdev starts here */ +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)) { + create_from_bdevs(vbdev, bdev, NULL); + break; + } + if (!strcmp(bdev_name, vbdev->core.name)) { + create_from_bdevs(vbdev, NULL, bdev); + break; + } + } + spdk_bdev_module_examine_done(&ocf_if); +} + +static int +vbdev_ocf_get_ctx_size(void) +{ + return sizeof(struct bdev_ocf_data); +} + +/* 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 = NULL, + .module_fini = vbdev_ocf_module_fini, + .config_text = NULL, + .get_ctx_size = vbdev_ocf_get_ctx_size, + .examine_config = vbdev_ocf_examine, +}; +SPDK_BDEV_MODULE_REGISTER(&ocf_if); + +SPDK_LOG_REGISTER_COMPONENT("vbdev_ocf", SPDK_TRACE_VBDEV_OCF) diff --git a/lib/bdev/ocf/vbdev_ocf.h b/lib/bdev/ocf/vbdev_ocf.h new file mode 100644 index 000000000..634bf9fd1 --- /dev/null +++ b/lib/bdev/ocf/vbdev_ocf.h @@ -0,0 +1,157 @@ +/*- + * 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 + +#include "spdk/bdev.h" +#include "spdk/bdev_module.h" + +struct vbdev_ocf; + +/* Context for OCF queue poller + * Used for mapping SPDK threads to OCF queues */ +struct vbdev_ocf_qcxt { + /* 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; + /* Link to per-bdev list of queue contexts */ + TAILQ_ENTRY(vbdev_ocf_qcxt) tailq; +}; + +/* Important states */ +struct vbdev_ocf_state { + /* 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; +}; + +/* + * 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; +}; + +/* Base device info */ +struct vbdev_ocf_base { + /* OCF unique internal id */ + int id; + + /* 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; + + /* Reference to main vbdev */ + struct vbdev_ocf *parent; +}; + +/* + * 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; + + /* Exposed SPDK bdev. Registered in bdev layer */ + struct spdk_bdev exp_bdev; + + /* Link to global list of this type structures */ + TAILQ_ENTRY(vbdev_ocf) tailq; + + /* List of queues contexts + * New items are added at io_channel creation */ + TAILQ_HEAD(, vbdev_ocf_qcxt) queues; + + /* Private per-bdev lock */ + pthread_mutex_t _lock; +}; + +int vbdev_ocf_construct( + const char *vbdev_name, + const char *cache_mode_name, + const char *cache_name, + const char *core_name); + +/* 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); + +#endif diff --git a/mk/spdk.modules.mk b/mk/spdk.modules.mk index 408fb1bf3..4e8aedd50 100644 --- a/mk/spdk.modules.mk +++ b/mk/spdk.modules.mk @@ -39,6 +39,11 @@ ifeq ($(CONFIG_CRYPTO),y) BLOCKDEV_MODULES_LIST += bdev_crypto endif +ifeq ($(CONFIG_OCF),y) +BLOCKDEV_MODULES_LIST += bdev_ocf +BLOCKDEV_MODULES_LIST += ocfenv +endif + ifeq ($(CONFIG_RDMA),y) SYS_LIBS += -libverbs -lrdmacm endif