Spdk/lib/accel/accel_sw.c
paul luse a6dbe3721e update Intel copyright notices
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
2022-11-10 08:28:53 +00:00

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)