From 7e480a254c9dfea4b1faaf1168a112de11959dd1 Mon Sep 17 00:00:00 2001 From: patacongo Date: Tue, 4 Sep 2012 00:54:09 +0000 Subject: Add support for multiple work queues git-svn-id: https://nuttx.svn.sourceforge.net/svnroot/nuttx/trunk@5081 7fd9a85b-ad96-42d3-883c-3090e2eb8679 --- nuttx/ChangeLog | 2 + nuttx/Documentation/NuttxPortingGuide.html | 14 ++ nuttx/arch/arm/src/kinetis/kinetis_sdhc.c | 6 +- nuttx/arch/arm/src/sam3u/sam3u_hsmci.c | 6 +- nuttx/arch/arm/src/stm32/stm32_sdio.c | 2 +- nuttx/configs/README.txt | 13 +- nuttx/configs/pic32mx7mmb/src/up_touchscreen.c | 4 +- nuttx/drivers/input/ads7843e.c | 4 +- nuttx/drivers/input/stmpe811_base.c | 2 +- nuttx/drivers/input/stmpe811_tsc.c | 2 +- nuttx/drivers/input/tsc2007.c | 4 +- nuttx/drivers/net/enc28j60.c | 2 +- nuttx/drivers/power/pm_update.c | 4 +- nuttx/drivers/rwbuffer.c | 4 +- nuttx/drivers/usbhost/usbhost_hidkbd.c | 2 +- nuttx/drivers/usbhost/usbhost_skeleton.c | 6 +- nuttx/drivers/usbhost/usbhost_storage.c | 2 +- nuttx/include/nuttx/wqueue.h | 84 +++++++++- nuttx/sched/Kconfig | 33 ++++ nuttx/sched/Makefile | 2 +- nuttx/sched/os_bringup.c | 24 ++- nuttx/sched/sched_free.c | 4 +- nuttx/sched/work_cancel.c | 16 +- nuttx/sched/work_internal.h | 48 +++--- nuttx/sched/work_queue.c | 14 +- nuttx/sched/work_signal.c | 96 +++++++++++ nuttx/sched/work_thread.c | 215 +++++++++++++++---------- nuttx/tools/configure.sh | 18 ++- 28 files changed, 466 insertions(+), 167 deletions(-) create mode 100644 nuttx/sched/work_signal.c diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index b67c066bcb..86e6b52572 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -3250,3 +3250,5 @@ * sched/work_cancel.c: Fix a bad assertion (reported by Mike Smith) * configs/stm3210e-eval/src/up_idle.c: Correct some power management compilation errors (reported by Diego Sanchez). + * include/nuttx/wqueue.h, sched/work*, and others: Added logic to support + a second, lower priority work queue (CONFIG_SCHED_LPWORK). diff --git a/nuttx/Documentation/NuttxPortingGuide.html b/nuttx/Documentation/NuttxPortingGuide.html index 9df1f9378f..a03d9810fc 100644 --- a/nuttx/Documentation/NuttxPortingGuide.html +++ b/nuttx/Documentation/NuttxPortingGuide.html @@ -4068,6 +4068,20 @@ build CONFIG_SIG_SIGWORK: The signal number that will be used to wake-up the worker thread. Default: 4 +
  • + CONFIG_SCHED_LPWORK: If CONFIG_SCHED_WORKQUEUE is defined, then a single work queue is created by default. + If CONFIG_SCHED_LPWORK is also defined then an additional, lower-priority work queue will also be created. + This lower priority work queue is better suited for more extended processing (such as file system clean-up operations) +
  • +
  • + CONFIG_SCHED_LPWORKPRIORITY: The execution priority of the lower priority worker thread. Default: 50 +
  • +
  • + CONFIG_SCHED_LPWORKPERIOD: How often the lower priority worker thread checks for work in units of microseconds. Default: 50*1000 (50 MS). +
  • +
  • + CONFIG_SCHED_LPWORKSTACKSIZE - The stack size allocated for the lower priority worker thread. Default: CONFIG_IDLETHREAD_STACKSIZE. +
  • CONFIG_SCHED_WAITPID: Enables the waitpid() API
  • diff --git a/nuttx/arch/arm/src/kinetis/kinetis_sdhc.c b/nuttx/arch/arm/src/kinetis/kinetis_sdhc.c index 8be3e092af..eec5fba0e0 100644 --- a/nuttx/arch/arm/src/kinetis/kinetis_sdhc.c +++ b/nuttx/arch/arm/src/kinetis/kinetis_sdhc.c @@ -1,8 +1,8 @@ /**************************************************************************** * arch/arm/src/kinetis/kinetis_sdhc.c * - * Copyright (C) 2011 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -2733,7 +2733,7 @@ static void kinetis_callback(void *arg) /* Yes.. queue it */ fvdbg("Queuing callback to %p(%p)\n", priv->callback, priv->cbarg); - (void)work_queue(&priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0); + (void)work_queue(HPWORK, &priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0); } else { diff --git a/nuttx/arch/arm/src/sam3u/sam3u_hsmci.c b/nuttx/arch/arm/src/sam3u/sam3u_hsmci.c index b7872a2195..9d7c308ca3 100644 --- a/nuttx/arch/arm/src/sam3u/sam3u_hsmci.c +++ b/nuttx/arch/arm/src/sam3u/sam3u_hsmci.c @@ -1,8 +1,8 @@ /**************************************************************************** * arch/arm/src/sam3u/sam3u_sdio.c * - * Copyright (C) 2010 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Copyright (C) 2010, 2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -2343,7 +2343,7 @@ static void sam3u_callback(void *arg) /* Yes.. queue it */ fvdbg("Queuing callback to %p(%p)\n", priv->callback, priv->cbarg); - (void)work_queue(&priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0); + (void)work_queue(HPWORK, &priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0); } else { diff --git a/nuttx/arch/arm/src/stm32/stm32_sdio.c b/nuttx/arch/arm/src/stm32/stm32_sdio.c index 57d5f80d96..057a5836fd 100644 --- a/nuttx/arch/arm/src/stm32/stm32_sdio.c +++ b/nuttx/arch/arm/src/stm32/stm32_sdio.c @@ -2660,7 +2660,7 @@ static void stm32_callback(void *arg) /* Yes.. queue it */ fvdbg("Queuing callback to %p(%p)\n", priv->callback, priv->cbarg); - (void)work_queue(&priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0); + (void)work_queue(HPWORK, &priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0); } else { diff --git a/nuttx/configs/README.txt b/nuttx/configs/README.txt index 1b405b3e45..7ad2825d0c 100644 --- a/nuttx/configs/README.txt +++ b/nuttx/configs/README.txt @@ -361,13 +361,24 @@ defconfig -- This is a configuration file similar to the Linux if memory reclamation is of high priority). If CONFIG_SCHED_WORKQUEUE is enabled, then the following options can also be used: CONFIG_SCHED_WORKPRIORITY - The execution priority of the worker - thread. Default: 50 + thread. Default: 192 CONFIG_SCHED_WORKPERIOD - How often the worker thread checks for work in units of microseconds. Default: 50*1000 (50 MS). CONFIG_SCHED_WORKSTACKSIZE - The stack size allocated for the worker thread. Default: CONFIG_IDLETHREAD_STACKSIZE. CONFIG_SIG_SIGWORK - The signal number that will be used to wake-up the worker thread. Default: 4 + CONFIG_SCHED_LPWORK. If CONFIG_SCHED_WORKQUEUE is defined, then a single + work queue is created by default. If CONFIG_SCHED_LPWORK is also defined + then an additional, lower-priority work queue will also be created. This + lower priority work queue is better suited for more extended processing + (such as file system clean-up operations) + CONFIG_SCHED_LPWORKPRIORITY - The execution priority of the lower priority + worker thread. Default: 50 + CONFIG_SCHED_LPWORKPERIOD - How often the lower priority worker thread + checks for work in units of microseconds. Default: 50*1000 (50 MS). + CONFIG_SCHED_LPWORKSTACKSIZE - The stack size allocated for the lower + priority worker thread. Default: CONFIG_IDLETHREAD_STACKSIZE. CONFIG_SCHED_WAITPID - Enables the waitpid() API CONFIG_SCHED_ATEXIT - Enables the atexit() API CONFIG_SCHED_ATEXIT_MAX - By default if CONFIG_SCHED_ATEXIT is diff --git a/nuttx/configs/pic32mx7mmb/src/up_touchscreen.c b/nuttx/configs/pic32mx7mmb/src/up_touchscreen.c index 33d7dcbe3f..c8fe53507a 100644 --- a/nuttx/configs/pic32mx7mmb/src/up_touchscreen.c +++ b/nuttx/configs/pic32mx7mmb/src/up_touchscreen.c @@ -972,7 +972,7 @@ static void tc_worker(FAR void *arg) /* Set up the next sample event */ - ret = work_queue(&priv->work, tc_worker, priv, delay); + ret = work_queue(HPWORK, &priv->work, tc_worker, priv, delay); ASSERT(ret == 0); } @@ -1420,7 +1420,7 @@ int arch_tcinitialize(int minor) */ priv->state = TC_READY; - ret = work_queue(&priv->work, tc_worker, priv, 0); + ret = work_queue(HPWORK, &priv->work, tc_worker, priv, 0); if (ret != 0) { idbg("Failed to queue work: %d\n", ret); diff --git a/nuttx/drivers/input/ads7843e.c b/nuttx/drivers/input/ads7843e.c index 750b91ff2e..e08a7a7280 100644 --- a/nuttx/drivers/input/ads7843e.c +++ b/nuttx/drivers/input/ads7843e.c @@ -521,7 +521,7 @@ static int ads7843e_schedule(FAR struct ads7843e_dev_s *priv) */ DEBUGASSERT(priv->work.worker == NULL); - ret = work_queue(&priv->work, ads7843e_worker, priv, 0); + ret = work_queue(HPWORK, &priv->work, ads7843e_worker, priv, 0); if (ret != 0) { illdbg("Failed to queue work: %d\n", ret); @@ -1179,7 +1179,7 @@ int ads7843e_register(FAR struct spi_dev_s *dev, * availability conditions. */ - ret = work_queue(&priv->work, ads7843e_worker, priv, 0); + ret = work_queue(HPWORK, &priv->work, ads7843e_worker, priv, 0); if (ret != 0) { idbg("Failed to queue work: %d\n", ret); diff --git a/nuttx/drivers/input/stmpe811_base.c b/nuttx/drivers/input/stmpe811_base.c index 72af1575a9..f37c24622c 100644 --- a/nuttx/drivers/input/stmpe811_base.c +++ b/nuttx/drivers/input/stmpe811_base.c @@ -195,7 +195,7 @@ static int stmpe811_interrupt(int irq, FAR void *context) * action should be required to protect the work queue. */ - ret = work_queue(&priv->work, stmpe811_worker, priv, 0); + ret = work_queue(HPWORK, &priv->work, stmpe811_worker, priv, 0); if (ret != 0) { illdbg("Failed to queue work: %d\n", ret); diff --git a/nuttx/drivers/input/stmpe811_tsc.c b/nuttx/drivers/input/stmpe811_tsc.c index e2ab2827d5..c7f8b473b8 100644 --- a/nuttx/drivers/input/stmpe811_tsc.c +++ b/nuttx/drivers/input/stmpe811_tsc.c @@ -783,7 +783,7 @@ static void stmpe811_timeout(int argc, uint32_t arg1, ...) * action should be required to protect the work queue. */ - ret = work_queue(&priv->timeout, stmpe811_timeoutworker, priv, 0); + ret = work_queue(HPWORK, &priv->timeout, stmpe811_timeoutworker, priv, 0); if (ret != 0) { illdbg("Failed to queue work: %d\n", ret); diff --git a/nuttx/drivers/input/tsc2007.c b/nuttx/drivers/input/tsc2007.c index 07acb53712..163118b95d 100644 --- a/nuttx/drivers/input/tsc2007.c +++ b/nuttx/drivers/input/tsc2007.c @@ -775,7 +775,7 @@ static int tsc2007_interrupt(int irq, FAR void *context) */ DEBUGASSERT(priv->work.worker == NULL); - ret = work_queue(&priv->work, tsc2007_worker, priv, 0); + ret = work_queue(HPWORK, &priv->work, tsc2007_worker, priv, 0); if (ret != 0) { illdbg("Failed to queue work: %d\n", ret); @@ -1316,7 +1316,7 @@ int tsc2007_register(FAR struct i2c_dev_s *dev, * availability conditions. */ - ret = work_queue(&priv->work, tsc2007_worker, priv, 0); + ret = work_queue(HPWORK, &priv->work, tsc2007_worker, priv, 0); if (ret != 0) { idbg("Failed to queue work: %d\n", ret); diff --git a/nuttx/drivers/net/enc28j60.c b/nuttx/drivers/net/enc28j60.c index 76b0f205b1..0239406a1c 100644 --- a/nuttx/drivers/net/enc28j60.c +++ b/nuttx/drivers/net/enc28j60.c @@ -1509,7 +1509,7 @@ static int enc_interrupt(int irq, FAR void *context) * a good thing to do in any event. */ - return work_queue(&priv->work, enc_worker, (FAR void *)priv, 0); + return work_queue(HPWORK, &priv->work, enc_worker, (FAR void *)priv, 0); #endif } diff --git a/nuttx/drivers/power/pm_update.c b/nuttx/drivers/power/pm_update.c index ae5e1f8409..4b6b58c550 100644 --- a/nuttx/drivers/power/pm_update.c +++ b/nuttx/drivers/power/pm_update.c @@ -328,7 +328,7 @@ void pm_update(int16_t accum) /* The work will be performed on the worker thread */ DEBUGASSERT(g_pmglobals.work.worker == NULL); - (void)work_queue(&g_pmglobals.work, pm_worker, (FAR void*)((intptr_t)accum), 0); + (void)work_queue(HPWORK, &g_pmglobals.work, pm_worker, (FAR void*)((intptr_t)accum), 0); } -#endif /* CONFIG_PM */ \ No newline at end of file +#endif /* CONFIG_PM */ diff --git a/nuttx/drivers/rwbuffer.c b/nuttx/drivers/rwbuffer.c index 1d924e2a05..076ebc7816 100644 --- a/nuttx/drivers/rwbuffer.c +++ b/nuttx/drivers/rwbuffer.c @@ -213,7 +213,7 @@ static void rwb_wrstarttimeout(FAR struct rwbuffer_s *rwb) */ int ticks = (CONFIG_FS_WRDELAY + CLK_TCK/2) / CLK_TCK; - (void)work_queue(&rwb->work, rwb_wrtimeout, (FAR void *)rwb, ticks); + (void)work_queue(LPWORK, &rwb->work, rwb_wrtimeout, (FAR void *)rwb, ticks); } /**************************************************************************** @@ -222,7 +222,7 @@ static void rwb_wrstarttimeout(FAR struct rwbuffer_s *rwb) static inline void rwb_wrcanceltimeout(struct rwbuffer_s *rwb) { - (void)work_cancel(&rwb->work); + (void)work_cancel(LPWORK, &rwb->work); } /**************************************************************************** diff --git a/nuttx/drivers/usbhost/usbhost_hidkbd.c b/nuttx/drivers/usbhost/usbhost_hidkbd.c index 73224ff5c0..bb3ecad9e4 100644 --- a/nuttx/drivers/usbhost/usbhost_hidkbd.c +++ b/nuttx/drivers/usbhost/usbhost_hidkbd.c @@ -1674,7 +1674,7 @@ static int usbhost_disconnected(struct usbhost_class_s *class) */ DEBUGASSERT(priv->work.worker == NULL); - (void)work_queue(&priv->work, usbhost_destroy, priv, 0); + (void)work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0); } return OK; diff --git a/nuttx/drivers/usbhost/usbhost_skeleton.c b/nuttx/drivers/usbhost/usbhost_skeleton.c index 0a88a4f1ba..c44a265e80 100644 --- a/nuttx/drivers/usbhost/usbhost_skeleton.c +++ b/nuttx/drivers/usbhost/usbhost_skeleton.c @@ -1,8 +1,8 @@ /**************************************************************************** * drivers/usbhost/usbhost_skeleton.c * - * Copyright (C) 2011 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Copyright (C) 2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -1015,7 +1015,7 @@ static int usbhost_disconnected(struct usbhost_class_s *class) uvdbg("Queuing destruction: worker %p->%p\n", priv->work.worker, usbhost_destroy); DEBUGASSERT(priv->work.worker == NULL); - (void)work_queue(&priv->work, usbhost_destroy, priv, 0); + (void)work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0); } else { diff --git a/nuttx/drivers/usbhost/usbhost_storage.c b/nuttx/drivers/usbhost/usbhost_storage.c index 2e3136b339..2b14676d71 100644 --- a/nuttx/drivers/usbhost/usbhost_storage.c +++ b/nuttx/drivers/usbhost/usbhost_storage.c @@ -1786,7 +1786,7 @@ static int usbhost_disconnected(struct usbhost_class_s *class) uvdbg("Queuing destruction: worker %p->%p\n", priv->work.worker, usbhost_destroy); DEBUGASSERT(priv->work.worker == NULL); - (void)work_queue(&priv->work, usbhost_destroy, priv, 0); + (void)work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0); } else { diff --git a/nuttx/include/nuttx/wqueue.h b/nuttx/include/nuttx/wqueue.h index dfd424c8dc..644585c56f 100644 --- a/nuttx/include/nuttx/wqueue.h +++ b/nuttx/include/nuttx/wqueue.h @@ -50,6 +50,75 @@ /**************************************************************************** * Pre-Processor Definitions ****************************************************************************/ +/* Configuration ************************************************************/ +/* CONFIG_SCHED_WORKQUEUE. Create a dedicated "worker" thread to + * handle delayed processing from interrupt handlers. This feature + * is required for some drivers but, if there are not complaints, + * can be safely disabled. The worker thread also performs + * garbage collection -- completing any delayed memory deallocations + * from interrupt handlers. If the worker thread is disabled, + * then that clean will be performed by the IDLE thread instead + * (which runs at the lowest of priority and may not be appropriate + * if memory reclamation is of high priority). If CONFIG_SCHED_WORKQUEUE + * is enabled, then the following options can also be used: + * CONFIG_SCHED_WORKPRIORITY - The execution priority of the worker + * thread. Default: 192 + * CONFIG_SCHED_WORKPERIOD - How often the worker thread checks for + * work in units of microseconds. Default: 50*1000 (50 MS). + * CONFIG_SCHED_WORKSTACKSIZE - The stack size allocated for the worker + * thread. Default: CONFIG_IDLETHREAD_STACKSIZE. + * CONFIG_SIG_SIGWORK - The signal number that will be used to wake-up + * the worker thread. Default: 4 + * + * CONFIG_SCHED_LPWORK. If CONFIG_SCHED_WORKQUEUE is defined, then a single + * work queue is created by default. If CONFIG_SCHED_LPWORK is also defined + * then an additional, lower-priority work queue will also be created. This + * lower priority work queue is better suited for more extended processing + * (such as file system clean-up operations) + * CONFIG_SCHED_LPWORKPRIORITY - The execution priority of the lower priority + * worker thread. Default: 50 + * CONFIG_SCHED_LPWORKPERIOD - How often the lower priority worker thread + * checks for work in units of microseconds. Default: 50*1000 (50 MS). + * CONFIG_SCHED_LPWORKSTACKSIZE - The stack size allocated for the lower + * priority worker thread. Default: CONFIG_IDLETHREAD_STACKSIZE. + */ + +#ifndef CONFIG_SCHED_WORKPRIORITY +# define CONFIG_SCHED_WORKPRIORITY 192 +#endif + +#ifndef CONFIG_SCHED_WORKPERIOD +# define CONFIG_SCHED_WORKPERIOD (50*1000) /* 50 milliseconds */ +#endif + +#ifndef CONFIG_SCHED_WORKSTACKSIZE +# define CONFIG_SCHED_WORKSTACKSIZE CONFIG_IDLETHREAD_STACKSIZE +#endif + +#ifdef CONFIG_SCHED_LPWORK +# ifndef CONFIG_SCHED_LPWORKPRIORITY +# define CONFIG_SCHED_LPWORKPRIORITY 50 +# endif + +# ifndef CONFIG_SCHED_LPWORKPERIOD +# define CONFIG_SCHED_LPWORKPERIOD (50*1000) /* 50 milliseconds */ +# endif + +# ifndef CONFIG_SCHED_LPWORKSTACKSIZE +# define CONFIG_SCHED_LPWORKSTACKSIZE CONFIG_IDLETHREAD_STACKSIZE +# endif +#endif + +/* Work queue IDs (indices). These are both zero if there is only one work + * queue. + */ + +#define HPWORK 0 +#ifdef CONFIG_SCHED_LPWORK +# define LPWORK 1 +#else +# define LPWORK HPWORK +#endif /**************************************************************************** * Public Types @@ -86,10 +155,6 @@ extern "C" { #define EXTERN extern #endif -/* The task ID of the worker thread */ - -EXTERN pid_t g_worker; - /**************************************************************************** * Public Function Prototypes ****************************************************************************/ @@ -109,6 +174,7 @@ EXTERN pid_t g_worker; * and remove it from the work queue. * * Input parameters: + * qid - The work queue ID * work - The work structure to queue * worker - The worker callback to be invoked. The callback will invoked * on the worker thread of execution. @@ -122,7 +188,8 @@ EXTERN pid_t g_worker; * ****************************************************************************/ -EXTERN int work_queue(struct work_s *work, worker_t worker, FAR void *arg, uint32_t delay); +EXTERN int work_queue(int qid, FAR struct work_s *work, worker_t worker, + FAR void *arg, uint32_t delay); /**************************************************************************** * Name: work_cancel @@ -133,6 +200,7 @@ EXTERN int work_queue(struct work_s *work, worker_t worker, FAR void *arg, uint3 * again. * * Input parameters: + * qid - The work queue ID * work - The previously queue work structure to cancel * * Returned Value: @@ -140,7 +208,7 @@ EXTERN int work_queue(struct work_s *work, worker_t worker, FAR void *arg, uint3 * ****************************************************************************/ -EXTERN int work_cancel(struct work_s *work); +EXTERN int work_cancel(int qid, FAR struct work_s *work); /**************************************************************************** * Name: work_signal @@ -151,14 +219,14 @@ EXTERN int work_cancel(struct work_s *work); * user to force an immediate re-assessment of pending work. * * Input parameters: - * None + * qid - The work queue ID * * Returned Value: * Zero on success, a negated errno on failure * ****************************************************************************/ -#define work_signal() kill(g_worker, SIGWORK) +EXTERN int work_signal(int qid); /**************************************************************************** * Name: work_available diff --git a/nuttx/sched/Kconfig b/nuttx/sched/Kconfig index 37ce0ebd61..58d6ead703 100644 --- a/nuttx/sched/Kconfig +++ b/nuttx/sched/Kconfig @@ -166,6 +166,39 @@ config SIG_SIGWORK The signal number that will be used to wake-up the worker thread. Default: 4 +config SCHED_LPWORK + bool "Enable a lower priority worker thread" + default n + depends on SCHED_WORKQUEUE + ---help--- + If SCHED_WORKQUEUE is defined, then a single work queue is created by + default. If SCHED_LPWORK is also defined then an additional, lower- + priority work queue will also be created. This lower priority work + queue is better suited for more extended processing (such as file system + clean-up operations) + +config SCHED_LPWORKPRIORITY + int "Lower priority worker thread priority" + default 192 + depends on SCHED_LPWORK + ---help--- + The execution priority of the lopwer priority worker thread. Default: 192 + +config SCHED_LPWORKPERIOD + int "Lower priority worker thread period" + default 50000 + depends on SCHED_LPWORK + ---help--- + How often the lower priority worker thread checks for work in units + of microseconds. Default: 50*1000 (50 MS). + +config SCHED_LPWORKSTACKSIZE + int "Lower priority worker thread stack size" + default 2048 + depends on SCHED_LPWORK + ---help--- + The stack size allocated for the lower priority worker thread. Default: 2K. + config SCHED_WAITPID bool "Enable waitpid() API" default n diff --git a/nuttx/sched/Makefile b/nuttx/sched/Makefile index dfdc6f68b6..1e0a55aeaf 100644 --- a/nuttx/sched/Makefile +++ b/nuttx/sched/Makefile @@ -141,7 +141,7 @@ TIMER_SRCS = timer_initialize.c timer_create.c timer_delete.c timer_getoverrun.c endif ifeq ($(CONFIG_SCHED_WORKQUEUE),y) -WORK_SRCS = work_thread.c work_queue.c work_cancel.c +WORK_SRCS = work_thread.c work_queue.c work_cancel.c work_signal.c endif ifeq ($(CONFIG_PAGING),y) diff --git a/nuttx/sched/os_bringup.c b/nuttx/sched/os_bringup.c index d6d9431377..ec61528914 100644 --- a/nuttx/sched/os_bringup.c +++ b/nuttx/sched/os_bringup.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/os_bringup.c * - * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * With extensions by: @@ -47,6 +47,7 @@ #include #include +#include #include "os_internal.h" #ifdef CONFIG_PAGING @@ -149,10 +150,23 @@ int os_bringup(void) #ifdef CONFIG_SCHED_WORKQUEUE svdbg("Starting worker thread\n"); - g_worker = KERNEL_THREAD("work", CONFIG_SCHED_WORKPRIORITY, - CONFIG_SCHED_WORKSTACKSIZE, - (main_t)work_thread, (const char **)NULL); - ASSERT(g_worker != ERROR); + g_work[HPWORK].pid = KERNEL_THREAD("work0", CONFIG_SCHED_WORKPRIORITY, + CONFIG_SCHED_WORKSTACKSIZE, + (main_t)work_hpthread, (const char **)NULL); + ASSERT(g_work[HPWORK].pid != ERROR); + + /* Start a lower priority worker thread for other, non-critical continuation + * tasks + */ + +#ifdef CONFIG_SCHED_LPWORK + svdbg("Starting worker thread\n"); + + g_work[LPWORK].pid = KERNEL_THREAD("work1", CONFIG_SCHED_LPWORKPRIORITY, + CONFIG_SCHED_LPWORKSTACKSIZE, + (main_t)work_lpthread, (const char **)NULL); + ASSERT(g_work[LPWORK].pid != ERROR); +#endif #endif /* Once the operating system has been initialized, the system must be diff --git a/nuttx/sched/sched_free.c b/nuttx/sched/sched_free.c index 4df77b109a..e5e0bdacf0 100644 --- a/nuttx/sched/sched_free.c +++ b/nuttx/sched/sched_free.c @@ -1,7 +1,7 @@ /************************************************************************ * sched/sched_free.c * - * Copyright (C) 2007, 2009 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009, 2012 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -99,7 +99,7 @@ void sched_free(FAR void *address) /* Signal the worker thread that is has some clean up to do */ #ifdef CONFIG_SCHED_WORKQUEUE - work_signal(); + work_signal(LPWORK); #endif irqrestore(saved_state); } diff --git a/nuttx/sched/work_cancel.c b/nuttx/sched/work_cancel.c index 30b650826b..55df86f44f 100644 --- a/nuttx/sched/work_cancel.c +++ b/nuttx/sched/work_cancel.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/work_cancel.c * - * Copyright (C) 2009-2010 Gregory Nutt. All rights reserved. + * Copyright (C) 2009-2010, 2012 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -74,6 +74,7 @@ /**************************************************************************** * Public Functions ****************************************************************************/ + /**************************************************************************** * Name: work_cancel * @@ -83,6 +84,7 @@ * again. * * Input parameters: + * qid - The work queue ID * work - The previously queue work structure to cancel * * Returned Value: @@ -90,11 +92,12 @@ * ****************************************************************************/ -int work_cancel(struct work_s *work) +int work_cancel(int qid, FAR struct work_s *work) { + FAR struct wqueue_s *wqueue = &g_work[qid]; irqstate_t flags; - DEBUGASSERT(work != NULL); + DEBUGASSERT(work != NULL && (unsigned)qid < NWORKERS); /* Cancelling the work is simply a matter of removing the work structure * from the work queue. This must be done with interrupts disabled because @@ -106,18 +109,19 @@ int work_cancel(struct work_s *work) { /* A little test of the integrity of the work queue */ - DEBUGASSERT(work->dq.flink ||(FAR dq_entry_t *)work == g_work.tail); - DEBUGASSERT(work->dq.blink ||(FAR dq_entry_t *)work == g_work.head); + DEBUGASSERT(work->dq.flink ||(FAR dq_entry_t *)work == wqueue->q.tail); + DEBUGASSERT(work->dq.blink ||(FAR dq_entry_t *)work == wqueue->q.head); /* Remove the entry from the work queue and make sure that it is * mark as availalbe (i.e., the worker field is nullified). */ - dq_rem((FAR dq_entry_t *)work, &g_work); + dq_rem((FAR dq_entry_t *)work, &wqueue->q); work->worker = NULL; } irqrestore(flags); return OK; } + #endif /* CONFIG_SCHED_WORKQUEUE */ diff --git a/nuttx/sched/work_internal.h b/nuttx/sched/work_internal.h index 69b7bf5470..7f6c1a9373 100644 --- a/nuttx/sched/work_internal.h +++ b/nuttx/sched/work_internal.h @@ -51,51 +51,49 @@ /* Configuration ************************************************************/ -#ifndef CONFIG_SCHED_WORKPRIORITY -# define CONFIG_SCHED_WORKPRIORITY 50 -#endif - -#ifndef CONFIG_SCHED_WORKPERIOD -# define CONFIG_SCHED_WORKPERIOD (50*1000) /* 50 milliseconds */ -#endif - -#ifndef CONFIG_SCHED_WORKSTACKSIZE -# define CONFIG_SCHED_WORKSTACKSIZE CONFIG_IDLETHREAD_STACKSIZE -#endif - #ifdef CONFIG_DISABLE_SIGNALS # warning "Worker thread support requires signals" #endif +#ifdef CONFIG_SCHED_LPWORK +# define NWORKERS 2 +#else +# define NWORKERS 1 +#endif + /**************************************************************************** * Public Types ****************************************************************************/ #ifndef __ASSEMBLY__ +/* This structure defines the state on one work queue */ + +struct wqueue_s +{ + pid_t pid; /* The task ID of the worker thread */ + struct dq_queue_s q; /* The queue of pending work */ +}; + /**************************************************************************** * Public Data ****************************************************************************/ -/* The queue of pending work */ - -extern struct dq_queue_s g_work; +/* The state of each work queue */ -/* The task ID of the worker thread */ - -extern pid_t g_worker; +extern struct wqueue_s g_work[NWORKERS]; /**************************************************************************** * Public Function Prototypes ****************************************************************************/ /**************************************************************************** - * Name: work_thread + * Name: work_hpthread and work_lpthread * * Description: - * This is the main worker thread that performs actions placed on the work - * queue. It also performs periodic garbage collection (that is performed - * by the idle thread if CONFIG_SCHED_WORKQUEUE is not defined). + * These are the main worker threads that performs actions placed on the + * work lists. One thread also performs periodic garbage collection (that + * is performed by the idle thread if CONFIG_SCHED_WORKQUEUE is not defined). * * Input parameters: * argc, argv (not used) @@ -105,7 +103,11 @@ extern pid_t g_worker; * ****************************************************************************/ -int work_thread(int argc, char *argv[]); +int work_hpthread(int argc, char *argv[]); + +#ifdef CONFIG_SCHED_LPWORK +int work_lpthread(int argc, char *argv[]); +#endif #endif /* __ASSEMBLY__ */ #endif /* CONFIG_SCHED_WORKQUEUE */ diff --git a/nuttx/sched/work_queue.c b/nuttx/sched/work_queue.c index beeac168dd..075f08a4d7 100644 --- a/nuttx/sched/work_queue.c +++ b/nuttx/sched/work_queue.c @@ -76,6 +76,7 @@ /**************************************************************************** * Public Functions ****************************************************************************/ + /**************************************************************************** * Name: work_queue * @@ -91,6 +92,7 @@ * and remove it from the work queue. * * Input parameters: + * qid - The work queue ID (index) * work - The work structure to queue * worker - The worker callback to be invoked. The callback will invoked * on the worker thread of execution. @@ -104,11 +106,13 @@ * ****************************************************************************/ -int work_queue(struct work_s *work, worker_t worker, FAR void *arg, uint32_t delay) +int work_queue(int qid, FAR struct work_s *work, worker_t worker, + FAR void *arg, uint32_t delay) { + FAR struct wqueue_s *wqueue = &g_work[qid]; irqstate_t flags; - DEBUGASSERT(work != NULL); + DEBUGASSERT(work != NULL && (unsigned)qid < NWORKERS); /* First, initialize the work structure */ @@ -123,8 +127,10 @@ int work_queue(struct work_s *work, worker_t worker, FAR void *arg, uint32_t del flags = irqsave(); work->qtime = clock_systimer(); /* Time work queued */ - dq_addlast((FAR dq_entry_t *)work, &g_work); - work_signal(); /* Wake up the worker thread */ + + dq_addlast((FAR dq_entry_t *)work, &wqueue->q); + kill(wqueue->pid, SIGWORK); /* Wake up the worker thread */ + irqrestore(flags); return OK; } diff --git a/nuttx/sched/work_signal.c b/nuttx/sched/work_signal.c new file mode 100644 index 0000000000..6e6838c695 --- /dev/null +++ b/nuttx/sched/work_signal.c @@ -0,0 +1,96 @@ +/**************************************************************************** + * sched/work_signal.c + * + * Copyright (C) 2009-2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include + +#include + +#include "work_internal.h" + +#ifdef CONFIG_SCHED_WORKQUEUE + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Type Declarations + ****************************************************************************/ + +/**************************************************************************** + * Public Variables + ****************************************************************************/ + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ +/**************************************************************************** + * Name: work_signal + * + * Description: + * Signal the worker thread to process the work queue now. This function + * is used internally by the work logic but could also be used by the + * user to force an immediate re-assessment of pending work. + * + * Input parameters: + * qid - The work queue ID + * + * Returned Value: + * Zero on success, a negated errno on failure + * + ****************************************************************************/ + +int work_signal(int qid) +{ + DEBUGASSERT((unsigned)qid < NWORKERS); + return kill(g_work[qid].pid, SIGWORK); +} + +#endif /* CONFIG_SCHED_WORKQUEUE */ diff --git a/nuttx/sched/work_thread.c b/nuttx/sched/work_thread.c index fe14ae5e58..abd86f7710 100644 --- a/nuttx/sched/work_thread.c +++ b/nuttx/sched/work_thread.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/work_thread.c * - * Copyright (C) 2009-2011 Gregory Nutt. All rights reserved. + * Copyright (C) 2009-2012 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -67,15 +67,9 @@ * Public Variables ****************************************************************************/ -/* The queue of pending work */ +/* The state of each work queue */ -struct dq_queue_s g_work; - -/* The task ID of the worker thread */ - -#ifdef CONFIG_SCHED_WORKQUEUE -pid_t g_worker; -#endif +struct wqueue_s g_work[NWORKERS]; /**************************************************************************** * Private Variables @@ -86,130 +80,177 @@ pid_t g_worker; ****************************************************************************/ /**************************************************************************** - * Public Functions - ****************************************************************************/ -/**************************************************************************** - * Name: work_thread + * Name: work_process * * Description: - * This is the main worker thread that performs actions placed on the work - * list. It also performs periodic garbage collection (that is performed - * by the idle thread if CONFIG_SCHED_WORKQUEUE is not defined). + * This is the logic that performs actions placed on any work list. * * Input parameters: - * argc, argv (not used) + * wqueue - Describes the work queue to be processed * * Returned Value: - * Does not return + * None * ****************************************************************************/ -int work_thread(int argc, char *argv[]) +static void work_process(FAR struct wqueue_s *wqueue) { volatile FAR struct work_s *work; worker_t worker; + irqstate_t flags; FAR void *arg; uint32_t elapsed; uint32_t remaining; uint32_t next; - int usec; - irqstate_t flags; - /* Loop forever */ + /* Then process queued work. We need to keep interrupts disabled while + * we process items in the work list. + */ - usec = CONFIG_SCHED_WORKPERIOD; + next = CONFIG_SCHED_WORKPERIOD / USEC_PER_TICK; flags = irqsave(); - for (;;) + work = (FAR struct work_s *)wqueue->q.head; + while (work) { - /* Wait awhile to check the work list. We will wait here until either - * the time elapses or until we are awakened by a signal. + /* Is this work ready? It is ready if there is no delay or if + * the delay has elapsed. qtime is the time that the work was added + * to the work queue. It will always be greater than or equal to + * zero. Therefore a delay of zero will always execute immediately. */ - usleep(usec); - irqrestore(flags); + elapsed = clock_systimer() - work->qtime; + if (elapsed >= work->delay) + { + /* Remove the ready-to-execute work from the list */ - /* First, perform garbage collection. This cleans-up memory de-allocations - * that were queued because they could not be freed in that execution - * context (for example, if the memory was freed from an interrupt handler). - * NOTE: If the work thread is disabled, this clean-up is performed by - * the IDLE thread (at a very, very lower priority). - */ + (void)dq_rem((struct dq_entry_s *)work, &wqueue->q); - sched_garbagecollection(); + /* Extract the work description from the entry (in case the work + * instance by the re-used after it has been de-queued). + */ - /* Then process queued work. We need to keep interrupts disabled while - * we process items in the work list. - */ + worker = work->worker; + arg = work->arg; + + /* Mark the work as no longer being queued */ + + work->worker = NULL; + + /* Do the work. Re-enable interrupts while the work is being + * performed... we don't have any idea how long that will take! + */ + + irqrestore(flags); + worker(arg); - next = CONFIG_SCHED_WORKPERIOD / USEC_PER_TICK; - flags = irqsave(); - work = (FAR struct work_s *)g_work.head; - while (work) + /* Now, unfortunately, since we re-enabled interrupts we don't + * know the state of the work list and we will have to start + * back at the head of the list. + */ + + flags = irqsave(); + work = (FAR struct work_s *)wqueue->q.head; + } + else { - /* Is this work ready? It is ready if there is no delay or if - * the delay has elapsed. qtime is the time that the work was added - * to the work queue. It will always be greater than or equal to - * zero. Therefore a delay of zero will always execute immediately. + /* This one is not ready.. will it be ready before the next + * scheduled wakeup interval? */ - elapsed = clock_systimer() - work->qtime; - if (elapsed >= work->delay) + remaining = elapsed - work->delay; + if (remaining < next) { - /* Remove the ready-to-execute work from the list */ + /* Yes.. Then schedule to wake up when the work is ready */ - (void)dq_rem((struct dq_entry_s *)work, &g_work); + next = remaining; + } + + /* Then try the next in the list. */ - /* Extract the work description from the entry (in case the work - * instance by the re-used after it has been de-queued). - */ + work = (FAR struct work_s *)work->dq.flink; + } + } - worker = work->worker; - arg = work->arg; + /* Wait awhile to check the work list. We will wait here until either + * the time elapses or until we are awakened by a signal. + */ - /* Mark the work as no longer being queued */ + usleep(next * USEC_PER_TICK); + irqrestore(flags); +} - work->worker = NULL; +/**************************************************************************** + * Public Functions + ****************************************************************************/ +/**************************************************************************** + * Name: work_hpthread and work_lpthread + * + * Description: + * These are the main worker threads that performs actions placed on the + * work lists. One thread also performs periodic garbage collection (that + * is performed by the idle thread if CONFIG_SCHED_WORKQUEUE is not defined). + * + * Input parameters: + * argc, argv (not used) + * + * Returned Value: + * Does not return + * + ****************************************************************************/ - /* Do the work. Re-enable interrupts while the work is being - * performed... we don't have any idea how long that will take! - */ +int work_hpthread(int argc, char *argv[]) +{ + /* Loop forever */ - irqrestore(flags); - worker(arg); + for (;;) + { + /* First, perform garbage collection. This cleans-up memory de-allocations + * that were queued because they could not be freed in that execution + * context (for example, if the memory was freed from an interrupt handler). + * NOTE: If the work thread is disabled, this clean-up is performed by + * the IDLE thread (at a very, very lower priority). + */ - /* Now, unfortunately, since we re-enabled interrupts we don't - * know the state of the work list and we will have to start - * back at the head of the list. - */ +#ifdef CONFIG_SCHED_LPWORK + sched_garbagecollection(); +#endif - flags = irqsave(); - work = (FAR struct work_s *)g_work.head; - } - else - { - /* This one is not ready.. will it be ready before the next - * scheduled wakeup interval? - */ + /* Then process queued work. We need to keep interrupts disabled while + * we process items in the work list. + */ + + work_process(&g_work[HPWORK]); + } - remaining = elapsed - work->delay; - if (remaining < next) - { - /* Yes.. Then schedule to wake up when the work is ready */ + return OK; /* To keep some compilers happy */ +} - next = remaining; - } - - /* Then try the next in the list. */ +#ifdef CONFIG_SCHED_LPWORK +int work_lpthread(int argc, char *argv[]) +{ + /* Loop forever */ - work = (FAR struct work_s *)work->dq.flink; - } - } + for (;;) + { + /* First, perform garbage collection. This cleans-up memory de-allocations + * that were queued because they could not be freed in that execution + * context (for example, if the memory was freed from an interrupt handler). + * NOTE: If the work thread is disabled, this clean-up is performed by + * the IDLE thread (at a very, very lower priority). + */ - /* Now calculate the microsecond delay we should wait */ + sched_garbagecollection(); - usec = next * USEC_PER_TICK; + /* Then process queued work. We need to keep interrupts disabled while + * we process items in the work list. + */ + + work_process(&g_work[LPWORK]); } return OK; /* To keep some compilers happy */ } + +#endif /* CONFIG_SCHED_LPWORK */ + #endif /* CONFIG_SCHED_WORKQUEUE */ diff --git a/nuttx/tools/configure.sh b/nuttx/tools/configure.sh index 0d6c412a5c..c0df7035f0 100755 --- a/nuttx/tools/configure.sh +++ b/nuttx/tools/configure.sh @@ -101,17 +101,17 @@ if [ ! -d "${configpath}" ]; then fi if [ ! -r "${configpath}/Make.defs" ]; then - echo "File ${configpath}/Make.defs does not exist" + echo "File \"${configpath}/Make.defs\" does not exist" exit 4 fi if [ ! -r "${configpath}/setenv.sh" ]; then - echo "File ${configpath}/setenv.sh does not exist" + echo "File \"${configpath}/setenv.sh\" does not exist" exit 5 fi if [ ! -r "${configpath}/defconfig" ]; then - echo "File ${configpath}/defconfig does not exist" + echo "File \"${configpath}/defconfig\" does not exist" exit 6 fi @@ -127,7 +127,7 @@ if [ -z "${appdir}" ]; then appdir=`grep CONFIG_APPS_DIR= "${configpath}/defconfig" | cut -d'=' -f2` fi -# Check for the apps/ dir in the usual place if appdir was not provided +# Check for the apps/ directory in the usual place if appdir was not provided if [ -z "${appdir}" ]; then @@ -152,7 +152,15 @@ if [ -z "${appdir}" ]; then fi fi -# Okay... setup the configuration +# If appsdir was provided (or discovered) then make sure that the apps/ +# directory exists + +if [ ! -z "${appdir}" -a ! -d "${TOPDIR}/${appdir}" ]; then + echo "Directory \"${TOPDIR}/${appdir}\" does not exist" + exit 7 +fi + +# Okay... Everything looks good. Setup the configuration install -C "${configpath}/Make.defs" "${TOPDIR}/." || \ { echo "Failed to copy ${configpath}/Make.defs" ; exit 7 ; } -- cgit v1.2.3