per Intel policy to include file commit date using git cmd below. The policy does not apply to non-Intel (C) notices. git log --follow -C90% --format=%ad --date default <file> | tail -1 and then pull just the 4 digit year from the result. Intel copyrights were not added to files where Intel either had no contribution ot the contribution lacked substance (ie license header updates, formatting changes, etc). Contribution date used "--follow -C95%" to get the most accurate date. Note that several files in this patch didn't end the license/(c) block with a blank comment line so these were added as the vast majority of files do have this last blank line. Simply there for consistency. Signed-off-by: paul luse <paul.e.luse@intel.com> Change-Id: Id5b7ce4f658fe87132f14139ead58d6e285c04d4 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15192 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Community-CI: Mellanox Build Bot
481 lines
12 KiB
C
481 lines
12 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright (C) 2022 Intel Corporation.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include "spdk/stdinc.h"
|
|
|
|
#include "spdk_internal/accel_module.h"
|
|
|
|
#include "spdk/env.h"
|
|
#include "spdk/likely.h"
|
|
#include "spdk/log.h"
|
|
#include "spdk/thread.h"
|
|
#include "spdk/json.h"
|
|
#include "spdk/crc32.h"
|
|
#include "spdk/util.h"
|
|
|
|
#ifdef SPDK_CONFIG_PMDK
|
|
#include "libpmem.h"
|
|
#endif
|
|
|
|
#ifdef SPDK_CONFIG_ISAL
|
|
#include "../isa-l/include/igzip_lib.h"
|
|
#endif
|
|
|
|
struct sw_accel_io_channel {
|
|
/* for ISAL */
|
|
#ifdef SPDK_CONFIG_ISAL
|
|
struct isal_zstream stream;
|
|
struct inflate_state state;
|
|
#endif
|
|
struct spdk_poller *completion_poller;
|
|
TAILQ_HEAD(, spdk_accel_task) tasks_to_complete;
|
|
};
|
|
|
|
/* Post SW completions to a list and complete in a poller as we don't want to
|
|
* complete them on the caller's stack as they'll likely submit another. */
|
|
inline static void
|
|
_add_to_comp_list(struct sw_accel_io_channel *sw_ch, struct spdk_accel_task *accel_task, int status)
|
|
{
|
|
accel_task->status = status;
|
|
TAILQ_INSERT_TAIL(&sw_ch->tasks_to_complete, accel_task, link);
|
|
}
|
|
|
|
/* Used when the SW engine is selected and the durable flag is set. */
|
|
inline static int
|
|
_check_flags(int flags)
|
|
{
|
|
if (flags & ACCEL_FLAG_PERSISTENT) {
|
|
#ifndef SPDK_CONFIG_PMDK
|
|
/* PMDK is required to use this flag. */
|
|
SPDK_ERRLOG("ACCEL_FLAG_PERSISTENT set but PMDK not configured. Configure PMDK or do not use this flag.\n");
|
|
return -EINVAL;
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
sw_accel_supports_opcode(enum accel_opcode opc)
|
|
{
|
|
switch (opc) {
|
|
case ACCEL_OPC_COPY:
|
|
case ACCEL_OPC_FILL:
|
|
case ACCEL_OPC_DUALCAST:
|
|
case ACCEL_OPC_COMPARE:
|
|
case ACCEL_OPC_CRC32C:
|
|
case ACCEL_OPC_COPY_CRC32C:
|
|
case ACCEL_OPC_COMPRESS:
|
|
case ACCEL_OPC_DECOMPRESS:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
_pmem_memcpy(void *dst, const void *src, size_t len)
|
|
{
|
|
#ifdef SPDK_CONFIG_PMDK
|
|
int is_pmem = pmem_is_pmem(dst, len);
|
|
|
|
if (is_pmem) {
|
|
pmem_memcpy_persist(dst, src, len);
|
|
} else {
|
|
memcpy(dst, src, len);
|
|
pmem_msync(dst, len);
|
|
}
|
|
#else
|
|
SPDK_ERRLOG("Function not defined without SPDK_CONFIG_PMDK enabled.\n");
|
|
assert(0);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
_sw_accel_dualcast(void *dst1, void *dst2, void *src, size_t nbytes, int flags)
|
|
{
|
|
if (flags & ACCEL_FLAG_PERSISTENT) {
|
|
_pmem_memcpy(dst1, src, nbytes);
|
|
_pmem_memcpy(dst2, src, nbytes);
|
|
} else {
|
|
memcpy(dst1, src, nbytes);
|
|
memcpy(dst2, src, nbytes);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_sw_accel_copy(void *dst, void *src, size_t nbytes, int flags)
|
|
{
|
|
|
|
if (flags & ACCEL_FLAG_PERSISTENT) {
|
|
_pmem_memcpy(dst, src, nbytes);
|
|
} else {
|
|
memcpy(dst, src, nbytes);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_sw_accel_copyv(void *dst, struct iovec *iov, uint32_t iovcnt, int flags)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < iovcnt; i++) {
|
|
assert(iov[i].iov_base != NULL);
|
|
if (flags & ACCEL_FLAG_PERSISTENT) {
|
|
_pmem_memcpy(dst, iov[i].iov_base, (size_t)iov[i].iov_len);
|
|
} else {
|
|
memcpy(dst, iov[i].iov_base, (size_t)iov[i].iov_len);
|
|
}
|
|
dst += iov[i].iov_len;
|
|
}
|
|
}
|
|
|
|
static int
|
|
_sw_accel_compare(void *src1, void *src2, size_t nbytes)
|
|
{
|
|
return memcmp(src1, src2, nbytes);
|
|
}
|
|
|
|
static void
|
|
_sw_accel_fill(void *dst, uint8_t fill, size_t nbytes, int flags)
|
|
{
|
|
if (flags & ACCEL_FLAG_PERSISTENT) {
|
|
#ifdef SPDK_CONFIG_PMDK
|
|
int is_pmem = pmem_is_pmem(dst, nbytes);
|
|
|
|
if (is_pmem) {
|
|
pmem_memset_persist(dst, fill, nbytes);
|
|
} else {
|
|
memset(dst, fill, nbytes);
|
|
pmem_msync(dst, nbytes);
|
|
}
|
|
#else
|
|
SPDK_ERRLOG("Function not defined without SPDK_CONFIG_PMDK enabled.\n");
|
|
assert(0);
|
|
#endif
|
|
} else {
|
|
memset(dst, fill, nbytes);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_sw_accel_crc32c(uint32_t *crc_dst, void *src, uint32_t seed, uint64_t nbytes)
|
|
{
|
|
*crc_dst = spdk_crc32c_update(src, nbytes, ~seed);
|
|
}
|
|
|
|
static void
|
|
_sw_accel_crc32cv(uint32_t *crc_dst, struct iovec *iov, uint32_t iovcnt, uint32_t seed)
|
|
{
|
|
*crc_dst = spdk_crc32c_iov_update(iov, iovcnt, ~seed);
|
|
}
|
|
|
|
static int
|
|
_sw_accel_compress(struct sw_accel_io_channel *sw_ch, struct spdk_accel_task *accel_task)
|
|
{
|
|
#ifdef SPDK_CONFIG_ISAL
|
|
size_t last_seglen = accel_task->s.iovs[accel_task->s.iovcnt - 1].iov_len;
|
|
struct iovec *siov = accel_task->s.iovs;
|
|
struct iovec *diov = accel_task->d.iovs;
|
|
size_t remaining = accel_task->nbytes;
|
|
uint32_t s = 0, d = 0;
|
|
int rc = 0;
|
|
|
|
accel_task->d.iovcnt = 1;
|
|
diov[0].iov_base = accel_task->dst;
|
|
diov[0].iov_len = accel_task->nbytes_dst;
|
|
|
|
isal_deflate_reset(&sw_ch->stream);
|
|
sw_ch->stream.end_of_stream = 0;
|
|
sw_ch->stream.next_out = diov[d].iov_base;
|
|
sw_ch->stream.avail_out = diov[d].iov_len;
|
|
sw_ch->stream.next_in = siov[s].iov_base;
|
|
sw_ch->stream.avail_in = siov[s].iov_len;
|
|
|
|
do {
|
|
/* if isal has exhausted the current dst iovec, move to the next
|
|
* one if there is one */
|
|
if (sw_ch->stream.avail_out == 0) {
|
|
if (++d < accel_task->d.iovcnt) {
|
|
sw_ch->stream.next_out = diov[d].iov_base;
|
|
sw_ch->stream.avail_out = diov[d].iov_len;
|
|
assert(sw_ch->stream.avail_out > 0);
|
|
} else {
|
|
/* we have no avail_out but also no more iovecs left so this is
|
|
* the case where either the output buffer was a perfect fit
|
|
* or not enough was provided. Check the ISAL state to determine
|
|
* which. */
|
|
if (sw_ch->stream.internal_state.state != ZSTATE_END) {
|
|
SPDK_ERRLOG("Not enough destination buffer provided.\n");
|
|
rc = -ENOMEM;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* if isal has exhausted the current src iovec, move to the next
|
|
* one if there is one */
|
|
if (sw_ch->stream.avail_in == 0 && ((s + 1) < accel_task->s.iovcnt)) {
|
|
s++;
|
|
sw_ch->stream.next_in = siov[s].iov_base;
|
|
sw_ch->stream.avail_in = siov[s].iov_len;
|
|
assert(sw_ch->stream.avail_in > 0);
|
|
}
|
|
|
|
if (remaining <= last_seglen) {
|
|
/* Need to set end of stream on last block */
|
|
sw_ch->stream.end_of_stream = 1;
|
|
}
|
|
|
|
rc = isal_deflate(&sw_ch->stream);
|
|
if (rc) {
|
|
SPDK_ERRLOG("isal_deflate retunred error %d.\n", rc);
|
|
}
|
|
|
|
if (remaining > 0) {
|
|
assert(siov[s].iov_len > sw_ch->stream.avail_in);
|
|
remaining -= (siov[s].iov_len - sw_ch->stream.avail_in);
|
|
}
|
|
|
|
} while (remaining > 0 || sw_ch->stream.avail_out == 0);
|
|
assert(sw_ch->stream.avail_in == 0);
|
|
|
|
/* Get our total output size */
|
|
if (accel_task->output_size != NULL) {
|
|
assert(sw_ch->stream.total_out > 0);
|
|
*accel_task->output_size = sw_ch->stream.total_out;
|
|
}
|
|
|
|
return rc;
|
|
#else
|
|
SPDK_ERRLOG("ISAL option is required to use software compression.\n");
|
|
return -EINVAL;
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
_sw_accel_decompress(struct sw_accel_io_channel *sw_ch, struct spdk_accel_task *accel_task)
|
|
{
|
|
#ifdef SPDK_CONFIG_ISAL
|
|
struct iovec *siov = accel_task->s.iovs;
|
|
struct iovec *diov = accel_task->d.iovs;
|
|
uint32_t s = 0, d = 0;
|
|
int rc = 0;
|
|
|
|
isal_inflate_reset(&sw_ch->state);
|
|
sw_ch->state.next_out = diov[d].iov_base;
|
|
sw_ch->state.avail_out = diov[d].iov_len;
|
|
sw_ch->state.next_in = siov[s].iov_base;
|
|
sw_ch->state.avail_in = siov[s].iov_len;
|
|
|
|
do {
|
|
/* if isal has exhausted the current dst iovec, move to the next
|
|
* one if there is one */
|
|
if (sw_ch->state.avail_out == 0 && ((d + 1) < accel_task->d.iovcnt)) {
|
|
d++;
|
|
sw_ch->state.next_out = diov[d].iov_base;
|
|
sw_ch->state.avail_out = diov[d].iov_len;
|
|
assert(sw_ch->state.avail_out > 0);
|
|
}
|
|
|
|
/* if isal has exhausted the current src iovec, move to the next
|
|
* one if there is one */
|
|
if (sw_ch->state.avail_in == 0 && ((s + 1) < accel_task->s.iovcnt)) {
|
|
s++;
|
|
sw_ch->state.next_in = siov[s].iov_base;
|
|
sw_ch->state.avail_in = siov[s].iov_len;
|
|
assert(sw_ch->state.avail_in > 0);
|
|
}
|
|
|
|
rc = isal_inflate(&sw_ch->state);
|
|
if (rc) {
|
|
SPDK_ERRLOG("isal_inflate retunred error %d.\n", rc);
|
|
}
|
|
|
|
} while (sw_ch->state.block_state < ISAL_BLOCK_FINISH);
|
|
assert(sw_ch->state.avail_in == 0);
|
|
|
|
return rc;
|
|
#else
|
|
SPDK_ERRLOG("ISAL option is required to use software decompression.\n");
|
|
return -EINVAL;
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
sw_accel_submit_tasks(struct spdk_io_channel *ch, struct spdk_accel_task *accel_task)
|
|
{
|
|
struct sw_accel_io_channel *sw_ch = spdk_io_channel_get_ctx(ch);
|
|
struct spdk_accel_task *tmp;
|
|
int rc = 0;
|
|
|
|
do {
|
|
switch (accel_task->op_code) {
|
|
case ACCEL_OPC_COPY:
|
|
rc = _check_flags(accel_task->flags);
|
|
if (rc == 0) {
|
|
_sw_accel_copy(accel_task->dst, accel_task->src, accel_task->nbytes, accel_task->flags);
|
|
}
|
|
break;
|
|
case ACCEL_OPC_FILL:
|
|
rc = _check_flags(accel_task->flags);
|
|
if (rc == 0) {
|
|
_sw_accel_fill(accel_task->dst, accel_task->fill_pattern, accel_task->nbytes, accel_task->flags);
|
|
}
|
|
break;
|
|
case ACCEL_OPC_DUALCAST:
|
|
rc = _check_flags(accel_task->flags);
|
|
if (rc == 0) {
|
|
_sw_accel_dualcast(accel_task->dst, accel_task->dst2, accel_task->src, accel_task->nbytes,
|
|
accel_task->flags);
|
|
}
|
|
break;
|
|
case ACCEL_OPC_COMPARE:
|
|
rc = _sw_accel_compare(accel_task->src, accel_task->src2, accel_task->nbytes);
|
|
break;
|
|
case ACCEL_OPC_CRC32C:
|
|
if (accel_task->s.iovcnt == 0) {
|
|
_sw_accel_crc32c(accel_task->crc_dst, accel_task->src, accel_task->seed, accel_task->nbytes);
|
|
} else {
|
|
_sw_accel_crc32cv(accel_task->crc_dst, accel_task->s.iovs, accel_task->s.iovcnt, accel_task->seed);
|
|
}
|
|
break;
|
|
case ACCEL_OPC_COPY_CRC32C:
|
|
rc = _check_flags(accel_task->flags);
|
|
if (rc == 0) {
|
|
if (accel_task->s.iovcnt == 0) {
|
|
_sw_accel_copy(accel_task->dst, accel_task->src, accel_task->nbytes, accel_task->flags);
|
|
_sw_accel_crc32c(accel_task->crc_dst, accel_task->src, accel_task->seed, accel_task->nbytes);
|
|
} else {
|
|
_sw_accel_copyv(accel_task->dst, accel_task->s.iovs, accel_task->s.iovcnt, accel_task->flags);
|
|
_sw_accel_crc32cv(accel_task->crc_dst, accel_task->s.iovs, accel_task->s.iovcnt, accel_task->seed);
|
|
}
|
|
}
|
|
break;
|
|
case ACCEL_OPC_COMPRESS:
|
|
rc = _sw_accel_compress(sw_ch, accel_task);
|
|
break;
|
|
case ACCEL_OPC_DECOMPRESS:
|
|
rc = _sw_accel_decompress(sw_ch, accel_task);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
tmp = TAILQ_NEXT(accel_task, link);
|
|
|
|
_add_to_comp_list(sw_ch, accel_task, rc);
|
|
|
|
accel_task = tmp;
|
|
} while (accel_task);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct spdk_io_channel *sw_accel_get_io_channel(void);
|
|
static int sw_accel_module_init(void);
|
|
static void sw_accel_module_fini(void *ctxt);
|
|
static size_t sw_accel_module_get_ctx_size(void);
|
|
|
|
static struct spdk_accel_module_if g_sw_module = {
|
|
.module_init = sw_accel_module_init,
|
|
.module_fini = sw_accel_module_fini,
|
|
.write_config_json = NULL,
|
|
.get_ctx_size = sw_accel_module_get_ctx_size,
|
|
.name = "software",
|
|
.supports_opcode = sw_accel_supports_opcode,
|
|
.get_io_channel = sw_accel_get_io_channel,
|
|
.submit_tasks = sw_accel_submit_tasks
|
|
};
|
|
|
|
static int
|
|
accel_comp_poll(void *arg)
|
|
{
|
|
struct sw_accel_io_channel *sw_ch = arg;
|
|
TAILQ_HEAD(, spdk_accel_task) tasks_to_complete;
|
|
struct spdk_accel_task *accel_task;
|
|
|
|
if (TAILQ_EMPTY(&sw_ch->tasks_to_complete)) {
|
|
return SPDK_POLLER_IDLE;
|
|
}
|
|
|
|
TAILQ_INIT(&tasks_to_complete);
|
|
TAILQ_SWAP(&tasks_to_complete, &sw_ch->tasks_to_complete, spdk_accel_task, link);
|
|
|
|
while ((accel_task = TAILQ_FIRST(&tasks_to_complete))) {
|
|
TAILQ_REMOVE(&tasks_to_complete, accel_task, link);
|
|
spdk_accel_task_complete(accel_task, accel_task->status);
|
|
}
|
|
|
|
return SPDK_POLLER_BUSY;
|
|
}
|
|
|
|
static int
|
|
sw_accel_create_cb(void *io_device, void *ctx_buf)
|
|
{
|
|
struct sw_accel_io_channel *sw_ch = ctx_buf;
|
|
|
|
TAILQ_INIT(&sw_ch->tasks_to_complete);
|
|
sw_ch->completion_poller = SPDK_POLLER_REGISTER(accel_comp_poll, sw_ch, 0);
|
|
|
|
#ifdef SPDK_CONFIG_ISAL
|
|
isal_deflate_init(&sw_ch->stream);
|
|
sw_ch->stream.flush = NO_FLUSH;
|
|
sw_ch->stream.level = 1;
|
|
sw_ch->stream.level_buf = calloc(1, ISAL_DEF_LVL1_DEFAULT);
|
|
if (sw_ch->stream.level_buf == NULL) {
|
|
SPDK_ERRLOG("Could not allocate isal internal buffer\n");
|
|
return -ENOMEM;
|
|
}
|
|
sw_ch->stream.level_buf_size = ISAL_DEF_LVL1_DEFAULT;
|
|
isal_inflate_init(&sw_ch->state);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
sw_accel_destroy_cb(void *io_device, void *ctx_buf)
|
|
{
|
|
struct sw_accel_io_channel *sw_ch = ctx_buf;
|
|
|
|
#ifdef SPDK_CONFIG_ISAL
|
|
free(sw_ch->stream.level_buf);
|
|
#endif
|
|
|
|
spdk_poller_unregister(&sw_ch->completion_poller);
|
|
}
|
|
|
|
static struct spdk_io_channel *
|
|
sw_accel_get_io_channel(void)
|
|
{
|
|
return spdk_get_io_channel(&g_sw_module);
|
|
}
|
|
|
|
static size_t
|
|
sw_accel_module_get_ctx_size(void)
|
|
{
|
|
return sizeof(struct spdk_accel_task);
|
|
}
|
|
|
|
static int
|
|
sw_accel_module_init(void)
|
|
{
|
|
SPDK_NOTICELOG("Accel framework software module initialized.\n");
|
|
spdk_io_device_register(&g_sw_module, sw_accel_create_cb, sw_accel_destroy_cb,
|
|
sizeof(struct sw_accel_io_channel), "sw_accel_module");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
sw_accel_module_fini(void *ctxt)
|
|
{
|
|
spdk_io_device_unregister(&g_sw_module, NULL);
|
|
spdk_accel_module_finish();
|
|
}
|
|
|
|
SPDK_ACCEL_MODULE_REGISTER(sw, &g_sw_module)
|