nuttx/sched/task/task_exit.c
hujun5 508c5889d7 sched: change nxsched_islocked_global to nxsched_islocked_tcb
reason:
1 To improve efficiency, we mimic Linux's behavior where preemption disabling is only applicable to the current CPU and does not affect other CPUs.
2 In the future, we will implement "spinlock+sched_lock", and use it extensively. Under such circumstances, if preemption is still globally disabled, it will seriously impact the scheduling efficiency.
3 We have removed g_cpu_lockset and used irqcount in order to eliminate the dependency of schedlock on critical sections in the future, simplify the logic, and further enhance the performance of sched_lock.
4 We set lockcount to 1 in order to lock scheduling on all CPUs during startup, without the need to provide additional functions to disable scheduling on other CPUs.
5 Cpu1~n must wait for cpu0 to enter the idle state before enabling scheduling because it prevents CPUs1~n from competing with cpu0 for the memory manager mutex, which could cause the cpu0 idle task to enter a wait state and trigger an assert.

size nuttx
before:
   text    data     bss     dec     hex filename
 265396   51057   63646  380099   5ccc3 nuttx
after:
   text    data     bss     dec     hex filename
 265184   51057   63642  379883   5cbeb nuttx

size -216

Configuring NuttX and compile:
$ ./tools/configure.sh -l qemu-armv8a:nsh_smp
$ make
Running with qemu
$ qemu-system-aarch64 -cpu cortex-a53 -smp 4 -nographic \
   -machine virt,virtualization=on,gic-version=3 \
   -net none -chardev stdio,id=con,mux=on -serial chardev:con \
   -mon chardev=con,mode=readline -kernel ./nuttx

Signed-off-by: hujun5 <hujun5@xiaomi.com>
2024-10-10 17:57:30 +08:00

176 lines
5.2 KiB
C

/****************************************************************************
* sched/task/task_exit.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sched.h>
#include <debug.h>
#include "sched/sched.h"
#ifdef CONFIG_SMP
# include "irq/irq.h"
#endif
#include "signal/signal.h"
#include "task/task.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxtask_exit
*
* Description:
* This is a part of the logic used to implement _exit(). The full
* implementation of _exit() is architecture-dependent. The _exit()
* function also implements the bottom half of exit() and pthread_exit().
*
* This function causes the currently running task (i.e., the task at the
* head of the ready-to-run list) to cease to exist. This function should
* never be called from normal user code, but only from the architecture-
* specific implementation of exit.
*
* Threads/tasks could also be terminated via pthread_cancel,
* task_delete(), and task_restart(). In the last two cases, the
* task will be terminated as though exit() were called.
*
* Input Parameters:
* None
*
* Returned Value:
* OK on success; or ERROR on failure
*
* Assumptions:
* Executing within a critical section established by the caller.
*
****************************************************************************/
int nxtask_exit(void)
{
FAR struct tcb_s *dtcb;
FAR struct tcb_s *rtcb;
int ret;
#ifdef CONFIG_SMP
int cpu;
/* Get the current CPU. By assumption, we are within a critical section
* and, hence, the CPU index will remain stable.
*
* Avoid using this_task() because it may assume a state that is not
* appropriate for an exiting task.
*/
cpu = this_cpu();
dtcb = current_task(cpu);
#else
dtcb = this_task();
#endif
#if CONFIG_TASK_NAME_SIZE > 0
sinfo("%s pid=%d,TCB=%p\n", dtcb->name,
#else
sinfo("pid=%d,TCB=%p\n",
#endif
dtcb->pid, dtcb);
/* Update scheduler parameters */
nxsched_suspend_scheduler(dtcb);
/* Remove the TCB of the current task from the ready-to-run list. A
* context switch will definitely be necessary -- that must be done
* by the architecture-specific logic.
*
* nxsched_remove_readytorun will mark the task at the head of the
* ready-to-run with state == TSTATE_TASK_RUNNING
*/
nxsched_remove_self(dtcb);
/* Get the new task at the head of the ready to run list */
#ifdef CONFIG_SMP
rtcb = current_task(cpu);
#else
rtcb = this_task();
#endif
/* NOTE: nxsched_resume_scheduler() was moved to up_exit()
* because the global IRQ control for SMP should be deferred until
* context switching, otherwise, the context switching would be done
* without a critical section
*/
/* We are now in a bad state -- the head of the ready to run task list
* does not correspond to the thread that is running. Disabling pre-
* emption on this TCB and marking the new ready-to-run task as not
* running.
*
* We disable pre-emption here by directly incrementing the lockcount
* (vs. calling sched_lock()).
*/
rtcb->lockcount++;
rtcb->task_state = TSTATE_TASK_READYTORUN;
/* Move the TCB to the specified blocked task list and delete it. Calling
* nxtask_terminate with non-blocking true will suppress atexit() and
* on-exit() calls and will cause buffered I/O to fail to be flushed. The
* former is required _exit() behavior; the latter is optional _exit()
* behavior.
*/
nxsched_add_blocked(dtcb, TSTATE_TASK_INACTIVE);
#ifdef CONFIG_SMP
/* NOTE:
* During nxtask_terminate(), enter_critical_section() will be called
* to deallocate tcb. However, this would acquire g_cpu_irqlock if
* rtcb->irqcount = 0, event though we are in critical section.
* To prevent from acquiring, increment rtcb->irqcount here.
*/
rtcb->irqcount++;
#endif
ret = nxtask_terminate(dtcb->pid);
#ifdef CONFIG_SMP
rtcb->irqcount--;
#endif
rtcb->task_state = TSTATE_TASK_RUNNING;
/* Decrement the lockcount on rctb. */
rtcb->lockcount--;
return ret;
}