lib: os: add sys_sem data type
For systems with userspace, the sys_sem exist in user memory working as counter semaphore for user mode thread. The implemention of sys_sem is based on k_futex. And the majority of the synchronization operations are performed in user mode to reduce the calling of system call. And for systems without userspace enabled, sys_sem behaves like k_sem. Fixes: #15139. Signed-off-by: Wentong Wu <wentong.wu@intel.com>
This commit is contained in:
parent
51bc0a065c
commit
715369350d
3 changed files with 244 additions and 0 deletions
104
include/sys/sem.h
Normal file
104
include/sys/sem.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief public sys_sem APIs.
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_SEM_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_SEM_H_
|
||||
|
||||
/*
|
||||
* sys_sem exists in user memory working as counter semaphore for
|
||||
* user mode thread when user mode enabled. When user mode isn't
|
||||
* enabled, sys_sem behaves like k_sem.
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <atomic.h>
|
||||
#include <zephyr/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* sys_sem structure
|
||||
*/
|
||||
struct sys_sem {
|
||||
#ifdef CONFIG_USERSPACE
|
||||
struct k_futex futex;
|
||||
int limit;
|
||||
#else
|
||||
struct k_sem kernel_sem;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initialize a semaphore.
|
||||
*
|
||||
* This routine initializes a semaphore instance, prior to its first use.
|
||||
*
|
||||
* @param sem Address of the semaphore.
|
||||
* @param initial_count Initial semaphore count.
|
||||
* @param limit Maximum permitted semaphore count.
|
||||
*
|
||||
* @retval 0 Initial success.
|
||||
* @retval -EINVAL Bad parameters, the value of limit should be located in
|
||||
* (0, INT_MAX] and initial_count shouldn't be greater than limit.
|
||||
*/
|
||||
int sys_sem_init(struct sys_sem *sem, unsigned int initial_count,
|
||||
unsigned int limit);
|
||||
|
||||
/**
|
||||
* @brief Give a semaphore.
|
||||
*
|
||||
* This routine gives @a sem, unless the semaphore is already at its
|
||||
* maximum permitted count.
|
||||
*
|
||||
* @param sem Address of the semaphore.
|
||||
*
|
||||
* @retval 0 Semaphore given.
|
||||
* @retval -EINVAL Parameter address not recognized.
|
||||
* @retval -EACCES Caller does not have enough access.
|
||||
* @retval -EAGAIN Count reached Maximum permitted count and try again.
|
||||
*/
|
||||
int sys_sem_give(struct sys_sem *sem);
|
||||
|
||||
/**
|
||||
* @brief Take a sys_sem.
|
||||
*
|
||||
* This routine takes @a sem.
|
||||
*
|
||||
* @param sem Address of the sys_sem.
|
||||
* @param timeout Waiting period to take the sys_sem (in milliseconds),
|
||||
* or one of the special values K_NO_WAIT and K_FOREVER.
|
||||
*
|
||||
* @retval 0 sys_sem taken.
|
||||
* @retval -EINVAL Parameter address not recognized.
|
||||
* @retval -ETIMEDOUT Waiting period timed out.
|
||||
* @retval -EACCES Caller does not have enough access.
|
||||
*/
|
||||
int sys_sem_take(struct sys_sem *sem, s32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Get sys_sem's value
|
||||
*
|
||||
* This routine returns the current value of @a sem.
|
||||
*
|
||||
* @param sem Address of the sys_sem.
|
||||
*
|
||||
* @return Current value of sys_sem.
|
||||
*/
|
||||
unsigned int sys_sem_count_get(struct sys_sem *sem);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -11,6 +11,7 @@ zephyr_sources(
|
|||
hex.c
|
||||
mempool.c
|
||||
rb.c
|
||||
sem.c
|
||||
thread_entry.c
|
||||
timeutil.c
|
||||
work_q.c
|
||||
|
|
139
lib/os/sem.c
Normal file
139
lib/os/sem.c
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <sys/sem.h>
|
||||
#include <syscall_handler.h>
|
||||
|
||||
#ifdef CONFIG_USERSPACE
|
||||
#define SYS_SEM_MINIMUN 0
|
||||
#define SYS_SEM_CONTENDED (SYS_SEM_MINIMUN - 1)
|
||||
|
||||
static inline atomic_t bounded_dec(atomic_t *val, atomic_t minimum)
|
||||
{
|
||||
atomic_t old_value, new_value;
|
||||
|
||||
do {
|
||||
old_value = atomic_get(val);
|
||||
if (old_value < minimum) {
|
||||
break;
|
||||
}
|
||||
|
||||
new_value = old_value - 1;
|
||||
} while (atomic_cas(val, old_value, new_value) == 0);
|
||||
|
||||
return old_value;
|
||||
}
|
||||
|
||||
static inline atomic_t bounded_inc(atomic_t *val, atomic_t minimum,
|
||||
atomic_t maximum)
|
||||
{
|
||||
atomic_t old_value, new_value;
|
||||
|
||||
do {
|
||||
old_value = atomic_get(val);
|
||||
if (old_value >= maximum) {
|
||||
break;
|
||||
}
|
||||
|
||||
new_value = old_value < minimum ?
|
||||
minimum + 1 : old_value + 1;
|
||||
} while (atomic_cas(val, old_value, new_value) == 0);
|
||||
|
||||
return old_value;
|
||||
}
|
||||
|
||||
int sys_sem_init(struct sys_sem *sem, unsigned int initial_count,
|
||||
unsigned int limit)
|
||||
{
|
||||
if (sem == NULL || limit == SYS_SEM_MINIMUN ||
|
||||
initial_count > limit || limit > INT_MAX) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
atomic_set(&sem->futex.val, initial_count);
|
||||
sem->limit = limit;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_sem_give(struct sys_sem *sem)
|
||||
{
|
||||
int ret = 0;
|
||||
atomic_t old_value;
|
||||
|
||||
old_value = bounded_inc(&sem->futex.val,
|
||||
SYS_SEM_MINIMUN, sem->limit);
|
||||
if (old_value < 0) {
|
||||
ret = k_futex_wake(&sem->futex, true);
|
||||
|
||||
if (ret > 0) {
|
||||
return 0;
|
||||
}
|
||||
} else if (old_value >= sem->limit) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sys_sem_take(struct sys_sem *sem, s32_t timeout)
|
||||
{
|
||||
int ret = 0;
|
||||
atomic_t old_value;
|
||||
|
||||
do {
|
||||
old_value = bounded_dec(&sem->futex.val,
|
||||
SYS_SEM_MINIMUN);
|
||||
if (old_value > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = k_futex_wait(&sem->futex,
|
||||
SYS_SEM_CONTENDED, timeout);
|
||||
} while (ret == 0 || ret == -EAGAIN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int sys_sem_count_get(struct sys_sem *sem)
|
||||
{
|
||||
int value = atomic_get(&sem->futex.val);
|
||||
|
||||
return value > SYS_SEM_MINIMUN ? value : SYS_SEM_MINIMUN;
|
||||
}
|
||||
#else
|
||||
int sys_sem_init(struct sys_sem *sem, unsigned int initial_count,
|
||||
unsigned int limit)
|
||||
{
|
||||
k_sem_init(&sem->kernel_sem, initial_count, limit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_sem_give(struct sys_sem *sem)
|
||||
{
|
||||
k_sem_give(&sem->kernel_sem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_sem_take(struct sys_sem *sem, s32_t timeout)
|
||||
{
|
||||
int ret_value = 0;
|
||||
|
||||
ret_value = k_sem_take(&sem->kernel_sem, timeout);
|
||||
if (ret_value == -EAGAIN || ret_value == -EBUSY) {
|
||||
ret_value = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
unsigned int sys_sem_count_get(struct sys_sem *sem)
|
||||
{
|
||||
return k_sem_count_get(&sem->kernel_sem);
|
||||
}
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue