Boot time initialization functions and data used there must be available at boot. With demand paging, these may not exist in memory when they are being used, resulting in page faults. So pin these functions and data in linker sections to make sure they are in memory at boot time. Signed-off-by: Daniel Leung <daniel.leung@intel.com>
262 lines
5.4 KiB
C
262 lines
5.4 KiB
C
/*
|
|
* Copyright (c) 2017 Intel Corporation
|
|
* Copyright (c) 2023 Meta
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include "posix_internal.h"
|
|
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/posix/pthread.h>
|
|
#include <zephyr/sys/bitarray.h>
|
|
|
|
LOG_MODULE_REGISTER(pthread_cond, CONFIG_PTHREAD_COND_LOG_LEVEL);
|
|
|
|
int64_t timespec_to_timeoutms(const struct timespec *abstime);
|
|
|
|
__pinned_bss
|
|
static struct k_condvar posix_cond_pool[CONFIG_MAX_PTHREAD_COND_COUNT];
|
|
|
|
SYS_BITARRAY_DEFINE_STATIC(posix_cond_bitarray, CONFIG_MAX_PTHREAD_COND_COUNT);
|
|
|
|
/*
|
|
* We reserve the MSB to mark a pthread_cond_t as initialized (from the
|
|
* perspective of the application). With a linear space, this means that
|
|
* the theoretical pthread_cond_t range is [0,2147483647].
|
|
*/
|
|
BUILD_ASSERT(CONFIG_MAX_PTHREAD_COND_COUNT < PTHREAD_OBJ_MASK_INIT,
|
|
"CONFIG_MAX_PTHREAD_COND_COUNT is too high");
|
|
|
|
static inline size_t posix_cond_to_offset(struct k_condvar *cv)
|
|
{
|
|
return cv - posix_cond_pool;
|
|
}
|
|
|
|
static inline size_t to_posix_cond_idx(pthread_cond_t cond)
|
|
{
|
|
return mark_pthread_obj_uninitialized(cond);
|
|
}
|
|
|
|
static struct k_condvar *get_posix_cond(pthread_cond_t cond)
|
|
{
|
|
int actually_initialized;
|
|
size_t bit = to_posix_cond_idx(cond);
|
|
|
|
/* if the provided cond does not claim to be initialized, its invalid */
|
|
if (!is_pthread_obj_initialized(cond)) {
|
|
LOG_DBG("Cond is uninitialized (%x)", cond);
|
|
return NULL;
|
|
}
|
|
|
|
/* Mask off the MSB to get the actual bit index */
|
|
if (sys_bitarray_test_bit(&posix_cond_bitarray, bit, &actually_initialized) < 0) {
|
|
LOG_DBG("Cond is invalid (%x)", cond);
|
|
return NULL;
|
|
}
|
|
|
|
if (actually_initialized == 0) {
|
|
/* The cond claims to be initialized but is actually not */
|
|
LOG_DBG("Cond claims to be initialized (%x)", cond);
|
|
return NULL;
|
|
}
|
|
|
|
return &posix_cond_pool[bit];
|
|
}
|
|
|
|
static struct k_condvar *to_posix_cond(pthread_cond_t *cvar)
|
|
{
|
|
size_t bit;
|
|
struct k_condvar *cv;
|
|
|
|
if (*cvar != PTHREAD_COND_INITIALIZER) {
|
|
return get_posix_cond(*cvar);
|
|
}
|
|
|
|
/* Try and automatically associate a posix_cond */
|
|
if (sys_bitarray_alloc(&posix_cond_bitarray, 1, &bit) < 0) {
|
|
/* No conds left to allocate */
|
|
LOG_DBG("Unable to allocate pthread_cond_t");
|
|
return NULL;
|
|
}
|
|
|
|
/* Record the associated posix_cond in mu and mark as initialized */
|
|
*cvar = mark_pthread_obj_initialized(bit);
|
|
cv = &posix_cond_pool[bit];
|
|
|
|
return cv;
|
|
}
|
|
|
|
static int cond_wait(pthread_cond_t *cond, pthread_mutex_t *mu, k_timeout_t timeout)
|
|
{
|
|
int ret;
|
|
struct k_mutex *m;
|
|
struct k_condvar *cv;
|
|
|
|
m = to_posix_mutex(mu);
|
|
cv = to_posix_cond(cond);
|
|
if (cv == NULL || m == NULL) {
|
|
return EINVAL;
|
|
}
|
|
|
|
LOG_DBG("Waiting on cond %p with timeout %llx", cv, timeout.ticks);
|
|
ret = k_condvar_wait(cv, m, timeout);
|
|
if (ret == -EAGAIN) {
|
|
LOG_DBG("Timeout waiting on cond %p", cv);
|
|
ret = ETIMEDOUT;
|
|
} else if (ret < 0) {
|
|
LOG_DBG("k_condvar_wait() failed: %d", ret);
|
|
ret = -ret;
|
|
} else {
|
|
__ASSERT_NO_MSG(ret == 0);
|
|
LOG_DBG("Cond %p received signal", cv);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int pthread_cond_signal(pthread_cond_t *cvar)
|
|
{
|
|
int ret;
|
|
struct k_condvar *cv;
|
|
|
|
cv = to_posix_cond(cvar);
|
|
if (cv == NULL) {
|
|
return EINVAL;
|
|
}
|
|
|
|
LOG_DBG("Signaling cond %p", cv);
|
|
ret = k_condvar_signal(cv);
|
|
if (ret < 0) {
|
|
LOG_DBG("k_condvar_signal() failed: %d", ret);
|
|
return -ret;
|
|
}
|
|
|
|
__ASSERT_NO_MSG(ret == 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pthread_cond_broadcast(pthread_cond_t *cvar)
|
|
{
|
|
int ret;
|
|
struct k_condvar *cv;
|
|
|
|
cv = get_posix_cond(*cvar);
|
|
if (cv == NULL) {
|
|
return EINVAL;
|
|
}
|
|
|
|
LOG_DBG("Broadcasting on cond %p", cv);
|
|
ret = k_condvar_broadcast(cv);
|
|
if (ret < 0) {
|
|
LOG_DBG("k_condvar_broadcast() failed: %d", ret);
|
|
return -ret;
|
|
}
|
|
|
|
__ASSERT_NO_MSG(ret >= 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mut)
|
|
{
|
|
return cond_wait(cv, mut, K_FOREVER);
|
|
}
|
|
|
|
int pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *mut, const struct timespec *abstime)
|
|
{
|
|
return cond_wait(cv, mut, K_MSEC((int32_t)timespec_to_timeoutms(abstime)));
|
|
}
|
|
|
|
int pthread_cond_init(pthread_cond_t *cvar, const pthread_condattr_t *att)
|
|
{
|
|
struct k_condvar *cv;
|
|
|
|
ARG_UNUSED(att);
|
|
*cvar = PTHREAD_COND_INITIALIZER;
|
|
|
|
/* calls k_condvar_init() */
|
|
cv = to_posix_cond(cvar);
|
|
if (cv == NULL) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
LOG_DBG("Initialized cond %p", cv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pthread_cond_destroy(pthread_cond_t *cvar)
|
|
{
|
|
int err;
|
|
size_t bit;
|
|
struct k_condvar *cv;
|
|
|
|
cv = get_posix_cond(*cvar);
|
|
if (cv == NULL) {
|
|
return EINVAL;
|
|
}
|
|
|
|
bit = posix_cond_to_offset(cv);
|
|
err = sys_bitarray_free(&posix_cond_bitarray, 1, bit);
|
|
__ASSERT_NO_MSG(err == 0);
|
|
|
|
*cvar = -1;
|
|
|
|
LOG_DBG("Destroyed cond %p", cv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
__boot_func
|
|
static int pthread_cond_pool_init(void)
|
|
{
|
|
int err;
|
|
size_t i;
|
|
|
|
for (i = 0; i < CONFIG_MAX_PTHREAD_COND_COUNT; ++i) {
|
|
err = k_condvar_init(&posix_cond_pool[i]);
|
|
__ASSERT_NO_MSG(err == 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pthread_condattr_init(pthread_condattr_t *att)
|
|
{
|
|
__ASSERT_NO_MSG(att != NULL);
|
|
|
|
att->clock = CLOCK_MONOTONIC;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pthread_condattr_destroy(pthread_condattr_t *att)
|
|
{
|
|
ARG_UNUSED(att);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pthread_condattr_getclock(const pthread_condattr_t *ZRESTRICT att,
|
|
clockid_t *ZRESTRICT clock_id)
|
|
{
|
|
*clock_id = att->clock;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pthread_condattr_setclock(pthread_condattr_t *att, clockid_t clock_id)
|
|
{
|
|
if (clock_id != CLOCK_REALTIME && clock_id != CLOCK_MONOTONIC) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
att->clock = clock_id;
|
|
|
|
return 0;
|
|
}
|
|
SYS_INIT(pthread_cond_pool_init, PRE_KERNEL_1, 0);
|