SMP: Add up_cpustart and up_cpurestart protoypes; fix some problems in sched_addreadytorun; first cut at SMP version of up_mergepending.

This commit is contained in:
Gregory Nutt 2016-02-12 16:13:14 -06:00
parent 85f663a8ee
commit 64b3ce8775
4 changed files with 218 additions and 97 deletions

View File

@ -1711,6 +1711,51 @@ int up_cpundx(void);
int up_cpustart(int cpu, main_t idletask, pid_t pid); int up_cpustart(int cpu, main_t idletask, pid_t pid);
#endif #endif
/****************************************************************************
* Name: up_cpustop
*
* Description:
* Save the state of the current task at the head of the
* g_assignedtasks[cpu] task list and then stop the CPU.
*
* This function is called by the OS when the logic executing on one CPU
* needs to modify the state of the g_assignedtasks[cpu] list for another
* CPU.
*
* Input Parameters:
* cpu - The index of the CPU to be stopped/
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
#ifdef CONFIG_SMP
int up_cpustop(int cpu);
#endif
/****************************************************************************
* Name: up_cpurestart
*
* Description:
* Restart the cpu, restoring the state of the task at the head of the
* g_assignedtasks[cpu] list.
*
* This function is called after up_cpustop in order resume operation of
* the CPU after modifying its g_assignedtasks[cpu] list.
*
* Input Parameters:
* cpu - The index of the CPU being re-started.
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
#ifdef CONFIG_SMP
int up_cpurestart(int cpu);
#endif
/**************************************************************************** /****************************************************************************
* Name: up_romgetc * Name: up_romgetc
* *

View File

@ -46,26 +46,6 @@
#include "sched/sched.h" #include "sched/sched.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Type Declarations
****************************************************************************/
/****************************************************************************
* Public Data
****************************************************************************/
/****************************************************************************
* Private Variables
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/

View File

@ -170,15 +170,27 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
FAR struct tcb_s *rtcb; FAR struct tcb_s *rtcb;
FAR struct tcb_s *next; FAR struct tcb_s *next;
FAR dq_queue_t *tasklist; FAR dq_queue_t *tasklist;
uint8_t minprio;
int task_state; int task_state;
int cpu; int cpu;
bool switched; bool switched;
bool doswitch; bool doswitch;
/* Check if the blocked TCB is already assigned to a CPU */
if ((btcb->flags & TCB_FLAG_CPU_ASSIGNED) != 0)
{
/* Yes.. that that is the CPU we must use */
cpu = btcb->cpu;
rtcb = (FAR struct tcb_s *)g_assignedtasks[cpu].head;
}
else
{
uint8_t minprio;
int i; int i;
/* Find the CPU that is executing the lowest priority task (possibly its /* Otherwise, find the CPU that is executing the lowest priority task
* IDLE task). * (possibly its IDLE task).
*/ */
rtcb = NULL; rtcb = NULL;
@ -190,18 +202,17 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
FAR struct tcb_s *candidate = FAR struct tcb_s *candidate =
(FAR struct tcb_s *)g_assignedtasks[i].head; (FAR struct tcb_s *)g_assignedtasks[i].head;
/* If this thread is executing its IDLE task, the use it. The IDLE /* If this thread is executing its IDLE task, the use it. The
* task is always the last task in the assigned task list. * IDLE task is always the last task in the assigned task list.
*/ */
if (candidate->flink == NULL) if (candidate->flink == NULL)
{ {
/* The IDLE task should always be assigned to this CPU and have a /* The IDLE task should always be assigned to this CPU and
* priority zero. * have a priority of zero.
*/ */
DEBUGASSERT((candidate->flags & TCB_FLAG_CPU_ASSIGNED) != 0 && DEBUGASSERT(candidate->sched_priority == 0);
candidate->sched_priority == 0);
rtcb = candidate; rtcb = candidate;
cpu = i; cpu = i;
@ -215,6 +226,7 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
cpu = i; cpu = i;
} }
} }
}
/* Determine the desired new task state. First, if the new task priority /* Determine the desired new task state. First, if the new task priority
* is higher then the priority of the lowest priority, running task, then * is higher then the priority of the lowest priority, running task, then
@ -258,20 +270,46 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
btcb->task_state = TSTATE_TASK_PENDING; btcb->task_state = TSTATE_TASK_PENDING;
doswitch = false; doswitch = false;
} }
else else if (task_state == TSTATE_TASK_READYTORUN)
{ {
/* The new btcb was added either (1) in the middle of the assigned
* task list (the btcb->cpu field is already valid) or (2) was
* added to the ready-to-run list (the btcb->cpu field does not
* matter). Either way, it won't be running.
*
* Add the task to the ready-to-run (but not running) task list
*/
(void)sched_addprioritized(btcb, (FAR dq_queue_t *)&g_readytorun);
btcb->task_state = TSTATE_TASK_READYTORUN;
doswitch = false;
}
else /* (task_state == TSTATE_TASK_ASSIGNED || task_state == TSTATE_TASK_RUNNING) */
{
int me = this_cpu();
/* If we are modifying some assigned task list other than our own, we will
* need to stop that CPU.
*/
if (cpu != me)
{
DEBUGVERIFY(up_cpustop(cpu));
}
/* Add the task to the list corresponding to the selected state /* Add the task to the list corresponding to the selected state
* and check if a context switch will occur * and check if a context switch will occur
*/ */
tasklist = TLIST_HEAD(task_state, cpu); tasklist = (FAR dq_queue_t *)g_assignedtasks[cpu].head;
switched = sched_addprioritized(btcb, tasklist); switched = sched_addprioritized(btcb, tasklist);
/* If the selected task was the g_assignedtasks[] list, then a context /* If the selected task was the g_assignedtasks[] list, then a context
* swith will occur. * swith will occur.
*/ */
if (switched && task_state != TSTATE_TASK_READYTORUN) if (switched)
{ {
/* The new btcb was added at the head of the ready-to-run list. It /* The new btcb was added at the head of the ready-to-run list. It
* is now the new active task! * is now the new active task!
@ -310,7 +348,7 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
* lifting machinery. * lifting machinery.
*/ */
next = (FAR dq_queue_t *)btcb->flink; next = (FAR struct tcb_s *)btcb->flink;
ASSERT(!rtcb->lockcount && next != NULL); ASSERT(!rtcb->lockcount && next != NULL);
if ((btcb->flags & TCB_FLAG_CPU_ASSIGNED) != 0) if ((btcb->flags & TCB_FLAG_CPU_ASSIGNED) != 0)
@ -328,22 +366,27 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
*/ */
next->task_state = TSTATE_TASK_READYTORUN; next->task_state = TSTATE_TASK_READYTORUN;
(void)sched_addprioritized(btcb, &g_readytorun); (void)sched_addprioritized(btcb,
(FAR dq_queue_t *)&g_readytorun);
} }
doswitch = true; doswitch = true;
} }
else else
{ {
/* The new btcb was added either (1) in the middle of the assigned /* No context switch. Assign the CPU and set the assigned state */
* task list (the btcb->cpu field is already valid) or (2) was
* added to the ready-to-run list (the btcb->cpu field does not
* matter). Either way, it won't be running.
*/
DEBUGASSERT(task_state != TSTATE_TASK_RUNNING); DEBUGASSERT(task_state == TSTATE_TASK_ASSIGNED);
btcb->task_state = TSTATE_TASK_READYTORUN; btcb->cpu = cpu;
btcb->task_state = TSTATE_TASK_ASSIGNED;
}
/* All done, restart the other that CPU. */
if (cpu != me)
{
DEBUGVERIFY(up_cpurestart(cpu));
doswitch = false; doswitch = false;
} }
} }

View File

@ -1,7 +1,7 @@
/**************************************************************************** /****************************************************************************
* sched/sched/sched_mergepending.c * sched/sched/sched_mergepending.c
* *
* Copyright (C) 2007, 2009, 2012 Gregory Nutt. All rights reserved. * Copyright (C) 2007, 2009, 2012, 2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org> * Author: Gregory Nutt <gnutt@nuttx.org>
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -55,13 +55,14 @@
* *
* Description: * Description:
* This function merges the prioritized g_pendingtasks list into the * This function merges the prioritized g_pendingtasks list into the
* prioritized g_readytorun task list. * prioritized ready-to-run task list.
* *
* Inputs: * Inputs:
* None * None
* *
* Return Value: * Return Value:
* true if the head of the g_readytorun task list has changed. * true if the head of the ready-to-run task list has changed indicating
* a context switch is needed.
* *
* Assumptions: * Assumptions:
* - The caller has established a critical section before * - The caller has established a critical section before
@ -72,75 +73,82 @@
* *
****************************************************************************/ ****************************************************************************/
#ifndef CONFIG_SMP
bool sched_mergepending(void) bool sched_mergepending(void)
{ {
FAR struct tcb_s *pndtcb; FAR struct tcb_s *ptcb;
FAR struct tcb_s *pndnext; FAR struct tcb_s *pnext;
FAR struct tcb_s *rtrtcb; FAR struct tcb_s *rtcb;
FAR struct tcb_s *rtrprev; FAR struct tcb_s *rprev;
bool ret = false; bool ret = false;
/* Initialize the inner search loop */ /* Initialize the inner search loop */
rtrtcb = this_task(); rtcb = this_task();
/* Process every TCB in the g_pendingtasks list */ /* Process every TCB in the g_pendingtasks list */
for (pndtcb = (FAR struct tcb_s *)g_pendingtasks.head; pndtcb; pndtcb = pndnext) for (ptcb = (FAR struct tcb_s *)g_pendingtasks.head;
ptcb;
ptcb = pnext)
{ {
pndnext = pndtcb->flink; pnext = ptcb->flink;
/* Search the g_readytorun list to find the location to insert the /* REVISIT: Why don't we just remove the ptcb from pending task list
* new pndtcb. Each is list is maintained in ascending sched_priority * and call sched_addreadytorun?
*/
/* Search the ready-to-run list to find the location to insert the
* new ptcb. Each is list is maintained in ascending sched_priority
* order. * order.
*/ */
for (; for (;
(rtrtcb && pndtcb->sched_priority <= rtrtcb->sched_priority); (rtcb && ptcb->sched_priority <= rtcb->sched_priority);
rtrtcb = rtrtcb->flink); rtcb = rtcb->flink);
/* Add the pndtcb to the spot found in the list. Check if the /* Add the ptcb to the spot found in the list. Check if the
* pndtcb goes at the ends of the g_readytorun list. This would be * ptcb goes at the ends of the ready-to-run list. This would be
* error condition since the idle test must always be at the end of * error condition since the idle test must always be at the end of
* the g_readytorun list! * the ready-to-run list!
*/ */
ASSERT(rtrtcb); ASSERT(rtcb);
/* The pndtcb goes just before rtrtcb */ /* The ptcb goes just before rtcb */
rtrprev = rtrtcb->blink; rprev = rtcb->blink;
if (!rtrprev) if (!rprev)
{ {
/* Special case: Inserting pndtcb at the head of the list */ /* Special case: Inserting ptcb at the head of the list */
/* Inform the instrumentation layer that we are switching tasks */ /* Inform the instrumentation layer that we are switching tasks */
sched_note_switch(rtrtcb, pndtcb); sched_note_switch(rtcb, ptcb);
/* Then insert at the head of the list */ /* Then insert at the head of the list */
pndtcb->flink = rtrtcb; ptcb->flink = rtcb;
pndtcb->blink = NULL; ptcb->blink = NULL;
rtrtcb->blink = pndtcb; rtcb->blink = ptcb;
g_readytorun.head = (FAR dq_entry_t *)pndtcb; g_readytorun.head = (FAR dq_entry_t *)ptcb;
rtrtcb->task_state = TSTATE_TASK_READYTORUN; rtcb->task_state = TSTATE_TASK_READYTORUN;
pndtcb->task_state = TSTATE_TASK_RUNNING; ptcb->task_state = TSTATE_TASK_RUNNING;
ret = true; ret = true;
} }
else else
{ {
/* Insert in the middle of the list */ /* Insert in the middle of the list */
pndtcb->flink = rtrtcb; ptcb->flink = rtcb;
pndtcb->blink = rtrprev; ptcb->blink = rprev;
rtrprev->flink = pndtcb; rprev->flink = ptcb;
rtrtcb->blink = pndtcb; rtcb->blink = ptcb;
pndtcb->task_state = TSTATE_TASK_READYTORUN; ptcb->task_state = TSTATE_TASK_READYTORUN;
} }
/* Set up for the next time through */ /* Set up for the next time through */
rtrtcb = pndtcb; rtcb = ptcb;
} }
/* Mark the input list empty */ /* Mark the input list empty */
@ -150,3 +158,48 @@ bool sched_mergepending(void)
return ret; return ret;
} }
#endif /* !CONFIG_SMP */
/****************************************************************************
* Name: sched_mergepending
*
* Description:
* This function merges the prioritized g_pendingtasks list into the
* prioritized ready-to-run task list.
*
* Inputs:
* None
*
* Return Value:
* true if the head of the ready-to-run task list has changed indicating
* a context switch is needed.
*
* Assumptions:
* - The caller has established a critical section before
* calling this function (calling sched_lock() first is NOT
* a good idea -- use irqsave()).
* - The caller handles the condition that occurs if the
* the head of the sched_mergTSTATE_TASK_PENDINGs is changed.
*
****************************************************************************/
#ifdef CONFIG_SMP
bool sched_mergepending(void)
{
FAR struct tcb_s *ptcb;
bool ret = false;
/* Remove and process every TCB in the g_pendingtasks list */
for (ptcb = (FAR struct tcb_s *)dq_remfirst((FAR dq_queue_t *)&g_pendingtasks);
ptcb != NULL;
ptcb = (FAR struct tcb_s *)dq_remfirst((FAR dq_queue_t *)&g_pendingtasks))
{
/* Add the pending task to the correct ready-to-run list */
ret |= sched_addreadytorun(ptcb);
}
return ret;
}
#endif /* CONFIG_SMP */