sched/tcb: use shared group for kthreads

Kthreads can share the group data so that to reduce overheads.
This implements shared kthread group via:

- use `tcb_s` instead of `task_tcb_s` for kthreads
- use `g_kthread_group` when creating kthreads
- use stackargs to start tasks and kthreads

see pull/12320 for test logs.

Signed-off-by: Yanfeng Liu <yfliu2008@qq.com>
This commit is contained in:
Yanfeng Liu 2024-05-17 06:11:52 +08:00 committed by Xiang Xiao
parent 1d169fe325
commit 8a8c1f943e
7 changed files with 90 additions and 61 deletions

View file

@ -72,7 +72,7 @@ int env_dup(FAR struct task_group_s *group, FAR char * const *envcp)
/* Is there an environment ? */ /* Is there an environment ? */
if (envcp != NULL) if (envcp != NULL && group->tg_envp == NULL)
{ {
/* Pre-emption must be disabled throughout the following because the /* Pre-emption must be disabled throughout the following because the
* environment may be shared. * environment may be shared.

View file

@ -40,9 +40,11 @@
#include "tls/tls.h" #include "tls/tls.h"
/**************************************************************************** /****************************************************************************
* Public Data * Private Data
****************************************************************************/ ****************************************************************************/
static struct task_group_s g_kthread_group; /* Shared among kthreads */
/**************************************************************************** /****************************************************************************
* Private Functions * Private Functions
****************************************************************************/ ****************************************************************************/
@ -119,9 +121,23 @@ int group_initialize(FAR struct task_tcb_s *tcb, uint8_t ttype)
DEBUGASSERT(tcb && !tcb->cmn.group); DEBUGASSERT(tcb && !tcb->cmn.group);
/* Allocate the group structure and assign it to the TCB */ ttype &= TCB_FLAG_TTYPE_MASK;
/* Initialize group pointer and assign to TCB */
if (ttype == TCB_FLAG_TTYPE_KERNEL)
{
group = &g_kthread_group;
tcb->cmn.group = group;
if (group->tg_info)
{
return OK;
}
}
else
{
group = &tcb->group; group = &tcb->group;
}
#if defined(CONFIG_MM_KERNEL_HEAP) #if defined(CONFIG_MM_KERNEL_HEAP)
/* If this group is being created for a privileged thread, then all /* If this group is being created for a privileged thread, then all
@ -222,5 +238,8 @@ void group_postinitialize(FAR struct task_tcb_s *tcb)
* task has exited. * task has exited.
*/ */
if (group != &g_kthread_group)
{
group->tg_pid = tcb->cmn.pid; group->tg_pid = tcb->cmn.pid;
}
} }

View file

@ -79,8 +79,12 @@ int group_setuptaskfiles(FAR struct task_tcb_s *tcb,
/* Duplicate the parent task's file descriptors */ /* Duplicate the parent task's file descriptors */
ret = files_duplist(&rtcb->group->tg_filelist, if (group != rtcb->group)
{
files_duplist(&rtcb->group->tg_filelist,
&group->tg_filelist, actions, cloexec); &group->tg_filelist, actions, cloexec);
}
if (ret >= 0 && actions != NULL) if (ret >= 0 && actions != NULL)
{ {
ret = spawn_file_actions(&tcb->cmn, actions); ret = spawn_file_actions(&tcb->cmn, actions);

View file

@ -206,7 +206,7 @@ uint8_t g_nx_initstate; /* See enum nx_initstate_e */
* bringing up the rest of the system. * bringing up the rest of the system.
*/ */
static struct task_tcb_s g_idletcb[CONFIG_SMP_NCPUS]; static struct tcb_s g_idletcb[CONFIG_SMP_NCPUS];
/* This is the name of the idle task */ /* This is the name of the idle task */
@ -351,10 +351,11 @@ static void tasklist_initialize(void)
static void idle_task_initialize(void) static void idle_task_initialize(void)
{ {
FAR struct task_tcb_s *tcb; FAR struct tcb_s *tcb;
FAR dq_queue_t *tasklist; FAR dq_queue_t *tasklist;
int i; int i;
memset(g_idletcb, 0, sizeof(g_idletcb));
for (i = 0; i < CONFIG_SMP_NCPUS; i++) for (i = 0; i < CONFIG_SMP_NCPUS; i++)
{ {
tcb = &g_idletcb[i]; tcb = &g_idletcb[i];
@ -366,9 +367,8 @@ static void idle_task_initialize(void)
* that has pid == 0 and sched_priority == 0. * that has pid == 0 and sched_priority == 0.
*/ */
memset(tcb, 0, sizeof(struct task_tcb_s)); tcb->pid = i;
tcb->cmn.pid = i; tcb->task_state = TSTATE_TASK_RUNNING;
tcb->cmn.task_state = TSTATE_TASK_RUNNING;
/* Set the entry point. This is only for debug purposes. NOTE: that /* Set the entry point. This is only for debug purposes. NOTE: that
* the start_t entry point is not saved. That is acceptable, however, * the start_t entry point is not saved. That is acceptable, however,
@ -379,14 +379,14 @@ static void idle_task_initialize(void)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
if (i > 0) if (i > 0)
{ {
tcb->cmn.start = nx_idle_trampoline; tcb->start = nx_idle_trampoline;
tcb->cmn.entry.main = (main_t)nx_idle_trampoline; tcb->entry.main = (main_t)nx_idle_trampoline;
} }
else else
#endif #endif
{ {
tcb->cmn.start = nx_start; tcb->start = nx_start;
tcb->cmn.entry.main = (main_t)nx_start; tcb->entry.main = (main_t)nx_start;
} }
/* Set the task flags to indicate that this is a kernel thread and, if /* Set the task flags to indicate that this is a kernel thread and, if
@ -394,8 +394,8 @@ static void idle_task_initialize(void)
*/ */
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
tcb->cmn.flags = (TCB_FLAG_TTYPE_KERNEL | TCB_FLAG_CPU_LOCKED); tcb->flags = (TCB_FLAG_TTYPE_KERNEL | TCB_FLAG_CPU_LOCKED);
tcb->cmn.cpu = i; tcb->cpu = i;
/* Set the affinity mask to allow the thread to run on all CPUs. No, /* Set the affinity mask to allow the thread to run on all CPUs. No,
* this IDLE thread can only run on its assigned CPU. That is * this IDLE thread can only run on its assigned CPU. That is
@ -405,19 +405,19 @@ static void idle_task_initialize(void)
* the IDLE task. * the IDLE task.
*/ */
tcb->cmn.affinity = tcb->affinity =
(cpu_set_t)(CONFIG_SMP_DEFAULT_CPUSET & SCHED_ALL_CPUS); (cpu_set_t)(CONFIG_SMP_DEFAULT_CPUSET & SCHED_ALL_CPUS);
#else #else
tcb->cmn.flags = TCB_FLAG_TTYPE_KERNEL; tcb->flags = TCB_FLAG_TTYPE_KERNEL;
#endif #endif
#if CONFIG_TASK_NAME_SIZE > 0 #if CONFIG_TASK_NAME_SIZE > 0
/* Set the IDLE task name */ /* Set the IDLE task name */
# ifdef CONFIG_SMP # ifdef CONFIG_SMP
snprintf(tcb->cmn.name, CONFIG_TASK_NAME_SIZE, "CPU%d IDLE", i); snprintf(tcb->name, CONFIG_TASK_NAME_SIZE, "CPU%d IDLE", i);
# else # else
strlcpy(tcb->cmn.name, g_idlename, CONFIG_TASK_NAME_SIZE); strlcpy(tcb->name, g_idlename, CONFIG_TASK_NAME_SIZE);
# endif # endif
/* Configure the task name in the argument list. The IDLE task does /* Configure the task name in the argument list. The IDLE task does
@ -428,7 +428,7 @@ static void idle_task_initialize(void)
* stack and there is no support that yet. * stack and there is no support that yet.
*/ */
g_idleargv[i][0] = tcb->cmn.name; g_idleargv[i][0] = tcb->name;
#else #else
g_idleargv[i][0] = (FAR char *)g_idlename; g_idleargv[i][0] = (FAR char *)g_idlename;
#endif /* CONFIG_TASK_NAME_SIZE */ #endif /* CONFIG_TASK_NAME_SIZE */
@ -438,15 +438,15 @@ static void idle_task_initialize(void)
*/ */
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
tasklist = TLIST_HEAD(&tcb->cmn, i); tasklist = TLIST_HEAD(tcb, i);
#else #else
tasklist = TLIST_HEAD(&tcb->cmn); tasklist = TLIST_HEAD(tcb);
#endif #endif
dq_addfirst((FAR dq_entry_t *)tcb, tasklist); dq_addfirst((FAR dq_entry_t *)tcb, tasklist);
/* Mark the idle task as the running task */ /* Mark the idle task as the running task */
g_running_tasks[i] = &tcb->cmn; g_running_tasks[i] = tcb;
} }
} }
@ -460,7 +460,7 @@ static void idle_task_initialize(void)
static void idle_group_initialize(void) static void idle_group_initialize(void)
{ {
FAR struct task_tcb_s *tcb; FAR struct tcb_s *tcb;
int hashndx; int hashndx;
int i; int i;
@ -471,16 +471,17 @@ static void idle_group_initialize(void)
tcb = &g_idletcb[i]; tcb = &g_idletcb[i];
hashndx = PIDHASH(i); hashndx = PIDHASH(i);
g_pidhash[hashndx] = &tcb->cmn; g_pidhash[hashndx] = tcb;
/* Allocate the IDLE group */ /* Allocate the IDLE group */
DEBUGVERIFY(group_initialize(tcb, tcb->cmn.flags)); DEBUGVERIFY(
tcb->cmn.group->tg_info->ta_argv = &g_idleargv[i][0]; group_initialize((FAR struct task_tcb_s *)tcb, tcb->flags));
tcb->group->tg_info->ta_argv = &g_idleargv[i][0];
/* Initialize the task join */ /* Initialize the task join */
nxtask_joininit(&tcb->cmn); nxtask_joininit(tcb);
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/* Create a stack for all CPU IDLE threads (except CPU0 which already /* Create a stack for all CPU IDLE threads (except CPU0 which already
@ -489,26 +490,24 @@ static void idle_group_initialize(void)
if (i > 0) if (i > 0)
{ {
DEBUGVERIFY(up_cpu_idlestack(i, &tcb->cmn, DEBUGVERIFY(up_cpu_idlestack(i, tcb, CONFIG_IDLETHREAD_STACKSIZE));
CONFIG_IDLETHREAD_STACKSIZE));
} }
#endif #endif
/* Initialize the processor-specific portion of the TCB */ /* Initialize the processor-specific portion of the TCB */
up_initial_state(&tcb->cmn); up_initial_state(tcb);
/* Initialize the thread local storage */ /* Initialize the thread local storage */
tls_init_info(&tcb->cmn); tls_init_info(tcb);
/* Complete initialization of the IDLE group. Suppress retention /* Complete initialization of the IDLE group. Suppress retention
* of child status in the IDLE group. * of child status in the IDLE group.
*/ */
group_postinitialize(tcb); group_postinitialize((FAR struct task_tcb_s *)tcb);
tcb->cmn.group->tg_flags = GROUP_FLAG_NOCLDWAIT | tcb->group->tg_flags = GROUP_FLAG_NOCLDWAIT | GROUP_FLAG_PRIVILEGED;
GROUP_FLAG_PRIVILEGED;
} }
} }
@ -727,7 +726,7 @@ void nx_start(void)
/* Announce that the CPU0 IDLE task has started */ /* Announce that the CPU0 IDLE task has started */
sched_note_start(&g_idletcb[0].cmn); sched_note_start(&g_idletcb[0]);
/* Initialize stdio for the IDLE task of each CPU */ /* Initialize stdio for the IDLE task of each CPU */
@ -737,7 +736,8 @@ void nx_start(void)
{ {
/* Clone stdout, stderr, stdin from the CPU0 IDLE task. */ /* Clone stdout, stderr, stdin from the CPU0 IDLE task. */
DEBUGVERIFY(group_setuptaskfiles(&g_idletcb[i], NULL, true)); DEBUGVERIFY(group_setuptaskfiles(
(FAR struct task_tcb_s *)&g_idletcb[i], NULL, true));
} }
else else
{ {

View file

@ -76,13 +76,14 @@ int nxthread_create(FAR const char *name, uint8_t ttype, int priority,
FAR void *stack_addr, int stack_size, main_t entry, FAR void *stack_addr, int stack_size, main_t entry,
FAR char * const argv[], FAR char * const envp[]) FAR char * const argv[], FAR char * const envp[])
{ {
FAR struct task_tcb_s *tcb; FAR struct tcb_s *tcb;
pid_t pid; pid_t pid;
int ret; int ret;
/* Allocate a TCB for the new task. */ /* Allocate a TCB for the new task. */
tcb = kmm_zalloc(sizeof(struct task_tcb_s)); tcb = kmm_zalloc(ttype == TCB_FLAG_TTYPE_KERNEL ?
sizeof(struct tcb_s) : sizeof(struct task_tcb_s));
if (!tcb) if (!tcb)
{ {
serr("ERROR: Failed to allocate TCB\n"); serr("ERROR: Failed to allocate TCB\n");
@ -91,12 +92,12 @@ int nxthread_create(FAR const char *name, uint8_t ttype, int priority,
/* Setup the task type */ /* Setup the task type */
tcb->cmn.flags = ttype | TCB_FLAG_FREE_TCB; tcb->flags = ttype | TCB_FLAG_FREE_TCB;
/* Initialize the task */ /* Initialize the task */
ret = nxtask_init(tcb, name, priority, stack_addr, stack_size, ret = nxtask_init((FAR struct task_tcb_s *)tcb, name, priority,
entry, argv, envp, NULL); stack_addr, stack_size, entry, argv, envp, NULL);
if (ret < OK) if (ret < OK)
{ {
kmm_free(tcb); kmm_free(tcb);
@ -105,11 +106,11 @@ int nxthread_create(FAR const char *name, uint8_t ttype, int priority,
/* Get the assigned pid before we start the task */ /* Get the assigned pid before we start the task */
pid = tcb->cmn.pid; pid = tcb->pid;
/* Activate the task */ /* Activate the task */
nxtask_activate(&tcb->cmn); nxtask_activate(tcb);
return pid; return pid;
} }

View file

@ -514,6 +514,7 @@ static void nxtask_setup_name(FAR struct task_tcb_s *tcb,
* *
* Input Parameters: * Input Parameters:
* tcb - Address of the new task's TCB * tcb - Address of the new task's TCB
* name - Name of the new task
* argv - A pointer to an array of input parameters. The array should be * argv - A pointer to an array of input parameters. The array should be
* terminated with a NULL argv[] value. If no parameters are * terminated with a NULL argv[] value. If no parameters are
* required, argv may be NULL. * required, argv may be NULL.
@ -527,6 +528,7 @@ static int nxtask_setup_stackargs(FAR struct task_tcb_s *tcb,
FAR const char *name, FAR const char *name,
FAR char * const argv[]) FAR char * const argv[])
{ {
uint8_t ttype = tcb->cmn.flags & TCB_FLAG_TTYPE_MASK;
FAR char **stackargv; FAR char **stackargv;
FAR char *str; FAR char *str;
size_t strtablen; size_t strtablen;
@ -630,8 +632,11 @@ static int nxtask_setup_stackargs(FAR struct task_tcb_s *tcb,
stackargv[argc + 1] = NULL; stackargv[argc + 1] = NULL;
if (ttype != TCB_FLAG_TTYPE_KERNEL)
{
tcb->cmn.group->tg_info->ta_argc = argc; tcb->cmn.group->tg_info->ta_argc = argc;
tcb->cmn.group->tg_info->ta_argv = stackargv; tcb->cmn.group->tg_info->ta_argv = stackargv;
}
return OK; return OK;
} }

View file

@ -66,17 +66,18 @@
void nxtask_start(void) void nxtask_start(void)
{ {
FAR struct tcb_s *tcb = this_task(); FAR struct tcb_s *tcb = this_task();
uint8_t ttype = tcb->flags & TCB_FLAG_TTYPE_MASK;
#ifdef CONFIG_SCHED_STARTHOOK #ifdef CONFIG_SCHED_STARTHOOK
FAR struct task_tcb_s *ttcb = (FAR struct task_tcb_s *)tcb; FAR struct task_tcb_s *ttcb = (FAR struct task_tcb_s *)tcb;
#endif #endif
int exitcode = EXIT_FAILURE; int exitcode = EXIT_FAILURE;
FAR char **argv;
int argc; int argc;
DEBUGASSERT((tcb->flags & TCB_FLAG_TTYPE_MASK) != \ DEBUGASSERT(ttype != TCB_FLAG_TTYPE_PTHREAD);
TCB_FLAG_TTYPE_PTHREAD);
#ifdef CONFIG_SIG_DEFAULT #ifdef CONFIG_SIG_DEFAULT
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_KERNEL) if (ttype != TCB_FLAG_TTYPE_KERNEL)
{ {
/* Set up default signal actions for NON-kernel thread */ /* Set up default signal actions for NON-kernel thread */
@ -87,33 +88,32 @@ void nxtask_start(void)
/* Execute the start hook if one has been registered */ /* Execute the start hook if one has been registered */
#ifdef CONFIG_SCHED_STARTHOOK #ifdef CONFIG_SCHED_STARTHOOK
if (ttcb->starthook != NULL) if (ttype != TCB_FLAG_TTYPE_KERNEL && ttcb->starthook != NULL)
{ {
ttcb->starthook(ttcb->starthookarg); ttcb->starthook(ttcb->starthookarg);
} }
#endif #endif
/* Add program name */ /* Take args from stack, as group is shared for kthreads */
argc = tcb->group->tg_info->ta_argc + 1; argv = nxsched_get_stackargs(tcb);
for (argc = 0; argv && argv[argc]; argc++);
/* Call the 'main' entry point passing argc and argv. In the kernel build /* Call the 'main' entry point passing argc and argv. In the kernel build
* this has to be handled differently if we are starting a user-space task; * this has to be handled differently if we are starting a user-space task;
* we have to switch to user-mode before calling the task. * we have to switch to user-mode before calling the task.
*/ */
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL) if (ttype == TCB_FLAG_TTYPE_KERNEL)
{ {
exitcode = tcb->entry.main(argc, tcb->group->tg_info->ta_argv); exitcode = tcb->entry.main(argc, argv);
} }
else else
{ {
#ifdef CONFIG_BUILD_FLAT #ifdef CONFIG_BUILD_FLAT
nxtask_startup(tcb->entry.main, argc, nxtask_startup(tcb->entry.main, argc, argv);
tcb->group->tg_info->ta_argv);
#else #else
up_task_start(tcb->entry.main, argc, up_task_start(tcb->entry.main, argc, argv);
tcb->group->tg_info->ta_argv);
#endif #endif
} }