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 ? */
if (envcp != NULL)
if (envcp != NULL && group->tg_envp == NULL)
{
/* Pre-emption must be disabled throughout the following because the
* environment may be shared.

View file

@ -40,9 +40,11 @@
#include "tls/tls.h"
/****************************************************************************
* Public Data
* Private Data
****************************************************************************/
static struct task_group_s g_kthread_group; /* Shared among kthreads */
/****************************************************************************
* Private Functions
****************************************************************************/
@ -119,9 +121,23 @@ int group_initialize(FAR struct task_tcb_s *tcb, uint8_t ttype)
DEBUGASSERT(tcb && !tcb->cmn.group);
/* Allocate the group structure and assign it to the TCB */
ttype &= TCB_FLAG_TTYPE_MASK;
group = &tcb->group;
/* 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;
}
#if defined(CONFIG_MM_KERNEL_HEAP)
/* 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.
*/
group->tg_pid = tcb->cmn.pid;
if (group != &g_kthread_group)
{
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 */
ret = files_duplist(&rtcb->group->tg_filelist,
&group->tg_filelist, actions, cloexec);
if (group != rtcb->group)
{
files_duplist(&rtcb->group->tg_filelist,
&group->tg_filelist, actions, cloexec);
}
if (ret >= 0 && actions != NULL)
{
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.
*/
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 */
@ -351,10 +351,11 @@ static void tasklist_initialize(void)
static void idle_task_initialize(void)
{
FAR struct task_tcb_s *tcb;
FAR struct tcb_s *tcb;
FAR dq_queue_t *tasklist;
int i;
memset(g_idletcb, 0, sizeof(g_idletcb));
for (i = 0; i < CONFIG_SMP_NCPUS; i++)
{
tcb = &g_idletcb[i];
@ -366,9 +367,8 @@ static void idle_task_initialize(void)
* that has pid == 0 and sched_priority == 0.
*/
memset(tcb, 0, sizeof(struct task_tcb_s));
tcb->cmn.pid = i;
tcb->cmn.task_state = TSTATE_TASK_RUNNING;
tcb->pid = i;
tcb->task_state = TSTATE_TASK_RUNNING;
/* Set the entry point. This is only for debug purposes. NOTE: that
* 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
if (i > 0)
{
tcb->cmn.start = nx_idle_trampoline;
tcb->cmn.entry.main = (main_t)nx_idle_trampoline;
tcb->start = nx_idle_trampoline;
tcb->entry.main = (main_t)nx_idle_trampoline;
}
else
#endif
{
tcb->cmn.start = nx_start;
tcb->cmn.entry.main = (main_t)nx_start;
tcb->start = nx_start;
tcb->entry.main = (main_t)nx_start;
}
/* 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
tcb->cmn.flags = (TCB_FLAG_TTYPE_KERNEL | TCB_FLAG_CPU_LOCKED);
tcb->cmn.cpu = i;
tcb->flags = (TCB_FLAG_TTYPE_KERNEL | TCB_FLAG_CPU_LOCKED);
tcb->cpu = i;
/* 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
@ -405,19 +405,19 @@ static void idle_task_initialize(void)
* the IDLE task.
*/
tcb->cmn.affinity =
tcb->affinity =
(cpu_set_t)(CONFIG_SMP_DEFAULT_CPUSET & SCHED_ALL_CPUS);
#else
tcb->cmn.flags = TCB_FLAG_TTYPE_KERNEL;
tcb->flags = TCB_FLAG_TTYPE_KERNEL;
#endif
#if CONFIG_TASK_NAME_SIZE > 0
/* Set the IDLE task name */
# 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
strlcpy(tcb->cmn.name, g_idlename, CONFIG_TASK_NAME_SIZE);
strlcpy(tcb->name, g_idlename, CONFIG_TASK_NAME_SIZE);
# endif
/* 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.
*/
g_idleargv[i][0] = tcb->cmn.name;
g_idleargv[i][0] = tcb->name;
#else
g_idleargv[i][0] = (FAR char *)g_idlename;
#endif /* CONFIG_TASK_NAME_SIZE */
@ -438,15 +438,15 @@ static void idle_task_initialize(void)
*/
#ifdef CONFIG_SMP
tasklist = TLIST_HEAD(&tcb->cmn, i);
tasklist = TLIST_HEAD(tcb, i);
#else
tasklist = TLIST_HEAD(&tcb->cmn);
tasklist = TLIST_HEAD(tcb);
#endif
dq_addfirst((FAR dq_entry_t *)tcb, tasklist);
/* 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)
{
FAR struct task_tcb_s *tcb;
FAR struct tcb_s *tcb;
int hashndx;
int i;
@ -471,16 +471,17 @@ static void idle_group_initialize(void)
tcb = &g_idletcb[i];
hashndx = PIDHASH(i);
g_pidhash[hashndx] = &tcb->cmn;
g_pidhash[hashndx] = tcb;
/* Allocate the IDLE group */
DEBUGVERIFY(group_initialize(tcb, tcb->cmn.flags));
tcb->cmn.group->tg_info->ta_argv = &g_idleargv[i][0];
DEBUGVERIFY(
group_initialize((FAR struct task_tcb_s *)tcb, tcb->flags));
tcb->group->tg_info->ta_argv = &g_idleargv[i][0];
/* Initialize the task join */
nxtask_joininit(&tcb->cmn);
nxtask_joininit(tcb);
#ifdef CONFIG_SMP
/* 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)
{
DEBUGVERIFY(up_cpu_idlestack(i, &tcb->cmn,
CONFIG_IDLETHREAD_STACKSIZE));
DEBUGVERIFY(up_cpu_idlestack(i, tcb, CONFIG_IDLETHREAD_STACKSIZE));
}
#endif
/* Initialize the processor-specific portion of the TCB */
up_initial_state(&tcb->cmn);
up_initial_state(tcb);
/* Initialize the thread local storage */
tls_init_info(&tcb->cmn);
tls_init_info(tcb);
/* Complete initialization of the IDLE group. Suppress retention
* of child status in the IDLE group.
*/
group_postinitialize(tcb);
tcb->cmn.group->tg_flags = GROUP_FLAG_NOCLDWAIT |
GROUP_FLAG_PRIVILEGED;
group_postinitialize((FAR struct task_tcb_s *)tcb);
tcb->group->tg_flags = GROUP_FLAG_NOCLDWAIT | GROUP_FLAG_PRIVILEGED;
}
}
@ -727,7 +726,7 @@ void nx_start(void)
/* 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 */
@ -737,7 +736,8 @@ void nx_start(void)
{
/* 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
{

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 char * const argv[], FAR char * const envp[])
{
FAR struct task_tcb_s *tcb;
FAR struct tcb_s *tcb;
pid_t pid;
int ret;
/* 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)
{
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 */
tcb->cmn.flags = ttype | TCB_FLAG_FREE_TCB;
tcb->flags = ttype | TCB_FLAG_FREE_TCB;
/* Initialize the task */
ret = nxtask_init(tcb, name, priority, stack_addr, stack_size,
entry, argv, envp, NULL);
ret = nxtask_init((FAR struct task_tcb_s *)tcb, name, priority,
stack_addr, stack_size, entry, argv, envp, NULL);
if (ret < OK)
{
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 */
pid = tcb->cmn.pid;
pid = tcb->pid;
/* Activate the task */
nxtask_activate(&tcb->cmn);
nxtask_activate(tcb);
return pid;
}

View file

@ -514,6 +514,7 @@ static void nxtask_setup_name(FAR struct task_tcb_s *tcb,
*
* Input Parameters:
* 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
* terminated with a NULL argv[] value. If no parameters are
* 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 char * const argv[])
{
uint8_t ttype = tcb->cmn.flags & TCB_FLAG_TTYPE_MASK;
FAR char **stackargv;
FAR char *str;
size_t strtablen;
@ -630,8 +632,11 @@ static int nxtask_setup_stackargs(FAR struct task_tcb_s *tcb,
stackargv[argc + 1] = NULL;
tcb->cmn.group->tg_info->ta_argc = argc;
tcb->cmn.group->tg_info->ta_argv = stackargv;
if (ttype != TCB_FLAG_TTYPE_KERNEL)
{
tcb->cmn.group->tg_info->ta_argc = argc;
tcb->cmn.group->tg_info->ta_argv = stackargv;
}
return OK;
}

View file

@ -66,17 +66,18 @@
void nxtask_start(void)
{
FAR struct tcb_s *tcb = this_task();
uint8_t ttype = tcb->flags & TCB_FLAG_TTYPE_MASK;
#ifdef CONFIG_SCHED_STARTHOOK
FAR struct task_tcb_s *ttcb = (FAR struct task_tcb_s *)tcb;
#endif
int exitcode = EXIT_FAILURE;
FAR char **argv;
int argc;
DEBUGASSERT((tcb->flags & TCB_FLAG_TTYPE_MASK) != \
TCB_FLAG_TTYPE_PTHREAD);
DEBUGASSERT(ttype != TCB_FLAG_TTYPE_PTHREAD);
#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 */
@ -87,33 +88,32 @@ void nxtask_start(void)
/* Execute the start hook if one has been registered */
#ifdef CONFIG_SCHED_STARTHOOK
if (ttcb->starthook != NULL)
if (ttype != TCB_FLAG_TTYPE_KERNEL && ttcb->starthook != NULL)
{
ttcb->starthook(ttcb->starthookarg);
}
#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
* 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.
*/
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
{
#ifdef CONFIG_BUILD_FLAT
nxtask_startup(tcb->entry.main, argc,
tcb->group->tg_info->ta_argv);
nxtask_startup(tcb->entry.main, argc, argv);
#else
up_task_start(tcb->entry.main, argc,
tcb->group->tg_info->ta_argv);
up_task_start(tcb->entry.main, argc, argv);
#endif
}