diff --git a/sched/Kconfig b/sched/Kconfig index 25aeff8895a545770c460a290e7a527b24a781ac..57e96e5712bf1b3049bd879bb018290b8ad4cb17 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -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" diff --git a/sched/wdog/wd_cancel.c b/sched/wdog/wd_cancel.c index 9093deed86362884f6db5587fe4f4a101ea6ec91..44fc95ab477d497270461840318cfa3b2e01dc01 100644 --- a/sched/wdog/wd_cancel.c +++ b/sched/wdog/wd_cancel.c @@ -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 */ diff --git a/sched/wdog/wd_create.c b/sched/wdog/wd_create.c index f766aca9cd55399fb1d861f9284d9a6fe7afe551..fb57d86b1c24607ee637bfbdd55ea1b940121b1e 100644 --- a/sched/wdog/wd_create.c +++ b/sched/wdog/wd_create.c @@ -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; diff --git a/sched/wdog/wd_delete.c b/sched/wdog/wd_delete.c index 8a1be3006b951b77563f464af9d2917679812d23..a98caa20e8dcba5b8e914526eecbea75ddce55cf 100644 --- a/sched/wdog/wd_delete.c +++ b/sched/wdog/wd_delete.c @@ -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 */ diff --git a/sched/wdog/wd_gettime.c b/sched/wdog/wd_gettime.c index a2983527b232c80495b75f23aa81dbfea427f4de..4f69bfeb99d9e6495d406855d63619c5ff7ebd41 100644 --- a/sched/wdog/wd_gettime.c +++ b/sched/wdog/wd_gettime.c @@ -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 diff --git a/sched/wdog/wd_initialize.c b/sched/wdog/wd_initialize.c index dd81a74be19407c9988693f7f7644c0859056ad6..eba4ebacdb9cd96d8bedbff254f200791f4e84db 100644 --- a/sched/wdog/wd_initialize.c +++ b/sched/wdog/wd_initialize.c @@ -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; } diff --git a/sched/wdog/wd_start.c b/sched/wdog/wd_start.c index 9cf7edc3529558a821426c607f98dd1906b42c13..009a5d5ee7624d07fc1461701bcfa8c9d2a13d6c 100644 --- a/sched/wdog/wd_start.c +++ b/sched/wdog/wd_start.c @@ -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. diff --git a/sched/wdog/wdog.h b/sched/wdog/wdog.h index 01c993bdc4270d1689d862b6d74ae3623101faff..73a73f1ad3da35d2bd9a255092d0c38722d59464 100644 --- a/sched/wdog/wdog.h +++ b/sched/wdog/wdog.h @@ -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 ************************************************************************/