Add basic sporadic schedule state machine

This commit is contained in:
Gregory Nutt 2015-07-24 09:03:21 -06:00
parent 3b1306078b
commit ea7dbc984b
14 changed files with 538 additions and 61 deletions

View File

@ -52,6 +52,7 @@
#include <time.h>
#include <nuttx/irq.h>
#include <nuttx/wdog.h>
#include <nuttx/mm/shm.h>
#include <nuttx/fs/fs.h>
#include <nuttx/net/net.h>
@ -485,8 +486,10 @@ struct tcb_s
uint8_t base_priority; /* "Normal" priority of the thread */
#endif
#ifdef CONFIG_SCHED_SPORADIC
uint8_t hi_priority; /* Sporadic high priority */
uint8_t low_priority; /* Sporadic low priority */
uint8_t max_repl; /* Max. replenishments */
uint8_t nrepl; /* Number replenishments remaining */
#endif
uint8_t task_state; /* Current state of the thread */
@ -494,16 +497,16 @@ struct tcb_s
int16_t lockcount; /* 0=preemptable (not-locked) */
#if CONFIG_RR_INTERVAL > 0 || defined(CONFIG_SCHED_SPORADIC)
int32_t timeslice; /* RR timeslice OR Sporadic */
/* replenishment interval remaining */
int32_t timeslice; /* RR timeslice OR Sporadic budget */
/* interval remaining */
#endif
#ifdef CONFIG_SCHED_SPORADIC
uint32_t spstart; /* Start time of execution budget */
uint32_t repl_period; /* Sporadic replenishment period */
uint32_t budget; /* Sporadic execution budget */
struct wdog_s low_dog; /* Times low-priority interval */
#endif
FAR struct wdog_s *waitdog; /* All timed waits used this wdog */
FAR struct wdog_s *waitdog; /* All timed waits use this wdog */
/* Stack-Related Fields *******************************************************/

View File

@ -43,7 +43,7 @@
#include <nuttx/config.h>
#include <stdint.h>
#include <sched.h>
#include <queue.h>
/****************************************************************************
* Pre-processor Definitions

View File

@ -45,6 +45,7 @@
#include <pthread.h>
#include <sched.h>
#include <debug.h>
#include <assert.h>
#include <errno.h>
#include <queue.h>
@ -334,6 +335,7 @@ int pthread_create(FAR pthread_t *thread, FAR const pthread_attr_t *attr,
#ifdef CONFIG_SCHED_SPORADIC
/* Save the sporadic scheduling parameters */
ptcb->cmn.hi_priority = priority;
ptcb->cmn.low_priority = param.sched_ss_low_priority;
ptcb->cmn.max_repl = param.sched_ss_max_repl;
@ -374,19 +376,15 @@ int pthread_create(FAR pthread_t *thread, FAR const pthread_attr_t *attr,
/* Save the sporadic scheduling parameters */
ptcb->cmn.hi_priority = priority;
ptcb->cmn.low_priority = attr->low_priority;
ptcb->cmn.max_repl = attr->max_repl;
ptcb->cmn.repl_period = repl_ticks;
ptcb->cmn.budget = budget_ticks;
}
else
{
/* Ignore sporadic scheduling parameters */
ptcb->cmn.low_priority = 0;
ptcb->cmn.max_repl = 0;
ptcb->cmn.repl_period = 0;
ptcb->cmn.budget = 0;
/* And start the frist replenishment interval */
DEBUGVERIFY(sched_sporadic_start(&ptcb->cmn));
}
#endif
}

View File

@ -54,6 +54,10 @@ CSRCS += sched_waitid.c sched_wait.c
endif
endif
ifeq ($(CONFIG_SCHED_SPORADIC),y)
CSRCS += sched_sporadic.c
endif
ifeq ($(CONFIG_SCHED_CPULOAD),y)
CSRCS += sched_cpuload.c
endif

View File

@ -230,7 +230,6 @@ int sched_reprioritize(FAR struct tcb_s *tcb, int sched_priority);
sched_setpriority(tcb,sched_priority)
#endif
#ifdef CONFIG_SCHED_TICKLESS
unsigned int sched_timer_cancel(void);
void sched_timer_resume(void);
@ -241,6 +240,14 @@ void sched_timer_reassess(void);
# define sched_timer_reassess()
#endif
#ifdef CONFIG_SCHED_SPORADIC
int sched_sporadic_start(FAR struct tcb_s *tcb);
int sched_sporadic_stop(FAR struct tcb_s *tcb);
int sched_sporadic_resume(FAR struct tcb_s *tcb);
uint32_t sched_sporadic_process(FAR struct tcb_s *tcb, uint32_t,
bool noswitches);
#endif
#if defined(CONFIG_SCHED_CPULOAD) && !defined(CONFIG_SCHED_CPULOAD_EXTCLK)
void weak_function sched_process_cpuload(void);
#endif

View File

@ -124,6 +124,7 @@ int sched_reprioritize(FAR struct tcb_s *tcb, int sched_priority)
tcb->npend_reprio = 0;
#endif
}
return ret;
}
#endif /* CONFIG_PRIORITY_INHERITANCE */

View File

@ -41,6 +41,7 @@
#include <sys/types.h>
#include <sched.h>
#include <assert.h>
#include <errno.h>
#include <nuttx/arch.h>
@ -130,6 +131,7 @@ int sched_setparam(pid_t pid, FAR const struct sched_param *param)
if ((rtcb->flags & TCB_FLAG_POLICY_MASK) == TCB_FLAG_SCHED_SPORADIC)
{
irqstate_t flags;
int repl_ticks;
int budget_ticks;
@ -150,17 +152,30 @@ int sched_setparam(pid_t pid, FAR const struct sched_param *param)
goto errout_with_lock;
}
/* Save the sporadic scheduling parameters */
/* Stop/reset current sporadic scheduling */
tcb->flags |= TCB_FLAG_SCHED_SPORADIC;
tcb->timeslice = MSEC2TICK(CONFIG_RR_INTERVAL);
flags = irqsave();
DEBUGVERIFY(sched_sporadic_stop(tcb));
/* Save the sporadic scheduling parameters and reset to the beginning
* to the replenishment interval.
*/
tcb->timeslice = budget_ticks;
tcb->hi_priority = param->sched_priority;
tcb->low_priority = param->sched_ss_low_priority;
tcb->max_repl = param->sched_ss_max_repl;
tcb->repl_period = repl_ticks;
tcb->budget = budget_ticks;
/* And restart at the next replenishment interval */
DEBUGVERIFY(sched_sporadic_start(tcb));
irqrestore(flags);
}
else
{
tcb->hi_priority = 0;
tcb->low_priority = 0;
tcb->max_repl = 0;
tcb->repl_period = 0;

View File

@ -42,6 +42,7 @@
#include <sys/types.h>
#include <unistd.h>
#include <sched.h>
#include <assert.h>
#include <errno.h>
#include <nuttx/arch.h>
@ -135,15 +136,19 @@ int sched_setscheduler(pid_t pid, int policy,
DEBUGPANIC();
case SCHED_FIFO:
{
#ifdef CONFIG_SCHED_SPORADIC
/* Cancel any on-going sporadic scheduling */
if ((tcb->flags & TCB_FLAG_POLICY_MASK) == TCB_FLAG_SCHED_SPORADIC)
{
DEBUGVERIFY(sched_sporadic_stop(tcb));
}
#endif
/* Save the FIFO scheduling parameters */
tcb->flags |= TCB_FLAG_SCHED_FIFO;
#if CONFIG_RR_INTERVAL > 0 || defined(CONFIG_SCHED_SPORADIC)
tcb->timeslice = 0;
#endif
#ifdef CONFIG_SCHED_SPORADIC
tcb->low_priority = 0;
tcb->max_repl = 0;
tcb->repl_period = 0;
tcb->budget = 0;
#endif
}
break;
@ -151,14 +156,18 @@ int sched_setscheduler(pid_t pid, int policy,
#if CONFIG_RR_INTERVAL > 0
case SCHED_RR:
{
#ifdef CONFIG_SCHED_SPORADIC
/* Cancel any on-going sporadic scheduling */
if ((tcb->flags & TCB_FLAG_POLICY_MASK) == TCB_FLAG_SCHED_SPORADIC)
{
DEBUGVERIFY(sched_sporadic_stop(tcb));
}
#endif
/* Save the round robin scheduling parameters */
tcb->flags |= TCB_FLAG_SCHED_RR;
tcb->timeslice = MSEC2TICK(CONFIG_RR_INTERVAL);
#ifdef CONFIG_SCHED_SPORADIC
tcb->low_priority = 0;
tcb->max_repl = 0;
tcb->repl_period = 0;
tcb->budget = 0;
#endif
}
break;
#endif
@ -183,18 +192,28 @@ int sched_setscheduler(pid_t pid, int policy,
if (repl_ticks < budget_ticks)
{
set_errno(EINVAL);
irqrestore(saved_state);
sched_unlock();
return ERROR;
}
/* Save the sporadic scheduling parameters */
/* Stop/reset current sporadic scheduling */
DEBUGVERIFY(sched_sporadic_stop(tcb));
/* Save the sporadic scheduling parameters. */
tcb->flags |= TCB_FLAG_SCHED_SPORADIC;
tcb->timeslice = MSEC2TICK(CONFIG_RR_INTERVAL);
tcb->timeslice = budget_ticks;
tcb->hi_priority = param->sched_priority;
tcb->low_priority = param->sched_ss_low_priority;
tcb->max_repl = param->sched_ss_max_repl;
tcb->repl_period = repl_ticks;
tcb->budget = budget_ticks;
/* And restart at the next replenishment interval */
DEBUGVERIFY(sched_sporadic_start(tcb));
}
break;
#endif

View File

@ -0,0 +1,436 @@
/************************************************************************
* sched/sched/sched_sporadic.c
*
* Copyright (C) 2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
************************************************************************/
/************************************************************************
* Included Files
************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <nuttx/sched.h>
#include <nuttx/wdog.h>
#include <nuttx/clock.h>
#include <arch/irq.h>
#include "sched/sched.h"
#ifdef CONFIG_SCHED_SPORADIC
/************************************************************************
* Pre-processor Definitions
************************************************************************/
#ifndef MIN
# define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
#define MSEC20_TICKS MIN(MSEC2TICK(20),1)
/************************************************************************
* Private Functions
************************************************************************/
/************************************************************************
* Name: sched_sporadic_replenish_start
*
* Description:
* Start the next replenishment cycle, increasing the priority of the
* thread to the high priority. This is normally a pretty trivial
* operation. But we do have to take a few precautions is priority
* inheritance is enabled.
*
* Parameters:
* tcb - TCB of the thread whose priority is being boosted.
*
* Return Value:
* Returns zero (OK) on success or a negated errno value on failure.
*
************************************************************************/
static int sched_sporadic_replenish_start(FAR struct tcb_s *tcb)
{
int ret;
/* Start the next replenishment interval */
tcb->timeslice = tcb->budget;
tcb->nrepl = tcb->max_repl;
#ifdef CONFIG_PRIORITY_INHERITANCE
/* If the priority was boosted above the higher priority, than just
* reset the base priority.
*/
if (tcb->sched_priority > tcb->base_priority)
{
/* Boosted... Do we still need to reprioritize? */
if (tcb->hi_priority < tcb->base_priority)
{
/* No.. the current execution priority is lower than the
* boosted priority. Just reset the base priority.
*/
tcb->base_priority = tcb->hi_priority;
return OK;
}
}
/* The thread priority has not been boosted or it has been boosted to a
* lower priority than the high priority. So, in either case, we need to
* reset the priority.
*/
#endif
/* Then reprioritize to the higher priority */
ret = sched_reprioritize(tcb, tcb->hi_priority);
if (ret < 0)
{
return -get_errno();
}
return OK;
}
/************************************************************************
* Name: sched_sporadic_expire
*
* Description:
* Handles the expiration of a replenishment interval by starting the
* next replenishment interval.
*
* Parameters:
* Standard watchdog parameters
*
* Return Value:
* None
*
* Assumptions:
* The thread is still running and is still using the sporadic
* scheduling policy.
*
************************************************************************/
static void sched_sporadic_expire(int argc, wdparm_t arg1, ...)
{
FAR struct tcb_s *tcb = (FAR struct tcb_s *)arg1;
DEBUGASSERT(argc == 1 && tcb != NULL);
/* Start the next replenishment interval */
DEBUGVERIFY(sched_sporadic_replenish_start(tcb));
}
/************************************************************************
* Public Functions
************************************************************************/
/************************************************************************
* Name: sched_sporadic_start
*
* Description:
* Called to initialize sporadic scheduling on a given thread. This
* function is called in the following circumstances:
*
* - When starting a pthread with sporadic scheduling specified in
* the pthread attributes.
* - When establishing sporadic scheduling policy via
* sched_setscheduler()
* - When the sporadic scheduling parameters are changed via
* sched_setparam().
*
* Parameters:
* tcb - The TCB of the thread that is beginning sporadic scheduling.
*
* Return Value:
* Returns zero (OK) on success or a negated errno value on failure.
*
* Assumptions:
* - Interrupts are disabled
* - All sporadic scheduling parameters in the TCB are valid
* - The thread is not currently using the sporadic scheduliing policy.
*
************************************************************************/
int sched_sporadic_start(FAR struct tcb_s *tcb)
{
DEBUGASSERT(tcb);
/* Cancel and pending low-priority interval timing and re-initialize
* the watchdog timer.
*/
wd_cancel(&tcb->low_dog);
memset(&tcb->low_dog, 0, sizeof(struct wdog_s));
/* Then start the first replenishment interval */
return sched_sporadic_replenish_start(tcb);
}
/************************************************************************
* Name: sched_sporadic_stop
*
* Description:
* Called to terminate sporadic scheduling on a given thread. This
* function is called in the following circumstances:
*
* - When any thread exits with sporadic scheduling active.
* - When any thread using sporadic scheduling is changed to use
* some other scheduling policy via sched_setscheduler()
* - When the sporadic scheduling parameters are changed via
* sched_setparam().
*
* Parameters:
* tcb - The TCB of the thread that is beginning sporadic scheduling.
*
* Return Value:
* Returns zero (OK) on success or a negated errno value on failure.
*
* Assumptions:
* - Interrupts are disabled
* - All sporadic scheduling parameters in the TCB are valid
* - The thread is currently using the sporadic scheduling policy.
*
************************************************************************/
int sched_sporadic_stop(FAR struct tcb_s *tcb)
{
DEBUGASSERT(tcb);
/* Cancel and pending low-priority interval timing and re-initialize
* the watchdog timer.
*/
wd_cancel(&tcb->low_dog);
memset(&tcb->low_dog, 0, sizeof(struct wdog_s));
/* Reset sporadic scheduling parameters */
tcb->hi_priority = 0;
tcb->low_priority = 0;
tcb->max_repl = 0;
tcb->nrepl = 0;
tcb->timeslice = 0;
tcb->repl_period = 0;
tcb->budget = 0;
return OK;
}
/************************************************************************
* Name: sched_sporadic_replenish_resume
*
* Description:
* Called to start the next replenishment interval. This function is
* called in the following circumstances:
*
* - When a task using the sporadic scheduling policy is resumed
* while in the budget interval of the replenishment cycle.
*
* Parameters:
* tcb - The TCB of the thread that is beginning sporadic scheduling.
*
* Return Value:
* Returns zero (OK) on success or a negated errno value on failure.
*
* Assumptions:
* - Interrupts are disabled
* - All sporadic scheduling parameters in the TCB are valid
* - The low priority interval timer is not running
*
************************************************************************/
int sched_sporadic_replenish_resume(FAR struct tcb_s *tcb)
{
DEBUGASSERT(tcb);
/* Make sure that we are in the budget portion of the replenishment
* interval. We know this is the case if the current timeslice is
* non-zero. Do not exceed the maximum number of replenishments.
*/
if (tcb->timeslice > 0 && tcb->nrepl > 0)
{
tcb->timeslice = tcb->budget;
tcb->nrepl--;
}
return OK;
}
/************************************************************************
* Name: sched_sporadic_process
*
* Description:
* Process the elapsed time interval. Called from this context:
*
* - From the timer interrupt handler while the thread with sporadic
* scheduling is running.
*
* Parameters:
* tcb - The TCB of the thread that is beginning sporadic scheduling.
* ticks - The number of elapsed ticks since the last time this
* function was called.
*
* Return Value:
* The number if ticks remaining until the budget interval expires.
* Zero is returned if we are in the low-prioriy phase of the the
* replenishment interval.
*
* Assumptions:
* - Interrupts are disabled
* - All sporadic scheduling parameters in the TCB are valid
*
************************************************************************/
uint32_t sched_sporadic_process(FAR struct tcb_s *tcb, uint32_t ticks,
bool noswitches)
{
DEBUGASSERT(tcb && ticks > 0);
/* If we are in the low-priority phase of the replenishment interval,
* then just return zero.
*/
if (tcb->timeslice <= 0)
{
return 0;
}
/* Check if the the budget interval has elapse */
if (ticks >= tcb->timeslice)
{
/* Does the thread have the scheduler locked? */
if (tcb->lockcount > 0)
{
/* Yes... then we have no option but to give the thread more
* time at the higher priority. Dropping the priority could
* result in a context switch.
*
* Let then have up to 20 milliseconds (in ticks)
*/
tcb->timeslice = MSEC20_TICKS;
return MSEC20_TICKS;
}
/* We will also suppress context switches if we were called via one of
* the unusual cases handled by sched_timer_reasses(). In that case,
* we will return a value of one so that the timer will expire as soon
* as possible and we can perform this action in the normal timer
* expiration context.
*
* This is kind of kludge, but I am not to concerned because I hope
* that the situation is impossible or at least could only occur on
* rare corner-cases.
*/
if (noswitches)
{
tcb->timeslice = 1;
return 1;
}
/* Another possibility is the the budget interval is equal to the
* entire replenishment interval. This would seem like such a good
* thing to do, but is certainly permitted.
*/
if (tcb->budget >= tcb->repl_period)
{
tcb->timeslice = tcb->budget;
return tcb->budget;
}
/* Otherwise enter the low-priority phase of the replenishment cycle */
tcb->timeslice = 0;
/* Start the timer that will terminate the low priority cycle. This timer
* expiration is independent of what else may occur (except that it must
* be cancelled if the thread exits.
*/
DEBUGVERIFY(wd_start(&tcb->low_dog, tcb->repl_period - tcb->budget,
sched_sporadic_expire, 1, (wdentry_t)tcb));
#ifdef CONFIG_PRIORITY_INHERITANCE
/* If the priority was boosted above the higher priority, than just
* reset the base priority.
*/
if (tcb->sched_priority > tcb->base_priority)
{
/* Thread priority was boosted while we were in the high priority
* state.
*/
tcb->base_priority = tcb->low_priority;
return 0;
}
#endif
/* Otherwise drop the priority of thread, possible causing a context
* switch.
*/
DEBUGVERIFY(sched_reprioritize(tcb, tcb->low_priority));
return 0;
}
/* No.. then just decrement the time remaining in the budget interval
* and continue.
*/
else
{
tcb->timeslice -= ticks;
return tcb->timeslice;
}
}
#endif /* CONFIG_SCHED_SPORADIC */

View File

@ -63,6 +63,8 @@
* Public Function Prototypes
****************************************************************************/
struct tcb_s; /* Forward reference */
/* Task start-up */
void task_start(void);

View File

@ -39,6 +39,8 @@
#include <nuttx/config.h>
#include <assert.h>
#include <nuttx/arch.h>
#include <nuttx/wdog.h>
#include <nuttx/sched.h>
@ -46,32 +48,9 @@
#include "semaphore/semaphore.h"
#include "wdog/wdog.h"
#include "mqueue/mqueue.h"
#include "sched/sched.h"
#include "task/task.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Type Declarations
****************************************************************************/
/****************************************************************************
* Global Variables
****************************************************************************/
/****************************************************************************
* Private Variables
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
@ -112,4 +91,13 @@ void task_recover(FAR struct tcb_s *tcb)
mq_recover(tcb);
#endif
#ifndef CONFIG_SCHED_SPORADIC
if ((tcb->flags & TCB_FLAG_POLICY_MASK) == TCB_FLAG_SCHED_SPORADIC)
{
/* Stop current sporadic scheduling */
DEBUGVERIFY(sched_sporadic_stop(tcb));
}
#endif
}

View File

@ -44,6 +44,7 @@
#include <sys/types.h>
#include <stdint.h>
#include <signal.h>
#include <nuttx/compiler.h>
#include <nuttx/wdog.h>

View File

@ -45,6 +45,8 @@
#include <queue.h>
#include <errno.h>
#include <arch/irq.h>
#include "timer/timer.h"
#ifndef CONFIG_DISABLE_POSIX_TIMERS

View File

@ -1,7 +1,7 @@
/********************************************************************************
* sched/wdog/wd_gettime.c
*
* Copyright (C) 2007, 2009, 2014 Gregory Nutt. All rights reserved.
* Copyright (C) 2007, 2009, 2014-2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -40,6 +40,7 @@
#include <nuttx/config.h>
#include <nuttx/wdog.h>
#include <arch/irq.h>
#include "wdog/wdog.h"