Implements the tickless OS

This commit is contained in:
Gregory Nutt 2014-08-07 11:39:16 -06:00
parent 317b66ca37
commit 520a51a3e1
16 changed files with 823 additions and 316 deletions

46
TODO
View File

@ -1,4 +1,4 @@
NuttX TODO List (Last updated August 6, 2014)
NuttX TODO List (Last updated August 7, 2014)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This file summarizes known NuttX bugs, limitations, inconsistencies with
@ -8,7 +8,7 @@ board port.
nuttx/
(10) Task/Scheduler (sched/)
(9) Task/Scheduler (sched/)
(1) Memory Managment (mm/)
(3) Signals (sched/, arch/)
(2) pthreads (sched/)
@ -18,7 +18,7 @@ nuttx/
(13) Network (net/, drivers/net)
(4) USB (drivers/usbdev, drivers/usbhost)
(10) Libraries (libc/, )
(12) File system/Generic drivers (fs/, drivers/)
(13) File system/Generic drivers (fs/, drivers/)
(6) Graphics subystem (graphics/)
(1) Pascal add-on (pcode/)
(1) Documentation (Documentation/)
@ -62,37 +62,6 @@ o Task/Scheduler (sched/)
Status: Closed. No, this behavior will not be implemented.
Priority: Medium, required for good emulation of process/pthread model.
Title: TICKLESS OS
Description: On a side note, I have thought about a tick-less timer for the OS
for a long time. Basically we could replace the periodic system
timer interrupt with a one-shot interval timer programmed for the
next interesting event time. That is one way to both reduce the
timer interrupt overhead and also to increase the accuracy of
delays.
Current timer processing is in sched/sched_processtimer.c:
1) Calls clock_timer() which just increments a counter (the system
timer -- basically "up-time"). This is only used when code asks
for the current time. In a tickless OS, some substitute answer
for the question "What time is it?" would need to be developed.
You could use an RTC? Or maybe logic that gets the time until the
next interval expiration and computes the current time. The
solution is not too difficult, but depends on a hardware solution.
2) Calls wd_timer() which handles the link list of ordered events:
Each timer event is saved with the delta time to the next event
in the list. So an interval timer would be perfect to implement this.
3) sched_process_timeslice(). Then there is round-robin time-slicing.
The primary advantage of a tickless OS is that is would allow for
reduce power consumptions. That is because timer interrupts will
usually awaken CPUs from reduced power consumption states.
Status: Open. There will probably be no tickless OS implementation unless
someone gets motivated and drives the change.
Priority: Low
Title: pause() NON-COMPLIANCE
Description: In the POSIX description of this function is the pause() function
will suspend the calling thread until delivery of a signal whose
@ -1221,6 +1190,15 @@ o File system / Generic drivers (fs/, drivers/)
Status: Open
Priority: Low
Title: FAT LONG FILENAME COMPATIBILTY
Description: Recently there have been reports that file with long file
names created by NuttX don't have long file names when viewed
on Windows. The long file name support has been around for a
long time and I don't ever having seen this before so I am
suspecting that some evil has crept in.
Status: Open
Priority: Medium
o Graphics subystem (graphics/)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -59,6 +59,7 @@ config ARCH_SH
config ARCH_SIM
bool "Simulation"
select ARCH_HAVE_TICKLESS
---help---
Linux/Cywgin user-mode simulation.

View File

@ -1,5 +1,5 @@
/****************************************************************************
* up_idle.c
* arch/sim/src/up_idle.c
*
* Copyright (C) 2007-2009, 2011-2012, 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>

View File

@ -1,5 +1,5 @@
/****************************************************************************
* arch/sim/up_tickless.c
* arch/sim/src/up_tickless.c
*
* Copyright (C) 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
@ -41,13 +41,11 @@
*
* void up_timer_initialize(void): Initializes the timer facilities. Called
* early in the intialization sequence (by up_intialize()).
* int up_timer_gettime(FAR struct timespec *tp): Returns the current
* int up_timer_gettime(FAR struct timespec *ts): Returns the current
* time from the platform specific time source.
* int up_timer_cancel(void): Cancels the interval timer.
* int up_timer_start(FAR const struct timespec *tp): Start (or re-starts)
* int up_timer_start(FAR const struct timespec *ts): Start (or re-starts)
* the interval timer.
* int up_timer_resolution(FAR struct timespec *tp): Returns the
* resolution of the interval timer.
*
* The RTOS will provide the following interfaces for use by the platform-
* specific interval timer implementation:
@ -138,17 +136,21 @@ void up_timer_initialize(void)
*
* Description:
* Return the elapsed time since power-up (or, more correctly, since
* up_timer_initialize() was called). This function is functionally equivalent
* to:
* up_timer_initialize() was called). This function is functionally
* equivalent to:
*
* int clock_gettime(clockid_t clockid, FAR struct timespec *tp);
* int clock_gettime(clockid_t clockid, FAR struct timespec *ts);
*
* when clockid is CLOCK_MONOTONIC.
*
* This function provides the basis for reporting the current time and
* also is used to eliminate error build-up from small erros in interval
* time calculations.
*
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* tp - Provides the location in which to return the up-time.
* ts - Provides the location in which to return the up-time.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
@ -162,10 +164,10 @@ void up_timer_initialize(void)
*
****************************************************************************/
int up_timer_gettime(FAR struct timespec *tp)
int up_timer_gettime(FAR struct timespec *ts)
{
tp->tv_sec = g_elapsed_time.tv_sec;
tp->tv_nsec = g_elapsed_time.tv_nsec;
ts->tv_sec = g_elapsed_time.tv_sec;
ts->tv_nsec = g_elapsed_time.tv_nsec;
return OK;
}
@ -173,12 +175,21 @@ int up_timer_gettime(FAR struct timespec *tp)
* Name: up_timer_cancel
*
* Description:
* Cancel the interval timer. up_timer_expiration() will not be called.
* Cancel the interval timer and return the time remaining on the timer.
* These two steps need to be as nearly atomic as possible.
* up_timer_expiration() will not be called unless the timer is restarted
* with up_timer_start().
*
* If, as a race condition, the timer has already expired when this
* function is called, then that pending interrupt must be cleared so
* that up_timer_start() and the remaining time of zero should be
* returned.
*
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* None
* ts - Location to return the remaining time. Zero should be returned
* if the timer is not active.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
@ -192,8 +203,23 @@ int up_timer_gettime(FAR struct timespec *tp)
****************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS
int up_timer_cancel(void)
int up_timer_cancel(FAR struct timespec *ts)
{
/* Return the time remaining on the simulated timer */
if (g_timer_active)
{
ts->tv_sec = g_interval_delay.tv_nsec;
ts->tv_nsec = g_interval_delay.tv_sec;
}
else
{
ts->tv_sec = 0;
ts->tv_nsec = 0;
}
/* Disable and reset the simulated timer */
g_interval_delay.tv_nsec = 0;
g_interval_delay.tv_sec = 0;
g_timer_active = false;
@ -211,7 +237,7 @@ int up_timer_cancel(void)
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* tp - Provides the time interval until up_timer_expiration() is called.
* ts - Provides the time interval until up_timer_expiration() is called.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
@ -225,82 +251,14 @@ int up_timer_cancel(void)
****************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS
int up_timer_start(FAR const struct timespec *tp)
int up_timer_start(FAR const struct timespec *ts)
{
g_interval_delay.tv_nsec = tp->tv_nsec;
g_interval_delay.tv_sec = tp->tv_sec;
g_interval_delay.tv_nsec = ts->tv_nsec;
g_interval_delay.tv_sec = ts->tv_sec;
g_timer_active = true;
}
#endif
/****************************************************************************
* Name: up_timer_remaining
*
* Description:
* Return the time remaining until the next timer expiratino.
*
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* tp - Location to return the remaining time. Zero should be returned
* if the timer is not active.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
* Assumptions:
* May be called from interrupt level handling or from the normal tasking
* level. Interrupts may need to be disabled internally to assure
* non-reentrancy.
*
****************************************************************************/
int up_timer_remaining(FAR struct timespec *tp)
{
if (g_timer_active)
{
tp->tv_sec = g_interval_delay.tv_nsec;
tp->tv_nsec = g_interval_delay.tv_sec;
}
else
{
tp->tv_sec = 0;
tp->tv_nsec = 0;
}
return OK;
}
/****************************************************************************
* Name: up_timer_resolution
*
* Description:
* Returns the resolution of the interval timer. This defines the minimal
* time separation between two events and is used for scheduling and for
* setting up timed events.
*
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* tp - Provides the location in which to return the timer resolution.
*
* Returned Value:
*
* Assumptions:
* May be called from interrupt level handling or from the normal tasking
* level. Interrupts may need to be disabled internally to assure
* non-reentrancy.
*
****************************************************************************/
int up_timer_resolution(FAR struct timespec *tp)
{
tp->tv_sec = TICK_SEC;
tp->tv_nsec = TICK_NSEC;
return 0;
}
/****************************************************************************
* Name: up_timer_update
*

View File

@ -948,13 +948,11 @@ int up_prioritize_irq(int irq, int priority);
*
* void up_timer_initialize(void): Initializes the timer facilities. Called
* early in the intialization sequence (by up_intialize()).
* int up_timer_gettime(FAR struct timespec *tp): Returns the current
* int up_timer_gettime(FAR struct timespec *ts): Returns the current
* time from the platform specific time source.
* int up_timer_cancel(void): Cancels the interval timer.
* int up_timer_start(FAR const struct timespec *tp): Start (or re-starts)
* int up_timer_start(FAR const struct timespec *ts): Start (or re-starts)
* the interval timer.
* int up_timer_resolution(FAR struct timespec *tp): Returns the
* resolution of the interval timer.
*
* The RTOS will provide the following interfaces for use by the platform-
* specific interval timer implementation:
@ -998,17 +996,21 @@ void up_timer_initialize(void);
*
* Description:
* Return the elapsed time since power-up (or, more correctly, since
* up_timer_initialize() was called). This function is functionally equivalent
* to:
* up_timer_initialize() was called). This function is functionally
* equivalent to:
*
* int clock_gettime(clockid_t clockid, FAR struct timespec *tp);
* int clock_gettime(clockid_t clockid, FAR struct timespec *ts);
*
* when clockid is CLOCK_MONOTONIC.
*
* This function provides the basis for reporting the current time and
* also is used to eliminate error build-up from small erros in interval
* time calculations.
*
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* tp - Provides the location in which to return the up-time.
* ts - Provides the location in which to return the up-time.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
@ -1023,73 +1025,27 @@ void up_timer_initialize(void);
****************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS
int up_timer_gettime(FAR struct timespec *tp);
int up_timer_gettime(FAR struct timespec *ts);
#endif
/****************************************************************************
* Name: up_timer_cancel
*
* Description:
* Cancel the interval timer. up_timer_expiration() will not be called.
* Cancel the interval timer and return the time remaining on the timer.
* These two steps need to be as nearly atomic as possible.
* up_timer_expiration() will not be called unless the timer is restarted
* with up_timer_start().
*
* If, as a race condition, the timer has already expired when this
* function is called, then that pending interrupt must be cleared so
* that up_timer_start() and the remaining time of zero should be
* returned.
*
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* None
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
* Assumptions:
* May be called from interrupt level handling or from the normal tasking
* level. Interrupts may need to be disabled internally to assure
* non-reentrancy.
*
****************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS
int up_timer_cancel(void);
#endif
/****************************************************************************
* Name: up_timer_start
*
* Description:
* Start the interval timer. up_timer_expiration() will be called at the
* completion of the timeout (unless up_timer_cancel is called to stop
* the timing.
*
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* tp - Provides the time interval until up_timer_expiration() is called.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
* Assumptions:
* May be called from interrupt level handling or from the normal tasking
* level. Interrupts may need to be disabled internally to assure
* non-reentrancy.
*
****************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS
int up_timer_start(FAR const struct timespec *tp);
#endif
/****************************************************************************
* Name: up_timer_remaining
*
* Description:
* Return the time remaining until the next timer expiratino.
*
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* tp - Location to return the remaining time. Zero should be returned
* ts - Location to return the remaining time. Zero should be returned
* if the timer is not active.
*
* Returned Value:
@ -1104,23 +1060,25 @@ int up_timer_start(FAR const struct timespec *tp);
****************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS
int up_timer_remaining(FAR struct timespec *tp);
int up_timer_cancel(FAR struct timespec *ts);
#endif
/****************************************************************************
* Name: up_timer_resolution
* Name: up_timer_start
*
* Description:
* Returns the resolution of the interval timer. This defines the minimal
* time separation between two events and is used for scheduling and for
* setting up timed events.
* Start the interval timer. up_timer_expiration() will be called at the
* completion of the timeout (unless up_timer_cancel is called to stop
* the timing.
*
* Provided by platform-specific code and called from the RTOS base code.
*
* Input Parameters:
* tp - Provides the location in which to return the timer resolution.
* ts - Provides the time interval until up_timer_expiration() is called.
*
* Returned Value:
* Zero (OK) is returned on success; a negated errno value is returned on
* any failure.
*
* Assumptions:
* May be called from interrupt level handling or from the normal tasking
@ -1130,7 +1088,7 @@ int up_timer_remaining(FAR struct timespec *tp);
****************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS
int up_timer_resolution(FAR struct timespec *tp);
int up_timer_start(FAR const struct timespec *ts);
#endif
/****************************************************************************
@ -1234,8 +1192,12 @@ void sched_process_timer(void);
* interval timer used to implemented the tick-less OS expires.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
* Assumptions/Limitations:
* Base code implementation assumes that this function is called from
* interrupt handling logic with interrupts disabled.
*

View File

@ -53,10 +53,13 @@ endif # DISABLE_OS_API
menu "Clocks and Timers"
config ARCH_HAVE_TICKLESS
bool
config SCHED_TICKLESS
bool "Support tick-less OS"
default n
depends on EXPERIMENTAL
depends on ARCH_HAVE_TICKLESS && EXPERIMENTAL
---help---
Be default, system time is driven by a periodic timer interrupt. An
alternative configurations is a tick-less configuration in which
@ -342,6 +345,7 @@ menu "Performance Monitoring"
config SCHED_CPULOAD
bool "Enable CPU load monitoring"
default n
select SCHED_CPULOAD_EXTCLK if SCHED_TICKLESS
---help---
If this option is selected, the timer interrupt handler will monitor
if the system is IDLE or busy at the time of that the timer interrupt

View File

@ -119,7 +119,9 @@ ENV_SRCS += env_clearenv.c env_getenv.c env_putenv.c env_setenv.c env_unsetenv.c
WDOG_SRCS = wd_initialize.c wd_create.c wd_start.c wd_cancel.c wd_delete.c
WDOG_SRCS += wd_gettime.c
ifneq ($(CONFIG_SCHED_TICKLESS),y)
ifeq ($(CONFIG_SCHED_TICKLESS),y)
TIME_SRCS += sched_timerexpiration.c
else
TIME_SRCS += sched_processtimer.c
endif

View File

@ -236,9 +236,10 @@ int os_bringup(void);
void weak_function task_initialize(void);
#endif
void task_start(void);
int task_schedsetup(FAR struct task_tcb_s *tcb, int priority, start_t start,
main_t main, uint8_t ttype);
int task_argsetup(FAR struct task_tcb_s *tcb, FAR const char *name, FAR char * const argv[]);
int task_schedsetup(FAR struct task_tcb_s *tcb, int priority,
start_t start, main_t main, uint8_t ttype);
int task_argsetup(FAR struct task_tcb_s *tcb, FAR const char *name,
FAR char * const argv[]);
int task_exit(void);
int task_terminate(pid_t pid, bool nonblocking);
void task_exithook(FAR struct tcb_s *tcb, int status, bool nonblocking);
@ -250,14 +251,24 @@ bool sched_mergepending(void);
void sched_addblocked(FAR struct tcb_s *btcb, tstate_t task_state);
void sched_removeblocked(FAR struct tcb_s *btcb);
int sched_setpriority(FAR struct tcb_s *tcb, int sched_priority);
#ifdef CONFIG_PRIORITY_INHERITANCE
int sched_reprioritize(FAR struct tcb_s *tcb, int sched_priority);
#else
# define sched_reprioritize(tcb,sched_priority) sched_setpriority(tcb,sched_priority)
# define sched_reprioritize(tcb,sched_priority) \
sched_setpriority(tcb,sched_priority)
#endif
#ifdef CONFIG_SCHED_TICKLESS
void sched_timer_reassess(void);
#else
# define sched_timer_reassess()
#endif
#if defined(CONFIG_SCHED_CPULOAD) && !defined(CONFIG_SCHED_CPULOAD_EXTCLK)
void weak_function sched_process_cpuload(void);
#endif
bool sched_verifytcb(FAR struct tcb_s *tcb);
int sched_releasetcb(FAR struct tcb_s *tcb, uint8_t ttype);

View File

@ -1,7 +1,7 @@
/****************************************************************************
* sched/sched_addreadytorun.c
*
* Copyright (C) 2007-2009 Gregory Nutt. All rights reserved.
* Copyright (C) 2007-2009, 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -75,7 +75,7 @@
* Description:
* This function adds a TCB to the ready to run
* list. If the currently active task has preemption disabled
* and the new TCB would cause this task to be preempted, the
* and the new TCB would cause this task to be pre-empted, the
* new task is added to the g_pendingtasks list instead. The
* pending tasks will be made ready-to-run when preemption
* is unlocked.
@ -84,7 +84,7 @@
* btcb - Points to the blocked TCB that is ready-to-run
*
* Return Value:
* true if the currently active task (the head of the g_readytorun list)
* true if the currently active task (the head of the ready-to-run list)
* has changed.
*
* Assumptions:
@ -94,7 +94,7 @@
* - The caller has already removed the input rtcb from
* whatever list it was in.
* - The caller handles the condition that occurs if the
* the head of the g_readytorun list is changed.
* the head of the ready-to-run list is changed.
*
****************************************************************************/
@ -105,7 +105,7 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
/* Check if pre-emption is disabled for the current running task and if
* the new ready-to-run task would cause the current running task to be
* preempted.
* pre-empted.
*/
if (rtcb->lockcount && rtcb->sched_priority < btcb->sched_priority)
@ -119,7 +119,7 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
ret = false;
}
/* Otherwise, add the new task to the g_readytorun task list */
/* Otherwise, add the new task to the ready-to-run task list */
else if (sched_addprioritized(btcb, (FAR dq_queue_t*)&g_readytorun))
{
@ -127,7 +127,7 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
sched_note_switch(rtcb, btcb);
/* The new btcb was added at the head of the g_readytorun list. It
/* The new btcb was added at the head of the ready-to-run list. It
* is now to new active task!
*/
@ -135,11 +135,19 @@ bool sched_addreadytorun(FAR struct tcb_s *btcb)
btcb->task_state = TSTATE_TASK_RUNNING;
btcb->flink->task_state = TSTATE_TASK_READYTORUN;
#if CONFIG_RR_INTERVAL > 0
/* Whenever the task at the head of the ready-to-run chances, we
* must reassess the interval time that controls time-slicing.
*/
sched_timer_reassess();
#endif
ret = true;
}
else
{
/* The new btcb was added in the middle of the g_readytorun list */
/* The new btcb was added in the middle of the ready-to-run list */
btcb->task_state = TSTATE_TASK_READYTORUN;
ret = false;

View File

@ -194,8 +194,8 @@ void sched_process_timer(void)
#endif
#if defined(CONFIG_SCHED_CPULOAD) && !defined(CONFIG_SCHED_CPULOAD_EXTCLK)
/* Perform CPU load measurements (before any timer-initiated context switches
* can occur)
/* Perform CPU load measurements (before any timer-initiated context
* switches can occur)
*/
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
@ -206,14 +206,9 @@ void sched_process_timer(void)
}
#endif
/* Process watchdogs (if in the link) */
/* Process watchdogs */
#ifdef CONFIG_HAVE_WEAKFUNCTIONS
if (wd_timer != NULL)
#endif
{
wd_timer();
}
wd_timer();
/* Check if the currently executing task has exceeded its
* timeslice.

View File

@ -0,0 +1,379 @@
/************************************************************************
* sched/sched_timerexpiration.c
*
* Copyright (C) 2014 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 <nuttx/compiler.h>
#include <time.h>
#include <assert.h>
#include <debug.h>
#if CONFIG_RR_INTERVAL > 0
# include <sched.h>
# include <nuttx/arch.h>
#endif
#include "os_internal.h"
#include "wd_internal.h"
#include "clock_internal.h"
#ifdef CONFIG_SCHED_TICKLESS
/************************************************************************
* Pre-processor Definitions
************************************************************************/
#ifndef MIN
# define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
# define MAX(a,b) (((a) > (b)) ? (a) : (b))
#endif
/************************************************************************
* Private Type Declarations
************************************************************************/
/************************************************************************
* Public Variables
************************************************************************/
/************************************************************************
* Private Variables
************************************************************************/
/* This is the duration of the currently active timer or, when
* sched_timer_expiration() is called, the duration of interval timer
* that just expired. The value zero means that no timer was active.
*/
static unsigned int g_timer_interval;
/************************************************************************
* Private Functions
************************************************************************/
/************************************************************************
* Name: sched_process_timeslice
*
* Description:
* Check if the currently executing task has exceeded its time slice.
*
* Inputs:
* ticks - The number of ticks that have elapsed on the interval timer.
* noswitches - True: Can't do context switches now.
*
* Return Value:
* The number if ticks remaining until the next time slice expires.
* Zero is returned if there is no time slicing (i.e., the task at the
* head of the ready-to-run list does not support round robin
* scheduling).
*
* The value one may returned under certain circumstances that probably
* can't happen. The value one is the minimal timer setup and it means
* that a context switch is needed now, but cannot be performed because
* noswitches == true.
*
************************************************************************/
#if CONFIG_RR_INTERVAL > 0
static unsigned int
sched_process_timeslice(unsigned int ticks, bool noswitches)
{
FAR struct tcb_s *rtcb = (FAR struct tcb_s*)g_readytorun.head;
unsigned int ret = 0;
int decr;
/* Check if the currently executing task uses round robin
* scheduling.
*/
if ((rtcb->flags & TCB_FLAG_ROUND_ROBIN) != 0)
{
/* Now much can we decrement the timeslice delay? If 'ticks'
* is greater than the timeslice value, then we ignore any
* excess amount.
*
* 'ticks' should never be greater than the remaining timeslice.
* We try to handle that gracefully but it would be an error
* in the scheduling if there ever were the case.
*/
DEBUGASSERT(ticks <= rtcb->timeslice);
decr = MIN(rtcb->timeslice, ticks);
/* Decrement the timeslice counter */
rtcb->timeslice -= decr;
/* Did decrementing the timeslice counter cause the timeslice to
* expire?
*
* If the task has pre-emption disabled. Then we will freeze the
* timeslice count at the value until pre-emption has been enabled.
*/
ret = rtcb->timeslice;
if (rtcb->timeslice <= 0 && rtcb->lockcount == 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 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)
{
ret = 1;
}
else
{
/* Reset the timeslice. */
rtcb->timeslice = CONFIG_RR_INTERVAL / MSEC_PER_TICK;
ret = rtcb->timeslice;
/* We know we are at the head of the ready to run
* prioritized list. We must be the highest priority
* task eligible for execution. Check the next task
* in the ready to run list. If it is the same
* priority, then we need to relinquish the CPU and
* give that task a shot.
*/
if (rtcb->flink &&
rtcb->flink->sched_priority >= rtcb->sched_priority)
{
/* Just resetting the task priority to its current
* value. This this will cause the task to be
* rescheduled behind any other tasks at the same
* priority.
*/
up_reprioritize_rtr(rtcb, rtcb->sched_priority);
/* We will then need to return timeslice remaining for
* the new task at the head of the ready to run list
*/
rtcb = (FAR struct tcb_s*)g_readytorun.head;
if ((rtcb->flags & TCB_FLAG_ROUND_ROBIN) != 0)
{
/* The new task at the head of the ready to run
* list does not support round robin scheduling.
*/
ret = 0;
}
else
{
/* Return the time remaining on slice for the new
* task (or at least one for the same reasons as
* discussed above).
*/
ret = rtcb->timeslice > 0 ? rtcb->timeslice : 1;
}
}
}
}
}
return ret;
}
#endif
/****************************************************************************
* Name: sched_timer_process
*
* Description:
* Process events on timer expiration.
*
* Input Parameters:
* ticks - The number of ticks that have elapsed on the interval timer.
* noswitches - True: Can't do context switches now.
*
* Returned Value:
* Base code implementation assumes that this function is called from
* interrupt handling logic with interrupts disabled.
*
****************************************************************************/
static void sched_timer_process(unsigned int ticks, bool noswitches)
{
unsigned int nextime = UINT_MAX;
bool needtimer = false;
uint32_t msecs;
uint32_t secs;
uint32_t nsecs;
unsigned int tmp;
int ret;
/* Process watchdogs */
tmp = wd_timer(ticks);
if (tmp > 0)
{
nextime = tmp;
needtimer = true;
}
#if CONFIG_RR_INTERVAL > 0
/* Check if the currently executing task has exceeded its
* timeslice.
*/
tmp = sched_process_timeslice(ticks, noswitches);
if (tmp > 0 && tmp < nextime)
{
nextime = tmp;
needtimer = true;
}
#endif
/* Set up the next timer interval (or not) */
g_timer_interval = 0;
if (needtimer)
{
struct timespec ts;
/* Save new timer interval */
g_timer_interval = nextime;
/* Convert ticks to a struct timespec that up_timer_start() can
* understand.
*/
msecs = MSEC_PER_TICK * nextime;
secs = msecs / MSEC_PER_SEC;
nsecs = (msecs - (secs * MSEC_PER_SEC)) * NSEC_PER_MSEC;
ts.tv_sec = (time_t)secs;
ts.tv_nsec = (long)nsecs;
/* [Re-]start the interval timer */
ret = up_timer_start(&ts);
if (ret < 0)
{
slldbg("ERROR: up_timer_start failed: %d\n");
UNUSED(ret);
}
}
}
/************************************************************************
* Public Functions
************************************************************************/
/****************************************************************************
* Name: sched_timer_expiration
*
* Description:
* if CONFIG_SCHED_TICKLESS is defined, then this function is provided by
* the RTOS base code and called from platform-specific code when the
* interval timer used to implemented the tick-less OS expires.
*
* Input Parameters:
*
* Returned Value:
* Base code implementation assumes that this function is called from
* interrupt handling logic with interrupts disabled.
*
****************************************************************************/
void sched_timer_expiration(void)
{
sched_timer_process(g_timer_interval, false);
}
/****************************************************************************
* Name: sched_timer_reassess
*
* Description:
* It is necessary to re-assess the timer interval in several
* circumstances:
*
* - If the watchdog at the head of the expiration list changes (or if its
* delay changes. This can occur as a consequence of the actions of
* wd_start() or wd_cancel().
* - Any context switch occurs, changing the task at the head of the
* ready-to-run list. The task at the head of list may require
* different timeslice processing (or no timeslice at all).
* - When pre-emption is re-enabled. A previous time slice may have
* expired while pre-emption was enabled and now needs to be executed.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void sched_timer_reassess(void)
{
struct timespec ts;
unsigned int ticks;
/* Get the time remaining on the interval timer and cancel the timer. */
(void)up_timer_cancel(&ts);
/* Convert to ticks */
ticks = SEC2TICK(ts.tv_sec);
ticks += NSEC2TICK(ts.tv_nsec);
/* Handle the partial timer. This will reassess all timer conditions and
* re-start the interval timer with the correct delay. Context switches
* are not permitted in this case because we are not certain of the
* calling conditions.
*/
sched_timer_process(g_timer_interval - ticks, true);
}
#endif /* CONFIG_SCHED_TICKLESS */

View File

@ -1,7 +1,7 @@
/************************************************************************
* sched/sched_unlock.c
*
* Copyright (C) 2007, 2009 Gregory Nutt. All rights reserved.
* Copyright (C) 2007, 2009, 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -86,7 +86,7 @@
int sched_unlock(void)
{
struct tcb_s *rtcb = (struct tcb_s*)g_readytorun.head;
FAR struct tcb_s *rtcb = (FAR struct tcb_s*)g_readytorun.head;
/* Check for some special cases: (1) rtcb may be NULL only during
* early boot-up phases, and (2) sched_unlock() should have no
@ -118,10 +118,41 @@ int sched_unlock(void)
* g_pendingtasks.
*/
if (g_pendingtasks.head)
{
up_release_pending();
}
if (g_pendingtasks.head)
{
up_release_pending();
}
#if CONFIG_RR_INTERVAL > 0
/* If (1) the task that was running running supported round-robin
* scheduling and (2) if its time slice has already expired, but (3)
* it could not slice out because pre-emption was disabled, then
* we need to swap the task out now and reassess the interval timer
* for the next time slice.
*/
if ((rtcb->flags & TCB_FLAG_ROUND_ROBIN) != 0 &&
rctb->timeslice == 0)
{
/* Yes.. that is the situation. But one more thing. The call
* to up_release_pending() above may have actually replaced
* the task at the head of the read-to-run list. In that case,
* we need only to reset the timeslice value back to the
* maximum.
*/
if (rtcb != (FAR struct tcb_s*)g_readytorun.head)
{
rtcb->timeslice = CONFIG_RR_INTERVAL / MSEC_PER_TICK;
}
#ifdef CONFIG_SCHED_TICKLESS
else
{
sched_timer_reassess();
}
#endif
}
#endif
}
irqrestore(flags);

View File

@ -1,7 +1,7 @@
/****************************************************************************
* sched/wd_cancel.c
*
* Copyright (C) 2007-2009 Gregory Nutt. All rights reserved.
* Copyright (C) 2007-2009, 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -76,7 +76,7 @@
*
* Description:
* This function cancels a currently running watchdog timer. Watchdog
* timers may be canceled from the interrupt level.
* timers may be cancelled from the interrupt level.
*
* Parameters:
* wdid - ID of the watchdog to cancel.
@ -88,7 +88,7 @@
*
****************************************************************************/
int wd_cancel (WDOG_ID wdid)
int wd_cancel(WDOG_ID wdid)
{
wdog_t *curr;
wdog_t *prev;
@ -101,7 +101,7 @@ int wd_cancel (WDOG_ID wdid)
saved_state = irqsave();
/* Make sure that the watchdog is initialed (non-NULL) and is still active */
/* Make sure that the watchdog is initialized (non-NULL) and is still active */
if (wdid && wdid->active)
{
@ -126,7 +126,7 @@ int wd_cancel (WDOG_ID wdid)
ASSERT(curr);
/* If there is a watchdog in the timer queue after the one that
* is being canceled, then it inherits the remaining ticks.
* is being cancelled, then it inherits the remaining ticks.
*/
if (curr->next)
@ -138,22 +138,31 @@ int wd_cancel (WDOG_ID wdid)
if (prev)
{
/* Remove the watchdog from mid- or end-of-queue */
(void)sq_remafter((FAR sq_entry_t*)prev, &g_wdactivelist);
}
else
{
/* Remove the watchdog at the head of the queue */
(void)sq_remfirst(&g_wdactivelist);
/* Reassess the interval timer that will generate the next
* interval event.
*/
sched_timer_reassess();
}
wdid->next = NULL;
/* Mark the watchdog inactive */
wdid->next = NULL;
wdid->active = false;
/* Return success */
ret = OK;
/* Mark the watchdog inactive */
wdid->active = false;
}
irqrestore(saved_state);

View File

@ -92,7 +92,7 @@ sq_queue_t g_wdactivelist;
* Name: wd_initialize
*
* Description:
* This function initalized the watchdog data structures
* This function initializes the watchdog data structures
*
* Parameters:
* None

View File

@ -78,6 +78,14 @@ typedef struct wdog_s wdog_t;
* Public Variables
************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/* The g_wdfreelist data structure is a singly linked list of watchdogs
* available to the system for delayed function use.
*/
@ -101,16 +109,57 @@ extern sq_queue_t g_wdactivelist;
* Public Function Prototypes
************************************************************************/
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/************************************************************************
* Name: wd_initialize
*
* Description:
* This function initializes the watchdog data structures
*
* Parameters:
* None
*
* Return Value:
* None
*
* Assumptions:
* This function must be called early in the initialization sequence
* before the timer interrupt is attached and before any watchdog
* services are used.
*
************************************************************************/
void weak_function wd_initialize(void);
void weak_function wd_timer(void);
/****************************************************************************
* Name: wd_timer
*
* Description:
* This function is called from the timer interrupt handler to determine
* if it is time to execute a watchdog function. If so, the watchdog
* function will be executed in the context of the timer interrupt
* handler.
*
* Parameters:
* ticks - If CONFIG_SCHED_TICKLESS is defined then the number of ticks
* in the the interval that just expired is provided. Otherwise,
* this function is called on each timer interrupt and a value of one
* is implicit.
*
* Return Value:
* If CONFIG_SCHED_TICKLESS is defined then the number of ticks for the
* next delay is provided (zero if no delay). Otherwise, this function
* has no returned value.
*
* Assumptions:
* Called from interrupt handler logic with interrupts disabled.
*
****************************************************************************/
#ifdef CONFIG_SCHED_TICKLESS
unsigned int wd_timer(int ticks);
#else
void wd_timer(void);
#endif
#undef EXTERN
#ifdef __cplusplus

View File

@ -1,7 +1,7 @@
/****************************************************************************
* sched/wd_start.c
*
* Copyright (C) 2007-2009, 2012 Gregory Nutt. All rights reserved.
* Copyright (C) 2007-2009, 2012, 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -45,6 +45,7 @@
#include <wdog.h>
#include <unistd.h>
#include <sched.h>
#include <assert.h>
#include <errno.h>
#include <nuttx/arch.h>
@ -56,6 +57,14 @@
* Pre-processor Definitions
****************************************************************************/
#ifndef MIN
# define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
# define MAX(a,b) (((a) > (b)) ? (a) : (b))
#endif
/****************************************************************************
* Private Type Declarations
****************************************************************************/
@ -87,6 +96,97 @@ typedef void (*wdentry4_t)(int argc, uint32_t arg1, uint32_t arg2,
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: wd_expiration
*
* Description:
* Check if the timer for the watchdog at the head of list is ready to
* run. If so, remove the watchdog from the list and execute it.
*
* Parameters:
* None
*
* Return Value:
* None
*
* Assumptions:
*
****************************************************************************/
static inline void wd_expiration(void)
{
FAR wdog_t *wdog;
/* Check if the watchdog at the head of the list is ready to run */
if (((FAR wdog_t*)g_wdactivelist.head)->lag <= 0)
{
/* Process the watchdog at the head of the list as well as any
* other watchdogs that became ready to run at this time
*/
while (g_wdactivelist.head &&
((FAR wdog_t*)g_wdactivelist.head)->lag <= 0)
{
/* Remove the watchdog from the head of the list */
wdog = (FAR wdog_t*)sq_remfirst(&g_wdactivelist);
/* If there is another watchdog behind this one, update its
* its lag (this shouldn't be necessary).
*/
if (g_wdactivelist.head)
{
((FAR wdog_t*)g_wdactivelist.head)->lag += wdog->lag;
}
/* Indicate that the watchdog is no longer active. */
wdog->active = false;
/* Execute the watchdog function */
up_setpicbase(wdog->picbase);
switch (wdog->argc)
{
default:
DEBUGPANIC();
break;
case 0:
(*((wdentry0_t)(wdog->func)))(0);
break;
#if CONFIG_MAX_WDOGPARMS > 0
case 1:
(*((wdentry1_t)(wdog->func)))(1, wdog->parm[0]);
break;
#endif
#if CONFIG_MAX_WDOGPARMS > 1
case 2:
(*((wdentry2_t)(wdog->func)))(2,
wdog->parm[0], wdog->parm[1]);
break;
#endif
#if CONFIG_MAX_WDOGPARMS > 2
case 3:
(*((wdentry3_t)(wdog->func)))(3,
wdog->parm[0], wdog->parm[1],
wdog->parm[2]);
break;
#endif
#if CONFIG_MAX_WDOGPARMS > 3
case 4:
(*((wdentry4_t)(wdog->func)))(4,
wdog->parm[0], wdog->parm[1],
wdog->parm[2] ,wdog->parm[3]);
break;
#endif
}
}
}
}
/****************************************************************************
* Public Functions
@ -101,7 +201,7 @@ typedef void (*wdentry4_t)(int argc, uint32_t arg1, uint32_t arg2,
* specified number of ticks has elapsed. Watchdog timers may be started
* from the interrupt level.
*
* Watchdog timers execute in the address enviroment that was in effect
* Watchdog timers execute in the address environment that was in effect
* when wd_start() is called.
*
* Watchdog timers execute only once.
@ -133,6 +233,9 @@ int wd_start(WDOG_ID wdog, int delay, wdentry_t wdentry, int argc, ...)
FAR wdog_t *next;
int32_t now;
irqstate_t saved_state;
#ifdef CONFIG_SCHED_TICKLESS
bool reassess = false;
#endif
int i;
/* Verify the wdog */
@ -189,7 +292,17 @@ int wd_start(WDOG_ID wdog, int delay, wdentry_t wdentry, int argc, ...)
if (g_wdactivelist.head == NULL)
{
/* Add the watchdog to the head of the queue. */
sq_addlast((FAR sq_entry_t*)wdog,&g_wdactivelist);
#ifdef CONFIG_SCHED_TICKLESS
/* Whenever the watchdog at the head of the queue changes, then we
* need to reassess the interval timer setting.
*/
reassess = true;
#endif
}
/* There are other active watchdogs in the timer queue */
@ -232,12 +345,24 @@ int wd_start(WDOG_ID wdog, int delay, wdentry_t wdentry, int argc, ...)
if (curr == (FAR wdog_t*)g_wdactivelist.head)
{
/* Insert the watchdog in mid- or end-of-queue */
sq_addfirst((FAR sq_entry_t*)wdog, &g_wdactivelist);
}
else
{
/* Insert the watchdog in mid- or end-of-queue */
sq_addafter((FAR sq_entry_t*)prev, (FAR sq_entry_t*)wdog,
&g_wdactivelist);
#ifdef CONFIG_SCHED_TICKLESS
/* If the watchdog at the head of the queue changes, then we
* need to reassess the interval timer setting.
*/
reassess = true;
#endif
}
}
@ -265,9 +390,23 @@ int wd_start(WDOG_ID wdog, int delay, wdentry_t wdentry, int argc, ...)
/* Put the lag into the watchdog structure and mark it as active. */
wdog->lag = delay;
wdog->lag = delay;
wdog->active = true;
#ifdef CONFIG_SCHED_TICKLESS
/* Reassess the interval timer that will generate the next interval event.
* In many cases, this will be unnecessary: This is really only necessary
* when the watchdog timer at the head of the queue is change. If the
* timer is inserted later in the queue then the timer at the head is
* unchanged.
*/
if (reassess)
{
sched_timer_reassess();
}
#endif
irqrestore(saved_state);
return OK;
}
@ -278,24 +417,70 @@ int wd_start(WDOG_ID wdog, int delay, wdentry_t wdentry, int argc, ...)
* Description:
* This function is called from the timer interrupt handler to determine
* if it is time to execute a watchdog function. If so, the watchdog
* function will be executed in the context of the timer interrupt handler.
* function will be executed in the context of the timer interrupt
* handler.
*
* Parameters:
* None
* ticks - If CONFIG_SCHED_TICKLESS is defined then the number of ticks
* in the the interval that just expired is provided. Otherwise,
* this function is called on each timer interrupt and a value of one
* is implicit.
*
* Return Value:
* None
* If CONFIG_SCHED_TICKLESS is defined then the number of ticks for the
* next delay is provided (zero if no delay). Otherwise, this function
* has no returned value.
*
* Assumptions:
* Called from interrupt handler logic with interrupts disabled.
*
****************************************************************************/
void wd_timer(void)
#ifdef CONFIG_SCHED_TICKLESS
unsigned int wd_timer(int ticks)
{
FAR wdog_t *wdog;
int decr;
/* Check if there are any active watchdogs to process */
while (g_wdactivelist.head && ticks > 0)
{
/* Get the watchdog at the head of the list */
wdog = (FAR wdog_t*)g_wdactivelist.head;
/* Decrement the lag for this watchdog.
*
* There is logic to handle the case where ticks is greater than
* the watchdog lag, but if the scheduling is working properly
* that should never happen.
*/
DEBUGASSERT(ticks <= wdog->lag);
decr = MIN(wdog->lag, ticks);
/* There are. Decrement the lag counter */
wdog->lag -= decr;
ticks -= ticks;
/* Check if the watchdog at the head of the list is ready to run */
wd_expiration();
}
/* Return the delay for the next watchdog to expire */
return g_wdactivelist.head ?
((FAR wdog_t*)g_wdactivelist.head)->lag : 0;
}
#else
void wd_timer(void)
{
/* Check if there are any active watchdogs to process */
if (g_wdactivelist.head)
{
/* There are. Decrement the lag counter */
@ -304,72 +489,7 @@ void wd_timer(void)
/* Check if the watchdog at the head of the list is ready to run */
if (((FAR wdog_t*)g_wdactivelist.head)->lag <= 0)
{
/* Process the watchdog at the head of the list as well as any
* other watchdogs that became ready to run at this time
*/
while (g_wdactivelist.head &&
((FAR wdog_t*)g_wdactivelist.head)->lag <= 0)
{
/* Remove the watchdog from the head of the list */
wdog = (FAR wdog_t*)sq_remfirst(&g_wdactivelist);
/* If there is another watchdog behind this one, update its
* its lag (this shouldn't be necessary).
*/
if (g_wdactivelist.head)
{
((FAR wdog_t*)g_wdactivelist.head)->lag += wdog->lag;
}
/* Indicate that the watchdog is no longer active. */
wdog->active = false;
/* Execute the watchdog function */
up_setpicbase(wdog->picbase);
switch (wdog->argc)
{
default:
#ifdef CONFIG_DEBUG
PANIC();
#endif
case 0:
(*((wdentry0_t)(wdog->func)))(0);
break;
#if CONFIG_MAX_WDOGPARMS > 0
case 1:
(*((wdentry1_t)(wdog->func)))(1, wdog->parm[0]);
break;
#endif
#if CONFIG_MAX_WDOGPARMS > 1
case 2:
(*((wdentry2_t)(wdog->func)))(2,
wdog->parm[0], wdog->parm[1]);
break;
#endif
#if CONFIG_MAX_WDOGPARMS > 2
case 3:
(*((wdentry3_t)(wdog->func)))(3,
wdog->parm[0], wdog->parm[1],
wdog->parm[2]);
break;
#endif
#if CONFIG_MAX_WDOGPARMS > 3
case 4:
(*((wdentry4_t)(wdog->func)))(4,
wdog->parm[0], wdog->parm[1],
wdog->parm[2] ,wdog->parm[3]);
break;
#endif
}
}
}
wd_expiration();
}
}
#endif /* CONFIG_SCHED_TICKLESS */