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);
#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
*

View File

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

View File

@ -170,49 +170,61 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
FAR struct tcb_s *rtcb;
FAR struct tcb_s *next;
FAR dq_queue_t *tasklist;
uint8_t minprio;
int task_state;
int cpu;
bool switched;
bool doswitch;
int i;
/* Find the CPU that is executing the lowest priority task (possibly its
* IDLE task).
*/
/* Check if the blocked TCB is already assigned to a CPU */
rtcb = NULL;
minprio = SCHED_PRIORITY_MAX;
cpu = 0;
for (i = 0; i < CONFIG_SMP_NCPUS; i++)
if ((btcb->flags & TCB_FLAG_CPU_ASSIGNED) != 0)
{
FAR struct tcb_s *candidate =
(FAR struct tcb_s *)g_assignedtasks[i].head;
/* Yes.. that that is the CPU we must use */
/* If this thread is executing its IDLE task, the use it. The IDLE
* task is always the last task in the assigned task list.
cpu = btcb->cpu;
rtcb = (FAR struct tcb_s *)g_assignedtasks[cpu].head;
}
else
{
uint8_t minprio;
int i;
/* Otherwise, find the CPU that is executing the lowest priority task
* (possibly its IDLE task).
*/
if (candidate->flink == NULL)
rtcb = NULL;
minprio = SCHED_PRIORITY_MAX;
cpu = 0;
for (i = 0; i < CONFIG_SMP_NCPUS; i++)
{
/* The IDLE task should always be assigned to this CPU and have a
* priority zero.
FAR struct tcb_s *candidate =
(FAR struct tcb_s *)g_assignedtasks[i].head;
/* If this thread is executing its IDLE task, the use it. The
* IDLE task is always the last task in the assigned task list.
*/
DEBUGASSERT((candidate->flags & TCB_FLAG_CPU_ASSIGNED) != 0 &&
candidate->sched_priority == 0);
if (candidate->flink == NULL)
{
/* The IDLE task should always be assigned to this CPU and
* have a priority of zero.
*/
rtcb = candidate;
cpu = i;
break;
}
else if (candidate->sched_priority < minprio)
{
DEBUGASSERT(candidate->sched_priority > 0);
DEBUGASSERT(candidate->sched_priority == 0);
rtcb = candidate;
cpu = i;
rtcb = candidate;
cpu = i;
break;
}
else if (candidate->sched_priority < minprio)
{
DEBUGASSERT(candidate->sched_priority > 0);
rtcb = candidate;
cpu = i;
}
}
}
@ -258,20 +270,46 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
btcb->task_state = TSTATE_TASK_PENDING;
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
* 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);
/* If the selected task was the g_assignedtasks[] list, then a context
* 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
* is now the new active task!
@ -310,7 +348,7 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
* lifting machinery.
*/
next = (FAR dq_queue_t *)btcb->flink;
next = (FAR struct tcb_s *)btcb->flink;
ASSERT(!rtcb->lockcount && next != NULL);
if ((btcb->flags & TCB_FLAG_CPU_ASSIGNED) != 0)
@ -328,23 +366,28 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
*/
next->task_state = TSTATE_TASK_READYTORUN;
(void)sched_addprioritized(btcb, &g_readytorun);
(void)sched_addprioritized(btcb,
(FAR dq_queue_t *)&g_readytorun);
}
doswitch = true;
}
else
{
/* 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.
*/
/* No context switch. Assign the CPU and set the assigned state */
DEBUGASSERT(task_state != TSTATE_TASK_RUNNING);
DEBUGASSERT(task_state == TSTATE_TASK_ASSIGNED);
btcb->task_state = TSTATE_TASK_READYTORUN;
doswitch = false;
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;
}
}

View File

@ -1,7 +1,7 @@
/****************************************************************************
* 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>
*
* Redistribution and use in source and binary forms, with or without
@ -55,13 +55,14 @@
*
* Description:
* This function merges the prioritized g_pendingtasks list into the
* prioritized g_readytorun task list.
* prioritized ready-to-run task list.
*
* Inputs:
* None
*
* 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:
* - The caller has established a critical section before
@ -72,75 +73,82 @@
*
****************************************************************************/
#ifndef CONFIG_SMP
bool sched_mergepending(void)
{
FAR struct tcb_s *pndtcb;
FAR struct tcb_s *pndnext;
FAR struct tcb_s *rtrtcb;
FAR struct tcb_s *rtrprev;
FAR struct tcb_s *ptcb;
FAR struct tcb_s *pnext;
FAR struct tcb_s *rtcb;
FAR struct tcb_s *rprev;
bool ret = false;
/* Initialize the inner search loop */
rtrtcb = this_task();
rtcb = this_task();
/* 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
* new pndtcb. Each is list is maintained in ascending sched_priority
/* REVISIT: Why don't we just remove the ptcb from pending task list
* 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.
*/
for (;
(rtrtcb && pndtcb->sched_priority <= rtrtcb->sched_priority);
rtrtcb = rtrtcb->flink);
(rtcb && ptcb->sched_priority <= rtcb->sched_priority);
rtcb = rtcb->flink);
/* Add the pndtcb to the spot found in the list. Check if the
* pndtcb goes at the ends of the g_readytorun list. This would be
/* Add the ptcb to the spot found in the list. Check if the
* 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
* 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;
if (!rtrprev)
rprev = rtcb->blink;
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 */
sched_note_switch(rtrtcb, pndtcb);
sched_note_switch(rtcb, ptcb);
/* Then insert at the head of the list */
pndtcb->flink = rtrtcb;
pndtcb->blink = NULL;
rtrtcb->blink = pndtcb;
g_readytorun.head = (FAR dq_entry_t *)pndtcb;
rtrtcb->task_state = TSTATE_TASK_READYTORUN;
pndtcb->task_state = TSTATE_TASK_RUNNING;
ret = true;
ptcb->flink = rtcb;
ptcb->blink = NULL;
rtcb->blink = ptcb;
g_readytorun.head = (FAR dq_entry_t *)ptcb;
rtcb->task_state = TSTATE_TASK_READYTORUN;
ptcb->task_state = TSTATE_TASK_RUNNING;
ret = true;
}
else
{
/* Insert in the middle of the list */
pndtcb->flink = rtrtcb;
pndtcb->blink = rtrprev;
rtrprev->flink = pndtcb;
rtrtcb->blink = pndtcb;
pndtcb->task_state = TSTATE_TASK_READYTORUN;
ptcb->flink = rtcb;
ptcb->blink = rprev;
rprev->flink = ptcb;
rtcb->blink = ptcb;
ptcb->task_state = TSTATE_TASK_READYTORUN;
}
/* Set up for the next time through */
rtrtcb = pndtcb;
rtcb = ptcb;
}
/* Mark the input list empty */
@ -150,3 +158,48 @@ bool sched_mergepending(void)
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 */