From 1cd5ef90a819ff053f36a44ea438b65dd150708c Mon Sep 17 00:00:00 2001 From: Christopher Friedt Date: Mon, 27 Apr 2020 21:44:38 -0400 Subject: [PATCH] kernel: pipe: fix !K_NO_WAIT and >= min_xfer bytes transferred If timeout != K_NO_WAIT, then return immediately when not all bytes_to_read or bytes_to_write have been transfered, but >= min_xfer have been transferred. Fixes #24485 Signed-off-by: Christopher Friedt --- kernel/pipes.c | 27 ++++++++++++- tests/kernel/pipe/pipe/src/main.c | 6 ++- tests/kernel/pipe/pipe/src/test_pipe.c | 52 +++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 5 deletions(-) diff --git a/kernel/pipes.c b/kernel/pipes.c index c6ae71555dbd3d..8b83c41ca32776 100644 --- a/kernel/pipes.c +++ b/kernel/pipes.c @@ -529,7 +529,20 @@ int z_pipe_put_internal(struct k_pipe *pipe, struct k_pipe_async *async_desc, return 0; } - /* Not all data was copied. */ + if (!K_TIMEOUT_EQ(timeout, K_NO_WAIT) + && num_bytes_written >= min_xfer + && min_xfer > 0) { + *bytes_written = num_bytes_written; +#if (CONFIG_NUM_PIPE_ASYNC_MSGS > 0) + if (async_desc != NULL) { + pipe_async_finish(async_desc); + } +#endif + k_sched_unlock(); + return 0; + } + + /* Not all data was copied */ #if (CONFIG_NUM_PIPE_ASYNC_MSGS > 0) if (async_desc != NULL) { @@ -695,7 +708,17 @@ int z_impl_k_pipe_get(struct k_pipe *pipe, void *data, size_t bytes_to_read, return 0; } - /* Not all data was read. */ + if (!K_TIMEOUT_EQ(timeout, K_NO_WAIT) + && num_bytes_read >= min_xfer + && min_xfer > 0) { + k_sched_unlock(); + + *bytes_read = num_bytes_read; + + return 0; + } + + /* Not all data was read */ struct k_pipe_desc pipe_desc; diff --git a/tests/kernel/pipe/pipe/src/main.c b/tests/kernel/pipe/pipe/src/main.c index abe81c52cb9391..5cc471604bb427 100644 --- a/tests/kernel/pipe/pipe/src/main.c +++ b/tests/kernel/pipe/pipe/src/main.c @@ -14,6 +14,8 @@ extern void test_pipe_get_on_empty_pipe(void); extern void test_pipe_forever_timeout(void); extern void test_pipe_get_timeout(void); extern void test_pipe_get_invalid_size(void); +extern void test_pipe_get_min_xfer(void); +extern void test_pipe_put_min_xfer(void); extern struct k_pipe test_pipe; extern struct k_sem put_sem, get_sem, sync_sem, multiple_send_sem; @@ -36,7 +38,9 @@ void test_main(void) ztest_user_unit_test(test_pipe_get_on_empty_pipe), ztest_user_unit_test(test_pipe_forever_timeout), ztest_user_unit_test(test_pipe_get_timeout), - ztest_user_unit_test(test_pipe_get_invalid_size) + ztest_user_unit_test(test_pipe_get_invalid_size), + ztest_user_unit_test(test_pipe_get_min_xfer), + ztest_user_unit_test(test_pipe_put_min_xfer) ); ztest_run_test_suite(test_pipe); diff --git a/tests/kernel/pipe/pipe/src/test_pipe.c b/tests/kernel/pipe/pipe/src/test_pipe.c index d63a3e10fcf7ce..7266f115632274 100644 --- a/tests/kernel/pipe/pipe/src/test_pipe.c +++ b/tests/kernel/pipe/pipe/src/test_pipe.c @@ -97,8 +97,6 @@ static const struct pipe_sequence wait_elements[] = { { PIPE_SIZE + 1, ALL_BYTES, PIPE_SIZE + 1, RETURN_SUCCESS }, { PIPE_SIZE - 1, ATLEAST_1, PIPE_SIZE - 1, RETURN_SUCCESS }, - { PIPE_SIZE, ATLEAST_1, PIPE_SIZE, RETURN_SUCCESS }, - { PIPE_SIZE + 1, ATLEAST_1, PIPE_SIZE + 1, RETURN_SUCCESS } }; static const struct pipe_sequence timeout_elements[] = { @@ -826,3 +824,53 @@ void test_pipe_get_invalid_size(void) zassert_equal(ret, -EINVAL, "fault didn't occur for min_xfer <= bytes_to_read"); } + +/** + * @brief Test pipe get returns immediately if >= min_xfer is available + * @ingroup kernel_pipe_tests + * @see k_pipe_get() + */ +void test_pipe_get_min_xfer(void) +{ + int res; + size_t bytes_written = 0; + size_t bytes_read = 0; + char buf[8] = {}; + + res = k_pipe_put(&test_pipe, "Hi!", 3, &bytes_written, + 3 /* min_xfer */, K_FOREVER); + zassert_equal(res, 0, "did not write entire message"); + zassert_equal(bytes_written, 3, "did not write entire message"); + + res = k_pipe_get(&test_pipe, buf, sizeof(buf), &bytes_read, + 1 /* min_xfer */, K_FOREVER); + zassert_equal(res, 0, "did not read at least one byte"); + zassert_equal(bytes_read, 3, "did not read all bytes available"); +} + +/** + * @brief Test pipe put returns immediately if >= min_xfer is available + * @ingroup kernel_pipe_tests + * @see k_pipe_put() + */ +void test_pipe_put_min_xfer(void) +{ + int res; + size_t bytes_written = 0; + + /* write 6 bytes into the pipe, so that 2 bytes are still free */ + for (size_t i = 0; i < 2; ++i) { + bytes_written = 0; + res = k_pipe_put(&test_pipe, "Hi!", 3, &bytes_written, + 3 /* min_xfer */, K_FOREVER); + zassert_equal(res, 0, "did not write entire message"); + zassert_equal(bytes_written, 3, "did not write entire message"); + } + + /* attempt to write 3 bytes, but allow success if >= 1 byte */ + bytes_written = 0; + res = k_pipe_put(&test_pipe, "Hi!", 3, &bytes_written, + 1 /* min_xfer */, K_FOREVER); + zassert_equal(res, 0, "did not write min_xfer"); + zassert_true(bytes_written >= 1, "did not write min_xfer"); +}