Simplify the logic, avoid repeated conditionals, avoid superfluous scheduler calls, make the code more efficient and easier to read. Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
269 lines
6.4 KiB
C
269 lines
6.4 KiB
C
/*
|
|
* Copyright (c) 2024 Måns Ansgariusson <mansgariusson@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/internal/syscall_handler.h>
|
|
#include <ksched.h>
|
|
#include <kthread.h>
|
|
#include <wait_q.h>
|
|
|
|
#ifdef CONFIG_OBJ_CORE_PIPE
|
|
static struct k_obj_type obj_type_pipe;
|
|
#endif /* CONFIG_OBJ_CORE_PIPE */
|
|
|
|
static inline bool pipe_closed(struct k_pipe *pipe)
|
|
{
|
|
return (pipe->flags & PIPE_FLAG_OPEN) == 0;
|
|
}
|
|
|
|
static inline bool pipe_resetting(struct k_pipe *pipe)
|
|
{
|
|
return (pipe->flags & PIPE_FLAG_RESET) != 0;
|
|
}
|
|
|
|
static inline bool pipe_full(struct k_pipe *pipe)
|
|
{
|
|
return ring_buf_space_get(&pipe->buf) == 0;
|
|
}
|
|
|
|
static inline bool pipe_empty(struct k_pipe *pipe)
|
|
{
|
|
return ring_buf_is_empty(&pipe->buf);
|
|
}
|
|
|
|
static int wait_for(_wait_q_t *waitq, struct k_pipe *pipe, k_spinlock_key_t *key,
|
|
k_timepoint_t time_limit)
|
|
{
|
|
k_timeout_t timeout = sys_timepoint_timeout(time_limit);
|
|
|
|
if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
pipe->waiting++;
|
|
SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_pipe, read, pipe, timeout);
|
|
z_pend_curr(&pipe->lock, *key, waitq, timeout);
|
|
*key = k_spin_lock(&pipe->lock);
|
|
pipe->waiting--;
|
|
if (unlikely(pipe_resetting(pipe))) {
|
|
if (pipe->waiting == 0) {
|
|
pipe->flags &= ~PIPE_FLAG_RESET;
|
|
}
|
|
return -ECANCELED;
|
|
} else if (sys_timepoint_expired(time_limit)) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void notify_waiter(_wait_q_t *waitq)
|
|
{
|
|
struct k_thread *thread_to_unblock = z_unpend_first_thread(waitq);
|
|
|
|
if (likely(thread_to_unblock != NULL)) {
|
|
z_ready_thread(thread_to_unblock);
|
|
}
|
|
}
|
|
|
|
void z_impl_k_pipe_init(struct k_pipe *pipe, uint8_t *buffer, size_t buffer_size)
|
|
{
|
|
ring_buf_init(&pipe->buf, buffer_size, buffer);
|
|
pipe->flags = PIPE_FLAG_OPEN;
|
|
pipe->waiting = 0;
|
|
|
|
pipe->lock = (struct k_spinlock){};
|
|
z_waitq_init(&pipe->data);
|
|
z_waitq_init(&pipe->space);
|
|
k_object_init(pipe);
|
|
|
|
#ifdef CONFIG_POLL
|
|
sys_dlist_init(&pipe->poll_events);
|
|
#endif /* CONFIG_POLL */
|
|
#ifdef CONFIG_OBJ_CORE_PIPE
|
|
k_obj_core_init_and_link(K_OBJ_CORE(pipe), &obj_type_pipe);
|
|
#endif /* CONFIG_OBJ_CORE_PIPE */
|
|
SYS_PORT_TRACING_OBJ_INIT(k_pipe, pipe, buffer, buffer_size);
|
|
}
|
|
|
|
int z_impl_k_pipe_write(struct k_pipe *pipe, const uint8_t *data, size_t len, k_timeout_t timeout)
|
|
{
|
|
int rc;
|
|
size_t written = 0;
|
|
k_timepoint_t end = sys_timepoint_calc(timeout);
|
|
k_spinlock_key_t key = k_spin_lock(&pipe->lock);
|
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_pipe, write, pipe, data, len, timeout);
|
|
|
|
if (unlikely(pipe_resetting(pipe))) {
|
|
rc = -ECANCELED;
|
|
goto exit;
|
|
}
|
|
|
|
for (;;) {
|
|
if (unlikely(pipe_closed(pipe))) {
|
|
rc = -EPIPE;
|
|
break;
|
|
}
|
|
|
|
if (pipe_empty(pipe)) {
|
|
notify_waiter(&pipe->data);
|
|
#ifdef CONFIG_POLL
|
|
z_handle_obj_poll_events(&pipe->poll_events,
|
|
K_POLL_STATE_PIPE_DATA_AVAILABLE);
|
|
#endif /* CONFIG_POLL */
|
|
}
|
|
|
|
written += ring_buf_put(&pipe->buf, &data[written], len - written);
|
|
if (likely(written == len)) {
|
|
rc = written;
|
|
break;
|
|
}
|
|
|
|
rc = wait_for(&pipe->space, pipe, &key, end);
|
|
if (rc != 0) {
|
|
if (rc == -EAGAIN) {
|
|
rc = written ? written : -EAGAIN;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
exit:
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_pipe, write, pipe, rc);
|
|
k_spin_unlock(&pipe->lock, key);
|
|
return rc;
|
|
}
|
|
|
|
int z_impl_k_pipe_read(struct k_pipe *pipe, uint8_t *data, size_t len, k_timeout_t timeout)
|
|
{
|
|
int rc;
|
|
size_t read = 0;
|
|
k_timepoint_t end = sys_timepoint_calc(timeout);
|
|
k_spinlock_key_t key = k_spin_lock(&pipe->lock);
|
|
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_pipe, read, pipe, data, len, timeout);
|
|
|
|
if (unlikely(pipe_resetting(pipe))) {
|
|
rc = -ECANCELED;
|
|
goto exit;
|
|
}
|
|
|
|
for (;;) {
|
|
if (pipe_full(pipe)) {
|
|
notify_waiter(&pipe->space);
|
|
}
|
|
|
|
read += ring_buf_get(&pipe->buf, &data[read], len - read);
|
|
if (likely(read == len)) {
|
|
rc = read;
|
|
break;
|
|
}
|
|
|
|
if (unlikely(pipe_closed(pipe))) {
|
|
rc = read ? read : -EPIPE;
|
|
break;
|
|
}
|
|
|
|
rc = wait_for(&pipe->data, pipe, &key, end);
|
|
if (rc != 0) {
|
|
if (rc == -EAGAIN) {
|
|
rc = read ? read : -EAGAIN;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
exit:
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_pipe, read, pipe, rc);
|
|
k_spin_unlock(&pipe->lock, key);
|
|
return rc;
|
|
}
|
|
|
|
void z_impl_k_pipe_reset(struct k_pipe *pipe)
|
|
{
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_pipe, reset, pipe);
|
|
K_SPINLOCK(&pipe->lock) {
|
|
ring_buf_reset(&pipe->buf);
|
|
if (likely(pipe->waiting != 0)) {
|
|
pipe->flags |= PIPE_FLAG_RESET;
|
|
z_unpend_all(&pipe->data);
|
|
z_unpend_all(&pipe->space);
|
|
}
|
|
}
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_pipe, reset, pipe);
|
|
}
|
|
|
|
void z_impl_k_pipe_close(struct k_pipe *pipe)
|
|
{
|
|
SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_pipe, close, pipe);
|
|
K_SPINLOCK(&pipe->lock) {
|
|
pipe->flags = 0;
|
|
z_unpend_all(&pipe->data);
|
|
z_unpend_all(&pipe->space);
|
|
}
|
|
SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_pipe, close, pipe);
|
|
}
|
|
|
|
#ifdef CONFIG_USERSPACE
|
|
void z_vrfy_k_pipe_init(struct k_pipe *pipe, uint8_t *buffer, size_t buffer_size)
|
|
{
|
|
K_OOPS(K_SYSCALL_OBJ(pipe, K_OBJ_PIPE));
|
|
K_OOPS(K_SYSCALL_MEMORY_WRITE(buffer, buffer_size));
|
|
|
|
z_impl_k_pipe_init(pipe, buffer, buffer_size);
|
|
}
|
|
#include <zephyr/syscalls/k_pipe_init_mrsh.c>
|
|
|
|
int z_vrfy_k_pipe_read(struct k_pipe *pipe, uint8_t *data, size_t len, k_timeout_t timeout)
|
|
{
|
|
K_OOPS(K_SYSCALL_OBJ(pipe, K_OBJ_PIPE));
|
|
K_OOPS(K_SYSCALL_MEMORY_WRITE(data, len));
|
|
|
|
return z_impl_k_pipe_read(pipe, data, len, timeout);
|
|
}
|
|
#include <zephyr/syscalls/k_pipe_read_mrsh.c>
|
|
|
|
int z_vrfy_k_pipe_write(struct k_pipe *pipe, const uint8_t *data, size_t len, k_timeout_t timeout)
|
|
{
|
|
K_OOPS(K_SYSCALL_OBJ(pipe, K_OBJ_PIPE));
|
|
K_OOPS(K_SYSCALL_MEMORY_READ(data, len));
|
|
|
|
return z_impl_k_pipe_write(pipe, data, len, timeout);
|
|
}
|
|
#include <zephyr/syscalls/k_pipe_write_mrsh.c>
|
|
|
|
void z_vrfy_k_pipe_reset(struct k_pipe *pipe)
|
|
{
|
|
K_OOPS(K_SYSCALL_OBJ(pipe, K_OBJ_PIPE));
|
|
z_impl_k_pipe_reset(pipe);
|
|
}
|
|
#include <zephyr/syscalls/k_pipe_reset_mrsh.c>
|
|
|
|
void z_vrfy_k_pipe_close(struct k_pipe *pipe)
|
|
{
|
|
K_OOPS(K_SYSCALL_OBJ(pipe, K_OBJ_PIPE));
|
|
z_impl_k_pipe_close(pipe);
|
|
}
|
|
#include <zephyr/syscalls/k_pipe_close_mrsh.c>
|
|
#endif /* CONFIG_USERSPACE */
|
|
|
|
#ifdef CONFIG_OBJ_CORE_PIPE
|
|
static int init_pipe_obj_core_list(void)
|
|
{
|
|
/* Initialize pipe object type */
|
|
z_obj_type_init(&obj_type_pipe, K_OBJ_TYPE_PIPE_ID,
|
|
offsetof(struct k_pipe, obj_core));
|
|
|
|
/* Initialize and link statically defined pipes */
|
|
STRUCT_SECTION_FOREACH(k_pipe, pipe) {
|
|
k_obj_core_init_and_link(K_OBJ_CORE(pipe), &obj_type_pipe);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SYS_INIT(init_pipe_obj_core_list, PRE_KERNEL_1,
|
|
CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
|
|
#endif /* CONFIG_OBJ_CORE_PIPE */
|