This is basically a complete redesign of the sporadic scheduling logic due to limitations in the initial design

This commit is contained in:
Gregory Nutt 2015-07-25 12:50:53 -06:00
parent a1031417e8
commit 27e21710fd
10 changed files with 887 additions and 285 deletions

20
TODO
View File

@ -1,4 +1,4 @@
NuttX TODO List (Last updated July 14, 2015)
NuttX TODO List (Last updated July 25, 2015)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This file summarizes known NuttX bugs, limitations, inconsistencies with
@ -9,7 +9,7 @@ issues reated to each board port.
nuttx/
(11) Task/Scheduler (sched/)
(12) Task/Scheduler (sched/)
(1) Memory Management (mm/)
(3) Signals (sched/signal, arch/)
(2) pthreads (sched/pthread)
@ -213,6 +213,22 @@ o Task/Scheduler (sched/)
Status: Open
Priority: Medium-ish
Title: TICKLESS SCHEDULING INACCURACIES
Description: Interval timers are set up to determine timing for round-
robin and sporadic scheduling policies. In the timer
interrupt mode, the budget remaining for the thread is
decremented on each timer interrupt and so is always
accurate to within one clock time. So when the task
suspended, the remaining budget is accurate.
But in tickless mode, the budget is only updated on the
expiration of the timer. So if the task is suspended by an
asynchronous event, the budget will fail to decrement and the
task will get a larger share of CPU that is deserves in those
cases.
Status: Open
Priority: Low. I am not aware of any real world issues.
o Memory Managment (mm/)
^^^^^^^^^^^^^^^^^^^^^^

View File

@ -116,6 +116,12 @@
# undef HAVE_GROUP_MEMBERS
#endif
/* Sporadic scheduling */
#ifndef CONFIG_SCHED_SPORADIC_MAXREPL
# define CONFIG_SCHED_SPORADIC_MAXREPL 3
#endif
/* Task Management Definitions **************************************************/
/* Special task IDS. Any negative PID is invalid. */
@ -234,10 +240,49 @@ typedef CODE void (*atexitfunc_t)(void);
typedef CODE void (*onexitfunc_t)(int exitcode, FAR void *arg);
#endif
/* struct sporadic_s *************************************************************/
#ifdef CONFIG_SCHED_SPORADIC
/* This structure represents oen replenishment interval. This is what is
* received by each timeout handler.
*/
struct sporadic_s;
struct replenishment_s
{
FAR struct tcb_s *tcb; /* The parent TCB structure */
struct wdog_s timer; /* Timer dedicated to this interval */
bool active; /* True: replenishment instance is busy */
};
/* This structure is an allocated "plug-in" to the main TCB structure. It is
* allocated when the sporadic scheduling policy is assigned to a thread. Thus,
* in the context of numerous threads of varying policies, there the overhead
* from this significant allocation is only borne by the threads with the
* sporadic scheduling policy.
*/
struct sporadic_s
{
uint8_t hi_priority; /* Sporadic high priority */
uint8_t low_priority; /* Sporadic low priority */
uint8_t max_repl; /* Maximum number of replenishments */
uint8_t nrepls; /* Number of active replenishments */
uint32_t repl_period; /* Sporadic replenishment period */
uint32_t budget; /* Sporadic execution budget period */
uint32_t pending; /* Unrealized, pending execution time */
/* This is the list of replenishment intervals */
struct replenishment_s replenishments[CONFIG_SCHED_SPORADIC_MAXREPL];
};
#endif /* CONFIG_SCHED_SPORADIC */
/* struct child_status_s *********************************************************/
/* This structure is used to maintin information about child tasks.
* pthreads work differently, they have join information. This is
* only for child tasks.
/* This structure is used to maintain information about child tasks. pthreads
* work differently, they have join information. This is only for child tasks.
*/
#ifdef CONFIG_SCHED_CHILD_STATUS
@ -245,9 +290,9 @@ struct child_status_s
{
FAR struct child_status_s *flink;
uint8_t ch_flags; /* Child status: See CHILD_FLAG_* definitions */
pid_t ch_pid; /* Child task ID */
int ch_status; /* Child exit status */
uint8_t ch_flags; /* Child status: See CHILD_FLAG_* defns */
pid_t ch_pid; /* Child task ID */
int ch_status; /* Child exit status */
};
#endif
@ -478,20 +523,12 @@ struct tcb_s
entry_t entry; /* Entry Point into the thread */
uint8_t sched_priority; /* Current priority of the thread */
#if defined(CONFIG_PRIORITY_INHERITANCE) && CONFIG_PRIORITY_INHERITANCE > 0
#ifdef CONFIG_PRIORITY_INHERITANCE
#if CONFIG_SEM_NNESTPRIO > 0
uint8_t npend_reprio; /* Number of nested reprioritizations */
uint8_t pend_reprios[CONFIG_SEM_NNESTPRIO];
#endif
#if defined(CONFIG_PRIORITY_INHERITANCE) || defined(CONFIG_SCHED_SPORADIC)
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 */
#ifdef __REVISIT_REPLENISHMENTS
uint8_t max_repl; /* Max. replenishments */
uint8_t nrepl; /* Number replenishments remaining */
#endif
#endif
uint8_t task_state; /* Current state of the thread */
@ -503,9 +540,7 @@ struct tcb_s
/* interval remaining */
#endif
#ifdef CONFIG_SCHED_SPORADIC
uint32_t repl_period; /* Sporadic replenishment period */
uint32_t budget; /* Sporadic execution budget */
struct wdog_s low_dog; /* Times low-priority interval */
FAR struct sporadic_s *sporadic; /* Sporadic scheduling parameters */
#endif
FAR struct wdog_s *waitdog; /* All timed waits use this timer */

View File

@ -311,7 +311,17 @@ config SCHED_SPORADIC
bool "Support sporadic scheduling"
default n
---help---
Build in additional logic to support sporadic scheduling (SCHED_SPORADIC).
Build in additional logic to support sporadic scheduling
(SCHED_SPORADIC).
config SCHED_SPORADIC_MAXREPL
int "Maximum number of replenishments"
default 3
range 1 255
depends on SCHED_SPORADIC
---help---
Controls the size of allocated replenishment structures and, hence,
also limits the maximum number of replenishments.
config TASK_NAME_SIZE
int "Maximum task name size"

View File

@ -228,7 +228,7 @@ int pthread_create(FAR pthread_t *thread, FAR const pthread_attr_t *attr,
{
FAR struct pthread_tcb_s *ptcb;
FAR struct join_s *pjoin;
int priority;
struct sched_param param;
int policy;
int errcode;
pid_t pid;
@ -298,18 +298,13 @@ int pthread_create(FAR pthread_t *thread, FAR const pthread_attr_t *attr,
goto errout_with_join;
}
/* Should we use the priority and scheduler specified in the
* pthread attributes? Or should we use the current thread's
* priority and scheduler?
/* Should we use the priority and scheduler specified in the pthread
* attributes? Or should we use the current thread's priority and
* scheduler?
*/
if (attr->inheritsched == PTHREAD_INHERIT_SCHED)
{
struct sched_param param;
#ifdef CONFIG_SCHED_SPORADIC
int ticks;
#endif
/* Get the priority (and any other scheduling parameters) for this
* thread.
*/
@ -321,8 +316,6 @@ int pthread_create(FAR pthread_t *thread, FAR const pthread_attr_t *attr,
goto errout_with_join;
}
priority = param.sched_priority;
/* Get the scheduler policy for this thread */
policy = sched_getscheduler(0);
@ -331,71 +324,81 @@ int pthread_create(FAR pthread_t *thread, FAR const pthread_attr_t *attr,
errcode = get_errno();
goto errout_with_join;
}
#ifdef CONFIG_SCHED_SPORADIC
/* Save the sporadic scheduling parameters */
ptcb->cmn.hi_priority = priority;
ptcb->cmn.low_priority = param.sched_ss_low_priority;
#ifdef __REVISIT_REPLENISHMENTS
ptcb->cmn.max_repl = param.sched_ss_max_repl;
#endif
(void)clock_time2ticks(&param.sched_ss_repl_period, &ticks);
ptcb->cmn.repl_period = ticks;
(void)clock_time2ticks(&param.sched_ss_init_budget, &ticks);
ptcb->cmn.budget = ticks;
#endif
}
else
{
/* Use the priority and scheduler from the attributes */
/* Use the scheduler policy and policy the attributes */
priority = attr->priority;
policy = attr->policy;
policy = attr->policy;
param.sched_priority = attr->priority;
#ifdef CONFIG_SCHED_SPORADIC
if (policy == SCHED_SPORADIC)
{
int repl_ticks;
int budget_ticks;
/* Convert timespec values to system clock ticks */
(void)clock_time2ticks(&attr->repl_period, &repl_ticks);
(void)clock_time2ticks(&attr->budget, &budget_ticks);
/* The replenishment period must be greater than or equal to the
* budget period.
*/
if (repl_ticks < budget_ticks)
{
errcode = EINVAL;
goto errout_with_join;
}
/* Save the sporadic scheduling parameters */
ptcb->cmn.hi_priority = priority;
ptcb->cmn.low_priority = attr->low_priority;
#ifdef __REVISIT_REPLENISHMENTS
ptcb->cmn.max_repl = attr->max_repl;
#endif
ptcb->cmn.repl_period = repl_ticks;
ptcb->cmn.budget = budget_ticks;
/* And start the frist replenishment interval */
DEBUGVERIFY(sched_sporadic_start(&ptcb->cmn));
}
param.sched_ss_low_priority = attr->low_priority;
param.sched_ss_max_repl = attr->max_repl;
param.sched_ss_repl_period.tv_sec = attr->repl_period.tv_sec;
param.sched_ss_repl_period.tv_nsec = attr->repl_period.tv_nsec;
param.sched_ss_init_budget.tv_sec = attr->budget.tv_sec;
param.sched_ss_init_budget.tv_nsec = attr->budget.tv_nsec;
#endif
}
#ifdef CONFIG_SCHED_SPORADIC
if (policy == SCHED_SPORADIC)
{
FAR struct sporadic_s *sporadic;
int repl_ticks;
int budget_ticks;
/* Convert timespec values to system clock ticks */
(void)clock_time2ticks(&param.sched_ss_repl_period, &repl_ticks);
(void)clock_time2ticks(&param.sched_ss_init_budget, &budget_ticks);
/* The replenishment period must be greater than or equal to the
* budget period.
*/
if (repl_ticks < budget_ticks)
{
errcode = EINVAL;
goto errout_with_join;
}
/* Initialize the sporadic policy */
ret = sched_sporadic_initialize(&ptcb->cmn);
if (ret >= 0)
{
sporadic = ptcb->cmn.sporadic;
DEBUGASSERT(sporadic != NULL);
/* Save the sporadic scheduling parameters */
sporadic->hi_priority = param.sched_priority;
sporadic->low_priority = param.sched_ss_low_priority;
sporadic->max_repl = param.sched_ss_max_repl;
sporadic->repl_period = repl_ticks;
sporadic->budget = budget_ticks;
/* And start the first replenishment interval */
ret = sched_sporadic_start(&ptcb->cmn);
}
/* Handle any failures */
if (ret < 0)
{
errcode = -ret;
goto errout_with_join;
}
}
#endif
/* Initialize the task control block */
ret = pthread_schedsetup(ptcb, priority, pthread_start, start_routine);
ret = pthread_schedsetup(ptcb, param.sched_priority, pthread_start,
start_routine);
if (ret != OK)
{
errcode = EBUSY;

View File

@ -246,9 +246,11 @@ uint32_t sched_roundrobin_process(FAR struct tcb_s *tcb, uint32_t ticks,
#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);
int sched_sporadic_initialize(FAR struct tcb_s *tcb);
int sched_sporadic_start(FAR struct tcb_s *tcb);
int sched_sporadic_stop(FAR struct tcb_s *tcb);
int sched_sporadic_reset(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 ticks,
bool noswitches);
void sched_sporadic_lowpriority(FAR struct tcb_s *tcb);

View File

@ -134,22 +134,35 @@ int sched_getparam (pid_t pid, FAR struct sched_param *param)
}
else
{
#ifdef CONFIG_SCHED_SPORADIC
#endif
/* Return the priority of the task */
param->sched_priority = (int)tcb->sched_priority;
#ifdef CONFIG_SCHED_SPORADIC
/* Return parameters associated with SCHED_SPORADIC */
if ((tcb->flags & TCB_FLAG_POLICY_MASK) == TCB_FLAG_SCHED_SPORADIC)
{
FAR struct sporadic_s *sporadic = tcb->sporadic;
DEBUGASSERT(sporadic != NULL);
param->sched_ss_low_priority = (int)tcb->low_priority;
#ifdef __REVISIT_REPLENISHMENTS
param->sched_ss_max_repl = (int)tcb->max_repl;
#else
param->sched_ss_max_repl = 1;
#endif
/* Return parameters associated with SCHED_SPORADIC */
clock_ticks2time((int)tcb->repl_period, &param->sched_ss_repl_period);
clock_ticks2time((int)tcb->budget, &param->sched_ss_init_budget);
param->sched_ss_low_priority = (int)sporadic->low_priority;
param->sched_ss_max_repl = (int)sporadic->max_repl;
clock_ticks2time((int)sporadic->repl_period, &param->sched_ss_repl_period);
clock_ticks2time((int)sporadic->budget, &param->sched_ss_init_budget);
}
else
{
param->sched_ss_low_priority = 0;
param->sched_ss_max_repl = 0;
param->sched_ss_repl_period.tv_sec = 0;
param->sched_ss_repl_period.tv_nsec = 0;
param->sched_ss_init_budget.tv_sec = 0;
param->sched_ss_init_budget.tv_nsec = 0;
}
#endif
}

View File

@ -131,19 +131,35 @@ int sched_setparam(pid_t pid, FAR const struct sched_param *param)
if ((rtcb->flags & TCB_FLAG_POLICY_MASK) == TCB_FLAG_SCHED_SPORADIC)
{
FAR struct sporadic_s *sporadic;
irqstate_t flags;
int repl_ticks;
int budget_ticks;
#ifdef __REVISIT_REPLENISHMENTS
DEBUGASSERT(param->sched_ss_max_repl <= UINT8_MAX);
#endif
if (param->sched_ss_max_repl < 1 ||
param->sched_ss_max_repl > CONFIG_SCHED_SPORADIC_MAXREPL)
{
errcode = EINVAL;
goto errout_with_lock;
}
/* Convert timespec values to system clock ticks */
(void)clock_time2ticks(&param->sched_ss_repl_period, &repl_ticks);
(void)clock_time2ticks(&param->sched_ss_init_budget, &budget_ticks);
/* Avoid zero/negative times */
if (repl_ticks < 1)
{
repl_ticks = 1;
}
if (budget_ticks < 1)
{
budget_ticks = 1;
}
/* The replenishment period must be greater than or equal to the
* budget period.
*/
@ -157,35 +173,37 @@ int sched_setparam(pid_t pid, FAR const struct sched_param *param)
/* Stop/reset current sporadic scheduling */
flags = irqsave();
DEBUGVERIFY(sched_sporadic_stop(tcb));
ret = sched_sporadic_reset(tcb);
if (ret >= 0)
{
/* Save the sporadic scheduling parameters and reset to the
* beginning to the replenishment interval.
*/
/* Save the sporadic scheduling parameters and reset to the beginning
* to the replenishment interval.
*/
tcb->timeslice = budget_ticks;
tcb->timeslice = budget_ticks;
tcb->hi_priority = param->sched_priority;
tcb->low_priority = param->sched_ss_low_priority;
#ifdef __REVISIT_REPLENISHMENTS
tcb->max_repl = param->sched_ss_max_repl;
#endif
tcb->repl_period = repl_ticks;
tcb->budget = budget_ticks;
sporadic = rtcb->sporadic;
DEBUGASSERT(sporadic != NULL);
/* And restart at the next replenishment interval */
sporadic->hi_priority = param->sched_priority;
sporadic->low_priority = param->sched_ss_low_priority;
sporadic->max_repl = param->sched_ss_max_repl;
sporadic->repl_period = repl_ticks;
sporadic->budget = budget_ticks;
/* And restart at the next replenishment interval */
ret = sched_sporadic_start(tcb);
}
/* Restore interrupts and handler errors */
DEBUGVERIFY(sched_sporadic_start(tcb));
irqrestore(flags);
}
else
{
tcb->hi_priority = 0;
tcb->low_priority = 0;
#ifdef __REVISIT_REPLENISHMENTS
tcb->max_repl = 0;
#endif
tcb->repl_period = 0;
tcb->budget = 0;
if (ret < 0)
{
errcode = -ret;
goto errout_with_lock;
}
}
#endif

View File

@ -87,6 +87,9 @@ int sched_setscheduler(pid_t pid, int policy,
{
FAR struct tcb_s *tcb;
irqstate_t saved_state;
#ifdef CONFIG_SCHED_SPORADIC
int errcode;
#endif
int ret;
/* Check for supported scheduling policy */
@ -175,49 +178,83 @@ int sched_setscheduler(pid_t pid, int policy,
#ifdef CONFIG_SCHED_SPORADIC
case SCHED_SPORADIC:
{
FAR struct sporadic_s *sporadic;
int repl_ticks;
int budget_ticks;
#ifdef __REVISIT_REPLENISHMENTS
DEBUGASSERT(param->sched_ss_max_repl <= UINT8_MAX);
#endif
if (param->sched_ss_max_repl < 1 ||
param->sched_ss_max_repl > CONFIG_SCHED_SPORADIC_MAXREPL)
{
errcode = EINVAL;
goto errout_with_irq;
}
/* Convert timespec values to system clock ticks */
(void)clock_time2ticks(&param->sched_ss_repl_period, &repl_ticks);
(void)clock_time2ticks(&param->sched_ss_init_budget, &budget_ticks);
/* Avoid zero/negative times */
if (repl_ticks < 1)
{
repl_ticks = 1;
}
if (budget_ticks < 1)
{
budget_ticks = 1;
}
/* The replenishment period must be greater than or equal to the
* budget period.
*/
if (repl_ticks < budget_ticks)
{
set_errno(EINVAL);
irqrestore(saved_state);
sched_unlock();
return ERROR;
errcode = EINVAL;
goto errout_with_irq;
}
/* Stop/reset current sporadic scheduling */
/* Initialize/reset current sporadic scheduling */
DEBUGVERIFY(sched_sporadic_stop(tcb));
if ((tcb->flags & TCB_FLAG_POLICY_MASK) == TCB_FLAG_SCHED_SPORADIC)
{
ret = sched_sporadic_reset(tcb);
}
else
{
ret = sched_sporadic_initialize(tcb);
}
/* Save the sporadic scheduling parameters. */
tcb->flags |= TCB_FLAG_SCHED_SPORADIC;
tcb->timeslice = budget_ticks;
tcb->hi_priority = param->sched_priority;
tcb->low_priority = param->sched_ss_low_priority;
#ifdef __REVISIT_REPLENISHMENTS
tcb->max_repl = param->sched_ss_max_repl;
#endif
tcb->repl_period = repl_ticks;
tcb->budget = budget_ticks;
if (ret >= 0)
{
tcb->flags |= TCB_FLAG_SCHED_SPORADIC;
tcb->timeslice = budget_ticks;
/* And restart at the next replenishment interval */
sporadic = tcb->sporadic;
DEBUGASSERT(sporadic != NULL);
DEBUGVERIFY(sched_sporadic_start(tcb));
sporadic->hi_priority = param->sched_priority;
sporadic->low_priority = param->sched_ss_low_priority;
sporadic->max_repl = param->sched_ss_max_repl;
sporadic->repl_period = repl_ticks;
sporadic->budget = budget_ticks;
/* And restart at the next replenishment interval */
ret = sched_sporadic_start(tcb);
}
/* Handle errors */
if (ret < 0)
{
errcode = -ret;
goto errout_with_irq;
}
}
break;
#endif
@ -236,4 +273,12 @@ int sched_setscheduler(pid_t pid, int policy,
ret = sched_reprioritize(tcb, param->sched_priority);
sched_unlock();
return (ret >= 0) ? OK : ERROR;
#ifdef CONFIG_SCHED_SPORADIC
errout_with_irq:
set_errno(errcode);
irqrestore(saved_state);
sched_unlock();
return ERROR;
#endif
}

View File

@ -1,4 +1,4 @@
/************************************************************************
/****************************************************************************
* sched/sched/sched_sporadic.c
*
* Copyright (C) 2015 Gregory Nutt. All rights reserved.
@ -31,11 +31,11 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
************************************************************************/
****************************************************************************/
/************************************************************************
/****************************************************************************
* Included Files
************************************************************************/
****************************************************************************/
#include <nuttx/config.h>
@ -55,45 +55,65 @@
#ifdef CONFIG_SCHED_SPORADIC
/************************************************************************
/****************************************************************************
* Pre-processor Definitions
************************************************************************/
****************************************************************************/
#ifndef MIN
# define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
/************************************************************************
* Private Functions
************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/************************************************************************
* Name: sched_sporadic_replenish_start
static int sporadic_budget_start(FAR struct tcb_s *tcb,
FAR struct replenishment_s *repl, uint32_t budget);
static int sporadic_budget_next(FAR struct replenishment_s *repl);
static int sporadic_interval_start(FAR struct replenishment_s *repl);
static void sporadic_budget_expire(int argc, wdparm_t arg1, ...);
static void sporadic_interval_expire(int argc, wdparm_t arg1, ...);
FAR struct replenishment_s *
sporadic_alloc_repl(FAR struct sporadic_s *sporadic);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: sporadic_budget_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.
* Start the next replenishment cycle by (1) increasing the priority of
* the thread to the high priority and (2) setting up a timer for the
* budgeted portion of the replenishment interval. We do have to take a
* few precautions is priority inheritance is enabled.
*
* Parameters:
* tcb - TCB of the thread whose priority is being boosted.
* Input Parameters:
* tcb - TCB of the thread whose priority is being boosted.
* repl - The replenishment timer to use
* budget - Budgeted execution time
*
* Returned Value:
* Returns zero (OK) on success or a negated errno value on failure.
*
************************************************************************/
****************************************************************************/
static int sched_sporadic_replenish_start(FAR struct tcb_s *tcb)
static int sporadic_budget_start(FAR struct tcb_s *tcb,
FAR struct replenishment_s *repl,
uint32_t budget)
{
FAR struct sporadic_s *sporadic;
int ret;
DEBUGASSERT(tcb && tcb->sporadic);
sporadic = tcb->sporadic;
/* Start the next replenishment interval */
tcb->timeslice = tcb->budget;
#ifdef __REVISIT_REPLENISHMENTS
tcb->nrepl = tcb->max_repl;
#endif
tcb->timeslice = budget;
ret = wd_start(&repl->timer, sporadic->budget, sporadic_budget_expire,
1, (wdentry_t)sporadic);
#ifdef CONFIG_PRIORITY_INHERITANCE
/* If the priority was boosted above the higher priority, than just
@ -104,13 +124,13 @@ static int sched_sporadic_replenish_start(FAR struct tcb_s *tcb)
{
/* Boosted... Do we still need to reprioritize? */
if (tcb->hi_priority < tcb->base_priority)
if (sporadic->hi_priority < sporadic->base_priority)
{
/* No.. the current execution priority is lower than the
* boosted priority. Just reset the base priority.
*/
tcb->base_priority = tcb->hi_priority;
tcb->base_priority = sporadic->hi_priority;
return OK;
}
}
@ -124,7 +144,7 @@ static int sched_sporadic_replenish_start(FAR struct tcb_s *tcb)
/* Then reprioritize to the higher priority */
ret = sched_reprioritize(tcb, tcb->hi_priority);
ret = sched_reprioritize(tcb, sporadic->hi_priority);
if (ret < 0)
{
return -get_errno();
@ -133,14 +153,194 @@ static int sched_sporadic_replenish_start(FAR struct tcb_s *tcb)
return OK;
}
/************************************************************************
* Name: sched_sporadic_expire
/****************************************************************************
* Name: sporadic_budget_next
*
* Description:
* Handles the expiration of a replenishment interval by starting the
* next replenishment interval.
* Start the next replenishment. This is called at the complete of each
* replenishment interval.
*
* Parameters:
* Input Parameters:
* repl - Replenishment structure whose budget timer just expired.
*
* Returned Value:
* Returns zero (OK) on success or a negated errno value on failure.
*
****************************************************************************/
static int sporadic_budget_next(FAR struct replenishment_s *repl)
{
FAR struct sporadic_s *sporadic;
FAR struct tcb_s *tcb;
uint32_t budget;
DEBUGASSERT(repl != NULL && repl->tcb != NULL);
tcb = repl->tcb;
sporadic = tcb->sporadic;
DEBUGASSERT(sporadic != NULL);
/* The budgeted interval will be the pending budget */
budget = sporadic->pending;
if (budget > sporadic->budget)
{
/* Clip to the maximum */
budget = sporadic->budget;
}
sporadic->pending -= budget;
/* If the budget zero, then all of the pending budget has been utilized.
* There are now two possibilities: (1) There are multiple, active
* replenishment threads so this one is no longer needed, or (2) there is
* only one and we need to restart the budget interval with the full
* budget.
*/
if (budget == 0)
{
if (sporadic->nrepls > 1)
{
/* Release this replenishment timer. Processing will continue
* to be driven by the remaining timers.
*/
repl->active = false;
sporadic->nrepls--;
return OK;
}
else
{
/* No, we are the only replenishment timer. Restart with the
* full budget.
*/
budget = sporadic->budget;
}
}
/* Start the next replenishment interval */
return sporadic_budget_start(tcb, repl, budget);
}
/****************************************************************************
* Name: sporadic_set_lowpriority
*
* Description:
* Force the thread to lower priority.
*
* Input Parameters:
* repl - Replenishment timer to be used
*
* Returned Value:
* Returns zero (OK) on success or a negated errno value on failure.
*
****************************************************************************/
static int sporadic_set_lowpriority(FAR struct tcb_s *tcb)
{
FAR struct sporadic_s *sporadic;
int ret;
DEBUGASSERT(tcb != NULL && tcb->sporadic != NULL);
sporadic = tcb->sporadic;
#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 = sporadic->low_priority;
}
#endif
/* Otherwise drop the priority of thread, possible causing a context
* switch.
*/
ret = sched_reprioritize(tcb, sporadic->low_priority);
if (ret < 0)
{
return -get_errno();
}
return OK;
}
/****************************************************************************
* Name: sporadic_interval_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.
*
* Input Parameters:
* repl - Replenishment timer to be used
*
* Returned Value:
* Returns zero (OK) on success or a negated errno value on failure.
*
****************************************************************************/
static int sporadic_interval_start(FAR struct replenishment_s *repl)
{
FAR struct sporadic_s *sporadic;
FAR struct tcb_s *tcb;
uint32_t remainder;
DEBUGASSERT(repl != NULL && repl->tcb != NULL);
tcb = repl->tcb;
sporadic = tcb->sporadic;
DEBUGASSERT(sporadic != NULL);
/* Enter the low-priority phase of the replenishment cycle */
tcb->timeslice = 0;
/* Calculate the remainder of the replenishment interval. This is
* permitted to be zero, in which case we just restart the budget
* interval without lowering the priority.
*/
DEBUGASSERT(sporadic->repl_period >= sporadic->budget);
remainder = sporadic->repl_period - sporadic->budget;
if (remainder == 0)
{
return sporadic_budget_next(repl);
}
/* 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(&repl->timer, remainder, sporadic_interval_expire,
1, (wdentry_t)repl));
/* Drop the priority of thread, possible causing a context switch. */
return sporadic_set_lowpriority(tcb);
}
/****************************************************************************
* Name: sporadic_budget_expire
*
* Description:
* Handles the expiration of a budget interval. It saves the budget
* For the next interval, drops the priority of the thread, and restarts
* the timer for the rest of the replenishment period.
*
* Input Parameters:
* Standard watchdog parameters
*
* Returned Value:
@ -150,24 +350,230 @@ static int sched_sporadic_replenish_start(FAR struct tcb_s *tcb)
* The thread is still running and is still using the sporadic
* scheduling policy.
*
************************************************************************/
****************************************************************************/
static void sched_sporadic_expire(int argc, wdparm_t arg1, ...)
static void sporadic_budget_expire(int argc, wdparm_t arg1, ...)
{
FAR struct tcb_s *tcb = (FAR struct tcb_s *)arg1;
FAR struct replenishment_s *repl = (FAR struct replenishment_s *)arg1;
FAR struct sporadic_s *sporadic;
FAR struct tcb_s *tcb;
uint32_t pending;
DEBUGASSERT(argc == 1 && tcb != NULL);
DEBUGASSERT(argc == 1 && repl != NULL && repl->tcb != NULL);
tcb = repl->tcb;
sporadic = tcb->sporadic;
DEBUGASSERT(sporadic != NULL);
/* As a special case, we can do nothing here if schedule has been locked.
* We cannot drop the priority because that might cause a context switch,
* violating the lock.
*
* What we do instead is just deallocate the timer. When the lock is
* finally released, sched_sporadic_lowpriority() and that will restart
* the interval period. timeslice == -1 is the cue to sched_unlock() that
* this operation is needed.
*/
if (tcb->lockcount > 0)
{
DEBUGASSERT(repl->active && sporadic->nrepls > 0);
tcb->timeslice = -1;
repl->active = false;
sporadic->nrepls--;
return;
}
/* Calculate any part of the budget that was not utilized */
DEBUGASSERT(sporadic->budget >= tcb->timeslice);
pending = sporadic->pending + sporadic->budget - tcb->timeslice;
if (pending > sporadic->budget)
{
/* Limit to the full budget. This can happen if we are falling
* behind and the thread is not getting enough CPU bandwidth to
* satisfy its budget.
*/
pending = sporadic->budget;
}
sporadic->pending = pending;
/* Drop the priority of the thread and start the timer for the rest of the interval */
DEBUGVERIFY(sporadic_interval_start(repl));
}
/****************************************************************************
* Name: sporadic_interval_expire
*
* Description:
* Handles the expiration of a replenishment interval by starting the
* next replenishment interval.
*
* Input Parameters:
* Standard watchdog parameters
*
* Returned Value:
* None
*
* Assumptions:
* The thread is still running and is still using the sporadic
* scheduling policy.
*
****************************************************************************/
static void sporadic_interval_expire(int argc, wdparm_t arg1, ...)
{
FAR struct replenishment_s *repl = (FAR struct replenishment_s *)arg1;
DEBUGASSERT(argc == 1 && repl != NULL);
/* Start the next replenishment interval */
DEBUGVERIFY(sched_sporadic_replenish_start(tcb));
DEBUGVERIFY(sporadic_budget_next(repl));
}
/************************************************************************
* Public Functions
************************************************************************/
/****************************************************************************
* Name: sporadic_timer_cancel
*
* Description:
* Cancel all timers. We do this if/when the budget time expires while the
* scheduler is locked. Basically we need to stop everything and restart
* when the scheduler is unlocked.
*
* Input Parameters:
* tcb - The TCB of the thread that has the scheduler locked
*
* Returned Value:
* None
*
****************************************************************************/
/************************************************************************
static void sporadic_timer_cancel(FAR struct tcb_s *tcb)
{
FAR struct sporadic_s *sporadic;
FAR struct replenishment_s *repl;
int i;
DEBUGASSERT(tcb && tcb->sporadic);
sporadic = tcb->sporadic;
/* Cancel all timers. */
for (i = 0; i < CONFIG_SCHED_SPORADIC_MAXREPL; i++)
{
repl = &sporadic->replenishments[i];
/* Cancel any outstanding timer activity */
wd_cancel(&repl->timer);
repl->active = false;
}
sporadic->nrepls = 0;
}
/****************************************************************************
* Name: sporadic_alloc_repl
*
* Description:
* Allocate a replenishment timer structure.
*
* Input Parameters:
* sporadic - The task's sporadic scheduling state.
*
* Returned Value:
* The allocated replenishment timer structure; NULL is returned on a failure
*
****************************************************************************/
FAR struct replenishment_s *
sporadic_alloc_repl(FAR struct sporadic_s *sporadic)
{
FAR struct replenishment_s *repl = NULL;
int i;
if (sporadic->nrepls < sporadic->max_repl)
{
/* Allocate a new replenishment timer */
DEBUGASSERT(sporadic->max_repl <= CONFIG_SCHED_SPORADIC_MAXREPL);
for (i = 0; i < sporadic->max_repl; i++)
{
FAR struct replenishment_s *tmp = &sporadic->replenishments[i];
if (!tmp->active)
{
repl = tmp;
repl->active = true;
sporadic->nrepls++;
break;
}
}
/* Since we no that we have not yet reached the max_repl number of
* timers, the above search should never fail.
*/
DEBUGASSERT(repl != NULL);
}
return repl;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: sched_sporadic_initialize
*
* Description:
* Allocate resources needed by the sporadic scheduling policy.
*
* Input Parameters:
* tcb - TCB of the thread whose priority is being boosted.
*
* Returned Value:
* Returns zero (OK) on success or a negated errno value on failure.
*
****************************************************************************/
int sched_sporadic_initialize(FAR struct tcb_s *tcb)
{
FAR struct sporadic_s *sporadic;
int i;
DEBUGASSERT(tcb != NULL && tcb->sporadic == NULL);
/* Allocate the sporadic add-on data structure that will hold the
* sporadic scheduling parameters and state data.
*/
sporadic = (FAR struct sporadic_s *)kmm_zalloc(sizeof(struct sporadic_s));
if (sporadic == NULL)
{
return -ENOMEM;
}
/* The initialize required is to set the back pointer to the TCB in
* each of the replenishment structures.
*/
for (i = 0; i < CONFIG_SCHED_SPORADIC_MAXREPL; i++)
{
sporadic->replenishments[i].tcb = tcb;
}
/* Hook the sporadic add-on into the TCB */
tcb->sporadic = sporadic;
return OK;
}
/****************************************************************************
* Name: sched_sporadic_start
*
* Description:
@ -176,12 +582,12 @@ static void sched_sporadic_expire(int argc, wdparm_t arg1, ...)
*
* - When starting a pthread with sporadic scheduling specified in
* the pthread attributes.
* - When establishing sporadic scheduling policy via
* - When establishing sporadic scheduling policy via
* sched_setscheduler()
* - When the sporadic scheduling parameters are changed via
* sched_setparam().
*
* Parameters:
* Input Parameters:
* tcb - The TCB of the thread that is beginning sporadic scheduling.
*
* Returned Value:
@ -192,38 +598,44 @@ static void sched_sporadic_expire(int argc, wdparm_t arg1, ...)
* - 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);
FAR struct sporadic_s *sporadic;
FAR struct replenishment_s *repl;
/* Cancel and pending low-priority interval timing and re-initialize
* the watchdog timer.
*/
DEBUGASSERT(tcb && tcb->sporadic);
sporadic = tcb->sporadic;
wd_cancel(&tcb->low_dog);
memset(&tcb->low_dog, 0, sizeof(struct wdog_s));
DEBUGASSERT(sporadic->low_priority <= sporadic->hi_priority);
DEBUGASSERT(sporadic->max_repl <= CONFIG_SCHED_SPORADIC_MAXREPL);
DEBUGASSERT(sporadic->budget > 0 && sporadic->budget <= sporadic->repl_period);
DEBUGASSERT(sporadic->nrepls == 0 && sporadic->pending == 0);
/* Then start the first replenishment interval */
/* Allocate the first replenishment timer (should never fail) */
return sched_sporadic_replenish_start(tcb);
repl = sporadic_alloc_repl(sporadic);
DEBUGASSERT(repl != NULL && sporadic->nrepls == 1);
/* Then start the first interval */
return sporadic_budget_start(tcb, repl, sporadic->budget);
}
/************************************************************************
/****************************************************************************
* Name: sched_sporadic_stop
*
* Description:
* Called to terminate sporadic scheduling on a given thread. This
* function is called in the following circumstances:
* Called to terminate sporadic scheduling on a given thread and to
* free all resources associated with the policy. 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:
* Input Parameters:
* tcb - The TCB of the thread that is beginning sporadic scheduling.
*
* Returned Value:
@ -234,34 +646,84 @@ int sched_sporadic_start(FAR struct tcb_s *tcb)
* - 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);
DEBUGASSERT(tcb && tcb->sporadic);
/* Cancel and pending low-priority interval timing and re-initialize
* the watchdog timer.
*/
/* Stop all timers, reset scheduling */
wd_cancel(&tcb->low_dog);
memset(&tcb->low_dog, 0, sizeof(struct wdog_s));
(void)sched_sporadic_reset(tcb);
/* Reset sporadic scheduling parameters */
/* The free the container holder the sporadic scheduling parameters */
tcb->hi_priority = 0;
tcb->low_priority = 0;
#ifdef __REVISIT_REPLENISHMENTS
tcb->max_repl = 0;
tcb->nrepl = 0;
#endif
tcb->timeslice = 0;
tcb->repl_period = 0;
tcb->budget = 0;
kmm_free(tcb->sporadic);
tcb->sporadic = NULL;
return OK;
}
/************************************************************************
/****************************************************************************
* Name: sched_sporadic_reset
*
* Description:
* Called to stop sporadic scheduling on a given thread. This
* function is called in the following circumstances:
*
* - When the sporadic scheduling parameters are changed via
* sched_setparam()
* - From sched_sporadic_stop when under those conditions.
*
* Input Parameters:
* tcb - The TCB of the thread that is beginning sporadic scheduling.
*
* Returned 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_reset(FAR struct tcb_s *tcb)
{
FAR struct sporadic_s *sporadic;
FAR struct replenishment_s *repl;
int i;
DEBUGASSERT(tcb && tcb->sporadic);
sporadic = tcb->sporadic;
/* Cancel all timers. */
for (i = 0; i < CONFIG_SCHED_SPORADIC_MAXREPL; i++)
{
repl = (FAR struct replenishment_s *)&sporadic->replenishments[i];
/* Cancel any outstanding timer activity */
wd_cancel(&repl->timer);
/* Re-initialize replenishment data */
repl->tcb = tcb;
}
/* Reset sporadic scheduling parameters and state data */
sporadic->hi_priority = 0;
sporadic->low_priority = 0;
sporadic->max_repl = 0;
sporadic->nrepls = 0;
sporadic->repl_period = 0;
sporadic->budget = 0;
sporadic->pending = 0;
return OK;
}
/****************************************************************************
* Name: sched_sporadic_resume
*
* Description:
@ -274,7 +736,7 @@ int sched_sporadic_stop(FAR struct tcb_s *tcb)
* This function does nothing if the budget phase as already elapsed or
* the maximum numer of replenishments have already been performed.
*
* Parameters:
* Input Parameters:
* tcb - The TCB of the thread that is beginning sporadic scheduling.
*
* Returned Value:
@ -285,37 +747,40 @@ int sched_sporadic_stop(FAR struct tcb_s *tcb)
* - All sporadic scheduling parameters in the TCB are valid
* - The low priority interval timer is not running
*
************************************************************************/
****************************************************************************/
int sched_sporadic_resume(FAR struct tcb_s *tcb)
{
DEBUGASSERT(tcb);
FAR struct sporadic_s *sporadic;
FAR struct replenishment_s *repl;
uint32_t budget;
/* REVISIT: This logic is wrong. In order to correctly implement
* replenishments, we would need to add: (1) logic to keep more
* accurate accounting of the expended budget execution time, and (2)
* multiple timers to handle the nested replenishment intervals.
*
* The logic here works as is but effective max_repl == 1.
DEBUGASSERT(tcb && tcb->sporadic);
sporadic = tcb->sporadic;
/* Check if 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.
*/
#ifdef __REVISIT_REPLENISHMENTS
/* 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)
if (tcb->timeslice > 0)
{
tcb->timeslice = tcb->budget;
tcb->nrepl--;
/* Allocate a new replenishment timer */
repl = sporadic_alloc_repl(sporadic);
if (repl)
{
budget = tcb->timeslice;
return sporadic_budget_start(tcb, repl, budget);
}
return -ENOMEM;
}
#endif
return OK;
}
/************************************************************************
/****************************************************************************
* Name: sched_sporadic_process
*
* Description:
@ -324,26 +789,31 @@ int sched_sporadic_resume(FAR struct tcb_s *tcb)
* - 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.
* Input 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.
* noswitches - We are running in a context where context switching is
* not permitted.
*
* Returned 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
* Zero is returned if we are in the low-priority 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);
FAR struct sporadic_s *sporadic;
DEBUGASSERT(tcb != NULL && tcb->sporadic != NULL && ticks > 0);
/* If we are in the low-priority phase of the replenishment interval,
* then just return zero.
@ -366,7 +836,7 @@ uint32_t sched_sporadic_process(FAR struct tcb_s *tcb, uint32_t ticks,
* if there ever were the case.
*
* Notice that in the case where were are stuck in the high priority
* phase with scheduler locke, timeslice will by -1 and any value of
* phase with scheduler locked, timeslice will by -1 and any value of
* ticks will pass this test.
*/
@ -384,10 +854,11 @@ uint32_t sched_sporadic_process(FAR struct tcb_s *tcb, uint32_t ticks,
* case.
*/
sporadic_timer_cancel(tcb);
tcb->timeslice = -1;
return 0;
}
/* 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
@ -410,15 +881,16 @@ uint32_t sched_sporadic_process(FAR struct tcb_s *tcb, uint32_t ticks,
* thing to do, but is certainly permitted.
*/
if (tcb->budget >= tcb->repl_period)
sporadic = tcb->sporadic;
if (sporadic->budget >= sporadic->repl_period)
{
tcb->timeslice = tcb->budget;
return tcb->budget;
tcb->timeslice = sporadic->budget;
return sporadic->budget;
}
/* Otherwise enter the low-priority phase of the replenishment cycle */
sched_sporadic_lowpriority(tcb);
sporadic_set_lowpriority(tcb);
return 0;
}
@ -433,7 +905,7 @@ uint32_t sched_sporadic_process(FAR struct tcb_s *tcb, uint32_t ticks,
}
}
/************************************************************************
/****************************************************************************
* Name: sched_sporadic_lowpriority
*
* Description:
@ -444,8 +916,9 @@ uint32_t sched_sporadic_process(FAR struct tcb_s *tcb, uint32_t ticks,
* - sched_unlock(). When the budget expires while the thread had the
* scheduler locked.
*
* Parameters:
* tcb - The TCB of the thread that is entering the low priority phase.
* Input Parameters:
* tcb - The TCB of the thread that is entering the low priority phase.
* restart - Restart timers.
*
* Returned Value:
* None
@ -454,44 +927,31 @@ uint32_t sched_sporadic_process(FAR struct tcb_s *tcb, uint32_t ticks,
* - Interrupts are disabled
* - All sporadic scheduling parameters in the TCB are valid
*
************************************************************************/
****************************************************************************/
void sched_sporadic_lowpriority(FAR struct tcb_s *tcb)
{
DEBUGASSERT(tcb);
FAR struct sporadic_s *sporadic;
FAR struct replenishment_s *repl;
/* Enter the low-priority phase of the replenishment cycle */
DEBUGASSERT(tcb && tcb->sporadic);
sporadic = tcb->sporadic;
/* 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.
/* Allocate a new replenishment timer. There should be no timers
* active at this phase since they were stopped in sched_sporadic_process().
*/
DEBUGVERIFY(wd_start(&tcb->low_dog, tcb->repl_period - tcb->budget,
sched_sporadic_expire, 1, (wdentry_t)tcb));
DEBUGASSERT(sporadic->nrepls < sporadic->max_repl);
repl = sporadic_alloc_repl(sporadic);
DEBUGASSERT(repl != NULL);
#ifdef CONFIG_PRIORITY_INHERITANCE
/* If the priority was boosted above the higher priority, than just
* reset the base priority.
*/
/* Drop the priority of thread, possible causing a context switch. */
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;
}
#endif
/* Otherwise drop the priority of thread, possible causing a context
* switch.
*/
DEBUGVERIFY(sched_reprioritize(tcb, tcb->low_priority));
DEBUGVERIFY(sporadic_interval_start(repl));
}
#endif /* CONFIG_SCHED_SPORADIC */

View File

@ -76,7 +76,7 @@
#if CONFIG_RR_INTERVAL > 0
# define KEEP_ALIVE_TICKS MSEC2TICK(CONFIG_RR_INTERVAL)
#else
# define KEEP_ALIVE_TICKS MSEC2TICK(50)
# define KEEP_ALIVE_TICKS MSEC2TICK(80)
#endif
#ifndef MIN