Commit 3772f771 authored by Andrew Boie's avatar Andrew Boie Committed by Anas Nashif
Browse files

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: default avatarAndrew Boie <andrew.p.boie@intel.com>
parent 8345e5eb
Loading
Loading
Loading
Loading
+38 −7
Original line number Diff line number Diff line
@@ -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,9 +4189,11 @@ 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,
__syscall int k_poll(struct k_poll_event *events, int num_events,
		     s32_t timeout);

/**
@@ -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
+119 −3
Original line number Diff line number Diff line
@@ -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);
}

#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;
}

int k_poll_signal(struct k_poll_signal *signal, int 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
+1 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ kobjects = [
    "k_mutex",
    "k_pipe",
    "k_queue",
    "k_poll_signal",
    "k_sem",
    "k_stack",
    "k_thread",
+1 −0
Original line number Diff line number Diff line
CONFIG_ZTEST=y
CONFIG_POLL=y
CONFIG_DYNAMIC_OBJECTS=y
+8 −1
Original line number Diff line number Diff line
@@ -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);
Loading