k_poll: expose to user mode

k_poll is now accessible from user mode. A memory allocation takes place
from the caller's resource pool to copy the provided poll_events
array; this can be large enough to make allocating it on the stack
not preferable.

k_poll_signal are now proper kernel objects. Two APIs have been added,
one to reset the signaled state and one to check the current signaled
state and result value.

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2018-05-07 16:52:57 -07:00 committed by Anas Nashif
commit 3772f77119
7 changed files with 198 additions and 26 deletions

View file

@ -4178,6 +4178,9 @@ extern void k_poll_event_init(struct k_poll_event *event, u32_t type,
* Before being reused for another call to k_poll(), the user has to reset the
* state field to K_POLL_STATE_NOT_READY.
*
* When called from user mode, a temporary memory allocation is required from
* the caller's resource pool.
*
* @param events An array of pointers to events to be polled for.
* @param num_events The number of events in the array.
* @param timeout Waiting period for an event to be ready (in milliseconds),
@ -4186,10 +4189,12 @@ extern void k_poll_event_init(struct k_poll_event *event, u32_t type,
* @retval 0 One or more events are ready.
* @retval -EAGAIN Waiting period timed out.
* @retval -EINTR Poller thread has been interrupted.
* @retval -ENOMEM Thread resource pool insufficient memory (user mode only)
* @retval -EINVAL Bad parameters (user mode only)
*/
extern int k_poll(struct k_poll_event *events, int num_events,
s32_t timeout);
__syscall int k_poll(struct k_poll_event *events, int num_events,
s32_t timeout);
/**
* @brief Initialize a poll signal object.
@ -4201,7 +4206,32 @@ extern int k_poll(struct k_poll_event *events, int num_events,
* @return N/A
*/
extern void k_poll_signal_init(struct k_poll_signal *signal);
__syscall void k_poll_signal_init(struct k_poll_signal *signal);
/*
* @brief Reset a poll signal object's state to unsignaled.
*
* @param signal A poll signal object
*/
__syscall void k_poll_signal_reset(struct k_poll_signal *signal);
static inline void _impl_k_poll_signal_reset(struct k_poll_signal *signal)
{
signal->signaled = 0;
}
/**
* @brief Fetch the signaled state and resylt value of a poll signal
*
* @param signal A poll signal object
* @param signaled An integer buffer which will be written nonzero if the
* object was signaled
* @param result An integer destination buffer which will be written with the
* result value if the object was signaed, or an undefined
* value if it was not.
*/
__syscall void k_poll_signal_check(struct k_poll_signal *signal,
unsigned int *signaled, int *result);
/**
* @brief Signal a poll signal object.
@ -4211,9 +4241,10 @@ extern void k_poll_signal_init(struct k_poll_signal *signal);
* made ready to run. A @a result value can be specified.
*
* The poll signal contains a 'signaled' field that, when set by
* k_poll_signal(), stays set until the user sets it back to 0. It thus has to
* be reset by the user before being passed again to k_poll() or k_poll() will
* consider it being signaled, and will return immediately.
* k_poll_signal(), stays set until the user sets it back to 0 with
* k_poll_signal_reset(). It thus has to be reset by the user before being
* passed again to k_poll() or k_poll() will consider it being signaled, and
* will return immediately.
*
* @param signal A poll signal.
* @param result The value to store in the result field of the signal.
@ -4222,7 +4253,7 @@ extern void k_poll_signal_init(struct k_poll_signal *signal);
* @retval -EAGAIN The polling thread's timeout is in the process of expiring.
*/
extern int k_poll_signal(struct k_poll_signal *signal, int result);
__syscall int k_poll_signal(struct k_poll_signal *signal, int result);
/**
* @internal

View file

@ -19,6 +19,7 @@
#include <kernel_internal.h>
#include <wait_q.h>
#include <ksched.h>
#include <syscall_handler.h>
#include <misc/slist.h>
#include <misc/dlist.h>
#include <misc/__assert.h>
@ -189,7 +190,7 @@ static inline void set_event_ready(struct k_poll_event *event, u32_t state)
event->state |= state;
}
int k_poll(struct k_poll_event *events, int num_events, s32_t timeout)
int _impl_k_poll(struct k_poll_event *events, int num_events, s32_t timeout)
{
__ASSERT(!_is_in_isr(), "");
__ASSERT(events, "NULL events\n");
@ -263,6 +264,80 @@ int k_poll(struct k_poll_event *events, int num_events, s32_t timeout)
return swap_rc;
}
#ifdef CONFIG_USERSPACE
Z_SYSCALL_HANDLER(k_poll, events, num_events, timeout)
{
int ret, key;
struct k_poll_event *events_copy = NULL;
unsigned int bounds;
/* Validate the events buffer and make a copy of it in an
* allocated kernel-side buffer.
*/
if (Z_SYSCALL_VERIFY(num_events > 0)) {
ret = -EINVAL;
goto out;
}
if (Z_SYSCALL_VERIFY_MSG(
!__builtin_umul_overflow(num_events,
sizeof(struct k_poll_event),
&bounds), "num_events too large")) {
ret = -EINVAL;
goto out;
}
events_copy = z_thread_malloc(bounds);
if (!events_copy) {
ret = -ENOMEM;
goto out;
}
key = irq_lock();
if (Z_SYSCALL_MEMORY_WRITE(events, bounds)) {
irq_unlock(key);
goto oops_free;
}
memcpy(events_copy, (void *)events, bounds);
irq_unlock(key);
/* Validate what's inside events_copy */
for (int i = 0; i < num_events; i++) {
struct k_poll_event *e = &events_copy[i];
if (Z_SYSCALL_VERIFY(e->mode == K_POLL_MODE_NOTIFY_ONLY)) {
ret = -EINVAL;
goto out_free;
}
switch (e->type) {
case K_POLL_TYPE_IGNORE:
break;
case K_POLL_TYPE_SIGNAL:
Z_OOPS(Z_SYSCALL_OBJ(e->signal, K_OBJ_POLL_SIGNAL));
break;
case K_POLL_TYPE_SEM_AVAILABLE:
Z_OOPS(Z_SYSCALL_OBJ(e->sem, K_OBJ_SEM));
break;
case K_POLL_TYPE_DATA_AVAILABLE:
Z_OOPS(Z_SYSCALL_OBJ(e->queue, K_OBJ_QUEUE));
break;
default:
ret = -EINVAL;
goto out_free;
}
}
ret = k_poll(events_copy, num_events, timeout);
memcpy((void *)events, events_copy, bounds);
out_free:
k_free(events_copy);
out:
return ret;
oops_free:
k_free(events_copy);
Z_OOPS(1);
}
#endif
/* must be called with interrupts locked */
static int signal_poll_event(struct k_poll_event *event, u32_t state)
{
@ -309,14 +384,44 @@ void _handle_obj_poll_events(sys_dlist_t *events, u32_t state)
}
}
void k_poll_signal_init(struct k_poll_signal *signal)
void _impl_k_poll_signal_init(struct k_poll_signal *signal)
{
sys_dlist_init(&signal->poll_events);
signal->signaled = 0;
/* signal->result is left unitialized */
_k_object_init(signal);
}
int k_poll_signal(struct k_poll_signal *signal, int result)
#ifdef CONFIG_USERSPACE
Z_SYSCALL_HANDLER(k_poll_signal_init, signal)
{
Z_OOPS(Z_SYSCALL_OBJ_INIT(signal, K_OBJ_POLL_SIGNAL));
_impl_k_poll_signal_init((struct k_poll_signal *)signal);
return 0;
}
#endif
void _impl_k_poll_signal_check(struct k_poll_signal *signal,
unsigned int *signaled, int *result)
{
*signaled = signal->signaled;
*result = signal->result;
}
#ifdef CONFIG_USERSPACE
Z_SYSCALL_HANDLER(k_poll_signal_check, signal, signaled, result)
{
Z_OOPS(Z_SYSCALL_OBJ(signal, K_OBJ_POLL_SIGNAL));
Z_OOPS(Z_SYSCALL_MEMORY_WRITE(signaled, sizeof(unsigned int)));
Z_OOPS(Z_SYSCALL_MEMORY_WRITE(result, sizeof(int)));
_impl_k_poll_signal_check((struct k_poll_signal *)signal,
(unsigned int *)signaled, (int *)result);
return 0;
}
#endif
int _impl_k_poll_signal(struct k_poll_signal *signal, int result)
{
unsigned int key = irq_lock();
struct k_poll_event *poll_event;
@ -335,3 +440,14 @@ int k_poll_signal(struct k_poll_signal *signal, int result)
_reschedule(key);
return rc;
}
#ifdef CONFIG_USERSPACE
Z_SYSCALL_HANDLER(k_poll_signal, signal, result)
{
Z_OOPS(Z_SYSCALL_OBJ(signal, K_OBJ_POLL_SIGNAL));
return _impl_k_poll_signal((struct k_poll_signal *)signal, result);
}
Z_SYSCALL_HANDLER1_SIMPLE_VOID(k_poll_signal_reset, K_OBJ_POLL_SIGNAL,
struct k_poll_signal *);
#endif

View file

@ -17,6 +17,7 @@ kobjects = [
"k_mutex",
"k_pipe",
"k_queue",
"k_poll_signal",
"k_sem",
"k_stack",
"k_thread",

View file

@ -1,2 +1,3 @@
CONFIG_ZTEST=y
CONFIG_POLL=y
CONFIG_DYNAMIC_OBJECTS=y

View file

@ -15,12 +15,19 @@
extern void test_poll_no_wait(void);
extern void test_poll_wait(void);
extern void test_poll_multi(void);
extern void test_poll_grant_access(void);
K_MEM_POOL_DEFINE(test_pool, 128, 128, 4, 4);
/*test case main entry*/
void test_main(void)
{
test_poll_grant_access();
k_thread_resource_pool_assign(k_current_get(), &test_pool);
ztest_test_suite(poll_api,
ztest_unit_test(test_poll_no_wait),
ztest_user_unit_test(test_poll_no_wait),
ztest_unit_test(test_poll_wait),
ztest_unit_test(test_poll_multi));
ztest_run_test_suite(poll_api);

View file

@ -29,13 +29,15 @@ struct fifo_msg {
#define FIFO_MSG_VALUE 0xdeadbeef
/* verify k_poll() without waiting */
static struct k_sem no_wait_sem;
static struct k_fifo no_wait_fifo;
static struct k_poll_signal no_wait_signal;
static __kernel struct k_sem no_wait_sem;
static __kernel struct k_fifo no_wait_fifo;
static __kernel struct k_poll_signal no_wait_signal;
void test_poll_no_wait(void)
{
struct fifo_msg msg = { NULL, FIFO_MSG_VALUE }, *msg_ptr;
unsigned int signaled;
int result;
k_sem_init(&no_wait_sem, 1, 1);
k_fifo_init(&no_wait_fifo);
@ -54,7 +56,7 @@ void test_poll_no_wait(void)
};
/* test polling events that are already ready */
k_fifo_put(&no_wait_fifo, &msg);
zassert_false(k_fifo_alloc_put(&no_wait_fifo, &msg), NULL);
k_poll_signal(&no_wait_signal, SIGNAL_RESULT);
zassert_equal(k_poll(events, ARRAY_SIZE(events), 0), 0, "");
@ -69,14 +71,15 @@ void test_poll_no_wait(void)
zassert_equal(msg_ptr->msg, FIFO_MSG_VALUE, "");
zassert_equal(events[2].state, K_POLL_STATE_SIGNALED, "");
zassert_equal(no_wait_signal.signaled, 1, "");
zassert_equal(no_wait_signal.result, SIGNAL_RESULT, "");
k_poll_signal_check(&no_wait_signal, &signaled, &result);
zassert_not_equal(signaled, 0, "");
zassert_equal(result, SIGNAL_RESULT, "");
/* verify events are not ready anymore (user has to clear them first) */
events[0].state = K_POLL_STATE_NOT_READY;
events[1].state = K_POLL_STATE_NOT_READY;
events[2].state = K_POLL_STATE_NOT_READY;
no_wait_signal.signaled = 0;
k_poll_signal_reset(&no_wait_signal);
zassert_equal(k_poll(events, ARRAY_SIZE(events), 0), -EAGAIN, "");
zassert_equal(events[0].state, K_POLL_STATE_NOT_READY, "");
@ -91,12 +94,12 @@ void test_poll_no_wait(void)
static K_SEM_DEFINE(wait_sem, 0, 1);
static K_FIFO_DEFINE(wait_fifo);
static struct k_poll_signal wait_signal =
static __kernel struct k_poll_signal wait_signal =
K_POLL_SIGNAL_INITIALIZER(wait_signal);
struct fifo_msg wait_msg = { NULL, FIFO_MSG_VALUE };
static struct k_thread poll_wait_helper_thread;
static __kernel struct k_thread poll_wait_helper_thread;
static K_THREAD_STACK_DEFINE(poll_wait_helper_stack, KB(1));
#define TAG_0 10
@ -124,7 +127,7 @@ static void poll_wait_helper(void *use_fifo, void *p2, void *p3)
k_sem_give(&wait_sem);
if ((intptr_t)use_fifo) {
k_fifo_put(&wait_fifo, &wait_msg);
k_fifo_alloc_put(&wait_fifo, &wait_msg);
}
k_poll_signal(&wait_signal, SIGNAL_RESULT);
@ -139,6 +142,7 @@ void test_poll_wait(void)
int old_prio = k_thread_priority_get(k_current_get());
k_poll_signal_init(&wait_signal);
/*
* Wait for 3 non-ready events to become ready from a higher priority
* thread.
@ -148,7 +152,7 @@ void test_poll_wait(void)
k_thread_create(&poll_wait_helper_thread, poll_wait_helper_stack,
K_THREAD_STACK_SIZEOF(poll_wait_helper_stack),
poll_wait_helper, (void *)1, 0, 0,
main_low_prio - 1, 0, 0);
main_low_prio - 1, K_USER | K_INHERIT_PERMS, 0);
rc = k_poll(wait_events, ARRAY_SIZE(wait_events), K_SECONDS(1));
@ -235,7 +239,8 @@ void test_poll_wait(void)
k_thread_create(&poll_wait_helper_thread, poll_wait_helper_stack,
K_THREAD_STACK_SIZEOF(poll_wait_helper_stack),
poll_wait_helper,
(void *)1, 0, 0, old_prio + 1, 0, 0);
(void *)1, 0, 0, old_prio + 1,
K_USER | K_INHERIT_PERMS, 0);
/* semaphore */
rc = k_poll(wait_events, ARRAY_SIZE(wait_events), K_SECONDS(1));
@ -303,7 +308,7 @@ void test_poll_wait(void)
static K_SEM_DEFINE(multi_sem, 0, 1);
static K_SEM_DEFINE(multi_reply, 0, 1);
static struct k_thread multi_thread;
static __kernel struct k_thread multi_thread;
static K_THREAD_STACK_DEFINE(multi_stack, KB(1));
static void multi(void *p1, void *p2, void *p3)
@ -341,7 +346,8 @@ void test_poll_multi(void)
k_thread_create(&multi_thread, multi_stack,
K_THREAD_STACK_SIZEOF(multi_stack),
multi, 0, 0, 0, main_low_prio - 1, 0, 0);
multi, 0, 0, 0, main_low_prio - 1,
K_USER | K_INHERIT_PERMS, 0);
rc = k_poll(events, ARRAY_SIZE(events), K_SECONDS(1));
@ -357,3 +363,13 @@ void test_poll_multi(void)
zassert_equal(rc, 0, "");
}
void test_poll_grant_access(void)
{
k_thread_access_grant(k_current_get(), &no_wait_sem, &no_wait_fifo,
&no_wait_signal, &wait_sem, &wait_fifo,
&wait_signal, &poll_wait_helper_thread,
&poll_wait_helper_stack, &multi_sem,
&multi_reply, &multi_thread, &multi_stack,
NULL);
}

View file

@ -1,3 +1,3 @@
tests:
kernel.poll:
tags: kernel
tags: kernel userspace