diff options
Diffstat (limited to 'src/spdk/module/bdev/ocf/volume.c')
-rw-r--r-- | src/spdk/module/bdev/ocf/volume.c | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/src/spdk/module/bdev/ocf/volume.c b/src/spdk/module/bdev/ocf/volume.c new file mode 100644 index 000000000..de683b852 --- /dev/null +++ b/src/spdk/module/bdev/ocf/volume.c @@ -0,0 +1,441 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <ocf/ocf.h> + +#include "spdk/bdev_module.h" +#include "spdk/env.h" +#include "spdk/thread.h" +#include "spdk_internal/log.h" + +#include "data.h" +#include "volume.h" +#include "ctx.h" +#include "vbdev_ocf.h" + +static int +vbdev_ocf_volume_open(ocf_volume_t volume, void *opts) +{ + struct vbdev_ocf_base **priv = ocf_volume_get_priv(volume); + struct vbdev_ocf_base *base; + + if (opts) { + base = opts; + } else { + base = vbdev_ocf_get_base_by_name(ocf_volume_get_uuid(volume)->data); + if (base == NULL) { + return -ENODEV; + } + } + + *priv = base; + + return 0; +} + +static void +vbdev_ocf_volume_close(ocf_volume_t volume) +{ +} + +static uint64_t +vbdev_ocf_volume_get_length(ocf_volume_t volume) +{ + struct vbdev_ocf_base *base = *((struct vbdev_ocf_base **)ocf_volume_get_priv(volume)); + uint64_t len; + + len = base->bdev->blocklen * base->bdev->blockcnt; + + return len; +} + +static int +vbdev_ocf_volume_io_set_data(struct ocf_io *io, ctx_data_t *data, + uint32_t offset) +{ + struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io); + + io_ctx->offset = offset; + io_ctx->data = data; + + if (io_ctx->data && offset >= io_ctx->data->size) { + return -ENOBUFS; + } + + return 0; +} + +static ctx_data_t * +vbdev_ocf_volume_io_get_data(struct ocf_io *io) +{ + return ocf_get_io_ctx(io)->data; +} + +static void +vbdev_ocf_volume_io_get(struct ocf_io *io) +{ + struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io); + + io_ctx->ref++; +} + +static void +vbdev_ocf_volume_io_put(struct ocf_io *io) +{ + struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io); + + if (--io_ctx->ref) { + return; + } +} + +static int +get_starting_vec(struct iovec *iovs, int iovcnt, int *offset) +{ + int i; + size_t off; + + off = *offset; + + for (i = 0; i < iovcnt; i++) { + if (off < iovs[i].iov_len) { + *offset = off; + return i; + } + off -= iovs[i].iov_len; + } + + return -1; +} + +static void +initialize_cpy_vector(struct iovec *cpy_vec, int cpy_vec_len, struct iovec *orig_vec, + int orig_vec_len, + size_t offset, size_t bytes) +{ + void *curr_base; + int len, i; + + i = 0; + + while (bytes > 0) { + curr_base = orig_vec[i].iov_base + offset; + len = MIN(bytes, orig_vec[i].iov_len - offset); + + cpy_vec[i].iov_base = curr_base; + cpy_vec[i].iov_len = len; + + bytes -= len; + offset = 0; + i++; + } +} + +static void +vbdev_ocf_volume_submit_io_cb(struct spdk_bdev_io *bdev_io, bool success, void *opaque) +{ + struct ocf_io *io; + struct ocf_io_ctx *io_ctx; + + assert(opaque); + + io = opaque; + io_ctx = ocf_get_io_ctx(io); + assert(io_ctx != NULL); + + if (!success) { + io_ctx->error |= 1; + } + + if (io_ctx->iovs_allocated && bdev_io != NULL) { + env_free(bdev_io->u.bdev.iovs); + } + + if (io_ctx->error) { + SPDK_DEBUGLOG(SPDK_TRACE_VBDEV_OCF_VOLUME, + "base returned error on io submission: %d\n", io_ctx->error); + } + + if (io->io_queue == NULL && io_ctx->ch != NULL) { + spdk_put_io_channel(io_ctx->ch); + } + + vbdev_ocf_volume_io_put(io); + if (bdev_io) { + spdk_bdev_free_io(bdev_io); + } + + if (--io_ctx->rq_cnt == 0) { + io->end(io, io_ctx->error); + } +} + +static int +prepare_submit(struct ocf_io *io) +{ + struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io); + struct vbdev_ocf_qctx *qctx; + struct vbdev_ocf_base *base; + ocf_queue_t q = io->io_queue; + ocf_cache_t cache; + struct vbdev_ocf_cache_ctx *cctx; + int rc = 0; + + io_ctx->rq_cnt++; + if (io_ctx->rq_cnt != 1) { + return 0; + } + + vbdev_ocf_volume_io_get(io); + base = *((struct vbdev_ocf_base **)ocf_volume_get_priv(ocf_io_get_volume(io))); + + if (io->io_queue == NULL) { + /* In case IO is initiated by OCF, queue is unknown + * so we have to get io channel ourselves */ + io_ctx->ch = spdk_bdev_get_io_channel(base->desc); + if (io_ctx->ch == NULL) { + return -EPERM; + } + return 0; + } + + cache = ocf_queue_get_cache(q); + cctx = ocf_cache_get_priv(cache); + + if (q == cctx->cleaner_queue || q == cctx->mngt_queue) { + io_ctx->ch = base->management_channel; + return 0; + } + + qctx = ocf_queue_get_priv(q); + if (qctx == NULL) { + return -EFAULT; + } + + if (base->is_cache) { + io_ctx->ch = qctx->cache_ch; + } else { + io_ctx->ch = qctx->core_ch; + } + + return rc; +} + +static void +vbdev_ocf_volume_submit_flush(struct ocf_io *io) +{ + struct vbdev_ocf_base *base = + *((struct vbdev_ocf_base **) + ocf_volume_get_priv(ocf_io_get_volume(io))); + struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io); + int status; + + status = prepare_submit(io); + if (status) { + SPDK_ERRLOG("Preparing io failed with status=%d\n", status); + vbdev_ocf_volume_submit_io_cb(NULL, false, io); + return; + } + + status = spdk_bdev_flush( + base->desc, io_ctx->ch, + io->addr, io->bytes, + vbdev_ocf_volume_submit_io_cb, io); + if (status) { + /* Since callback is not called, we need to do it manually to free io structures */ + SPDK_ERRLOG("Submission failed with status=%d\n", status); + vbdev_ocf_volume_submit_io_cb(NULL, false, io); + } +} + +static void +vbdev_ocf_volume_submit_io(struct ocf_io *io) +{ + struct vbdev_ocf_base *base = + *((struct vbdev_ocf_base **) + ocf_volume_get_priv(ocf_io_get_volume(io))); + struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io); + struct iovec *iovs; + int iovcnt, status = 0, i, offset; + uint64_t addr, len; + + if (io->flags == OCF_WRITE_FLUSH) { + vbdev_ocf_volume_submit_flush(io); + return; + } + + status = prepare_submit(io); + if (status) { + SPDK_ERRLOG("Preparing io failed with status=%d\n", status); + vbdev_ocf_volume_submit_io_cb(NULL, false, io); + return; + } + + /* IO fields */ + addr = io->addr; + len = io->bytes; + offset = io_ctx->offset; + + if (len < io_ctx->data->size) { + if (io_ctx->data->iovcnt == 1) { + if (io->dir == OCF_READ) { + status = spdk_bdev_read(base->desc, io_ctx->ch, + io_ctx->data->iovs[0].iov_base + offset, addr, len, + vbdev_ocf_volume_submit_io_cb, io); + } else if (io->dir == OCF_WRITE) { + status = spdk_bdev_write(base->desc, io_ctx->ch, + io_ctx->data->iovs[0].iov_base + offset, addr, len, + vbdev_ocf_volume_submit_io_cb, io); + } + goto end; + } else { + i = get_starting_vec(io_ctx->data->iovs, io_ctx->data->iovcnt, &offset); + + if (i < 0) { + SPDK_ERRLOG("offset bigger than data size\n"); + vbdev_ocf_volume_submit_io_cb(NULL, false, io); + return; + } + + iovcnt = io_ctx->data->iovcnt - i; + + io_ctx->iovs_allocated = true; + iovs = env_malloc(sizeof(*iovs) * iovcnt, ENV_MEM_NOIO); + + if (!iovs) { + SPDK_ERRLOG("allocation failed\n"); + vbdev_ocf_volume_submit_io_cb(NULL, false, io); + return; + } + + initialize_cpy_vector(iovs, io_ctx->data->iovcnt, &io_ctx->data->iovs[i], + iovcnt, offset, len); + } + } else { + iovs = io_ctx->data->iovs; + iovcnt = io_ctx->data->iovcnt; + } + + if (io->dir == OCF_READ) { + status = spdk_bdev_readv(base->desc, io_ctx->ch, + iovs, iovcnt, addr, len, vbdev_ocf_volume_submit_io_cb, io); + } else if (io->dir == OCF_WRITE) { + status = spdk_bdev_writev(base->desc, io_ctx->ch, + iovs, iovcnt, addr, len, vbdev_ocf_volume_submit_io_cb, io); + } + +end: + if (status) { + /* TODO [ENOMEM]: implement ENOMEM handling when submitting IO to base device */ + + /* Since callback is not called, we need to do it manually to free io structures */ + SPDK_ERRLOG("submission failed with status=%d\n", status); + vbdev_ocf_volume_submit_io_cb(NULL, false, io); + } +} + +static void +vbdev_ocf_volume_submit_discard(struct ocf_io *io) +{ + struct vbdev_ocf_base *base = + *((struct vbdev_ocf_base **) + ocf_volume_get_priv(ocf_io_get_volume(io))); + struct ocf_io_ctx *io_ctx = ocf_get_io_ctx(io); + int status = 0; + + status = prepare_submit(io); + if (status) { + SPDK_ERRLOG("Preparing io failed with status=%d\n", status); + vbdev_ocf_volume_submit_io_cb(NULL, false, io); + return; + } + + status = spdk_bdev_unmap( + base->desc, io_ctx->ch, + io->addr, io->bytes, + vbdev_ocf_volume_submit_io_cb, io); + if (status) { + /* Since callback is not called, we need to do it manually to free io structures */ + SPDK_ERRLOG("Submission failed with status=%d\n", status); + vbdev_ocf_volume_submit_io_cb(NULL, false, io); + } +} + +static void +vbdev_ocf_volume_submit_metadata(struct ocf_io *io) +{ + /* Implement with persistent metadata support */ +} + +static unsigned int +vbdev_ocf_volume_get_max_io_size(ocf_volume_t volume) +{ + return 131072; +} + +static struct ocf_volume_properties vbdev_volume_props = { + .name = "SPDK block device", + .io_priv_size = sizeof(struct ocf_io_ctx), + .volume_priv_size = sizeof(struct vbdev_ocf_base *), + .caps = { + .atomic_writes = 0 /* to enable need to have ops->submit_metadata */ + }, + .ops = { + .open = vbdev_ocf_volume_open, + .close = vbdev_ocf_volume_close, + .get_length = vbdev_ocf_volume_get_length, + .submit_io = vbdev_ocf_volume_submit_io, + .submit_discard = vbdev_ocf_volume_submit_discard, + .submit_flush = vbdev_ocf_volume_submit_flush, + .get_max_io_size = vbdev_ocf_volume_get_max_io_size, + .submit_metadata = vbdev_ocf_volume_submit_metadata, + }, + .io_ops = { + .set_data = vbdev_ocf_volume_io_set_data, + .get_data = vbdev_ocf_volume_io_get_data, + }, +}; + +int +vbdev_ocf_volume_init(void) +{ + return ocf_ctx_register_volume_type(vbdev_ocf_ctx, SPDK_OBJECT, &vbdev_volume_props); +} + +void +vbdev_ocf_volume_cleanup(void) +{ + ocf_ctx_unregister_volume_type(vbdev_ocf_ctx, SPDK_OBJECT); +} + +SPDK_LOG_REGISTER_COMPONENT("vbdev_ocf_volume", SPDK_TRACE_VBDEV_OCF_VOLUME) |