diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2014-08-07 11:39:16 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2014-08-07 11:39:16 -0600 |
commit | e45f88d7cc67518a52a056602506de1fd489ca74 (patch) | |
tree | 267a9e3b9e04a429415de49bf7ede19c47552f0e | |
parent | e70f66c7c493fafd4e319c9fd1c7c6d0422fafbe (diff) | |
download | px4-nuttx-e45f88d7cc67518a52a056602506de1fd489ca74.tar.gz px4-nuttx-e45f88d7cc67518a52a056602506de1fd489ca74.tar.bz2 px4-nuttx-e45f88d7cc67518a52a056602506de1fd489ca74.zip |
Implements the tickless OS
-rw-r--r-- | nuttx/TODO | 46 | ||||
-rw-r--r-- | nuttx/arch/Kconfig | 1 | ||||
-rw-r--r-- | nuttx/arch/sim/src/up_idle.c | 2 | ||||
-rw-r--r-- | nuttx/arch/sim/src/up_tickless.c | 132 | ||||
-rw-r--r-- | nuttx/include/nuttx/arch.h | 96 | ||||
-rw-r--r-- | nuttx/sched/Kconfig | 6 | ||||
-rw-r--r-- | nuttx/sched/Makefile | 4 | ||||
-rw-r--r-- | nuttx/sched/os_internal.h | 19 | ||||
-rw-r--r-- | nuttx/sched/sched_addreadytorun.c | 24 | ||||
-rw-r--r-- | nuttx/sched/sched_processtimer.c | 13 | ||||
-rw-r--r-- | nuttx/sched/sched_timerexpiration.c | 379 | ||||
-rw-r--r-- | nuttx/sched/sched_unlock.c | 43 | ||||
-rw-r--r-- | nuttx/sched/wd_cancel.c | 29 | ||||
-rw-r--r-- | nuttx/sched/wd_initialize.c | 2 | ||||
-rw-r--r-- | nuttx/sched/wd_internal.h | 65 | ||||
-rw-r--r-- | nuttx/sched/wd_start.c | 258 |
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 */ |