diff --git a/include/spdk/accel.h b/include/spdk/accel.h index 482a77bc7..ac442b6a7 100644 --- a/include/spdk/accel.h +++ b/include/spdk/accel.h @@ -319,6 +319,32 @@ int spdk_accel_append_fill(struct spdk_accel_sequence **seq, struct spdk_io_chan struct spdk_memory_domain *domain, void *domain_ctx, uint8_t pattern, int flags, spdk_accel_step_cb cb_fn, void *cb_arg); +/** + * Append a decompress operation to a sequence. + * + * \param seq Sequence object. If NULL, a new sequence object will be created. + * \param ch I/O channel. + * \param dst_iovs Destination I/O vector array. + * \param dst_iovcnt Size of the `dst_iovs` array. + * \param dst_domain Memory domain to which the destination buffers belong. + * \param dst_domain_ctx Destination buffer domain context. + * \param src_iovs Source I/O vector array. + * \param src_iovcnt Size of the `src_iovs` array. + * \param src_domain Memory domain to which the source buffers belong. + * \param src_domain_ctx Source buffer domain context. + * \param flags Accel operation flags. + * \param cb_fn Callback to be executed once this operation is completed. + * \param cb_arg Argument to be passed to `cb_fn`. + * + * \return 0 if operation was successfully added to the sequence, negative errno otherwise. + */ +int spdk_accel_append_decompress(struct spdk_accel_sequence **seq, struct spdk_io_channel *ch, + struct iovec *dst_iovs, size_t dst_iovcnt, + struct spdk_memory_domain *dst_domain, void *dst_domain_ctx, + struct iovec *src_iovs, size_t src_iovcnt, + struct spdk_memory_domain *src_domain, void *src_domain_ctx, + int flags, spdk_accel_step_cb cb_fn, void *cb_arg); + /** * Finish a sequence and execute all its operations. After the completion callback is executed, the * sequence object is automatically freed. diff --git a/lib/accel/accel.c b/lib/accel/accel.c index a6ec13fe4..ed02f3d5d 100644 --- a/lib/accel/accel.c +++ b/lib/accel/accel.c @@ -662,6 +662,57 @@ spdk_accel_append_fill(struct spdk_accel_sequence **pseq, struct spdk_io_channel return 0; } +int +spdk_accel_append_decompress(struct spdk_accel_sequence **pseq, struct spdk_io_channel *ch, + struct iovec *dst_iovs, size_t dst_iovcnt, + struct spdk_memory_domain *dst_domain, void *dst_domain_ctx, + struct iovec *src_iovs, size_t src_iovcnt, + struct spdk_memory_domain *src_domain, void *src_domain_ctx, + int flags, spdk_accel_step_cb cb_fn, void *cb_arg) +{ + struct accel_io_channel *accel_ch = spdk_io_channel_get_ctx(ch); + struct spdk_accel_task *task; + struct spdk_accel_sequence *seq = *pseq; + + if (dst_domain != NULL || src_domain != NULL) { + SPDK_ERRLOG("Memory domains are currently unsupported\n"); + return -EINVAL; + } + + if (seq == NULL) { + seq = accel_sequence_get(accel_ch); + if (spdk_unlikely(seq == NULL)) { + return -ENOMEM; + } + } + + assert(seq->ch == accel_ch); + task = accel_sequence_get_task(accel_ch, seq, cb_fn, cb_arg); + if (spdk_unlikely(task == NULL)) { + if (*pseq == NULL) { + accel_sequence_put(seq); + } + + return -ENOMEM; + } + + task->dst_domain = dst_domain; + task->dst_domain_ctx = dst_domain_ctx; + task->d.iovs = dst_iovs; + task->d.iovcnt = dst_iovcnt; + task->src_domain = src_domain; + task->src_domain_ctx = src_domain_ctx; + task->s.iovs = src_iovs; + task->s.iovcnt = src_iovcnt; + task->flags = flags; + task->op_code = ACCEL_OPC_DECOMPRESS; + + TAILQ_INSERT_TAIL(&seq->tasks, task, seq_link); + *pseq = seq; + + return 0; +} + static void accel_sequence_complete_tasks(struct spdk_accel_sequence *seq) { diff --git a/lib/accel/spdk_accel.map b/lib/accel/spdk_accel.map index 1862c33d1..f85eafb57 100644 --- a/lib/accel/spdk_accel.map +++ b/lib/accel/spdk_accel.map @@ -20,6 +20,7 @@ spdk_accel_write_config_json; spdk_accel_append_copy; spdk_accel_append_fill; + spdk_accel_append_decompress; spdk_accel_sequence_finish; spdk_accel_sequence_abort; spdk_accel_sequence_reverse; diff --git a/test/unit/lib/accel/accel.c/accel_ut.c b/test/unit/lib/accel/accel.c/accel_ut.c index 9e1779d4a..a4bc359aa 100644 --- a/test/unit/lib/accel/accel.c/accel_ut.c +++ b/test/unit/lib/accel/accel.c/accel_ut.c @@ -848,6 +848,15 @@ test_sequence_append_error(void) CU_ASSERT_EQUAL(rc, -ENOMEM); CU_ASSERT_PTR_NULL(seq); + dst_iovs.iov_base = buf; + dst_iovs.iov_len = 2048; + src_iovs.iov_base = &buf[2048]; + src_iovs.iov_len = 2048; + rc = spdk_accel_append_decompress(&seq, ioch, &dst_iovs, 1, NULL, NULL, + &src_iovs, 1, NULL, NULL, 0, ut_sequence_step_cb, NULL); + CU_ASSERT_EQUAL(rc, -ENOMEM); + CU_ASSERT_PTR_NULL(seq); + /* Check that the same happens when the sequence queue is empty */ TAILQ_SWAP(&tasks, &accel_ch->task_pool, spdk_accel_task, link); TAILQ_SWAP(&seqs, &accel_ch->seq_pool, spdk_accel_sequence, link); @@ -866,6 +875,15 @@ test_sequence_append_error(void) CU_ASSERT_EQUAL(rc, -ENOMEM); CU_ASSERT_PTR_NULL(seq); + dst_iovs.iov_base = buf; + dst_iovs.iov_len = 2048; + src_iovs.iov_base = &buf[2048]; + src_iovs.iov_len = 2048; + rc = spdk_accel_append_decompress(&seq, ioch, &dst_iovs, 1, NULL, NULL, + &src_iovs, 1, NULL, NULL, 0, ut_sequence_step_cb, NULL); + CU_ASSERT_EQUAL(rc, -ENOMEM); + CU_ASSERT_PTR_NULL(seq); + TAILQ_SWAP(&tasks, &accel_ch->task_pool, spdk_accel_task, link); spdk_put_io_channel(ioch); @@ -1033,6 +1051,147 @@ test_sequence_completion_error(void) poll_threads(); } +#ifdef SPDK_CONFIG_ISAL +static void +ut_compress_cb(void *cb_arg, int status) +{ + int *completed = cb_arg; + + CU_ASSERT_EQUAL(status, 0); + + *completed = 1; +} + +static void +test_sequence_decompress(void) +{ + struct spdk_accel_sequence *seq = NULL; + struct spdk_io_channel *ioch; + struct ut_sequence ut_seq; + char buf[4096], tmp[2][4096], expected[4096]; + struct iovec src_iovs[2], dst_iovs[2]; + uint32_t compressed_size; + int rc, completed = 0; + + ioch = spdk_accel_get_io_channel(); + SPDK_CU_ASSERT_FATAL(ioch != NULL); + + memset(expected, 0xa5, sizeof(expected)); + src_iovs[0].iov_base = expected; + src_iovs[0].iov_len = sizeof(expected); + rc = spdk_accel_submit_compress(ioch, tmp[0], sizeof(tmp[0]), &src_iovs[0], 1, + &compressed_size, 0, ut_compress_cb, &completed); + CU_ASSERT_EQUAL(rc, 0); + + while (!completed) { + poll_threads(); + } + + /* Check a single decompress operation in a sequence */ + seq = NULL; + completed = 0; + + dst_iovs[0].iov_base = buf; + dst_iovs[0].iov_len = sizeof(buf); + src_iovs[0].iov_base = tmp[0]; + src_iovs[0].iov_len = compressed_size; + rc = spdk_accel_append_decompress(&seq, ioch, &dst_iovs[0], 1, NULL, NULL, + &src_iovs[0], 1, NULL, NULL, 0, + ut_sequence_step_cb, &completed); + CU_ASSERT_EQUAL(rc, 0); + + ut_seq.complete = false; + rc = spdk_accel_sequence_finish(seq, ut_sequence_complete_cb, &ut_seq); + CU_ASSERT_EQUAL(rc, 0); + + poll_threads(); + + CU_ASSERT_EQUAL(completed, 1); + CU_ASSERT(ut_seq.complete); + CU_ASSERT_EQUAL(ut_seq.status, 0); + CU_ASSERT_EQUAL(memcmp(buf, expected, sizeof(buf)), 0); + + /* Put the decompress operation in the middle of a sequence with a copy operation at the + * beginning and a fill at the end modifying the first 2048B of the buffer. + */ + memset(expected, 0xfe, 2048); + memset(buf, 0, sizeof(buf)); + seq = NULL; + completed = 0; + + dst_iovs[0].iov_base = tmp[1]; + dst_iovs[0].iov_len = compressed_size; + src_iovs[0].iov_base = tmp[0]; + src_iovs[0].iov_len = compressed_size; + rc = spdk_accel_append_copy(&seq, ioch, &dst_iovs[0], 1, NULL, NULL, + &src_iovs[0], 1, NULL, NULL, 0, + ut_sequence_step_cb, &completed); + CU_ASSERT_EQUAL(rc, 0); + + dst_iovs[1].iov_base = buf; + dst_iovs[1].iov_len = sizeof(buf); + src_iovs[1].iov_base = tmp[1]; + src_iovs[1].iov_len = compressed_size; + rc = spdk_accel_append_decompress(&seq, ioch, &dst_iovs[1], 1, NULL, NULL, + &src_iovs[1], 1, NULL, NULL, 0, + ut_sequence_step_cb, &completed); + CU_ASSERT_EQUAL(rc, 0); + + rc = spdk_accel_append_fill(&seq, ioch, buf, 2048, NULL, NULL, 0xfe, 0, + ut_sequence_step_cb, &completed); + CU_ASSERT_EQUAL(rc, 0); + + ut_seq.complete = false; + rc = spdk_accel_sequence_finish(seq, ut_sequence_complete_cb, &ut_seq); + CU_ASSERT_EQUAL(rc, 0); + + poll_threads(); + + CU_ASSERT_EQUAL(completed, 3); + CU_ASSERT(ut_seq.complete); + CU_ASSERT_EQUAL(ut_seq.status, 0); + CU_ASSERT_EQUAL(memcmp(buf, expected, sizeof(buf)), 0); + + /* Check sequence with decompress at the beginning: decompress -> copy */ + memset(expected, 0xa5, sizeof(expected)); + memset(buf, 0, sizeof(buf)); + seq = NULL; + completed = 0; + + dst_iovs[0].iov_base = tmp[1]; + dst_iovs[0].iov_len = sizeof(tmp[1]); + src_iovs[0].iov_base = tmp[0]; + src_iovs[0].iov_len = compressed_size; + rc = spdk_accel_append_decompress(&seq, ioch, &dst_iovs[0], 1, NULL, NULL, + &src_iovs[0], 1, NULL, NULL, 0, + ut_sequence_step_cb, &completed); + CU_ASSERT_EQUAL(rc, 0); + + dst_iovs[1].iov_base = buf; + dst_iovs[1].iov_len = sizeof(buf); + src_iovs[1].iov_base = tmp[1]; + src_iovs[1].iov_len = sizeof(tmp[1]); + rc = spdk_accel_append_copy(&seq, ioch, &dst_iovs[1], 1, NULL, NULL, + &src_iovs[1], 1, NULL, NULL, 0, + ut_sequence_step_cb, &completed); + CU_ASSERT_EQUAL(rc, 0); + + ut_seq.complete = false; + rc = spdk_accel_sequence_finish(seq, ut_sequence_complete_cb, &ut_seq); + CU_ASSERT_EQUAL(rc, 0); + + poll_threads(); + + CU_ASSERT_EQUAL(completed, 2); + CU_ASSERT(ut_seq.complete); + CU_ASSERT_EQUAL(ut_seq.status, 0); + CU_ASSERT_EQUAL(memcmp(buf, expected, sizeof(buf)), 0); + + spdk_put_io_channel(ioch); + poll_threads(); +} +#endif + static int test_sequence_setup(void) { @@ -1094,6 +1253,9 @@ main(int argc, char **argv) CU_ADD_TEST(seq_suite, test_sequence_abort); CU_ADD_TEST(seq_suite, test_sequence_append_error); CU_ADD_TEST(seq_suite, test_sequence_completion_error); +#ifdef SPDK_CONFIG_ISAL /* accel_sw requires isa-l for compression */ + CU_ADD_TEST(seq_suite, test_sequence_decompress); +#endif suite = CU_add_suite("accel", test_setup, test_cleanup); CU_ADD_TEST(suite, test_spdk_accel_task_complete);