From 2b9b4b2cf71e5c7d3620a713cae2a92c248649d2 Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 27 Apr 2018 13:21:22 -0700 Subject: [PATCH] k_queue: allow user mode access via allocators User mode may now use queue objects. Instead of embedding the kernel's linked list information directly in the data item, a container struct is allocated from the caller's resource pool which is then added to the queue. The new sflist type is now used to store a flag indicating whether a data item needs to be freed when removed from the queue. FIFO/LIFOs are derived from k_queues and have had allocator functions added. Signed-off-by: Andrew Boie --- doc/kernel/data_passing/fifos.rst | 15 ++- doc/kernel/data_passing/lifos.rst | 14 +- include/kernel.h | 101 +++++++++++++-- kernel/queue.c | 158 ++++++++++++++++++++--- scripts/gen_kobject_list.py | 1 + tests/kernel/queue/prj.conf | 2 + tests/kernel/queue/prj_poll.conf | 2 + tests/kernel/queue/src/main.c | 21 ++- tests/kernel/queue/src/test_queue.h | 12 ++ tests/kernel/queue/src/test_queue_fail.c | 2 +- tests/kernel/queue/src/test_queue_user.c | 126 ++++++++++++++++++ tests/kernel/queue/testcase.yaml | 2 +- 12 files changed, 414 insertions(+), 42 deletions(-) create mode 100644 tests/kernel/queue/src/test_queue_user.c diff --git a/doc/kernel/data_passing/fifos.rst b/doc/kernel/data_passing/fifos.rst index d4d3a1fe89c..fba7db17c15 100644 --- a/doc/kernel/data_passing/fifos.rst +++ b/doc/kernel/data_passing/fifos.rst @@ -25,9 +25,12 @@ A fifo has the following key properties: A fifo must be initialized before it can be used. This sets its queue to empty. FIFO data items must be aligned on a 4-byte boundary, as the kernel reserves -the first 32 bits of an item for use as a pointer to the next data item in -the queue. Consequently, a data item that holds N bytes of application data -requires N+4 bytes of memory. +the first 32 bits of an item for use as a pointer to the next data item in the +queue. Consequently, a data item that holds N bytes of application data +requires N+4 bytes of memory. There are no alignment or reserved space +requirements for data items if they are added with +:cpp:func:`k_fifo_alloc_put()`, instead additional memory is temporarily +allocated from the calling thread's resource pool. A data item may be **added** to a fifo by a thread or an ISR. The item is given directly to a waiting thread, if one exists; @@ -112,6 +115,11 @@ to send data to one or more consumer threads. Additionally, a singly-linked list of data items can be added to a fifo by calling :cpp:func:`k_fifo_put_list()` or :cpp:func:`k_fifo_put_slist()`. +Finally, a data item can be added to a fifo with :cpp:func:`k_fifo_alloc_put()`. +With this API, there is no need to reserve space for the kernel's use in +the data item, instead additional memory will be allocated from the calling +thread's resource pool until the item is read. + Reading from a FIFO =================== @@ -155,6 +163,7 @@ The following fifo APIs are provided by :file:`kernel.h`: * :c:macro:`K_FIFO_DEFINE` * :cpp:func:`k_fifo_init()` +* :cpp:func:`k_fifo_alloc_put()` * :cpp:func:`k_fifo_put()` * :cpp:func:`k_fifo_put_list()` * :cpp:func:`k_fifo_put_slist()` diff --git a/doc/kernel/data_passing/lifos.rst b/doc/kernel/data_passing/lifos.rst index 5fa4b1050ae..d7b13893062 100644 --- a/doc/kernel/data_passing/lifos.rst +++ b/doc/kernel/data_passing/lifos.rst @@ -25,9 +25,12 @@ A lifo has the following key properties: A lifo must be initialized before it can be used. This sets its queue to empty. LIFO data items must be aligned on a 4-byte boundary, as the kernel reserves -the first 32 bits of an item for use as a pointer to the next data item in -the queue. Consequently, a data item that holds N bytes of application data -requires N+4 bytes of memory. +the first 32 bits of an item for use as a pointer to the next data item in the +queue. Consequently, a data item that holds N bytes of application data +requires N+4 bytes of memory. There are no alignment or reserved space +requirements for data items if they are added with +:cpp:func:`k_lifo_alloc_put()`, instead additional memory is temporarily +allocated from the calling thread's resource pool. A data item may be **added** to a lifo by a thread or an ISR. The item is given directly to a waiting thread, if one exists; @@ -100,6 +103,11 @@ to send data to one or more consumer threads. } } +A data item can be added to a lifo with :cpp:func:`k_lifo_alloc_put()`. +With this API, there is no need to reserve space for the kernel's use in +the data item, instead additional memory will be allocated from the calling +thread's resource pool until the item is read. + Reading from a LIFO =================== diff --git a/include/kernel.h b/include/kernel.h index 8d4f415b439..a46ce430265 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1634,7 +1635,7 @@ extern u32_t k_uptime_delta_32(s64_t *reftime); */ struct k_queue { - sys_slist_t data_q; + sys_sflist_t data_q; union { _wait_q_t wait_q; @@ -1654,6 +1655,8 @@ struct k_queue { #define K_QUEUE_INITIALIZER DEPRECATED_MACRO _K_QUEUE_INITIALIZER +extern void *z_queue_node_peek(sys_sfnode_t *node, bool needs_free); + /** * INTERNAL_HIDDEN @endcond */ @@ -1673,7 +1676,7 @@ struct k_queue { * * @return N/A */ -extern void k_queue_init(struct k_queue *queue); +__syscall void k_queue_init(struct k_queue *queue); /** * @brief Cancel waiting on a queue. @@ -1687,7 +1690,7 @@ extern void k_queue_init(struct k_queue *queue); * * @return N/A */ -extern void k_queue_cancel_wait(struct k_queue *queue); +__syscall void k_queue_cancel_wait(struct k_queue *queue); /** * @brief Append an element to the end of a queue. @@ -1705,6 +1708,23 @@ extern void k_queue_cancel_wait(struct k_queue *queue); */ extern void k_queue_append(struct k_queue *queue, void *data); +/** + * @brief Append an element to a queue. + * + * This routine appends a data item to @a queue. There is an implicit + * memory allocation from the calling thread's resource pool, which is + * automatically freed when the item is removed from the queue. + * + * @note Can be called by ISRs. + * + * @param queue Address of the queue. + * @param data Address of the data item. + * + * @retval 0 on success + * @retval -ENOMEM if there isn't sufficient RAM in the caller's resource pool + */ +__syscall int k_queue_alloc_append(struct k_queue *queue, void *data); + /** * @brief Prepend an element to a queue. * @@ -1721,6 +1741,23 @@ extern void k_queue_append(struct k_queue *queue, void *data); */ extern void k_queue_prepend(struct k_queue *queue, void *data); +/** + * @brief Prepend an element to a queue. + * + * This routine prepends a data item to @a queue. There is an implicit + * memory allocation from the calling thread's resource pool, which is + * automatically freed when the item is removed from the queue. + * + * @note Can be called by ISRs. + * + * @param queue Address of the queue. + * @param data Address of the data item. + * + * @retval 0 on success + * @retval -ENOMEM if there isn't sufficient RAM in the caller's resource pool + */ +__syscall int k_queue_alloc_prepend(struct k_queue *queue, void *data); + /** * @brief Inserts an element to a queue. * @@ -1787,7 +1824,7 @@ extern void k_queue_merge_slist(struct k_queue *queue, sys_slist_t *list); * @return Address of the data item if successful; NULL if returned * without waiting, or waiting period timed out. */ -extern void *k_queue_get(struct k_queue *queue, s32_t timeout); +__syscall void *k_queue_get(struct k_queue *queue, s32_t timeout); /** * @brief Remove an element from a queue. @@ -1805,7 +1842,7 @@ extern void *k_queue_get(struct k_queue *queue, s32_t timeout); */ static inline bool k_queue_remove(struct k_queue *queue, void *data) { - return sys_slist_find_and_remove(&queue->data_q, (sys_snode_t *)data); + return sys_sflist_find_and_remove(&queue->data_q, (sys_sfnode_t *)data); } /** @@ -1821,9 +1858,11 @@ static inline bool k_queue_remove(struct k_queue *queue, void *data) * @return Non-zero if the queue is empty. * @return 0 if data is available. */ -static inline int k_queue_is_empty(struct k_queue *queue) +__syscall int k_queue_is_empty(struct k_queue *queue); + +static inline int _impl_k_queue_is_empty(struct k_queue *queue) { - return (int)sys_slist_is_empty(&queue->data_q); + return (int)sys_sflist_is_empty(&queue->data_q); } /** @@ -1835,9 +1874,11 @@ static inline int k_queue_is_empty(struct k_queue *queue) * * @return Head element, or NULL if queue is empty. */ -static inline void *k_queue_peek_head(struct k_queue *queue) +__syscall void *k_queue_peek_head(struct k_queue *queue); + +static inline void *_impl_k_queue_peek_head(struct k_queue *queue) { - return sys_slist_peek_head(&queue->data_q); + return z_queue_node_peek(sys_sflist_peek_head(&queue->data_q), false); } /** @@ -1849,9 +1890,11 @@ static inline void *k_queue_peek_head(struct k_queue *queue) * * @return Tail element, or NULL if queue is empty. */ -static inline void *k_queue_peek_tail(struct k_queue *queue) +__syscall void *k_queue_peek_tail(struct k_queue *queue); + +static inline void *_impl_k_queue_peek_tail(struct k_queue *queue) { - return sys_slist_peek_tail(&queue->data_q); + return z_queue_node_peek(sys_sflist_peek_tail(&queue->data_q), false); } /** @@ -1940,6 +1983,24 @@ struct k_fifo { #define k_fifo_put(fifo, data) \ k_queue_append((struct k_queue *) fifo, data) +/** + * @brief Add an element to a FIFO queue. + * + * This routine adds a data item to @a fifo. There is an implicit + * memory allocation from the calling thread's resource pool, which is + * automatically freed when the item is removed. + * + * @note Can be called by ISRs. + * + * @param fifo Address of the FIFO. + * @param data Address of the data item. + * + * @retval 0 on success + * @retval -ENOMEM if there isn't sufficient RAM in the caller's resource pool + */ +#define k_fifo_alloc_put(fifo, data) \ + k_queue_alloc_append((struct k_queue *) fifo, data) + /** * @brief Atomically add a list of elements to a FIFO. * @@ -2111,6 +2172,24 @@ struct k_lifo { #define k_lifo_put(lifo, data) \ k_queue_prepend((struct k_queue *) lifo, data) +/** + * @brief Add an element to a LIFO queue. + * + * This routine adds a data item to @a lifo. There is an implicit + * memory allocation from the calling thread's resource pool, which is + * automatically freed when the item is removed. + * + * @note Can be called by ISRs. + * + * @param lifo Address of the LIFO. + * @param data Address of the data item. + * + * @retval 0 on success + * @retval -ENOMEM if there isn't sufficient RAM in the caller's resource pool + */ +#define k_lifo_alloc_put(lifo, data) \ + k_queue_alloc_prepend((struct k_queue *) lifo, data) + /** * @brief Get an element from a LIFO queue. * diff --git a/kernel/queue.c b/kernel/queue.c index 5f5a18bc9a4..426588f7149 100644 --- a/kernel/queue.c +++ b/kernel/queue.c @@ -18,12 +18,46 @@ #include #include #include -#include +#include #include +#include extern struct k_queue _k_queue_list_start[]; extern struct k_queue _k_queue_list_end[]; +struct alloc_node { + sys_sfnode_t node; + void *data; +}; + +void *z_queue_node_peek(sys_sfnode_t *node, bool needs_free) +{ + void *ret; + + if (node && sys_sfnode_flags_get(node)) { + /* If the flag is set, then the enqueue operation for this item + * did a behind-the scenes memory allocation of an alloc_node + * struct, which is what got put in the queue. Free it and pass + * back the data pointer. + */ + struct alloc_node *anode; + + anode = CONTAINER_OF(node, struct alloc_node, node); + ret = anode->data; + if (needs_free) { + k_free(anode); + } + } else { + /* Data was directly placed in the queue, the first 4 bytes + * reserved for the linked list. User mode isn't allowed to + * do this, although it can get data sent this way. + */ + ret = (void *)node; + } + + return ret; +} + #ifdef CONFIG_OBJECT_TRACING struct k_queue *_trace_list_k_queue; @@ -47,17 +81,29 @@ SYS_INIT(init_queue_module, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS); #endif /* CONFIG_OBJECT_TRACING */ -void k_queue_init(struct k_queue *queue) +void _impl_k_queue_init(struct k_queue *queue) { - sys_slist_init(&queue->data_q); + sys_sflist_init(&queue->data_q); sys_dlist_init(&queue->wait_q); #if defined(CONFIG_POLL) sys_dlist_init(&queue->poll_events); #endif SYS_TRACING_OBJ_INIT(k_queue, queue); + _k_object_init(queue); } +#ifdef CONFIG_USERSPACE +_SYSCALL_HANDLER(k_queue_init, queue_ptr) +{ + struct k_queue *queue = (struct k_queue *)queue_ptr; + + _SYSCALL_OBJ_NEVER_INIT(queue, K_OBJ_QUEUE); + _impl_k_queue_init(queue); + return 0; +} +#endif + #if !defined(CONFIG_POLL) static void prepare_thread_to_run(struct k_thread *thread, void *data) { @@ -73,7 +119,7 @@ static inline void handle_poll_events(struct k_queue *queue, u32_t state) #endif } -void k_queue_cancel_wait(struct k_queue *queue) +void _impl_k_queue_cancel_wait(struct k_queue *queue) { unsigned int key = irq_lock(); #if !defined(CONFIG_POLL) @@ -91,7 +137,13 @@ void k_queue_cancel_wait(struct k_queue *queue) _reschedule(key); } -void k_queue_insert(struct k_queue *queue, void *prev, void *data) +#ifdef CONFIG_USERSPACE +_SYSCALL_HANDLER1_SIMPLE_VOID(k_queue_cancel_wait, K_OBJ_QUEUE, + struct k_queue *); +#endif + +static int queue_insert(struct k_queue *queue, void *prev, void *data, + bool alloc) { unsigned int key = irq_lock(); #if !defined(CONFIG_POLL) @@ -102,29 +154,80 @@ void k_queue_insert(struct k_queue *queue, void *prev, void *data) if (first_pending_thread) { prepare_thread_to_run(first_pending_thread, data); _reschedule(key); - return; + return 0; } #endif /* !CONFIG_POLL */ - sys_slist_insert(&queue->data_q, prev, data); + /* Only need to actually allocate if no threads are pending */ + if (alloc) { + struct alloc_node *anode; + + anode = z_thread_malloc(sizeof(*anode)); + if (!anode) { + return -ENOMEM; + } + anode->data = data; + sys_sfnode_init(&anode->node, 0x1); + data = anode; + } else { + sys_sfnode_init(data, 0x0); + } + sys_sflist_insert(&queue->data_q, prev, data); #if defined(CONFIG_POLL) handle_poll_events(queue, K_POLL_STATE_DATA_AVAILABLE); #endif /* CONFIG_POLL */ _reschedule(key); + return 0; +} + +void k_queue_insert(struct k_queue *queue, void *prev, void *data) +{ + queue_insert(queue, prev, data, false); } void k_queue_append(struct k_queue *queue, void *data) { - return k_queue_insert(queue, queue->data_q.tail, data); + queue_insert(queue, sys_sflist_peek_tail(&queue->data_q), data, false); } void k_queue_prepend(struct k_queue *queue, void *data) { - return k_queue_insert(queue, NULL, data); + queue_insert(queue, NULL, data, false); } +int _impl_k_queue_alloc_append(struct k_queue *queue, void *data) +{ + return queue_insert(queue, sys_sflist_peek_tail(&queue->data_q), data, + true); +} + +#ifdef CONFIG_USERSPACE +_SYSCALL_HANDLER(k_queue_alloc_append, queue, data) +{ + _SYSCALL_OBJ(queue, K_OBJ_QUEUE); + + return _impl_k_queue_alloc_append((struct k_queue *)queue, + (void *)data); +} +#endif + +int _impl_k_queue_alloc_prepend(struct k_queue *queue, void *data) +{ + return queue_insert(queue, NULL, data, true); +} + +#ifdef CONFIG_USERSPACE +_SYSCALL_HANDLER(k_queue_alloc_prepend, queue, data) +{ + _SYSCALL_OBJ(queue, K_OBJ_QUEUE); + + return _impl_k_queue_alloc_prepend((struct k_queue *)queue, + (void *)data); +} +#endif + void k_queue_append_list(struct k_queue *queue, void *head, void *tail) { __ASSERT(head && tail, "invalid head or tail"); @@ -139,11 +242,11 @@ void k_queue_append_list(struct k_queue *queue, void *head, void *tail) } if (head) { - sys_slist_append_list(&queue->data_q, head, tail); + sys_sflist_append_list(&queue->data_q, head, tail); } #else - sys_slist_append_list(&queue->data_q, head, tail); + sys_sflist_append_list(&queue->data_q, head, tail); handle_poll_events(queue, K_POLL_STATE_DATA_AVAILABLE); #endif /* !CONFIG_POLL */ @@ -159,6 +262,9 @@ void k_queue_merge_slist(struct k_queue *queue, sys_slist_t *list) * - the slist implementation keeps the next pointer as the first * field of the node object type * - list->tail->next = NULL. + * - sflist implementation only differs from slist by stuffing + * flag bytes in the lower order bits of the data pointer + * - source list is really an slist and not an sflist with flags set */ k_queue_append_list(queue, list->head, list->tail); sys_slist_init(list); @@ -186,11 +292,11 @@ static void *k_queue_poll(struct k_queue *queue, s32_t timeout) __ASSERT_NO_MSG(event.state == K_POLL_STATE_FIFO_DATA_AVAILABLE); - /* sys_slist_* aren't threadsafe, so must be always protected by - * irq_lock. + /* sys_sflist_* aren't threadsafe, so must be always protected + * by irq_lock. */ key = irq_lock(); - val = sys_slist_get(&queue->data_q); + val = z_queue_node_peek(sys_sflist_get(&queue->data_q), true); irq_unlock(key); } while (!val && timeout == K_FOREVER); @@ -198,15 +304,18 @@ static void *k_queue_poll(struct k_queue *queue, s32_t timeout) } #endif /* CONFIG_POLL */ -void *k_queue_get(struct k_queue *queue, s32_t timeout) +void *_impl_k_queue_get(struct k_queue *queue, s32_t timeout) { unsigned int key; void *data; key = irq_lock(); - if (likely(!sys_slist_is_empty(&queue->data_q))) { - data = sys_slist_get_not_empty(&queue->data_q); + if (likely(!sys_sflist_is_empty(&queue->data_q))) { + sys_sfnode_t *node; + + node = sys_sflist_get_not_empty(&queue->data_q); + data = z_queue_node_peek(node, true); irq_unlock(key); return data; } @@ -227,3 +336,18 @@ void *k_queue_get(struct k_queue *queue, s32_t timeout) return ret ? NULL : _current->base.swap_data; #endif /* CONFIG_POLL */ } + +#ifdef CONFIG_USERSPACE +_SYSCALL_HANDLER(k_queue_get, queue, timeout_p) +{ + s32_t timeout = timeout_p; + + _SYSCALL_OBJ(queue, K_OBJ_QUEUE); + + return (u32_t)_impl_k_queue_get((struct k_queue *)queue, timeout); +} + +_SYSCALL_HANDLER1_SIMPLE(k_queue_is_empty, K_OBJ_QUEUE, struct k_queue *); +_SYSCALL_HANDLER1_SIMPLE(k_queue_peek_head, K_OBJ_QUEUE, struct k_queue *); +_SYSCALL_HANDLER1_SIMPLE(k_queue_peek_tail, K_OBJ_QUEUE, struct k_queue *); +#endif /* CONFIG_USERSPACE */ diff --git a/scripts/gen_kobject_list.py b/scripts/gen_kobject_list.py index 4e0a88f6a75..df4ab4465f2 100755 --- a/scripts/gen_kobject_list.py +++ b/scripts/gen_kobject_list.py @@ -16,6 +16,7 @@ kobjects = [ "k_msgq", "k_mutex", "k_pipe", + "k_queue", "k_sem", "k_stack", "k_thread", diff --git a/tests/kernel/queue/prj.conf b/tests/kernel/queue/prj.conf index 9a75212e89d..117448d7f07 100644 --- a/tests/kernel/queue/prj.conf +++ b/tests/kernel/queue/prj.conf @@ -1,2 +1,4 @@ CONFIG_ZTEST=y CONFIG_IRQ_OFFLOAD=y +CONFIG_USERSPACE=y +CONFIG_DYNAMIC_OBJECTS=y diff --git a/tests/kernel/queue/prj_poll.conf b/tests/kernel/queue/prj_poll.conf index ff5574c46a5..2e59fd7f26e 100644 --- a/tests/kernel/queue/prj_poll.conf +++ b/tests/kernel/queue/prj_poll.conf @@ -1,3 +1,5 @@ CONFIG_ZTEST=y CONFIG_IRQ_OFFLOAD=y CONFIG_POLL=y +CONFIG_USERSPACE=y +CONFIG_DYNAMIC_OBJECTS=y diff --git a/tests/kernel/queue/src/main.c b/tests/kernel/queue/src/main.c index 262adab237e..b899c77fb61 100644 --- a/tests/kernel/queue/src/main.c +++ b/tests/kernel/queue/src/main.c @@ -12,17 +12,26 @@ */ #include -extern void test_queue_thread2thread(void); -extern void test_queue_thread2isr(void); -extern void test_queue_isr2thread(void); -extern void test_queue_get_2threads(void); -extern void test_queue_get_fail(void); -extern void test_queue_loop(void); +#include "test_queue.h" + +#ifndef CONFIG_USERSPACE +static void test_queue_supv_to_user(void) +{ + ztest_test_skip(); +} + +static void test_auto_free(void) +{ + ztest_test_skip(); +} +#endif /*test case main entry*/ void test_main(void) { ztest_test_suite(queue_api, + ztest_unit_test(test_queue_supv_to_user), + ztest_unit_test(test_auto_free), ztest_unit_test(test_queue_thread2thread), ztest_unit_test(test_queue_thread2isr), ztest_unit_test(test_queue_isr2thread), diff --git a/tests/kernel/queue/src/test_queue.h b/tests/kernel/queue/src/test_queue.h index e91f767ff53..cee6dd25047 100644 --- a/tests/kernel/queue/src/test_queue.h +++ b/tests/kernel/queue/src/test_queue.h @@ -10,8 +10,20 @@ #include #include +extern void test_queue_thread2thread(void); +extern void test_queue_thread2isr(void); +extern void test_queue_isr2thread(void); +extern void test_queue_get_2threads(void); +extern void test_queue_get_fail(void); +extern void test_queue_loop(void); +#ifdef CONFIG_USERSPACE +extern void test_queue_supv_to_user(void); +extern void test_auto_free(void); +#endif + typedef struct qdata { sys_snode_t snode; u32_t data; + bool allocated; } qdata_t; #endif diff --git a/tests/kernel/queue/src/test_queue_fail.c b/tests/kernel/queue/src/test_queue_fail.c index c54ac82ed8e..4d488195275 100644 --- a/tests/kernel/queue/src/test_queue_fail.c +++ b/tests/kernel/queue/src/test_queue_fail.c @@ -20,7 +20,7 @@ #define TIMEOUT 100 /*test cases*/ -void test_queue_get_fail(void *p1, void *p2, void *p3) +void test_queue_get_fail(void) { struct k_queue queue; diff --git a/tests/kernel/queue/src/test_queue_user.c b/tests/kernel/queue/src/test_queue_user.c new file mode 100644 index 00000000000..b93d6ca40b4 --- /dev/null +++ b/tests/kernel/queue/src/test_queue_user.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "test_queue.h" + +#ifdef CONFIG_USERSPACE + +#define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACKSIZE) +#define LIST_LEN 5 + +static K_THREAD_STACK_DEFINE(child_stack, STACK_SIZE); +static __kernel struct k_thread child_thread; +static struct qdata qdata[LIST_LEN * 2]; + +K_MEM_POOL_DEFINE(test_pool, 16, 64, 4, 4); + +/* Higher priority than the thread putting stuff in the queue */ +void child_thread_get(void *p1, void *p2, void *p3) +{ + struct qdata *qd; + struct k_queue *q = p1; + struct k_sem *sem = p2; + + zassert_false(k_queue_is_empty(q), NULL); + qd = k_queue_peek_head(q); + zassert_equal(qd->data, 0, NULL); + qd = k_queue_peek_tail(q); + zassert_equal(qd->data, (LIST_LEN * 2) - 1, + "got %d expected %d", qd->data, (LIST_LEN * 2) - 1); + + for (int i = 0; i < (LIST_LEN * 2); i++) { + qd = k_queue_get(q, K_FOREVER); + + zassert_equal(qd->data, i, NULL); + if (qd->allocated) { + /* snode should never have been touched */ + zassert_is_null(qd->snode.next, NULL); + } + } + + + zassert_true(k_queue_is_empty(q), NULL); + + /* This one gets canceled */ + qd = k_queue_get(q, K_FOREVER); + zassert_is_null(qd, NULL); + + k_sem_give(sem); +} + +void test_queue_supv_to_user(void) +{ + /* Supervisor mode will add a bunch of data, some with alloc + * and some not + */ + + struct k_queue *q; + struct k_sem *sem; + + k_thread_resource_pool_assign(k_current_get(), &test_pool); + + q = k_object_alloc(K_OBJ_QUEUE); + zassert_not_null(q, "no memory for allocated queue object\n"); + k_queue_init(q); + + sem = k_object_alloc(K_OBJ_SEM); + zassert_not_null(sem, "no memory for semaphore object\n"); + k_sem_init(sem, 0, 1); + + for (int i = 0; i < (LIST_LEN * 2); i = i + 2) { + /* Just for test purposes -- not safe to do this in the + * real world as user mode shouldn't have any access to the + * snode struct + */ + qdata[i].data = i; + qdata[i].allocated = false; + qdata[i].snode.next = NULL; + k_queue_append(q, &qdata[i]); + + qdata[i + 1].data = i + 1; + qdata[i + 1].allocated = true; + qdata[i + 1].snode.next = NULL; + zassert_false(k_queue_alloc_append(q, &qdata[i + 1]), NULL); + } + + k_thread_create(&child_thread, child_stack, STACK_SIZE, + child_thread_get, q, sem, NULL, K_HIGHEST_THREAD_PRIO, + K_USER | K_INHERIT_PERMS, 0); + + k_yield(); + + /* child thread runs until blocking on the last k_queue_get() call */ + k_queue_cancel_wait(q); + k_sem_take(sem, K_FOREVER); +} + +void test_auto_free(void) +{ + /* Ensure any resources requested by the previous test were released + * by allocating the entire pool. It would have allocated two kernel + * objects and five queue elements. The queue elements should be + * auto-freed when they are de-queued, and the objects when all + * threads with permissions exit. + */ + + struct k_mem_block b[4]; + int i; + + for (i = 0; i < 4; i++) { + zassert_false(k_mem_pool_alloc(&test_pool, &b[i], 64, + K_FOREVER), + "memory not auto released!"); + } + + /* Free everything so that the pool is back to a pristine state in + * case we want to use it again. + */ + for (i = 0; i < 4; i++) { + k_mem_pool_free(&b[i]); + } +} + +#endif /* CONFIG_USERSPACE */ diff --git a/tests/kernel/queue/testcase.yaml b/tests/kernel/queue/testcase.yaml index a9d24601595..8fcb188e6ee 100644 --- a/tests/kernel/queue/testcase.yaml +++ b/tests/kernel/queue/testcase.yaml @@ -3,4 +3,4 @@ tests: tags: kernel kernel.queue.poll: extra_args: CONF_FILE="prj_poll.conf" - tags: kernel + tags: kernel userspace