ARMv7-A/i.MX6: Add logic to handle allocation of CPU IDLE thread stacks more efficiently

This commit is contained in:
Gregory Nutt 2016-05-13 11:39:42 -06:00
parent d14d84c1a6
commit faca2fb1e7
9 changed files with 364 additions and 27 deletions

View File

@ -45,6 +45,8 @@
#include "cp15.h"
#include "sctlr.h"
#include "mmu.h"
#include "smp.h"
#include "chip.h"
#include "up_internal.h"
@ -114,7 +116,7 @@ __cpu1_start:
b .Lcpu_start
.Lcpu1_stackpointer:
.long .Lcpu1_stackbottom
.long .Lcpu1_stacktop
.size __cpu1_start, .-__cpu1_start
#if CONFIG_SMP_NCPUS > 2
@ -132,7 +134,7 @@ __cpu2_start:
b .Lcpu_start
.Lcpu2_stackpointer:
.long .Lcpu2_stackbottom
.long .Lcpu2_stacktop
.size __cpu2_start, .-__cpu2_start
#if CONFIG_SMP_NCPUS > 3
@ -150,7 +152,7 @@ __cpu3_start:
b .Lcpu_start
.Lcpu3_stackpointer:
.long .Lcpu3_stackbottom
.long .Lcpu3_stacktop
.size __cpu3_start, .-__cpu3_start
#if CONFIG_SMP_NCPUS > 4
@ -168,7 +170,7 @@ __cpu3_start:
* Common CPUn startup logic (n > 0)
*
* On input:
* SP = Set to bottom of CPU IDLE stack (virtual)
* SP = Set to top of CPU IDLE stack (virtual)
* R5 = CPU number
*
****************************************************************************/
@ -395,7 +397,7 @@ __cpu3_start:
* absolute addresses; this is not position independent.
*
* On input:
* SP = Set to bottom of CPU IDLE stack (virtual)
* SP = Set to top of CPU IDLE stack (virtual)
* R5 = CPU number
*
****************************************************************************/
@ -438,7 +440,7 @@ __cpu3_start:
#ifdef CONFIG_STACK_COLORATION
.type .Lstkinit, %object
.Lstkinit:
.long ((CONFIG_IDLETHREAD_STACKSIZE + 7) & ~7) >> 2)
.long SMP_STACK_WORDS
.long STACK_COLOR /* Stack coloration word */
.size .Lstkinit, . -.Lstkinit
#endif
@ -451,30 +453,33 @@ __cpu3_start:
#if CONFIG_SMP_NCPUS > 1
.align 8
.type .Lcpu1_idlestack, object
.globl g_cpu1_idlestack
.type g_cpu1_idlestack, object
.Lcpu1_idlestack:
.space ((CONFIG_SMP_IDLETHREAD_STACKSIZE + 7) & ~7)
.Lcpu1_stackbottom:
.size .Lcpu1_idlestack, .Lcpu1_stackbottom-.Lcpu1_idlestack
g_cpu1_idlestack:
.space SMP_STACK_SIZE
.Lcpu1_stacktop:
.size g_cpu1_idlestack, .Lcpu1_stacktop-g_cpu1_idlestack
#if CONFIG_SMP_NCPUS > 2
.align 8
.type .Lcpu2_idlestack, object
.globl g_cpu2_idlestack
.type g_cpu2_idlestack, object
.Lcpu2_idlestack:
.space ((CONFIG_SMP_IDLETHREAD_STACKSIZE + 7) & ~7)
.Lcpu2_stackbottom:
.size .Lcpu2_idlestack, .Lcpu2_stackbottom-.Lcpu2_idlestack
g_cpu2_idlestack:
.space SMP_STACK_SIZE
.Lcpu2_stacktop:
.size g_cpu2_idlestack, .Lcpu2_stacktop-g_cpu2_idlestack
#if CONFIG_SMP_NCPUS > 3
.align 8
.type .Lcpu3_idlestack, object
.globl g_cpu3_idlestack
.type g_cpu3_idlestack, object
.Lcpu3_idlestack:
.space ((CONFIG_SMP_IDLETHREAD_STACKSIZE + 7) & ~7)
.Lcpu3_stackbottom:
.size .Lcpu3_idlestack, .Lcpu3_stackbottom-.Lcpu3_idlestack
g_cpu3_idlestack:
.space SMP_STACK_SIZE
.Lcpu3_stacktop:
.size g_cpu3_idlestack, .Lcpu3_stacktop-g_cpu3_idlestack
#if CONFIG_SMP_NCPUS > 4
# error This logic needs to extended for CONFIG_SMP_NCPUS > 4

View File

@ -0,0 +1,145 @@
/****************************************************************************
* arch/arm/src/armv7-a/arm_cpuidlestack.c
*
* Copyright (C) 2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <nuttx/arch.h>
#include <nuttx/sched.h>
#include "smp.h"
#include "up_internal.h"
#if defined(CONFIG_SMP) && CONFIG_SMP_NCPUS > 1
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Stack alignment macros */
#define STACK_ISALIGNED(a) ((uintptr_t)(a) & ~SMP_STACK_MASK)
/****************************************************************************
* Private Data
****************************************************************************/
static FAR const uint32_t *g_cpu_stackalloc[CONFIG_SMP_NCPUS] =
{
0
, g_cpu1_idlestack
#if CONFIG_SMP_NCPUS > 2
, g_cpu2_idlestack
#if CONFIG_SMP_NCPUS > 3
, g_cpu3_idlestack
#endif /* CONFIG_SMP_NCPUS > 3 */
#endif /* CONFIG_SMP_NCPUS > 2 */
};
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_cpu_idlestack
*
* Description:
* Allocate a stack for the CPU[n] IDLE task (n > 0) if appropriate and
* setup up stack-related information in the IDLE task's TCB. This
* function is always called before up_cpu_start(). This function is
* only called for the CPU's initial IDLE task; up_create_task is used for
* all normal tasks, pthreads, and kernel threads for all CPUs.
*
* The initial IDLE task is a special case because the CPUs can be started
* in different wans in different environments:
*
* 1. The CPU may already have been started and waiting in a low power
* state for up_cpu_start(). In this case, the IDLE thread's stack
* has already been allocated and is already in use. Here
* up_cpu_idlestack() only has to provide information about the
* already allocated stack.
*
* 2. The CPU may be disabled but started when up_cpu_start() is called.
* In this case, a new stack will need to be created for the IDLE
* thread and this function is then equivalent to:
*
* up_create_stack(tcb, stack_size, TCB_FLAG_TTYPE_KERNEL);
*
* The following TCB fields must be initialized by this function:
*
* - adj_stack_size: Stack size after adjustment for hardware, processor,
* etc. This value is retained only for debug purposes.
* - stack_alloc_ptr: Pointer to allocated stack
* - adj_stack_ptr: Adjusted stack_alloc_ptr for HW. The initial value of
* the stack pointer.
*
* Inputs:
* - cpu: CPU index that indicates which CPU the IDLE task is
* being created for.
* - tcb: The TCB of new CPU IDLE task
* - stack_size: The requested stack size for the IDLE task. At least
* this much must be allocated. This should be
* CONFIG_SMP_STACK_SIZE.
*
****************************************************************************/
int up_cpu_idlestack(int cpu, FAR struct tcb_s *tcb, size_t stack_size)
{
uintptr_t stack_alloc;
uintptr_t top_of_stack;
DEBUGASSERT(cpu > 0 && cpu < CONFIG_SMP_NCPUS && tcb != NULL &&
stack_size <= SMP_STACK_SIZE);
/* Get the top of the stack */
stack_alloc = (uintptr_t)g_cpu_stackalloc[cpu];
DEBUGASSERT(stack_alloc != 0 && STACK_ISALIGNED(stack_alloc));
top_of_stack = stack_alloc + SMP_STACK_TOP;
tcb->adj_stack_size = SMP_STACK_SIZE;
tcb->stack_alloc_ptr = (FAR uint32_t *)stack_alloc;
tcb->adj_stack_ptr = (FAR uint32_t *)top_of_stack;
return OK;
}
#endif /* CONFIG_SMP && CONFIG_SMP_NCPUS > 1 */

View File

@ -45,6 +45,40 @@
#ifdef CONFIG_SMP
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* ARM requires at least a 4-byte stack alignment. For use with EABI and
* floating point, the stack must be aligned to 8-byte addresses. We will
* always use the EABI stack alignment
*/
#define SMP_STACK_ALIGNMENT 8
#define SMP_STACK_MASK 7
#define SMP_STACK_SIZE ((CONFIG_SMP_IDLETHREAD_STACKSIZE + 7) & ~7)
#define SMP_STACK_WORDS (SMP_STACK_SIZE >> 2)
#define SMP_STACK_TOP (SMP_STACK_SIZE - 8)
/****************************************************************************
* Public Data
****************************************************************************/
#ifndef __ASSEMBLY__
#if CONFIG_SMP_NCPUS > 1
extern uint32_t g_cpu1_idlestack[SMP_STACK_WORDS];
#if CONFIG_SMP_NCPUS > 2
extern uint32_t g_cpu2_idlestack[SMP_STACK_WORDS];
#if CONFIG_SMP_NCPUS > 3
extern uint32_t g_cpu3_idlestack[SMP_STACK_WORDS];
#if CONFIG_SMP_NCPUS > 4
# error This logic needs to extended for CONFIG_SMP_NCPUS > 4
#endif /* CONFIG_SMP_NCPUS > 4 */
#endif /* CONFIG_SMP_NCPUS > 3 */
#endif /* CONFIG_SMP_NCPUS > 2 */
#endif /* CONFIG_SMP_NCPUS > 1 */
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
@ -106,5 +140,6 @@ void __cpu3_start(void);
void arm_cpu_boot(int cpu);
#endif /* __ASSEMBLY__ */
#endif /* CONFIG_SMP */
#endif /* __ARCH_ARM_SRC_ARMV7_A_SMP_H */

View File

@ -80,7 +80,7 @@ CMN_CSRCS += arm_schedulesigaction.c arm_sigdeliver.c arm_syscall.c
CMN_CSRCS += arm_unblocktask.c arm_undefinedinsn.c
ifeq ($(CONFIG_SMP),y)
CMN_CSRCS += arm_cpuindex.c arm_cpustart.c arm_cpupause.c
CMN_CSRCS += arm_cpuindex.c arm_cpustart.c arm_cpupause.c arm_cpuidlestack.c
endif
ifeq ($(CONFIG_DEBUG_IRQ),y)

View File

@ -309,7 +309,7 @@ static void imx_copyvectorblock(void)
#ifndef CONFIG_IMX6_WDT
static inline void imx_wdtdisable(void)
{
# warning REVISIT WDT initialization
/* REVISIT: WDT initialization */
}
#else
# define imx_wdtdisable()

View File

@ -70,7 +70,7 @@ ifeq ($(CONFIG_SPINLOCK),y)
endif
ifeq ($(CONFIG_SMP),y)
CSRCS += up_smpsignal.c up_smphook.c
CSRCS += up_smpsignal.c up_smphook.c up_cpuidlestack.c
HOSTSRCS += up_simsmp.c
endif

View File

@ -0,0 +1,108 @@
/****************************************************************************
* arch/sim/src/up_cpuidlestack.c
*
* Copyright (C) 2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <nuttx/arch.h>
#include <nuttx/sched.h>
#include "up_internal.h"
#ifdef CONFIG_SMP
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_cpu_idlestack
*
* Description:
* Allocate a stack for the CPU[n] IDLE task (n > 0) if appropriate and
* setup up stack-related information in the IDLE task's TCB. This
* function is always called before up_cpu_start(). This function is
* only called for the CPU's initial IDLE task; up_create_task is used for
* all normal tasks, pthreads, and kernel threads for all CPUs.
*
* The initial IDLE task is a special case because the CPUs can be started
* in different wans in different environments:
*
* 1. The CPU may already have been started and waiting in a low power
* state for up_cpu_start(). In this case, the IDLE thread's stack
* has already been allocated and is already in use. Here
* up_cpu_idlestack() only has to provide information about the
* already allocated stack.
*
* 2. The CPU may be disabled but started when up_cpu_start() is called.
* In this case, a new stack will need to be created for the IDLE
* thread and this function is then equivalent to:
*
* up_create_stack(tcb, stack_size, TCB_FLAG_TTYPE_KERNEL);
*
* The following TCB fields must be initialized by this function:
*
* - adj_stack_size: Stack size after adjustment for hardware, processor,
* etc. This value is retained only for debug purposes.
* - stack_alloc_ptr: Pointer to allocated stack
* - adj_stack_ptr: Adjusted stack_alloc_ptr for HW. The initial value of
* the stack pointer.
*
* Inputs:
* - cpu: CPU index that indicates which CPU the IDLE task is
* being created for.
* - tcb: The TCB of new CPU IDLE task
* - stack_size: The requested stack size for the IDLE task. At least
* this much must be allocated. This should be
* CONFIG_SMP_IDLETHREAD_STACKSIZE.
*
****************************************************************************/
int up_cpu_idlestack(int cpu, FAR struct tcb_s *tcb, size_t stack_size)
{
/* REVISIT: I don't think anything is needed here */
tcb->adj_stack_size = stack_size;
tcb->stack_alloc_ptr = NULL;
tcb->adj_stack_ptr = NULL;
return OK;
}
#endif /* CONFIG_SMP */

View File

@ -1644,7 +1644,7 @@ int up_timer_start(FAR const struct timespec *ts);
*
* Description:
* Return the TLS information structure for the currently executing thread.
* When TLS is enabled, up_createstack() will align allocated stacks to
* When TLS is enabled, up_create_stack() will align allocated stacks to
* the TLS_STACK_ALIGN value. An instance of the following structure will
* be implicitly positioned at the "lower" end of the stack. Assuming a
* "push down" stack, this is at the "far" end of the stack (and can be
@ -1722,6 +1722,51 @@ int up_cpu_index(void);
# define up_cpu_index() (0)
#endif
/****************************************************************************
* Name: up_cpu_idlestack
*
* Description:
* Allocate a stack for the CPU[n] IDLE task (n > 0) if appropriate and
* setup up stack-related information in the IDLE task's TCB. This
* function is always called before up_cpu_start(). This function is
* only called for the CPU's initial IDLE task; up_create_task is used for
* all normal tasks, pthreads, and kernel threads for all CPUs.
*
* The initial IDLE task is a special case because the CPUs can be started
* in different wans in different environments:
*
* 1. The CPU may already have been started and waiting in a low power
* state for up_cpu_start(). In this case, the IDLE thread's stack
* has already been allocated and is already in use. Here
* up_cpu_idlestack() only has to provide information about the
* already allocated stack.
*
* 2. The CPU may be disabled but started when up_cpu_start() is called.
* In this case, a new stack will need to be created for the IDLE
* thread and this function is then equivalent to:
*
* up_create_stack(tcb, stack_size, TCB_FLAG_TTYPE_KERNEL);
*
* The following TCB fields must be initialized by this function:
*
* - adj_stack_size: Stack size after adjustment for hardware, processor,
* etc. This value is retained only for debug purposes.
* - stack_alloc_ptr: Pointer to allocated stack
* - adj_stack_ptr: Adjusted stack_alloc_ptr for HW. The initial value of
* the stack pointer.
*
* Inputs:
* - cpu: CPU index that indicates which CPU the IDLE task is
* being created for.
* - tcb: The TCB of new CPU IDLE task
* - stack_size: The requested stack size for the IDLE task. At least
* this much must be allocated. This should be
* CONFIG_SMP_IDLETHREAD_STACKSIZE.
*
****************************************************************************/
int up_cpu_idlestack(int cpu, FAR struct tcb_s *tcb, size_t stack_size);
/****************************************************************************
* Name: up_cpu_start
*

View File

@ -203,8 +203,7 @@ int os_smp_start(void)
FAR struct tcb_s *tcb = current_task(cpu);
DEBUGASSERT(tcb != NULL);
ret = up_create_stack(tcb, CONFIG_SMP_IDLETHREAD_STACKSIZE,
TCB_FLAG_TTYPE_KERNEL);
ret = up_cpu_idlestack(cpu, tcb, CONFIG_SMP_IDLETHREAD_STACKSIZE);
if (ret < 0)
{
sdbg("ERROR: Failed to allocate stack for CPU%d\n", cpu);