From 7b36fe52387b5d6334b61ce3bcb0491e9b9c126d Mon Sep 17 00:00:00 2001 From: Konrad Sztyber Date: Wed, 23 Nov 2022 10:36:25 +0100 Subject: [PATCH] accel: add support for reversing a sequence Reversing a sequence means that the order of its operations is reversed, i.e. 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. Signed-off-by: Konrad Sztyber Change-Id: I93d617c1e6d251f8c59b94c50dc4300e51908096 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15636 Tested-by: SPDK CI Jenkins Reviewed-by: Ben Walker Reviewed-by: Jim Harris Reviewed-by: Aleksey Marchuk --- lib/accel/accel.c | 12 +- test/unit/lib/accel/accel.c/accel_ut.c | 183 +++++++++++++++++++++++++ 2 files changed, 194 insertions(+), 1 deletion(-) diff --git a/lib/accel/accel.c b/lib/accel/accel.c index 01e4f6350..a0340e64f 100644 --- a/lib/accel/accel.c +++ b/lib/accel/accel.c @@ -929,7 +929,17 @@ spdk_accel_sequence_finish(struct spdk_accel_sequence *seq, void spdk_accel_sequence_reverse(struct spdk_accel_sequence *seq) { - assert(0 && "unsupported"); + struct accel_sequence_tasks tasks = TAILQ_HEAD_INITIALIZER(tasks); + struct spdk_accel_task *task; + + assert(TAILQ_EMPTY(&seq->completed)); + TAILQ_SWAP(&tasks, &seq->tasks, spdk_accel_task, seq_link); + + while (!TAILQ_EMPTY(&tasks)) { + task = TAILQ_FIRST(&tasks); + TAILQ_REMOVE(&tasks, task, seq_link); + TAILQ_INSERT_HEAD(&seq->tasks, task, seq_link); + } } void diff --git a/test/unit/lib/accel/accel.c/accel_ut.c b/test/unit/lib/accel/accel.c/accel_ut.c index 40e9003ae..1c6c45407 100644 --- a/test/unit/lib/accel/accel.c/accel_ut.c +++ b/test/unit/lib/accel/accel.c/accel_ut.c @@ -1207,6 +1207,188 @@ test_sequence_decompress(void) spdk_put_io_channel(ioch); poll_threads(); } + +static void +test_sequence_reverse(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(); + } + + /* First check that reversing a sequnce with a single operation is a no-op */ + memset(buf, 0, sizeof(buf)); + 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); + + spdk_accel_sequence_reverse(seq); + + 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); + + /* Add a copy operation at the end with src set to the compressed data. After reverse(), + * that copy operation should be first, so decompress() should receive compressed data in + * its src buffer. + */ + memset(buf, 0, sizeof(buf)); + memset(tmp[1], 0, sizeof(tmp[1])); + seq = NULL; + completed = 0; + + dst_iovs[0].iov_base = buf; + dst_iovs[0].iov_len = sizeof(buf); + src_iovs[0].iov_base = tmp[1]; + 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 = tmp[1]; + dst_iovs[1].iov_len = compressed_size; + src_iovs[1].iov_base = tmp[0]; + src_iovs[1].iov_len = compressed_size; + 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); + + spdk_accel_sequence_reverse(seq); + + 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); + + /* Check the same, but add an extra fill operation at the beginning that should execute last + * after reverse(). + */ + memset(buf, 0, sizeof(buf)); + memset(tmp[1], 0, sizeof(tmp[1])); + memset(expected, 0xfe, 2048); + seq = NULL; + completed = 0; + + rc = spdk_accel_append_fill(&seq, ioch, buf, 2048, NULL, NULL, 0xfe, 0, + ut_sequence_step_cb, &completed); + CU_ASSERT_EQUAL(rc, 0); + + dst_iovs[0].iov_base = buf; + dst_iovs[0].iov_len = sizeof(buf); + src_iovs[0].iov_base = tmp[1]; + 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 = tmp[1]; + dst_iovs[1].iov_len = compressed_size; + src_iovs[1].iov_base = tmp[0]; + src_iovs[1].iov_len = compressed_size; + 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); + + spdk_accel_sequence_reverse(seq); + + 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); + + /* Build the sequence in order and then reverse it twice */ + memset(buf, 0, sizeof(buf)); + memset(tmp[1], 0, sizeof(tmp[1])); + 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); + + spdk_accel_sequence_reverse(seq); + spdk_accel_sequence_reverse(seq); + + 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); + + spdk_put_io_channel(ioch); + poll_threads(); +} #endif static void @@ -1535,6 +1717,7 @@ main(int argc, char **argv) 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); + CU_ADD_TEST(seq_suite, test_sequence_reverse); #endif CU_ADD_TEST(seq_suite, test_sequence_copy_elision);