diff --git a/src/os/vxworks-rtp/CMakeLists.txt b/src/os/vxworks-rtp/CMakeLists.txt index 018403091..a048e1f8b 100644 --- a/src/os/vxworks-rtp/CMakeLists.txt +++ b/src/os/vxworks-rtp/CMakeLists.txt @@ -19,11 +19,11 @@ set(POSIX_BASE_SRCLIST ../posix/src/os-impl-idmap.c ../posix/src/os-impl-mutex.c ../posix/src/os-impl-queues.c - ../posix/src/os-impl-tasks.c ../posix/src/os-impl-timebase.c - src/os-impl-filesys.c # VxWorks RTP specific implementation - src/os-impl-files.c # VxWorks RTP specific implementation - ../portable/os-impl-pthread-no-affinity-np.c + src/os-impl-tasks.c # VxWorks RTP specific implementation + src/os-impl-filesys.c # VxWorks RTP specific implementation + src/os-impl-files.c # VxWorks RTP specific implementation + ../portable/os-impl-posix-gettime-no-monotonic.c ) # Use portable blocks for basic I/O @@ -99,6 +99,16 @@ else() ) endif () +if (OSAL_CONFIG_MAX_CPUS GREATER 1) + list(APPEND POSIX_IMPL_SRCLIST + src/os-impl-pthread-affinity.c # use the real affinity code + ) +else() + list(APPEND POSIX_IMPL_SRCLIST + ../portable/os-impl-pthread-no-affinity-np.c + ) +endif() + # Defines an OBJECT target named "osal_posix_impl" with selected source files add_library(osal_vxworks-rtp_impl OBJECT ${POSIX_BASE_SRCLIST} @@ -106,8 +116,8 @@ add_library(osal_vxworks-rtp_impl OBJECT ) target_include_directories(osal_vxworks-rtp_impl PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../posix/inc ${CMAKE_CURRENT_SOURCE_DIR}/inc + ${CMAKE_CURRENT_SOURCE_DIR}/../posix/inc ) target_compile_definitions(osal_public_api INTERFACE _POSIX_OS_ diff --git a/src/os/vxworks-rtp/inc/os-impl-taskaffinity.h b/src/os/vxworks-rtp/inc/os-impl-taskaffinity.h new file mode 100644 index 000000000..44cc887b7 --- /dev/null +++ b/src/os/vxworks-rtp/inc/os-impl-taskaffinity.h @@ -0,0 +1,44 @@ +/************************************************************************ + * NASA Docket No. GSC-19,200-1, and identified as "cFS Draco" + * + * Copyright (c) 2023 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ************************************************************************/ + +/** + * \file + * + * \ingroup vxworks + * + */ + +#ifndef OS_IMPL_TASKAFFINITY_H +#define OS_IMPL_TASKAFFINITY_H + +/* VxWorks Native Headers */ +#include +#include + +#include "common_types.h" + +/* Explicitly declare to avoid implicit declaration warnings */ +extern unsigned int vxCpuConfiguredGet(void); + +/* Define the function used by the OSAL initialization */ +static inline uint32 OS_TaskAffinity_Proc_Conf(void) +{ + return (uint32)vxCpuConfiguredGet(); +} + +#endif /* OS_IMPL_TASKAFFINITY_H */ diff --git a/src/os/vxworks-rtp/inc/os-impl-tasks.h b/src/os/vxworks-rtp/inc/os-impl-tasks.h new file mode 100644 index 000000000..710a84acf --- /dev/null +++ b/src/os/vxworks-rtp/inc/os-impl-tasks.h @@ -0,0 +1,54 @@ +/************************************************************************ + * NASA Docket No. GSC-19,200-1, and identified as "cFS Draco" + * + * Copyright (c) 2023 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ************************************************************************/ + +/** + * \file + * + * \ingroup posix + * + */ + +#ifndef OS_IMPL_TASKS_H +#define OS_IMPL_TASKS_H + +#include "os-shared-task.h" + +#include "osconfig.h" +#include +#include + +typedef void *(*PthreadFuncPtr_t)(void *entry_arg); + +/*tasks */ +typedef struct +{ + pthread_t id; + TASK_ID vxid; +} OS_impl_task_internal_record_t; + +/* Tables where the OS object information is stored */ +extern OS_impl_task_internal_record_t OS_impl_task_table[OS_MAX_TASKS]; + +int32 OS_Posix_InternalTaskCreate_Impl(pthread_t *pthr, + osal_priority_t priority, + osal_stackptr_t stackptr, + size_t stacksz, + PthreadFuncPtr_t entry, + void *entry_arg); + +#endif /* OS_IMPL_TASKS_H */ diff --git a/src/os/vxworks-rtp/src/os-impl-pthread-affinity.c b/src/os/vxworks-rtp/src/os-impl-pthread-affinity.c new file mode 100644 index 000000000..436d8ccb7 --- /dev/null +++ b/src/os/vxworks-rtp/src/os-impl-pthread-affinity.c @@ -0,0 +1,144 @@ +/************************************************************************ + * NASA Docket No. GSC-19,200-1, and identified as "cFS Draco" + * + * Copyright (c) 2023 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ************************************************************************/ + +/** + * \file + * + * OSAL Task Affinity Implementation for VxWorks RTP + * This bridges the POSIX OSAL task structure with native VxWorks CPU Affinity APIs + */ + +/**************************************************************************************** + INCLUDE FILES + ***************************************************************************************/ + +#include +#include +#include +#include + +/* POSIX thread support (needed for the OSAL internal records) */ +#include + +/* Native VxWorks headers required for Task Affinity in RTP */ +#include +#include +#include +#include +#include + +#include "os-impl-taskaffinity.h" +#include "osapi-task-affinity.h" +#include "os-impl-tasks.h" +#include "os-shared-file.h" +#include "os-shared-idmap.h" + +/**************************************************************************************** + Task Affinity API + ***************************************************************************************/ + +/* + * ---------------------------------------------------------------------- + * The OS_TaskAffinityGetCoresConfigured_Impl() + * Returns the number of configured cores + * ---------------------------------------------------------------------- + */ +uint32 OS_TaskAffinityGetCoresConfigured_Impl(void) +{ + return OS_TaskAffinity_Proc_Conf(); +} + +/* + * ---------------------------------------------------------------------- + * The OS_TaskAffinitySetAffinity_Impl() + * Sets an affinity from cpuset to task with provided task_id + * ---------------------------------------------------------------------- + */ +int32 OS_TaskAffinitySetAffinity_Impl(const OS_object_token_t *token, const OS_cpuset_t cpuset) +{ + cpuset_t local_cpuset; + OS_impl_task_internal_record_t *impl = OS_OBJECT_TABLE_GET(OS_impl_task_table, *token); + uint32 i; + int32 return_value; + + /* Check if the thread has actually started and recorded its ID yet */ + if (impl->vxid == 0 || impl->vxid == (TASK_ID)ERROR) + { + return OS_ERROR; + } + + CPUSET_ZERO(local_cpuset); + for (i = 0; (i < OS_MAX_CPUS) && (i < OS_TaskAffinityGetCoresConfigured()); i++) + { + if (OS_CPUSET_ISSET(i, &cpuset)) + { + CPUSET_SET(local_cpuset, i); + } + } + + /* Pass the true native TASK_ID to VxWorks */ + return_value = taskCpuAffinitySet(impl->vxid, local_cpuset); + + if (return_value == 0) + { + return OS_SUCCESS; + } + else + { + return OS_ERROR; + } +} + +/* + * ---------------------------------------------------------------------- + * The OS_TaskAffinityGetAffinity_Impl() is an api call to get affinity from a task + * Writes affinity to cpuset from task with provided task_id + * ---------------------------------------------------------------------- + */ +int32 OS_TaskAffinityGetAffinity_Impl(const OS_object_token_t *token, OS_cpuset_t *cpuset) +{ + cpuset_t local_cpuset; + OS_impl_task_internal_record_t *impl = OS_OBJECT_TABLE_GET(OS_impl_task_table, *token); + uint32 i; + int32 return_value; + + /* Check if the thread has actually started and recorded its ID yet */ + if (impl->vxid == 0 || impl->vxid == (TASK_ID)ERROR) + { + return OS_ERROR; + } + + CPUSET_ZERO(local_cpuset); + + /* Pass the true native TASK_ID to VxWorks */ + return_value = taskCpuAffinityGet(impl->vxid, &local_cpuset); + + if (return_value == 0) + { + OS_CPUSET_ZERO(cpuset); + for (i = 0; (i < OS_MAX_CPUS) && (i < OS_TaskAffinityGetCoresConfigured()); i++) + { + if (CPUSET_ISSET(local_cpuset, i)) + { + OS_CPUSET_SET(i, cpuset); + } + } + return OS_SUCCESS; + } + return OS_ERROR; +} diff --git a/src/os/vxworks-rtp/src/os-impl-tasks.c b/src/os/vxworks-rtp/src/os-impl-tasks.c new file mode 100644 index 000000000..50a7bf3e5 --- /dev/null +++ b/src/os/vxworks-rtp/src/os-impl-tasks.c @@ -0,0 +1,924 @@ +/************************************************************************ + * NASA Docket No. GSC-19,200-1, and identified as "cFS Draco" + * + * Copyright (c) 2023 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ************************************************************************/ + +/** + * \file + * \ingroup posix + * \author joseph.p.hickey@nasa.gov + * + */ + +/**************************************************************************************** + INCLUDE FILES + ***************************************************************************************/ + +#include "os-posix.h" +#include "bsp-impl.h" +#include + +#include "os-impl-tasks.h" + +#include "os-shared-task.h" +#include "os-shared-task-affinity.h" +#include "os-shared-idmap.h" +#include "os-shared-common.h" +#include + +/* + * Extra Stack Space for overhead - + * + * glibc pthreads implicitly puts its TCB/TLS structures at the base of the stack. + * The actual size of these structures is highly system and configuration dependent. + * Experimentation/measurement on an x64-64 system with glibc shows this to consume + * between 9-10kB of stack space off the top. + * + * There does not seem to be a reliable/standardized way of determining how large this + * is going to be, but PTHREAD_STACK_MIN (if defined) should include adequate space for it. + * If this is not defined, then just assume 1 page. + * + * Importantly - when the user passes a stack size to OS_TaskCreate() - the expectation is + * that there will be at least this amount of real _usable_ space for the task. If 10kB + * is used before the entry point is even called, this needs to be accounted for, or else + * the stack might end up being too small. + */ +#ifdef PTHREAD_STACK_MIN +#define OS_IMPL_STACK_EXTRA PTHREAD_STACK_MIN +#else +#define OS_IMPL_STACK_EXTRA POSIX_GlobalVars.PageSize +#endif + +/* Tables where the OS object information is stored */ +OS_impl_task_internal_record_t OS_impl_task_table[OS_MAX_TASKS]; + +/* + * Local Function Prototypes + */ + +/*---------------------------------------------------------------------------- + * Name: OS_PriorityRemap + * + * Purpose: Remaps the OSAL priority into one that is viable for this OS + * + * Note: This implementation assumes that InputPri has already been verified + * to be within the range of [0,OS_MAX_TASK_PRIORITY] + * +----------------------------------------------------------------------------*/ +static int OS_PriorityRemap(osal_priority_t InputPri) +{ + int OutputPri; + + if (InputPri == 0) + { + /* use the "MAX" local priority only for OSAL tasks with priority=0 */ + OutputPri = POSIX_GlobalVars.PriLimits.PriorityMax; + } + else if (InputPri >= OS_MAX_TASK_PRIORITY) + { + /* use the "MIN" local priority only for OSAL tasks with priority=255 */ + OutputPri = POSIX_GlobalVars.PriLimits.PriorityMin; + } + else + { + /* + * Spread the remainder of OSAL priorities over the remainder of local priorities + * + * Note OSAL priorities use the VxWorks style with zero being the + * highest and OS_MAX_TASK_PRIORITY being the lowest, this inverts it + */ + OutputPri = (OS_MAX_TASK_PRIORITY - 1) - (int)InputPri; + + OutputPri *= (POSIX_GlobalVars.PriLimits.PriorityMax - POSIX_GlobalVars.PriLimits.PriorityMin) - 2; + OutputPri += OS_MAX_TASK_PRIORITY / 2; + OutputPri /= (OS_MAX_TASK_PRIORITY - 2); + OutputPri += POSIX_GlobalVars.PriLimits.PriorityMin + 1; + } + + return OutputPri; +} + +/*---------------------------------------------------------------- + * + * Purpose: Local helper routine, not part of OSAL API. + * A POSIX signal handler that does nothing + * + *-----------------------------------------------------------------*/ +static void OS_NoopSigHandler(int signal) +{ +} /* end OS_NoopSigHandler */ + +/*--------------------------------------------------------------------------------------- + Name: OS_PthreadEntry + + Purpose: A Simple pthread-compatible entry point that calls the real task function + + returns: NULL + + NOTES: This wrapper function is only used locally by OS_TaskCreate below + +---------------------------------------------------------------------------------------*/ +static void *OS_PthreadTaskEntry(void *arg) +{ + OS_VoidPtrValueWrapper_t local_arg; + uint32 array_index = 0; + + /* cppcheck-suppress unreadVariable // intentional use of other union member */ + local_arg.opaque_arg = arg; + + /* Get the OSAL array index for this task and save its true VxWorks TASK_ID */ + if (OS_ObjectIdToArrayIndex(OS_OBJECT_TYPE_OS_TASK, local_arg.id, &array_index) == OS_SUCCESS) + { + OS_impl_task_table[array_index].vxid = taskIdSelf(); + } + + OS_TaskEntryPoint(local_arg.id); /* Never returns */ + + return NULL; +} + +/*--------------------------------------------------------------------------------------- + Name: OS_Posix_GetSchedulerParams + + Purpose: Helper function to get the details of the given OS scheduling policy. + Determines if the policy is usable by OSAL - namely, that it provides + enough priority levels to be useful. + + returns: true if policy is suitable for use by OSAL + + NOTES: Only used locally by task API initialization + +---------------------------------------------------------------------------------------*/ +static bool OS_Posix_GetSchedulerParams(int sched_policy, POSIX_PriorityLimits_t *PriLim) +{ + int ret; + + /* + * Set up the local Min/Max priority levels (varies by OS and scheduler policy) + * + * Per POSIX: + * - The sched_get_priority_min/max() returns a number >= 0 on success. + * (-1 indicates an error) + * - Numerically higher values are scheduled before numerically lower values + * - A compliant OS will have a spread of at least 32 between min and max + */ + ret = sched_get_priority_max(sched_policy); + if (ret < 0) + { + OS_DEBUG("Policy %d: Unable to obtain maximum scheduling priority: %s\n", sched_policy, strerror(errno)); + return false; + } + + PriLim->PriorityMax = ret; + + ret = sched_get_priority_min(sched_policy); + if (ret < 0) + { + OS_DEBUG("Policy %d: Unable to obtain minimum scheduling priority: %s\n", sched_policy, strerror(errno)); + return false; + } + + PriLim->PriorityMin = ret; + + /* + * For OSAL, the absolute minimum spread between min and max must be 4. + * + * Although POSIX stipulates 32, we don't necessarily need that many, but we + * also want to confirm that there is an acceptable spread. + * + * - Highest is reserved for the root task + * - Next highest is reserved for OSAL priority=0 task(s) + * - Lowest is reserved for OSAL priority=255 tasks(s) + * - Need at least 1 for everything else. + */ + if ((PriLim->PriorityMax - PriLim->PriorityMin) < 4) + { + OS_DEBUG("Policy %d: Insufficient spread between priority min-max: %d-%d\n", + sched_policy, + (int)PriLim->PriorityMin, + (int)PriLim->PriorityMax); + return false; + } + + /* If we get here, then the sched_policy is potentially valid */ + OS_DEBUG("Policy %d: available, min-max: %d-%d\n", + sched_policy, + (int)PriLim->PriorityMin, + (int)PriLim->PriorityMax); + return true; +} + +/* + ********************************************************************************* + * TASK API + ********************************************************************************* + */ + +/*--------------------------------------------------------------------------------------- + Name: OS_Posix_TaskAPI_Impl_Init + + Purpose: Initialize the Posix Task data structures + + ----------------------------------------------------------------------------------------*/ +int32 OS_Posix_TaskAPI_Impl_Init(void) +{ + int ret; + long ret_long; + int sig; + struct sched_param sched_param; + int sched_policy; + POSIX_PriorityLimits_t sched_fifo_limits; + bool sched_fifo_valid; + POSIX_PriorityLimits_t sched_rr_limits; + bool sched_rr_valid; + + /* Initialize Local Tables */ + memset(OS_impl_task_table, 0, sizeof(OS_impl_task_table)); + + /* Clear the "limits" structs otherwise the compiler may warn + * about possibly being used uninitialized (false warning) + */ + memset(&sched_fifo_limits, 0, sizeof(sched_fifo_limits)); + memset(&sched_rr_limits, 0, sizeof(sched_rr_limits)); + + /* + * Get number of configured cores + */ + OS_SharedGlobalVars.CoresConfigured = OS_TaskAffinityGetCoresConfigured_Impl(); + + /* + * Create the key used to store OSAL task IDs + */ + ret = pthread_key_create(&POSIX_GlobalVars.ThreadKey, NULL); + if (ret != 0) + { + OS_DEBUG("Error creating thread key: %s (%d)\n", strerror(ret), ret); + return OS_ERROR; + } + + /* + ** Disable Signals to parent thread and therefore all + ** child threads create will block all signals + ** Note: Timers will not work in the application unless + ** threads are spawned in OS_Application_Startup. + */ + sigfillset(&POSIX_GlobalVars.MaximumSigMask); + + /* + * Keep these signals unblocked so the process can be interrupted + */ + sigdelset(&POSIX_GlobalVars.MaximumSigMask, SIGINT); /* CTRL+C */ + sigdelset(&POSIX_GlobalVars.MaximumSigMask, SIGABRT); /* Abort */ + + /* + * One should not typically block ANY of the synchronous error + * signals, i.e. SIGSEGV, SIGFPE, SIGILL, SIGBUS + * + * The kernel generates these signals in response to hardware events + * and they get routed to the _specific thread_ that was executing when + * the problem occurred. + * + * While it is technically possible to block these signals, the result is + * undefined, and it makes debugging _REALLY_ hard. If the kernel ever does + * send one it means there really is a major problem, best to listen to it, + * and not ignore it. + */ + sigdelset(&POSIX_GlobalVars.MaximumSigMask, SIGSEGV); /* Segfault */ + sigdelset(&POSIX_GlobalVars.MaximumSigMask, SIGILL); /* Illegal instruction */ + sigdelset(&POSIX_GlobalVars.MaximumSigMask, SIGBUS); /* Bus Error */ + sigdelset(&POSIX_GlobalVars.MaximumSigMask, SIGFPE); /* Floating Point Exception */ + + /* + * Set the mask and store the original (default) mask in the POSIX_GlobalVars.NormalSigMask + */ + sigprocmask(SIG_SETMASK, &POSIX_GlobalVars.MaximumSigMask, &POSIX_GlobalVars.NormalSigMask); + + /* + * Add all "RT" signals into the POSIX_GlobalVars.NormalSigMask + * This will be used for the signal mask of the main thread + * (This way it will end up as the default/original signal mask plus all RT sigs) + */ + for (sig = SIGRTMIN; sig <= SIGRTMAX; ++sig) + { + sigaddset(&POSIX_GlobalVars.NormalSigMask, sig); + } + + /* + * SIGHUP is used to wake up the main thread when necessary, + * so make sure it is NOT in the set. + */ + sigdelset(&POSIX_GlobalVars.NormalSigMask, SIGHUP); + + /* + ** Install noop as the signal handler for SIGUP. + */ + signal(SIGHUP, OS_NoopSigHandler); + + /* + ** Raise the priority of the current (main) thread so that subsequent + ** application initialization will complete. This had previously been + ** done by the BSP and but it is moved here. + ** + ** This will only work if the user owning this process has permission + ** to create real time threads. Otherwise, the default priority will + ** be retained. Typically this is only the root user, but finer grained + ** permission controls are out there. So if it works, great, but if + ** a permission denied error is generated, that is OK too - this allows + ** easily debugging code as a normal user. + */ + ret = pthread_getschedparam(pthread_self(), &sched_policy, &sched_param); + if (ret == 0) + { + POSIX_GlobalVars.SelectedRtScheduler = sched_policy; /* Fallback/default */ + do + { + sched_fifo_valid = OS_Posix_GetSchedulerParams(SCHED_FIFO, &sched_fifo_limits); + sched_rr_valid = OS_Posix_GetSchedulerParams(SCHED_RR, &sched_rr_limits); + + /* + * If both policies are valid, choose the best. In general, FIFO is preferred + * since it is simpler. + * + * But, RR is preferred if mapping several OSAL priority levels into the + * same local priority level. For instance, if 2 OSAL tasks are created at priorities + * "2" and "1", both may get mapped to local priority 98, and if using FIFO then the + * task at priority "2" could run indefinitely, never letting priority "1" execute. + * + * This violates the original intent, which would be to have priority "1" preempt + * priority "2" tasks. RR is less bad since it at least guarantees both tasks some + * CPU time, + */ + if (sched_fifo_valid && sched_rr_valid) + { + /* + * If the spread from min->max is greater than what OSAL actually needs, + * then FIFO is the preferred scheduler. Must take into account one extra level + * for the root task. + */ + if ((sched_fifo_limits.PriorityMax - sched_fifo_limits.PriorityMin) > OS_MAX_TASK_PRIORITY) + { + sched_policy = SCHED_FIFO; + POSIX_GlobalVars.PriLimits = sched_fifo_limits; + } + else + { + sched_policy = SCHED_RR; + POSIX_GlobalVars.PriLimits = sched_rr_limits; + } + } + else if (sched_fifo_valid) + { + /* only FIFO is available */ + sched_policy = SCHED_FIFO; + POSIX_GlobalVars.PriLimits = sched_fifo_limits; + } + else if (sched_rr_valid) + { + /* only RR is available */ + sched_policy = SCHED_RR; + POSIX_GlobalVars.PriLimits = sched_rr_limits; + } + else + { + /* Nothing is valid, use default */ + break; + } + + /* + * This OSAL POSIX implementation will reserve the absolute highest priority + * for the root thread, which ultimately will just pend in sigsuspend() so + * it will not actually DO anything, except if sent a signal. This way, + * that thread will still be able to preempt a high-priority user thread that + * has gone awry (i.e. using 100% cpu in FIFO mode). + */ + sched_param.sched_priority = POSIX_GlobalVars.PriLimits.PriorityMax; + --POSIX_GlobalVars.PriLimits.PriorityMax; + + OS_DEBUG("Selected policy %d for RT tasks, root task = %d\n", + sched_policy, + (int)sched_param.sched_priority); + + /* + * If the spread from min->max is greater than what OSAL actually needs, + * then truncate it at the number of OSAL priorities. This will end up mapping 1:1. + * and leaving the highest priority numbers unused. + */ + if ((POSIX_GlobalVars.PriLimits.PriorityMax - POSIX_GlobalVars.PriLimits.PriorityMin) + > OS_MAX_TASK_PRIORITY) + { + POSIX_GlobalVars.PriLimits.PriorityMax = POSIX_GlobalVars.PriLimits.PriorityMin + OS_MAX_TASK_PRIORITY; + } + + ret = pthread_setschedparam(pthread_self(), sched_policy, &sched_param); + if (ret != 0) + { + OS_DEBUG("Could not setschedparam in main thread: %s (%d)\n", strerror(ret), ret); + break; + } + + /* + * Set the boolean to indicate that "setschedparam" worked -- + * This means that it is also expected to work for future calls. + */ + POSIX_GlobalVars.SelectedRtScheduler = sched_policy; + POSIX_GlobalVars.EnableTaskPriorities = true; + } while (0); + } + else + { + OS_DEBUG("Could not getschedparam in main thread: %s (%d)\n", strerror(ret), ret); + } + +#if !defined(OSAL_CONFIG_DEBUG_PERMISSIVE_MODE) + /* + * In strict (non-permissive) mode, if the task priority setting did not work, fail with an error. + * This would be used on a real target where it needs to be ensured that priorities are active + * and the "silent fallback" of debug mode operation is not desired. + */ + if (!POSIX_GlobalVars.EnableTaskPriorities) + { + return OS_ERROR; + } +#endif + + ret_long = sysconf(_SC_PAGESIZE); + if (ret_long < 0) + { + OS_DEBUG("Could not get page size via sysconf: %s\n", strerror(errno)); + return OS_ERROR; + } + POSIX_GlobalVars.PageSize = ret_long; + + return OS_SUCCESS; +} + +/*---------------------------------------------------------------- + * + * Purpose: Local helper routine, not part of OSAL API. + * + *-----------------------------------------------------------------*/ +int32 OS_Posix_InternalTaskCreate_Impl(pthread_t *pthr, + osal_priority_t priority, + osal_stackptr_t stackptr, + size_t stacksz, + PthreadFuncPtr_t entry, + void *entry_arg) +{ + int return_code = 0; + pthread_attr_t custom_attr; + struct sched_param priority_holder; + + /* + ** Initialize the pthread_attr structure. + ** The structure is used to set the stack and priority + */ + memset(&custom_attr, 0, sizeof(custom_attr)); + return_code = pthread_attr_init(&custom_attr); + if (return_code != 0) + { + OS_DEBUG("pthread_attr_init error in OS_TaskCreate: %s\n", strerror(return_code)); + return OS_ERROR; + } + + /* + ** Set the Stack Pointer and/or Size + */ + if (stackptr != OSAL_TASK_STACK_ALLOCATE) + { + return_code = pthread_attr_setstack(&custom_attr, stackptr, stacksz); + } + else + { + /* + * Adjust the stack size parameter, add budget for TCB/TLS overhead. + * Note that this budget can only be added when allocating the stack here, + * if the caller passed in a stack, they take responsibility for adding this. + */ + stacksz += OS_IMPL_STACK_EXTRA; + + stacksz += POSIX_GlobalVars.PageSize - 1; + stacksz -= stacksz % POSIX_GlobalVars.PageSize; + + return_code = pthread_attr_setstacksize(&custom_attr, stacksz); + } + + if (return_code != 0) + { + OS_DEBUG("Error configuring stack in OS_TaskCreate: %s\n", strerror(return_code)); + return OS_ERROR; + } + + /* + ** Set the thread to be joinable by default + */ + return_code = pthread_attr_setdetachstate(&custom_attr, PTHREAD_CREATE_JOINABLE); + if (return_code != 0) + { + OS_DEBUG("pthread_attr_setdetachstate error in OS_TaskCreate: %s\n", strerror(return_code)); + return OS_ERROR; + } + + /* + ** Test to see if the original main task scheduling priority worked. + ** If so, then also set the attributes for this task. Otherwise attributes + ** are left at default. + */ + if (POSIX_GlobalVars.EnableTaskPriorities) + { + /* + ** Set the scheduling inherit attribute to EXPLICIT + */ + return_code = pthread_attr_setinheritsched(&custom_attr, PTHREAD_EXPLICIT_SCHED); + if (return_code != 0) + { + OS_DEBUG("pthread_attr_setinheritsched error in OS_TaskCreate, errno = %s\n", strerror(return_code)); + return OS_ERROR; + } + + /* + ** Set the scheduling policy + ** The best policy is determined during initialization + */ + return_code = pthread_attr_setschedpolicy(&custom_attr, POSIX_GlobalVars.SelectedRtScheduler); + if (return_code != 0) + { + OS_DEBUG("pthread_attr_setschedpolity error in OS_TaskCreate: %s\n", strerror(return_code)); + return OS_ERROR; + } + + /* + ** Set priority + */ + return_code = pthread_attr_getschedparam(&custom_attr, &priority_holder); + if (return_code != 0) + { + OS_DEBUG("pthread_attr_getschedparam error in OS_TaskCreate: %s\n", strerror(return_code)); + return OS_ERROR; + } + + priority_holder.sched_priority = OS_PriorityRemap(priority); + return_code = pthread_attr_setschedparam(&custom_attr, &priority_holder); + if (return_code != 0) + { + OS_DEBUG("pthread_attr_setschedparam error in OS_TaskCreate: %s\n", strerror(return_code)); + return OS_ERROR; + } + } /* End if user is root */ + + /* + ** Create thread + */ + return_code = pthread_create(pthr, &custom_attr, entry, entry_arg); + if (return_code != 0) + { + OS_DEBUG("pthread_create error in OS_TaskCreate: %s\n", strerror(return_code)); + return OS_ERROR; + } + + /* + ** Free the resources that are no longer needed + ** Since the task is now running - pthread_create() was successful - + ** Do not treat anything bad that happens after this point as fatal. + ** The task is running, after all - better to leave well enough alone. + */ + return_code = pthread_attr_destroy(&custom_attr); + if (return_code != 0) + { + OS_DEBUG("pthread_attr_destroy error in OS_TaskCreate: %s\n", strerror(return_code)); + } + + return OS_SUCCESS; +} + +/*---------------------------------------------------------------- + * + * Purpose: Implemented per internal OSAL API + * See prototype for argument/return detail + * + *-----------------------------------------------------------------*/ +int32 OS_TaskCreate_Impl(const OS_object_token_t *token, uint32 flags) +{ + OS_VoidPtrValueWrapper_t arg; + int32 return_code; + OS_impl_task_internal_record_t *impl; + OS_task_internal_record_t *task; + + memset(&arg, 0, sizeof(arg)); + + /* cppcheck-suppress unreadVariable // intentional use of other union member */ + arg.id = OS_ObjectIdFromToken(token); + + task = OS_OBJECT_TABLE_GET(OS_task_table, *token); + impl = OS_OBJECT_TABLE_GET(OS_impl_task_table, *token); + + /* If task priorities are not enabled then override the user-supplied priority value */ + if (!POSIX_GlobalVars.EnableTaskPriorities) + { + task->priority = OS_MAX_TASK_PRIORITY; + } + + return_code = OS_Posix_InternalTaskCreate_Impl(&impl->id, + task->priority, + task->stack_pointer, + task->stack_size, + OS_PthreadTaskEntry, + arg.opaque_arg); + + return return_code; +} + +/*---------------------------------------------------------------- + * + * Purpose: Implemented per internal OSAL API + * See prototype for argument/return detail + * + *-----------------------------------------------------------------*/ +int32 OS_TaskDetach_Impl(const OS_object_token_t *token) +{ + OS_impl_task_internal_record_t *impl; + int ret; + + impl = OS_OBJECT_TABLE_GET(OS_impl_task_table, *token); + + ret = pthread_detach(impl->id); + + if (ret != 0) + { + OS_DEBUG("pthread_detach: Failed on Task ID = %lu, err = %s\n", + OS_ObjectIdToInteger(OS_ObjectIdFromToken(token)), + strerror(ret)); + return OS_ERROR; + } + + return OS_SUCCESS; +} + +/*---------------------------------------------------------------- + * + * Purpose: Implemented per internal OSAL API + * See prototype for argument/return detail + * + *-----------------------------------------------------------------*/ +int32 OS_TaskMatch_Impl(const OS_object_token_t *token) +{ + OS_impl_task_internal_record_t *impl; + + impl = OS_OBJECT_TABLE_GET(OS_impl_task_table, *token); + + if (pthread_equal(pthread_self(), impl->id) == 0) + { + return OS_ERROR; + } + + return OS_SUCCESS; +} + +/*---------------------------------------------------------------- + * + * Purpose: Implemented per internal OSAL API + * See prototype for argument/return detail + * + *-----------------------------------------------------------------*/ +int32 OS_TaskDelete_Impl(const OS_object_token_t *token) +{ + OS_impl_task_internal_record_t *impl; + void *retval; + int ret; + + impl = OS_OBJECT_TABLE_GET(OS_impl_task_table, *token); + + /* + ** Try to delete the task + ** If this fails, not much recourse - the only potential cause of failure + ** to cancel here is that the thread ID is invalid because it already exited itself, + ** and if that is true there is nothing wrong - everything is OK to continue normally. + */ + ret = pthread_cancel(impl->id); + if (ret != 0) + { + OS_DEBUG("pthread_cancel: Failed on Task ID = %lu, err = %s\n", + OS_ObjectIdToInteger(OS_ObjectIdFromToken(token)), + strerror(ret)); + + /* fall through (will still return OS_SUCCESS) */ + } + else + { + /* + * Note that "pthread_cancel" is a request - and successful return above + * only means that the cancellation request is pending. + * + * pthread_join() will wait until the thread has actually exited. + * + * This is important for CFE, as task deletion often occurs in + * conjunction with an application reload - which means the next + * call is likely to be OS_ModuleUnload(). So is critical that all + * tasks potentially executing code within that module have actually + * been stopped - not just pending cancellation. + */ + ret = pthread_join(impl->id, &retval); + if (ret != 0) + { + OS_DEBUG("pthread_join: Failed on Task ID = %lu, err = %s\n", + OS_ObjectIdToInteger(OS_ObjectIdFromToken(token)), + strerror(ret)); + } + } + return OS_SUCCESS; +} + +/*---------------------------------------------------------------- + * + * Purpose: Implemented per internal OSAL API + * See prototype for argument/return detail + * + *-----------------------------------------------------------------*/ +void OS_TaskExit_Impl(void) +{ + pthread_exit(NULL); +} + +/*---------------------------------------------------------------- + * + * Purpose: Implemented per internal OSAL API + * See prototype for argument/return detail + * + *-----------------------------------------------------------------*/ +int32 OS_TaskDelay_Impl(uint32 millisecond) +{ + struct timespec sleep_end; + int status; + + clock_gettime(CLOCK_MONOTONIC, &sleep_end); + sleep_end.tv_sec += millisecond / 1000; + sleep_end.tv_nsec += 1000000 * (millisecond % 1000); + + if (sleep_end.tv_nsec >= 1000000000) + { + sleep_end.tv_nsec -= 1000000000; + ++sleep_end.tv_sec; + } + + do + { + status = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleep_end, NULL); + } while (status == EINTR); + + if (status != 0) + { + return OS_ERROR; + } + else + { + return OS_SUCCESS; + } +} + +/*---------------------------------------------------------------- + * + * Purpose: Implemented per internal OSAL API + * See prototype for argument/return detail + * + *-----------------------------------------------------------------*/ +int32 OS_TaskSetPriority_Impl(const OS_object_token_t *token, osal_priority_t new_priority) +{ + int os_priority; + int ret; + + OS_impl_task_internal_record_t *impl; + + impl = OS_OBJECT_TABLE_GET(OS_impl_task_table, *token); + + if (POSIX_GlobalVars.EnableTaskPriorities) + { + /* Change OSAL priority into a priority that will work for this OS */ + os_priority = OS_PriorityRemap(new_priority); + + /* + ** Set priority + */ + ret = pthread_setschedprio(impl->id, os_priority); + if (ret != 0) + { + OS_DEBUG("pthread_setschedprio: Task ID = %lu, prio = %d, err = %s\n", + OS_ObjectIdToInteger(OS_ObjectIdFromToken(token)), + os_priority, + strerror(ret)); + return OS_ERROR; + } + } + + return OS_SUCCESS; +} + +/*---------------------------------------------------------------- + * + * Purpose: Implemented per internal OSAL API + * See prototype for argument/return detail + * + *-----------------------------------------------------------------*/ +int32 OS_TaskRegister_Impl(osal_id_t global_task_id) +{ + int32 return_code; + OS_VoidPtrValueWrapper_t arg; + int old_state; + int old_type; + + /* + * Set cancel state=ENABLED, type=DEFERRED + * This should be the default for new threads, but + * setting explicitly to be sure that a pthread_join() + * will work as expected in case this thread is deleted. + */ + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_state); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old_type); + + memset(&arg, 0, sizeof(arg)); + + /* cppcheck-suppress unreadVariable // intentional use of other union member */ + arg.id = global_task_id; + + return_code = pthread_setspecific(POSIX_GlobalVars.ThreadKey, arg.opaque_arg); + if (return_code == 0) + { + return_code = OS_SUCCESS; + } + else + { + OS_DEBUG("OS_TaskRegister_Impl failed during pthread_setspecific() error=%s\n", strerror(return_code)); + return_code = OS_ERROR; + } + + return return_code; +} + +/*---------------------------------------------------------------- + * + * Purpose: Implemented per internal OSAL API + * See prototype for argument/return detail + * + *-----------------------------------------------------------------*/ +osal_id_t OS_TaskGetId_Impl(void) +{ + OS_VoidPtrValueWrapper_t self_record; + + /* cppcheck-suppress unreadVariable // intentional use of other union member */ + self_record.opaque_arg = pthread_getspecific(POSIX_GlobalVars.ThreadKey); + + return self_record.id; +} + +/*---------------------------------------------------------------- + * + * Purpose: Implemented per internal OSAL API + * See prototype for argument/return detail + * + *-----------------------------------------------------------------*/ +int32 OS_TaskGetInfo_Impl(const OS_object_token_t *token, OS_task_prop_t *task_prop) +{ + return OS_SUCCESS; +} + +/*---------------------------------------------------------------- + * + * Purpose: Implemented per internal OSAL API + * See prototype for argument/return detail + * + *-----------------------------------------------------------------*/ +bool OS_TaskIdMatchSystemData_Impl(void *ref, const OS_object_token_t *token, const OS_common_record_t *obj) +{ + const pthread_t *target = (const pthread_t *)ref; + OS_impl_task_internal_record_t *impl; + + impl = OS_OBJECT_TABLE_GET(OS_impl_task_table, *token); + + return (pthread_equal(*target, impl->id) != 0); +} + +/*---------------------------------------------------------------- + * + * Purpose: Implemented per internal OSAL API + * See prototype for argument/return detail + * + *-----------------------------------------------------------------*/ +int32 OS_TaskValidateSystemData_Impl(const void *sysdata, size_t sysdata_size) +{ + if (sysdata == NULL || sysdata_size != sizeof(pthread_t)) + { + return OS_INVALID_POINTER; + } + return OS_SUCCESS; +} diff --git a/src/os/vxworks/src/os-impl-pthread-affinity.c b/src/os/vxworks/src/os-impl-pthread-affinity.c index 792a7d6c4..9aaa1153a 100644 --- a/src/os/vxworks/src/os-impl-pthread-affinity.c +++ b/src/os/vxworks/src/os-impl-pthread-affinity.c @@ -107,8 +107,7 @@ int32 OS_TaskAffinitySetAffinity_Impl(const OS_object_token_t *token, const OS_c /* * ---------------------------------------------------------------------- - * The OS_TaskAffinityGetAffinity() is an api call to get affinity to a task - * + * The OS_TaskAffinityGetAffinity_Impl() is an api call to get affinity from a task * Writes affinity to cpuset from task with provided task_id * ---------------------------------------------------------------------- */