summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-08-07 11:39:16 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-08-07 11:39:16 -0600
commite45f88d7cc67518a52a056602506de1fd489ca74 (patch)
tree267a9e3b9e04a429415de49bf7ede19c47552f0e
parente70f66c7c493fafd4e319c9fd1c7c6d0422fafbe (diff)
downloadpx4-nuttx-e45f88d7cc67518a52a056602506de1fd489ca74.tar.gz
px4-nuttx-e45f88d7cc67518a52a056602506de1fd489ca74.tar.bz2
px4-nuttx-e45f88d7cc67518a52a056602506de1fd489ca74.zip
Implements the tickless OS
-rw-r--r--nuttx/TODO46
-rw-r--r--nuttx/arch/Kconfig1
-rw-r--r--nuttx/arch/sim/src/up_idle.c2
-rw-r--r--nuttx/arch/sim/src/up_tickless.c132
-rw-r--r--nuttx/include/nuttx/arch.h96
-rw-r--r--nuttx/sched/Kconfig6
-rw-r--r--nuttx/sched/Makefile4
-rw-r--r--nuttx/sched/os_internal.h19
-rw-r--r--nuttx/sched/sched_addreadytorun.c24
-rw-r--r--nuttx/sched/sched_processtimer.c13
-rw-r--r--nuttx/sched/sched_timerexpiration.c379
-rw-r--r--nuttx/sched/sched_unlock.c43
-rw-r--r--nuttx/sched/wd_cancel.c29
-rw-r--r--nuttx/sched/wd_initialize.c2
-rw-r--r--nuttx/sched/wd_internal.h65
-rw-r--r--nuttx/sched/wd_start.c258
16 files changed, 813 insertions, 306 deletions
diff --git a/nuttx/TODO b/nuttx/TODO
index 1adea3d5d..b046b385f 100644
--- a/nuttx/TODO
+++ b/nuttx/TODO
@@ -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/)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/nuttx/arch/Kconfig b/nuttx/arch/Kconfig
index 7cb6af186..71049ff51 100644
--- a/nuttx/arch/Kconfig
+++ b/nuttx/arch/Kconfig
@@ -59,6 +59,7 @@ config ARCH_SH
config ARCH_SIM
bool "Simulation"
+ select ARCH_HAVE_TICKLESS
---help---
Linux/Cywgin user-mode simulation.
diff --git a/nuttx/arch/sim/src/up_idle.c b/nuttx/arch/sim/src/up_idle.c
index 8d04000c4..783b57eea 100644
--- a/nuttx/arch/sim/src/up_idle.c
+++ b/nuttx/arch/sim/src/up_idle.c
@@ -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>
diff --git a/nuttx/arch/sim/src/up_tickless.c b/nuttx/arch/sim/src/up_tickless.c
index 56202e7c1..57a793534 100644
--- a/nuttx/arch/sim/src/up_tickless.c
+++ b/nuttx/arch/sim/src/up_tickless.c
@@ -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,83 +251,15 @@ 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
*
* Description:
diff --git a/nuttx/include/nuttx/arch.h b/nuttx/include/nuttx/arch.h
index 8f2bd3cf9..7e121cc2f 100644
--- a/nuttx/include/nuttx/arch.h
+++ b/nuttx/include/nuttx/arch.h
@@ -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,19 +1025,28 @@ 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
+ * 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
@@ -1049,7 +1060,7 @@ 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);
#endif
/****************************************************************************
@@ -1063,7 +1074,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
@@ -1077,60 +1088,7 @@ int up_timer_cancel(void);
****************************************************************************/
#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
- * 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.
- *
- ****************************************************************************/
-
-#ifdef CONFIG_SCHED_TICKLESS
-int up_timer_remaining(FAR struct timespec *tp);
-#endif
-
-/****************************************************************************
- * 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.
- *
- ****************************************************************************/
-
-#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.
*
diff --git a/nuttx/sched/Kconfig b/nuttx/sched/Kconfig
index c1af2c252..f33f4b4e5 100644
--- a/nuttx/sched/Kconfig
+++ b/nuttx/sched/Kconfig
@@ -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
diff --git a/nuttx/sched/Makefile b/nuttx/sched/Makefile
index 26c790c84..da89d19d2 100644
--- a/nuttx/sched/Makefile
+++ b/nuttx/sched/Makefile
@@ -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
diff --git a/nuttx/sched/os_internal.h b/nuttx/sched/os_internal.h
index 3e292e58b..6ec20bda0 100644
--- a/nuttx/sched/os_internal.h
+++ b/nuttx/sched/os_internal.h
@@ -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);
diff --git a/nuttx/sched/sched_addreadytorun.c b/nuttx/sched/sched_addreadytorun.c
index a30b7d8c5..156598cd2 100644
--- a/nuttx/sched/sched_addreadytorun.c
+++ b/nuttx/sched/sched_addreadytorun.c
@@ -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;
diff --git a/nuttx/sched/sched_processtimer.c b/nuttx/sched/sched_processtimer.c
index 60c2e6702..64329a79d 100644
--- a/nuttx/sched/sched_processtimer.c
+++ b/nuttx/sched/sched_processtimer.c
@@ -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.
diff --git a/nuttx/sched/sched_timerexpiration.c b/nuttx/sched/sched_timerexpiration.c
new file mode 100644
index 000000000..7f2a5b863
--- /dev/null
+++ b/nuttx/sched/sched_timerexpiration.c
@@ -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 */
diff --git a/nuttx/sched/sched_unlock.c b/nuttx/sched/sched_unlock.c
index bb07b5b83..aa0fa75ee 100644
--- a/nuttx/sched/sched_unlock.c
+++ b/nuttx/sched/sched_unlock.c
@@ -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);
diff --git a/nuttx/sched/wd_cancel.c b/nuttx/sched/wd_cancel.c
index 3a6e67eef..8a258c35e 100644
--- a/nuttx/sched/wd_cancel.c
+++ b/nuttx/sched/wd_cancel.c
@@ -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
{
- (void)sq_remfirst(&g_wdactivelist);
- }
+ /* Remove the watchdog at the head of the queue */
- wdid->next = NULL;
+ (void)sq_remfirst(&g_wdactivelist);
- /* Return success */
+ /* Reassess the interval timer that will generate the next
+ * interval event.
+ */
- ret = OK;
+ sched_timer_reassess();
+ }
/* Mark the watchdog inactive */
+ wdid->next = NULL;
wdid->active = false;
+
+ /* Return success */
+
+ ret = OK;
}
irqrestore(saved_state);
diff --git a/nuttx/sched/wd_initialize.c b/nuttx/sched/wd_initialize.c
index db7aba502..9d65d840d 100644
--- a/nuttx/sched/wd_initialize.c
+++ b/nuttx/sched/wd_initialize.c
@@ -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
diff --git a/nuttx/sched/wd_internal.h b/nuttx/sched/wd_internal.h
index cbbc91cd1..902edf4f6 100644
--- a/nuttx/sched/wd_internal.h
+++ b/nuttx/sched/wd_internal.h
@@ -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
diff --git a/nuttx/sched/wd_start.c b/nuttx/sched/wd_start.c
index 440c83b0c..6f519f915 100644
--- a/nuttx/sched/wd_start.c
+++ b/nuttx/sched/wd_start.c
@@ -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,98 +417,79 @@ 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 */
- if (g_wdactivelist.head)
+ while (g_wdactivelist.head && ticks > 0)
{
- /* There are. Decrement the lag counter */
+ /* Get the watchdog at the head of the list */
- --(((FAR wdog_t*)g_wdactivelist.head)->lag);
+ wdog = (FAR wdog_t*)g_wdactivelist.head;
- /* Check if the watchdog at the head of the list is ready to run */
+ /* 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.
+ */
- 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
- */
+ DEBUGASSERT(ticks <= wdog->lag);
+ decr = MIN(wdog->lag, ticks);
- while (g_wdactivelist.head &&
- ((FAR wdog_t*)g_wdactivelist.head)->lag <= 0)
- {
- /* Remove the watchdog from the head of the list */
+ /* There are. Decrement the lag counter */
- wdog = (FAR wdog_t*)sq_remfirst(&g_wdactivelist);
+ wdog->lag -= decr;
+ ticks -= ticks;
- /* If there is another watchdog behind this one, update its
- * its lag (this shouldn't be necessary).
- */
+ /* Check if the watchdog at the head of the list is ready to run */
- if (g_wdactivelist.head)
- {
- ((FAR wdog_t*)g_wdactivelist.head)->lag += wdog->lag;
- }
+ wd_expiration();
+ }
- /* Indicate that the watchdog is no longer active. */
+ /* Return the delay for the next watchdog to expire */
- wdog->active = false;
+ return g_wdactivelist.head ?
+ ((FAR wdog_t*)g_wdactivelist.head)->lag : 0;
+}
- /* Execute the watchdog function */
+#else
+void wd_timer(void)
+{
+ /* Check if there are any active watchdogs to process */
- up_setpicbase(wdog->picbase);
- switch (wdog->argc)
- {
- default:
-#ifdef CONFIG_DEBUG
- PANIC();
-#endif
- case 0:
- (*((wdentry0_t)(wdog->func)))(0);
- break;
+ if (g_wdactivelist.head)
+ {
+ /* There are. Decrement the lag counter */
-#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
- }
- }
- }
+ --(((FAR wdog_t*)g_wdactivelist.head)->lag);
+
+ /* Check if the watchdog at the head of the list is ready to run */
+
+ wd_expiration();
}
}
+#endif /* CONFIG_SCHED_TICKLESS */