If we run out of pre-allocated watchdog times, the logic will allocate additional timers from the heap. A reserve of pre-allocated watchdog timers is maintained so that there will always be timers available for interrupt handlers.

This commit is contained in:
Gregory Nutt 2014-08-21 08:44:29 -06:00
parent e1d29d8718
commit 952d87587f
8 changed files with 198 additions and 72 deletions

View File

@ -188,8 +188,23 @@ config PREALLOC_WDOGS
int "Number of pre-allocated watchdog timers"
default 32
---help---
The number of pre-allocated watchdog structures. The system manages a
pool of preallocated watchdog structures to minimize dynamic allocations
The number of pre-allocated watchdog structures. The system manages
a pool of preallocated watchdog structures to minimize dynamic
allocations. Dynamic allocations will still be made if this pool is
exhausted. You will, however, get better performance and memory
usage if this value is tuned to minimize such allocations.
config WDOG_INTRESERVE
int "Watchdog structures reserved for interrupt handlers"
default 4
---help---
Watchdog structures may be allocated from normal task and also from
interrupt handlers. Interrupt handlers, however, can only use pre-
allocated watchdog timer. So, in order to keep normal task
allocations from exhausting all watchdog structures, a small number
of pre-allocated watchdog timers must be reserved for exclusive use
by interrupt handler. This setting determines that number of
reserved watchdogs.
config PREALLOC_TIMERS
int "Number of pre-allocated POSIX timers"

View File

@ -79,7 +79,7 @@
* timers may be cancelled from the interrupt level.
*
* Parameters:
* wdid - ID of the watchdog to cancel.
* wdog - ID of the watchdog to cancel.
*
* Return Value:
* OK or ERROR
@ -88,7 +88,7 @@
*
****************************************************************************/
int wd_cancel(WDOG_ID wdid)
int wd_cancel(WDOG_ID wdog)
{
wdog_t *curr;
wdog_t *prev;
@ -101,9 +101,11 @@ int wd_cancel(WDOG_ID wdid)
saved_state = irqsave();
/* Make sure that the watchdog is initialized (non-NULL) and is still active */
/* Make sure that the watchdog is initialized (non-NULL) and is still
* active.
*/
if (wdid && wdid->active)
if (wdog && WDOG_ISACTIVE(wdog))
{
/* Search the g_wdactivelist for the target FCB. We can't use sq_rem
* to do this because there are additional operations that need to be
@ -113,7 +115,7 @@ int wd_cancel(WDOG_ID wdid)
prev = NULL;
curr = (wdog_t*)g_wdactivelist.head;
while ((curr) && (curr != wdid))
while ((curr) && (curr != wdog))
{
prev = curr;
curr = curr->next;
@ -157,8 +159,8 @@ int wd_cancel(WDOG_ID wdid)
/* Mark the watchdog inactive */
wdid->next = NULL;
wdid->active = false;
wdog->next = NULL;
WDOG_CLRACTIVE(wdog);
/* Return success */

View File

@ -1,7 +1,7 @@
/****************************************************************************
* sched/wdog/wd_create.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
@ -44,6 +44,7 @@
#include <queue.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include "wdog/wdog.h"
@ -92,18 +93,63 @@
WDOG_ID wd_create (void)
{
FAR wdog_t *wdog;
irqstate_t saved_state;
irqstate_t state;
saved_state = irqsave();
wdog = (FAR wdog_t*)sq_remfirst(&g_wdfreelist);
irqrestore(saved_state);
/* These actions must be atomic with respect to other tasks and also with
* respect to interrupt handlers that may be allocating or freeing watchdog
* timers.
*/
/* Indicate that the watchdog is not actively timing */
state = irqsave();
if (wdog)
/* If we are in an interrupt handler -OR- if the number of pre-allocated
* timer structures exceeds the reserve, then take the the next timer from
* the head of the free list.
*/
if (g_wdnfree > CONFIG_WDOG_INTRESERVE || !up_interrupt_context())
{
wdog->next = NULL;
wdog->active = false;
/* Remove the watchdog timer from the free list and decrement the
* count of free timers all with interrupts disabled.
*/
wdog = (FAR wdog_t*)sq_remfirst(&g_wdfreelist);
DEBUGASSERT(g_wdnfree > 0);
g_wdnfree--;
irqrestore(state);
/* Did we get one? */
if (wdog)
{
/* Yes.. Clear the forward link and all flags */
wdog->next = NULL;
wdog->flags = 0;
}
}
/* We are in a normal tasking context AND there are not enough unreserved,
* pre-allocated watchdog timers. We need to allocate one from the kernel
* heap.
*/
else
{
/* We do not require that interrupts be disabled to do this. */
irqrestore(state);
wdog = (FAR wdog_t *)kmalloc(sizeof(wdog_t));
/* Did we get one? */
if (wdog)
{
/* Yes.. Clear the forward link and set the allocated flag */
wdog->next = NULL;
wdog->flags = WDOGF_ALLOCED;
}
}
return (WDOG_ID)wdog;

View File

@ -1,7 +1,7 @@
/****************************************************************************
* sched/wdog/wd_delete.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
@ -41,9 +41,11 @@
#include <wdog.h>
#include <queue.h>
#include <assert.h>
#include <errno.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include "wdog/wdog.h"
@ -80,7 +82,7 @@
* queue if has been started.
*
* Parameters:
* wdId - The watchdog ID to delete. This is actually a pointer to a
* wdog - The watchdog ID to delete. This is actually a pointer to a
* watchdog structure.
*
* Return Value:
@ -91,35 +93,58 @@
*
****************************************************************************/
int wd_delete(WDOG_ID wdId)
int wd_delete(WDOG_ID wdog)
{
irqstate_t saved_state;
irqstate_t state;
/* Verify that a valid watchdog was provided */
if (!wdId)
{
set_errno(EINVAL);
return ERROR;
}
DEBUGASSERT(wdog);
/* The following steps are atomic... the watchdog must not be active when
* it is being deallocated.
*/
saved_state = irqsave();
state = irqsave();
/* Check if the watchdog has been started. */
if (wdId->active)
if (WDOG_ISACTIVE(wdog))
{
wd_cancel(wdId);
/* Yes.. stop it */
wd_cancel(wdog);
}
/* Put the watchdog back on the free list */
/* Did this watchdog come from the pool of pre-allocated timers? Or, was
* it allocated from the heap?
*/
sq_addlast((FAR sq_entry_t*)wdId, &g_wdfreelist);
irqrestore(saved_state);
if (WDOG_ISALLOCED(wdog))
{
/* It was allocated from the heap. Use sched_kfree() to release the
* memory. If the timer was released from an interrupt handler,
* sched_kfree() will defer the actual deallocation of the memory
* until a more appropriate time.
*
* We don't need interrupts disabled to do this.
*/
irqrestore(state);
sched_kfree(wdog);
}
/* This was a pre-allocated timer. */
else
{
/* Put the timer back on the free list and increment the count of free
* timers, all with interrupts disabled.
*/
sq_addlast((FAR sq_entry_t*)wdog, &g_wdfreelist);
g_wdnfree++;
DEBUGASSERT(g_wdnfree <= CONFIG_PREALLOC_WDOGS);
irqrestore(state);
}
/* Return success */

View File

@ -1,7 +1,7 @@
/********************************************************************************
* sched/wdog/wd_gettime.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
@ -92,7 +92,7 @@ int wd_gettime(WDOG_ID wdog)
/* Verify the wdog */
flags = irqsave();
if (wdog && wdog->active)
if (wdog && WDOG_ISACTIVE(wdog))
{
/* Traverse the watchdog list accumulating lag times until we find the wdog
* that we are looking for

View File

@ -1,7 +1,7 @@
/************************************************************************
* sched/wdog/wd_initialize.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
@ -40,12 +40,11 @@
#include <nuttx/config.h>
#include <queue.h>
#include <nuttx/kmalloc.h>
#include "wdog/wdog.h"
/************************************************************************
* Definitions
* Pre-processor Definitions
************************************************************************/
/************************************************************************
@ -53,7 +52,7 @@
************************************************************************/
/************************************************************************
* Global Variables
* Public Variables
************************************************************************/
/* The g_wdfreelist data structure is a singly linked list of watchdogs
@ -62,12 +61,6 @@
sq_queue_t g_wdfreelist;
/* g_wdpool is a pointer to a list of pre-allocated watchdogs. The number
* of watchdogs in the pool is a configuration item.
*/
FAR wdog_t *g_wdpool;
/* The g_wdactivelist data structure is a singly linked list ordered by
* watchdog expiration time. When watchdog timers expire,the functions on
* this linked list are removed and the function is called.
@ -75,10 +68,23 @@ FAR wdog_t *g_wdpool;
sq_queue_t g_wdactivelist;
/* This is the number of free, pre-allocated watchdog structures in the
* g_wdfreelist. This value is used to enforce a reserve for interrupt
* handlers.
*/
uint16_t g_wdnfree;
/************************************************************************
* Private Variables
* Private Data
************************************************************************/
/* g_wdpool is a list of pre-allocated watchdogs. The number of watchdogs
* in the pool is a configuration item.
*/
static FAR wdog_t g_wdpool[CONFIG_PREALLOC_WDOGS];
/************************************************************************
* Private Functions
************************************************************************/
@ -108,27 +114,24 @@ sq_queue_t g_wdactivelist;
void wd_initialize(void)
{
/* Initialize the free watchdog list */
FAR wdog_t *wdog = g_wdpool;
int i;
/* Initialize watchdog lists */
sq_init(&g_wdfreelist);
sq_init(&g_wdactivelist);
/* The g_wdfreelist must be loaded at initialization time to hold the
* configured number of watchdogs.
*/
g_wdpool = (FAR wdog_t*)kmalloc(sizeof(wdog_t) * CONFIG_PREALLOC_WDOGS);
if (g_wdpool)
for (i = 0; i < CONFIG_PREALLOC_WDOGS; i++)
{
FAR wdog_t *wdog = g_wdpool;
int i;
for (i = 0; i < CONFIG_PREALLOC_WDOGS; i++)
{
sq_addlast((FAR sq_entry_t*)wdog++, &g_wdfreelist);
}
sq_addlast((FAR sq_entry_t*)wdog++, &g_wdfreelist);
}
/* The g_wdactivelist queue must be reset at initialization time. */
/* All watchdogs are free */
sq_init(&g_wdactivelist);
g_wdnfree = CONFIG_PREALLOC_WDOGS;
}

View File

@ -143,7 +143,7 @@ static inline void wd_expiration(void)
/* Indicate that the watchdog is no longer active. */
wdog->active = false;
WDOG_CLRACTIVE(wdog);
/* Execute the watchdog function */
@ -250,7 +250,7 @@ int wd_start(WDOG_ID wdog, int delay, wdentry_t wdentry, int argc, ...)
*/
saved_state = irqsave();
if (wdog->active)
if (WDOG_ISACTIVE(wdog))
{
wd_cancel(wdog);
}
@ -382,8 +382,8 @@ 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->active = true;
wdog->lag = delay;
WDOG_SETACTIVE(wdog);
#ifdef CONFIG_SCHED_TICKLESS
/* Resume the interval timer that will generate the next interval event.

View File

@ -1,7 +1,7 @@
/************************************************************************
* sched/wdog/wdog.h
*
* 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
@ -51,6 +51,40 @@
/************************************************************************
* Pre-processor Definitions
************************************************************************/
/* Configuration ********************************************************/
#ifndef CONFIG_PREALLOC_WDOGS
# define CONFIG_PREALLOC_WDOGS 32
#endif
#ifndef CONFIG_WDOG_INTRESERVE
# if CONFIG_PREALLOC_WDOGS > 16
# define CONFIG_WDOG_INTRESERVE 4
# elif CONFIG_PREALLOC_WDOGS > 8
# define CONFIG_WDOG_INTRESERVE 2
# else
# define CONFIG_WDOG_INTRESERVE 1
# endif
#endif
#if CONFIG_WDOG_INTRESERVE >= CONFIG_PREALLOC_WDOGS
# error CONFIG_WDOG_INTRESERVE >= CONFIG_PREALLOC_WDOGS
#endif
/* Watchdog Definitions *************************************************/
/* Flag bits for the flags field of struct wdog_s */
#define WDOGF_ACTIVE (1 << 0) /* Watchdog is actively timing */
#define WDOGF_ALLOCED (1 << 1) /* 0:Pre-allocated, 1:Allocated */
#define WDOG_SETACTIVE(w) do { (w)->flags |= WDOGF_ACTIVE; } while (0)
#define WDOG_SETALLOCED(w) do { (w)->flags |= WDOGF_ALLOCED; } while (0)
#define WDOG_CLRACTIVE(w) do { (w)->flags &= ~WDOGF_ACTIVE; } while (0)
#define WDOG_CLRALLOCED(w) do { (w)->flags &= ~WDOGF_ALLOCED; } while (0)
#define WDOG_ISACTIVE(w) (((w)->flags & WDOGF_ACTIVE) != 0)
#define WDOG_ISALLOCED(w) (((w)->flags & WDOGF_ALLOCED) != 0)
/************************************************************************
* Public Type Declarations
@ -68,7 +102,7 @@ struct wdog_s
FAR void *picbase; /* PIC base address */
#endif
int lag; /* Timer associated with the delay */
bool active; /* true if the watchdog is actively timing */
uint8_t flags; /* See WDOGF_* definitions above */
uint8_t argc; /* The number of parameters to pass */
uint32_t parm[CONFIG_MAX_WDOGPARMS];
};
@ -92,12 +126,6 @@ extern "C"
extern sq_queue_t g_wdfreelist;
/* g_wdpool is a pointer to a list of pre-allocated watchdogs. The number
* of watchdogs in the pool is a configuration item.
*/
extern FAR wdog_t *g_wdpool;
/* The g_wdactivelist data structure is a singly linked list ordered by
* watchdog expiration time. When watchdog timers expire,the functions on
* this linked list are removed and the function is called.
@ -105,6 +133,13 @@ extern FAR wdog_t *g_wdpool;
extern sq_queue_t g_wdactivelist;
/* This is the number of free, pre-allocated watchdog structures in the
* g_wdfreelist. This value is used to enforce a reserve for interrupt
* handlers.
*/
extern uint16_t g_wdnfree;
/************************************************************************
* Public Function Prototypes
************************************************************************/