Commit 8976b47d authored by Eugene Kobyak's avatar Eugene Kobyak Committed by Tomasz Zawadzki
Browse files

external_code/accel: Add external accel module



Example implementation of accel module. This change includes the module itself and also an application to test all supported operations.

Change-Id: I029f877f9ec9a90ceaf5207e4213bbad9b7d2ccd
Signed-off-by: default avatarEugene Kobyak <eugene.kobyak@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/20340


Reviewed-by: default avatarJim Harris <jim.harris@samsung.com>
Reviewed-by: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
Reviewed-by: default avatarKrzysztof Karas <krzysztof.karas@intel.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
parent 7ffbcd88
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -37,6 +37,8 @@ test/unit/lib/iscsi/common
test/unit/lib/json_mock

# These files are in the external_code directory which doesn't get compiled with SPDK.
test/external_code/accel/accel_module
test/external_code/accel/accel_run
test/external_code/hello_world/hello_bdev
test/external_code/passthru/vbdev_passthru
test/external_code/passthru/vbdev_passthru_rpc
+8 −1
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ endif
export
.PHONY: all

all: hello_world_bdev_shared_combo nvme_shared
all: hello_world_bdev_shared_combo nvme_shared accel_shared

static: hello_world_bdev_static nvme_static

@@ -50,6 +50,12 @@ hello_world_bdev_static: passthru_static
hello_world_no_bdev_static:
	$(MAKE) --directory=hello_world alone_static

accel_shared:
	$(MAKE) --directory=accel shared

accel_static:
	$(MAKE) --directory=accel static

passthru_shared:
	$(MAKE) --directory=passthru shared

@@ -66,3 +72,4 @@ clean:
	rm -f ./hello_world/hello_bdev
	rm -f ./passthru/libpassthru_external.*
	rm -f ./nvme/*.{so,o} ./nvme/identify
	rm -f ./accel/accel_module
+21 −0
Original line number Diff line number Diff line
#  SPDX-License-Identifier: BSD-3-Clause
#  Copyright (C) 2023 Intel Corporation.
#  All rights reserved.
#

PKG_CONFIG_PATH = $(SPDK_LIB_DIR)/pkgconfig

SYS_LIB := $(shell PKG_CONFIG_PATH="$(PKG_CONFIG_PATH)" pkg-config --libs --static spdk_syslibs)
SPDK_LIB := $(shell PKG_CONFIG_PATH="$(PKG_CONFIG_PATH)" pkg-config \
		--libs spdk_event spdk_event_accel spdk_accel spdk_env_dpdk)

shared:
	$(CC) $(COMMON_CFLAGS) -c -fPIC accel_module.c -o accel_module.o
	$(CC) $(COMMON_CFLAGS) -shared accel_module.o -o libaccel_external.so
	$(CC) $(COMMON_CFLAGS) -o accel_module accel_run.c -L. -laccel_external \
		-lspdk $(SPDK_LIB)

static:
	$(CC) $(COMMON_CFLAGS) -o accel_module accel_module.c accel_run.c -pthread \
		-Wl,--whole-archive,-Bstatic $(SPDK_LIB) \
		-Wl,--no-whole-archive,-Bdynamic $(SYS_LIB)
+197 −0
Original line number Diff line number Diff line
/*   SPDX-License-Identifier: BSD-3-Clause
 *   Copyright (C) 2023 Intel Corporation.
 *   All rights reserved.
 */

#include "spdk/accel.h"
#include "spdk/accel_module.h"
#include "spdk/thread.h"

static struct spdk_accel_module_if g_ex_module;

struct ex_accel_io_channel {
	struct spdk_poller *completion_poller;
	TAILQ_HEAD(, spdk_accel_task) tasks_to_complete;
};

static int
ex_accel_copy_iovs(struct iovec *dst_iovs, uint32_t dst_iovcnt,
		   struct iovec *src_iovs, uint32_t src_iovcnt)
{
	struct spdk_ioviter iter;
	void *src, *dst;
	size_t len;

	for (len = spdk_ioviter_first(&iter, src_iovs, src_iovcnt,
				      dst_iovs, dst_iovcnt, &src, &dst);
	     len != 0;
	     len = spdk_ioviter_next(&iter, &src, &dst)) {
		memcpy(dst, src, len);
	}

	return 0;
}

static int
ex_accel_compare(struct iovec *src_iovs, uint32_t src_iovcnt,
		 struct iovec *src2_iovs, uint32_t src2_iovcnt)
{
	if (spdk_unlikely(src_iovcnt != 1 || src2_iovcnt != 1)) {
		return -EINVAL;
	}

	if (spdk_unlikely(src_iovs[0].iov_len != src2_iovs[0].iov_len)) {
		return -EINVAL;
	}

	return memcmp(src_iovs[0].iov_base, src2_iovs[0].iov_base, src_iovs[0].iov_len);
}

static int
ex_accel_fill(struct iovec *iovs, uint32_t iovcnt, uint8_t fill)
{
	void *dst;
	size_t nbytes;

	if (spdk_unlikely(iovcnt != 1)) {
		fprintf(stderr, "Unexpected number of iovs: %" PRIu32 "\n", iovcnt);
		return -EINVAL;
	}

	dst = iovs[0].iov_base;
	nbytes = iovs[0].iov_len;

	memset(dst, fill, nbytes);

	return 0;
}

static int
ex_accel_comp_poll(void *arg)
{
	struct ex_accel_io_channel *ex_ch = arg;
	TAILQ_HEAD(, spdk_accel_task) tasks_to_complete;
	struct spdk_accel_task *accel_task;

	if (TAILQ_EMPTY(&ex_ch->tasks_to_complete)) {
		return SPDK_POLLER_IDLE;
	}

	TAILQ_INIT(&tasks_to_complete);
	TAILQ_SWAP(&tasks_to_complete, &ex_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
ex_accel_create_cb(void *io_device, void *ctx_buf)
{
	struct ex_accel_io_channel *ex_ch = ctx_buf;

	TAILQ_INIT(&ex_ch->tasks_to_complete);
	ex_ch->completion_poller = SPDK_POLLER_REGISTER(ex_accel_comp_poll, ex_ch, 0);

	return 0;
}

static void
ex_accel_destroy_cb(void *io_device, void *ctx_buf)
{
	struct ex_accel_io_channel *ex_ch = ctx_buf;

	spdk_poller_unregister(&ex_ch->completion_poller);
}

static int
ex_accel_module_init(void)
{
	spdk_io_device_register(&g_ex_module, ex_accel_create_cb, ex_accel_destroy_cb,
				sizeof(struct ex_accel_io_channel), "external_accel_module");

	return 0;
}

static void
ex_accel_module_fini(void *ctx)
{
	spdk_io_device_unregister(&g_ex_module, NULL);
	spdk_accel_module_finish();
}

static size_t
ex_accel_module_get_ctx_size(void)
{
	return sizeof(struct spdk_accel_task);
}

inline static void
add_to_comp_list(struct ex_accel_io_channel *ex_ch, struct spdk_accel_task *accel_task)
{
	TAILQ_INSERT_TAIL(&ex_ch->tasks_to_complete, accel_task, link);
}

static bool
ex_accel_supports_opcode(enum spdk_accel_opcode opc)
{
	switch (opc) {
	case SPDK_ACCEL_OPC_COPY:
	case SPDK_ACCEL_OPC_FILL:
	case SPDK_ACCEL_OPC_COMPARE:
		return true;
	default:
		return false;
	}
}

static struct spdk_io_channel *
ex_accel_get_io_channel(void)
{
	return spdk_get_io_channel(&g_ex_module);
}

static int
ex_accel_submit_tasks(struct spdk_io_channel *ch, struct spdk_accel_task *accel_task)
{
	struct ex_accel_io_channel *ex_ch = spdk_io_channel_get_ctx(ch);

	printf("Running on accel module task with code: %" PRIu8 "\n", accel_task->op_code);
	switch (accel_task->op_code) {
	case SPDK_ACCEL_OPC_COPY:
		accel_task->status = ex_accel_copy_iovs(accel_task->d.iovs, accel_task->d.iovcnt,
							accel_task->s.iovs, accel_task->s.iovcnt);
		break;
	case SPDK_ACCEL_OPC_FILL:
		accel_task->status = ex_accel_fill(accel_task->d.iovs, accel_task->d.iovcnt,
						   accel_task->fill_pattern);
		break;
	case SPDK_ACCEL_OPC_COMPARE:
		accel_task->status = ex_accel_compare(accel_task->s.iovs, accel_task->s.iovcnt,
						      accel_task->s2.iovs, accel_task->s2.iovcnt);
		break;
	default:
		fprintf(stderr, "Unsupported accel opcode: %" PRIu8 "\n", accel_task->op_code);
		accel_task->status = 1;
		break;
	}

	add_to_comp_list(ex_ch, accel_task);

	return accel_task->status;
}

static struct spdk_accel_module_if g_ex_module = {
	.module_init			= ex_accel_module_init,
	.module_fini			= ex_accel_module_fini,
	.get_ctx_size			= ex_accel_module_get_ctx_size,
	.name				= "external",
	.supports_opcode		= ex_accel_supports_opcode,
	.get_io_channel			= ex_accel_get_io_channel,
	.submit_tasks			= ex_accel_submit_tasks,
};

SPDK_ACCEL_MODULE_REGISTER(external, &g_ex_module)
+186 −0
Original line number Diff line number Diff line
/*   SPDX-License-Identifier: BSD-3-Clause
 *   Copyright (C) 2023 Intel Corporation.
 *   All rights reserved.
 */

#include "spdk/event.h"
#include "spdk/accel.h"

#define TEST_XFER_SIZE 4096
#define FILL_PATTERN 255

enum test_state {
	TEST_STATE_FILL,
	TEST_STATE_COPY,
	TEST_STATE_COMPARE,
	TEST_STATE_WAIT_COMPLETION,
	TEST_STATE_DONE
};

struct test_ctx {
	enum test_state state;
	int status;
	char buf1[TEST_XFER_SIZE];
	char buf2[TEST_XFER_SIZE];
	struct spdk_io_channel *ch;
};

static void
test_ctx_fail(struct test_ctx *ctx)
{
	ctx->status = 1;
	ctx->state = TEST_STATE_DONE;
}

static void process_accel(void *);

static void
fill_cb(void *arg, int status)
{
	struct test_ctx *ctx = arg;
	char expected[TEST_XFER_SIZE];

	printf("Running fill callback\n");
	if (status != 0) {
		test_ctx_fail(ctx);
		goto out;
	}

	memset(expected, FILL_PATTERN, sizeof(expected));
	if (memcmp(ctx->buf1, expected,  TEST_XFER_SIZE) != 0) {
		SPDK_ERRLOG("Fill failed: buffer mismatch\n");
		test_ctx_fail(ctx);
		goto out;
	}

	ctx->state = TEST_STATE_COPY;
out:
	process_accel(ctx);
}

static void
copy_cb(void *arg, int status)
{
	struct test_ctx *ctx = arg;

	printf("Running copy callback\n");
	if (status != 0) {
		test_ctx_fail(ctx);
		goto out;
	}
	if (memcmp(ctx->buf1, ctx->buf2, TEST_XFER_SIZE) != 0) {
		SPDK_ERRLOG("Copy failed: buffer mismatch\n");
		test_ctx_fail(ctx);
		goto out;
	}

	ctx->state = TEST_STATE_COMPARE;
out:
	process_accel(ctx);
}

static void
compare_cb(void *arg, int status)
{
	struct test_ctx *ctx = arg;

	printf("Running compare callback\n");
	if (status != 0) {
		test_ctx_fail(ctx);
		goto out;
	}
	if (memcmp(ctx->buf1, ctx->buf2, TEST_XFER_SIZE) != 0) {
		SPDK_ERRLOG("Compare failed: buffer mismatch\n");
		test_ctx_fail(ctx);
		goto out;
	}

	ctx->state = TEST_STATE_DONE;
out:
	process_accel(ctx);
}

static void
process_accel(void *_ctx)
{
	int rc;
	struct test_ctx *ctx = _ctx;
	enum test_state prev_state;

	do {
		prev_state = ctx->state;

		switch (ctx->state) {
		case TEST_STATE_FILL:
			memset(ctx->buf1, 0, sizeof(ctx->buf1));
			memset(ctx->buf2, 0, sizeof(ctx->buf2));
			ctx->state = TEST_STATE_WAIT_COMPLETION;
			/* Submit fill command */
			rc = spdk_accel_submit_fill(ctx->ch, ctx->buf1, FILL_PATTERN,
						    TEST_XFER_SIZE, 0, fill_cb, ctx);
			if (rc) {
				SPDK_ERRLOG("ERROR running submit fill! exiting.\n");
				test_ctx_fail(ctx);
			}
			break;
		case TEST_STATE_COPY:
			ctx->state = TEST_STATE_WAIT_COMPLETION;
			/* Submit copy command */
			rc = spdk_accel_submit_copy(ctx->ch, ctx->buf1, ctx->buf2,
						    TEST_XFER_SIZE, 0, copy_cb, ctx);
			if (rc) {
				SPDK_ERRLOG("ERROR running submit copy! exiting.\n");
				test_ctx_fail(ctx);
			}
			break;
		case TEST_STATE_COMPARE:
			ctx->state = TEST_STATE_WAIT_COMPLETION;
			/* Submit compare command */
			rc = spdk_accel_submit_compare(ctx->ch, ctx->buf1, ctx->buf2,
						       TEST_XFER_SIZE, compare_cb, ctx);
			if (rc) {
				SPDK_ERRLOG("ERROR running submit compare! exiting.\n");
				test_ctx_fail(ctx);
			}
			break;
		case TEST_STATE_WAIT_COMPLETION:
			break;
		case TEST_STATE_DONE:
			spdk_put_io_channel(ctx->ch);
			spdk_app_stop(ctx->status);
			break;
		}
	} while (ctx->state != prev_state);
}

static void
start_accel(void *_ctx)
{
	struct test_ctx *ctx = _ctx;

	ctx->ch = spdk_accel_get_io_channel();
	if (ctx->ch == NULL) {
		SPDK_ERRLOG("Failed to get IO channel\n");
		spdk_app_stop(1);
		return;
	}

	process_accel(ctx);
}

int
main(void)
{
	int rc;
	struct spdk_app_opts g_opts = {};
	struct test_ctx ctx = {.state = TEST_STATE_FILL, .status = 0};

	spdk_app_opts_init(&g_opts, sizeof(g_opts));
	g_opts.name = "accel_external_opts";
	g_opts.reactor_mask = "0x1";

	rc = spdk_app_start(&g_opts, start_accel, &ctx);

	spdk_app_fini();
	return rc;
}
Loading