From 64b3ce8775047ea1c8890847130eed71460d72be Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 12 Feb 2016 16:13:14 -0600 Subject: [PATCH] SMP: Add up_cpustart and up_cpurestart protoypes; fix some problems in sched_addreadytorun; first cut at SMP version of up_mergepending. --- include/nuttx/arch.h | 45 +++++++++++ sched/sched/sched_addprioritized.c | 20 ----- sched/sched/sched_addreadytorun.c | 125 +++++++++++++++++++---------- sched/sched/sched_mergepending.c | 125 ++++++++++++++++++++--------- 4 files changed, 218 insertions(+), 97 deletions(-) diff --git a/include/nuttx/arch.h b/include/nuttx/arch.h index 1eb455ee3d..de85b99d13 100644 --- a/include/nuttx/arch.h +++ b/include/nuttx/arch.h @@ -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 * diff --git a/sched/sched/sched_addprioritized.c b/sched/sched/sched_addprioritized.c index 3eff24b82c..199ea5bc66 100644 --- a/sched/sched/sched_addprioritized.c +++ b/sched/sched/sched_addprioritized.c @@ -46,26 +46,6 @@ #include "sched/sched.h" -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/**************************************************************************** - * Private Type Declarations - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Variables - ****************************************************************************/ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/sched/sched/sched_addreadytorun.c b/sched/sched/sched_addreadytorun.c index ffe18acaf0..9e851757c4 100644 --- a/sched/sched/sched_addreadytorun.c +++ b/sched/sched/sched_addreadytorun.c @@ -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; } } diff --git a/sched/sched/sched_mergepending.c b/sched/sched/sched_mergepending.c index c62bb70b55..81c2c934e0 100644 --- a/sched/sched/sched_mergepending.c +++ b/sched/sched/sched_mergepending.c @@ -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 * * 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 */