2022-06-03 19:15:11 +00:00
|
|
|
/* SPDX-License-Identifier: BSD-3-Clause
|
2022-11-01 20:26:26 +00:00
|
|
|
* Copyright (C) 2020 Intel Corporation.
|
2022-10-04 19:16:21 +00:00
|
|
|
* Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES
|
2016-11-16 21:20:30 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2022-08-08 21:43:24 +00:00
|
|
|
#ifndef SPDK_ACCEL_MODULE_H
|
|
|
|
#define SPDK_ACCEL_MODULE_H
|
2016-11-16 21:20:30 +00:00
|
|
|
|
2017-05-02 18:18:25 +00:00
|
|
|
#include "spdk/stdinc.h"
|
2016-11-16 21:20:30 +00:00
|
|
|
|
2022-08-08 20:31:08 +00:00
|
|
|
#include "spdk/accel.h"
|
2016-11-16 21:20:30 +00:00
|
|
|
#include "spdk/queue.h"
|
2022-04-19 22:09:18 +00:00
|
|
|
#include "spdk/config.h"
|
|
|
|
|
2022-10-04 19:16:21 +00:00
|
|
|
struct spdk_accel_module_if;
|
2020-07-28 17:51:20 +00:00
|
|
|
struct spdk_accel_task;
|
|
|
|
|
|
|
|
void spdk_accel_task_complete(struct spdk_accel_task *task, int status);
|
|
|
|
|
2022-10-04 19:16:21 +00:00
|
|
|
/** Some reasonable key length used with strnlen() */
|
|
|
|
#define SPDK_ACCEL_CRYPTO_KEY_MAX_HEX_LENGTH (256 + 1)
|
|
|
|
|
|
|
|
struct spdk_accel_crypto_key {
|
|
|
|
void *priv; /**< Module private data */
|
|
|
|
char *key; /**< Key in binary form */
|
|
|
|
size_t key_size; /**< Key size in bytes */
|
|
|
|
char *key2; /**< Key2 in binary form */
|
|
|
|
size_t key2_size; /**< Key2 size in bytes */
|
|
|
|
struct spdk_accel_module_if *module_if; /**< Accel module the key belongs to */
|
|
|
|
struct spdk_accel_crypto_key_create_param param; /**< User input parameters */
|
|
|
|
TAILQ_ENTRY(spdk_accel_crypto_key) link;
|
|
|
|
};
|
|
|
|
|
2022-12-12 11:03:54 +00:00
|
|
|
/**
|
|
|
|
* Describes user's buffers in remote memory domains in case a module doesn't support memory domains
|
|
|
|
* and accel needs to pull/push the data before submitting a task. Should only be used by accel
|
|
|
|
* itself and should not be touched by accel modules.
|
|
|
|
*/
|
|
|
|
struct spdk_accel_bounce_buffer {
|
|
|
|
struct iovec *orig_iovs;
|
|
|
|
uint32_t orig_iovcnt;
|
|
|
|
struct spdk_memory_domain *orig_domain;
|
|
|
|
void *orig_domain_ctx;
|
|
|
|
struct iovec iov;
|
|
|
|
};
|
|
|
|
|
2022-12-09 12:59:50 +00:00
|
|
|
enum spdk_accel_aux_iov_type {
|
|
|
|
SPDK_ACCEL_AUX_IOV_SRC,
|
|
|
|
SPDK_ACCEL_AUX_IOV_DST,
|
|
|
|
SPDK_ACCEL_AUX_IOV_SRC2,
|
|
|
|
SPDK_ACCEL_AUX_IOV_DST2,
|
|
|
|
SPDK_ACCEL_AUX_IOV_MAX,
|
|
|
|
};
|
|
|
|
|
2020-07-28 17:51:20 +00:00
|
|
|
struct spdk_accel_task {
|
2021-11-17 18:48:24 +00:00
|
|
|
struct accel_io_channel *accel_ch;
|
|
|
|
spdk_accel_completion_cb cb_fn;
|
|
|
|
void *cb_arg;
|
accel: initial operation chaining support
This patch introduces the concept of chaining multiple accel operations
and executing them all at once in a single step. This means that it
will be possible to schedule accel operations at different layers of the
stack (e.g. copy in NVMe-oF transport, crypto in bdev_crypto), but
execute them all in a single place. Thanks to this, we can take
advantage of hardware accelerators that supports executing multiple
operations as a single operation (e.g. copy + crypto).
This operation group is called spdk_accel_sequence and operations can be
appended to that object via one of the spdk_accel_append_* functions.
New operations are always added at the end of a sequence. Users can
specify a callback to be notified when a particular operation in a
sequence is completed, but they don't receive the status of whether it
was successful or not. This is by design, as they shouldn't care about
the status of an individual operation and should rely on other means to
receive the status of the whole sequence. It's also important to note
that any intermediate steps within a sequence may not produce observable
results. For instance, appending a copy from A to B and then a copy
from B to C, it's indeterminate whether A's data will be in B after a
sequence is executed. It is only guaranteed that A's data will be in C.
A sequence can also be reversed using spdk_accel_sequence_reverse(),
meaning that the first operation becomes last and vice versa. It's
especially useful in read paths, as it makes it possible to build the
sequence during submission, then, once the data is read from storage,
reverse the sequence and execute it.
Finally, there are two ways to terminate a sequence: aborting or
executing. It can be aborted via spdk_accel_sequence_abort() which will
execute individual operations' callbacks and free any allocated
resources. To execute it, one must use spdk_accel_sequence_finish().
For now, each operation is executed one by one and is submitted to the
appropriate accel module. Executing multiple operations as a single one
will be added in the future.
Also, currently, only fill and copy operations can be appended to a
sequence. Support for more operations will be added in subsequent
patches.
Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Change-Id: Id35d093e14feb59b996f780ef77e000e10bfcd20
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15529
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Aleksey Marchuk <alexeymar@nvidia.com>
2022-11-16 07:22:55 +00:00
|
|
|
spdk_accel_step_cb step_cb_fn;
|
|
|
|
void *step_cb_arg;
|
|
|
|
struct spdk_memory_domain *src_domain;
|
|
|
|
void *src_domain_ctx;
|
|
|
|
struct spdk_memory_domain *dst_domain;
|
|
|
|
void *dst_domain_ctx;
|
2022-10-04 13:15:52 +00:00
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
struct iovec *iovs; /* iovs passed by the caller */
|
|
|
|
uint32_t iovcnt; /* iovcnt passed by the caller */
|
|
|
|
} s;
|
|
|
|
struct {
|
|
|
|
void **srcs;
|
|
|
|
uint32_t cnt;
|
|
|
|
} nsrcs;
|
|
|
|
};
|
2020-07-28 17:51:20 +00:00
|
|
|
union {
|
2022-09-22 19:01:56 +00:00
|
|
|
struct {
|
|
|
|
struct iovec *iovs; /* iovs passed by the caller */
|
|
|
|
uint32_t iovcnt; /* iovcnt passed by the caller */
|
|
|
|
} d;
|
2022-12-09 14:42:33 +00:00
|
|
|
struct {
|
|
|
|
struct iovec *iovs;
|
|
|
|
uint32_t iovcnt;
|
|
|
|
} s2;
|
2020-07-28 17:51:20 +00:00
|
|
|
};
|
2020-12-21 12:17:06 +00:00
|
|
|
union {
|
2022-12-09 14:26:37 +00:00
|
|
|
struct {
|
|
|
|
struct iovec *iovs;
|
|
|
|
uint32_t iovcnt;
|
|
|
|
} d2;
|
2020-12-21 12:17:06 +00:00
|
|
|
uint32_t seed;
|
|
|
|
uint64_t fill_pattern;
|
2022-10-04 19:16:21 +00:00
|
|
|
struct spdk_accel_crypto_key *crypto_key;
|
2020-12-21 12:17:06 +00:00
|
|
|
};
|
2022-05-20 19:08:38 +00:00
|
|
|
union {
|
|
|
|
uint32_t *crc_dst;
|
|
|
|
uint32_t *output_size;
|
2022-10-04 19:16:21 +00:00
|
|
|
uint32_t block_size; /* for crypto op */
|
2022-05-20 19:08:38 +00:00
|
|
|
};
|
2022-12-12 11:03:54 +00:00
|
|
|
struct {
|
|
|
|
struct spdk_accel_bounce_buffer s;
|
|
|
|
struct spdk_accel_bounce_buffer d;
|
|
|
|
} bounce;
|
2020-07-28 17:51:20 +00:00
|
|
|
enum accel_opcode op_code;
|
2022-12-10 13:06:47 +00:00
|
|
|
uint64_t iv; /* Initialization vector (tweak) for crypto op */
|
2021-08-24 21:08:29 +00:00
|
|
|
int flags;
|
2021-08-13 06:12:47 +00:00
|
|
|
int status;
|
2022-12-09 12:59:50 +00:00
|
|
|
struct iovec aux_iovs[SPDK_ACCEL_AUX_IOV_MAX];
|
2020-08-11 18:17:14 +00:00
|
|
|
TAILQ_ENTRY(spdk_accel_task) link;
|
accel: initial operation chaining support
This patch introduces the concept of chaining multiple accel operations
and executing them all at once in a single step. This means that it
will be possible to schedule accel operations at different layers of the
stack (e.g. copy in NVMe-oF transport, crypto in bdev_crypto), but
execute them all in a single place. Thanks to this, we can take
advantage of hardware accelerators that supports executing multiple
operations as a single operation (e.g. copy + crypto).
This operation group is called spdk_accel_sequence and operations can be
appended to that object via one of the spdk_accel_append_* functions.
New operations are always added at the end of a sequence. Users can
specify a callback to be notified when a particular operation in a
sequence is completed, but they don't receive the status of whether it
was successful or not. This is by design, as they shouldn't care about
the status of an individual operation and should rely on other means to
receive the status of the whole sequence. It's also important to note
that any intermediate steps within a sequence may not produce observable
results. For instance, appending a copy from A to B and then a copy
from B to C, it's indeterminate whether A's data will be in B after a
sequence is executed. It is only guaranteed that A's data will be in C.
A sequence can also be reversed using spdk_accel_sequence_reverse(),
meaning that the first operation becomes last and vice versa. It's
especially useful in read paths, as it makes it possible to build the
sequence during submission, then, once the data is read from storage,
reverse the sequence and execute it.
Finally, there are two ways to terminate a sequence: aborting or
executing. It can be aborted via spdk_accel_sequence_abort() which will
execute individual operations' callbacks and free any allocated
resources. To execute it, one must use spdk_accel_sequence_finish().
For now, each operation is executed one by one and is submitted to the
appropriate accel module. Executing multiple operations as a single one
will be added in the future.
Also, currently, only fill and copy operations can be appended to a
sequence. Support for more operations will be added in subsequent
patches.
Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Change-Id: Id35d093e14feb59b996f780ef77e000e10bfcd20
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15529
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Aleksey Marchuk <alexeymar@nvidia.com>
2022-11-16 07:22:55 +00:00
|
|
|
TAILQ_ENTRY(spdk_accel_task) seq_link;
|
2016-11-16 21:20:30 +00:00
|
|
|
};
|
|
|
|
|
2020-02-05 17:10:05 +00:00
|
|
|
struct spdk_accel_module_if {
|
2016-11-16 21:20:30 +00:00
|
|
|
/** Initialization function for the module. Called by the spdk
|
|
|
|
* application during startup.
|
|
|
|
*
|
|
|
|
* Modules are required to define this function.
|
|
|
|
*/
|
|
|
|
int (*module_init)(void);
|
|
|
|
|
|
|
|
/** Finish function for the module. Called by the spdk application
|
|
|
|
* before the spdk application exits to perform any necessary cleanup.
|
|
|
|
*
|
|
|
|
* Modules are not required to define this function.
|
|
|
|
*/
|
2017-10-25 13:58:02 +00:00
|
|
|
void (*module_fini)(void *ctx);
|
2016-11-16 21:20:30 +00:00
|
|
|
|
2020-04-27 15:45:46 +00:00
|
|
|
/**
|
|
|
|
* Write Acceleration module configuration into provided JSON context.
|
|
|
|
*/
|
|
|
|
void (*write_config_json)(struct spdk_json_write_ctx *w);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the allocation size required for the modules to use for context.
|
|
|
|
*/
|
2016-11-16 21:23:57 +00:00
|
|
|
size_t (*get_ctx_size)(void);
|
2020-04-27 15:45:46 +00:00
|
|
|
|
2022-08-05 19:25:33 +00:00
|
|
|
const char *name;
|
|
|
|
bool (*supports_opcode)(enum accel_opcode);
|
|
|
|
struct spdk_io_channel *(*get_io_channel)(void);
|
|
|
|
int (*submit_tasks)(struct spdk_io_channel *ch, struct spdk_accel_task *accel_task);
|
|
|
|
|
2022-10-04 19:16:21 +00:00
|
|
|
/**
|
|
|
|
* Create crypto key function. Module is responsible to fill all necessary parameters in
|
|
|
|
* \b spdk_accel_crypto_key structure
|
|
|
|
*/
|
|
|
|
int (*crypto_key_init)(struct spdk_accel_crypto_key *key);
|
|
|
|
void (*crypto_key_deinit)(struct spdk_accel_crypto_key *key);
|
|
|
|
|
2022-12-16 09:47:35 +00:00
|
|
|
/**
|
|
|
|
* Returns memory domains supported by the module. If NULL, the module does not support
|
|
|
|
* memory domains. The `domains` array can be NULL, in which case this function only
|
|
|
|
* returns the number of supported memory domains.
|
|
|
|
*
|
|
|
|
* \param domains Memory domain array.
|
|
|
|
* \param num_domains Size of the `domains` array.
|
|
|
|
*
|
|
|
|
* \return Number of supported memory domains.
|
|
|
|
*/
|
|
|
|
int (*get_memory_domains)(struct spdk_memory_domain **domains, int num_domains);
|
|
|
|
|
2020-02-05 17:10:05 +00:00
|
|
|
TAILQ_ENTRY(spdk_accel_module_if) tailq;
|
2016-11-16 21:20:30 +00:00
|
|
|
};
|
|
|
|
|
2020-02-05 17:10:05 +00:00
|
|
|
void spdk_accel_module_list_add(struct spdk_accel_module_if *accel_module);
|
2016-11-16 21:20:30 +00:00
|
|
|
|
2022-08-05 19:01:47 +00:00
|
|
|
#define SPDK_ACCEL_MODULE_REGISTER(name, module) \
|
|
|
|
static void __attribute__((constructor)) _spdk_accel_module_register_##name(void) \
|
|
|
|
{ \
|
|
|
|
spdk_accel_module_list_add(module); \
|
|
|
|
}
|
2016-11-16 21:20:30 +00:00
|
|
|
|
2022-08-08 20:36:34 +00:00
|
|
|
/**
|
|
|
|
* Called by an accel module when cleanup initiated during .module_fini has completed
|
|
|
|
*/
|
|
|
|
void spdk_accel_module_finish(void);
|
|
|
|
|
2023-01-05 13:55:04 +00:00
|
|
|
/**
|
|
|
|
* Platform driver responsible for executing tasks in a sequence. If no driver is selected, tasks
|
|
|
|
* are submitted to accel modules. All drivers are required to be aware of memory domains.
|
|
|
|
*/
|
|
|
|
struct spdk_accel_driver {
|
|
|
|
/** Name of the driver */
|
|
|
|
const char *name;
|
|
|
|
/**
|
|
|
|
* Executes a sequence of accel operations. The driver should notify accel about each
|
|
|
|
* completed task using `spdk_accel_task_complete()`. Once all tasks are completed or the
|
|
|
|
* driver cannot proceed with a given task (e.g. because it doesn't handle specific opcode),
|
|
|
|
* accel should be notified via `spdk_accel_sequence_continue()`. If there are tasks left
|
|
|
|
* in a sequence, the first will be submitted to a module, while the rest will be sent back
|
|
|
|
* to the driver. `spdk_accel_sequence_continue()` should only be called if this function
|
|
|
|
* succeeds (i.e. returns 0).
|
|
|
|
*
|
|
|
|
* \param Sequence of tasks to execute.
|
|
|
|
*
|
|
|
|
* \return 0 on success, negative errno on failure.
|
|
|
|
*/
|
|
|
|
int (*execute_sequence)(struct spdk_accel_sequence *seq);
|
|
|
|
|
|
|
|
TAILQ_ENTRY(spdk_accel_driver) tailq;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Notifies accel that a driver has finished executing a sequence (or its part) and accel should
|
|
|
|
* continue processing it.
|
|
|
|
*
|
|
|
|
* \param seq Sequence object.
|
|
|
|
*/
|
|
|
|
void spdk_accel_sequence_continue(struct spdk_accel_sequence *seq);
|
|
|
|
|
|
|
|
void spdk_accel_driver_register(struct spdk_accel_driver *driver);
|
|
|
|
|
|
|
|
#define SPDK_ACCEL_DRIVER_REGISTER(name, driver) \
|
|
|
|
static void __attribute__((constructor)) _spdk_accel_driver_register_##name(void) \
|
|
|
|
{ \
|
|
|
|
spdk_accel_driver_register(driver); \
|
|
|
|
}
|
|
|
|
|
2023-01-04 12:59:15 +00:00
|
|
|
typedef void (*spdk_accel_sequence_get_buf_cb)(struct spdk_accel_sequence *seq, void *cb_arg);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocates memory for an accel buffer in a given sequence. The callback is only executed if the
|
|
|
|
* buffer couldn't be allocated immediately.
|
|
|
|
*
|
|
|
|
* \param seq Sequence object.
|
|
|
|
* \param buf Accel buffer to allocate.
|
|
|
|
* \param domain Accel memory domain.
|
|
|
|
* \param domain_ctx Memory domain context.
|
|
|
|
* \param cb_fn Callback to be executed once the buffer is allocated.
|
|
|
|
* \param cb_ctx Argument to be passed to `cb_fn`.
|
|
|
|
*
|
|
|
|
* \return true if the buffer was immediately allocated, false otherwise.
|
|
|
|
*/
|
|
|
|
bool spdk_accel_alloc_sequence_buf(struct spdk_accel_sequence *seq, void *buf,
|
|
|
|
struct spdk_memory_domain *domain, void *domain_ctx,
|
|
|
|
spdk_accel_sequence_get_buf_cb cb_fn, void *cb_ctx);
|
|
|
|
|
2023-01-18 14:23:22 +00:00
|
|
|
/**
|
|
|
|
* Returns the first task remaining to be executed in a given sequence.
|
|
|
|
*
|
|
|
|
* \param seq Sequence object.
|
|
|
|
*
|
|
|
|
* \return the first remaining task or NULL if all tasks are already completed.
|
|
|
|
*/
|
|
|
|
struct spdk_accel_task *spdk_accel_sequence_first_task(struct spdk_accel_sequence *seq);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the next remaining task that follows a given task in a sequence.
|
|
|
|
*
|
|
|
|
* \param task Accel task. This task must be still oustanding (i.e. it wasn't completed through
|
|
|
|
* `spdk_accel_task_complete()`).
|
|
|
|
*
|
|
|
|
* \return the next task or NULL if `task` was the last task in a sequence.
|
|
|
|
*/
|
|
|
|
struct spdk_accel_task *spdk_accel_sequence_next_task(struct spdk_accel_task *task);
|
|
|
|
|
2016-11-16 21:20:30 +00:00
|
|
|
#endif
|