sched: fix task_delete crash in SMP case

A testcase as following:

child_task()
{
  sleep(3);
}

main_task()
{
  while (1)
    {
      ret = task_create("child_task", child_task, );
      sleep(1);

      task_delete(ret);
    }
}

Root casuse:
task_delete hasn's cover the condition that the deleted-task
is justing running on the other CPU.

Fix:
Let the nxsched_remove_readytorun() do the real work

Signed-off-by: ligd <liguiding1@xiaomi.com>
This commit is contained in:
ligd 2023-01-16 18:20:22 +08:00 committed by Masayuki Ishikawa
parent f35f32d4ee
commit 0a88799bab
2 changed files with 12 additions and 66 deletions

View file

@ -63,15 +63,18 @@
#ifndef CONFIG_SMP
bool nxsched_remove_readytorun(FAR struct tcb_s *rtcb, bool merge)
{
FAR dq_queue_t *tasklist;
bool doswitch = false;
tasklist = TLIST_HEAD(rtcb);
/* Check if the TCB to be removed is at the head of the ready to run list.
* There is only one list, g_readytorun, and it always contains the
* currently running task. If we are removing the head of this list,
* then we are removing the currently active task.
*/
if (rtcb->blink == NULL)
if (rtcb->blink == NULL && TLIST_ISRUNNABLE(rtcb->task_state))
{
/* There must always be at least one task in the list (the IDLE task)
* after the TCB being removed.
@ -88,7 +91,7 @@ bool nxsched_remove_readytorun(FAR struct tcb_s *rtcb, bool merge)
* is always the g_readytorun list.
*/
dq_rem((FAR dq_entry_t *)rtcb, &g_readytorun);
dq_rem((FAR dq_entry_t *)rtcb, tasklist);
/* Since the TCB is not in any list, it is now invalid */

View file

@ -78,17 +78,8 @@
int nxtask_terminate(pid_t pid)
{
FAR struct tcb_s *dtcb;
FAR dq_queue_t *tasklist;
uint8_t task_state;
irqstate_t flags;
#ifdef CONFIG_SMP
int cpu;
#endif
int ret;
/* Make sure the task does not become ready-to-run while we are futzing
* with its TCB. Within the critical section, no new task may be started
* or terminated (even in the SMP case).
*/
flags = enter_critical_section();
@ -97,59 +88,15 @@ int nxtask_terminate(pid_t pid)
dtcb = nxsched_get_tcb(pid);
if (!dtcb)
{
/* This PID does not correspond to any known task */
ret = -ESRCH;
goto errout_with_lock;
leave_critical_section(flags);
return -ESRCH;
}
/* Verify our internal sanity */
/* Remove dtcb from tasklist, let remove_readtorun() do the job */
#ifdef CONFIG_SMP
DEBUGASSERT(dtcb->task_state < NUM_TASK_STATES);
#else
DEBUGASSERT(dtcb->task_state != TSTATE_TASK_RUNNING &&
dtcb->task_state < NUM_TASK_STATES);
#endif
/* Remove the task from the OS's task lists. We must be in a critical
* section and the must must not be running to do this.
*/
#ifdef CONFIG_SMP
/* In the SMP case, the thread may be running on another CPU. If that is
* the case, then we will pause the CPU that the thread is running on.
*/
cpu = nxsched_pause_cpu(dtcb);
/* Get the task list associated with the thread's state and CPU */
tasklist = TLIST_HEAD(dtcb, cpu);
#else
/* In the non-SMP case, we can be assured that the task to be terminated
* is not running. get the task list associated with the task state.
*/
tasklist = TLIST_HEAD(dtcb);
#endif
/* Remove the task from the task list */
dq_rem((FAR dq_entry_t *)dtcb, tasklist);
/* At this point, the TCB should no longer be accessible to the system */
#ifdef CONFIG_SMP
/* Resume the paused CPU (if any) */
if (cpu >= 0)
{
/* I am not yet sure how to handle a failure here. */
DEBUGVERIFY(up_cpu_resume(cpu));
}
#endif /* CONFIG_SMP */
task_state = dtcb->task_state;
nxsched_remove_readytorun(dtcb, false);
dtcb->task_state = task_state;
leave_critical_section(flags);
@ -172,8 +119,4 @@ int nxtask_terminate(pid_t pid)
/* Deallocate its TCB */
return nxsched_release_tcb(dtcb, dtcb->flags & TCB_FLAG_TTYPE_MASK);
errout_with_lock:
leave_critical_section(flags);
return ret;
}