Compare commits

...

3 commits

Author SHA1 Message Date
hujun5
625a58440e
Merge cedd250c1f into 43797ea6cc 2025-01-12 11:47:33 +08:00
yaojiaqi
43797ea6cc drivers/timers/watchdog: add watchdog timer notifier chain
Add support for watchdog timer notifer chain so that users
can customize the callback function when the watchdog timer
times out which enabled by Auto-monitor

Signed-off-by: yaojiaqi <yaojiaqi@lixiang.com>
2025-01-12 11:15:42 +08:00
hujun5
cedd250c1f wqueue: wqueue remove csection
reason:
We decouple semcount from business logic
by using an independent counting variable,
which allows us to remove critical sections in many cases.

Signed-off-by: hujun5 <hujun5@xiaomi.com>
2025-01-10 22:58:14 +08:00
11 changed files with 233 additions and 63 deletions

View file

@ -3215,8 +3215,6 @@ static void sam_callback(void *arg)
ret = work_cancel(LPWORK, &priv->cbwork);
if (ret < 0)
{
/* NOTE: Currently, work_cancel only returns success */
lcderr("ERROR: Failed to cancel work: %d\n", ret);
}
@ -3225,8 +3223,6 @@ static void sam_callback(void *arg)
priv->cbarg, 0);
if (ret < 0)
{
/* NOTE: Currently, work_queue only returns success */
lcderr("ERROR: Failed to schedule work: %d\n", ret);
}
}

View file

@ -3355,8 +3355,6 @@ static void sam_callback(void *arg)
ret = work_cancel(LPWORK, &priv->cbwork);
if (ret < 0)
{
/* NOTE: Currently, work_cancel only returns success */
mcerr("ERROR: Failed to cancel work: %d\n", ret);
}
@ -3365,8 +3363,6 @@ static void sam_callback(void *arg)
priv->cbarg, 0);
if (ret < 0)
{
/* NOTE: Currently, work_queue only returns success */
mcerr("ERROR: Failed to schedule work: %d\n", ret);
}
}

View file

@ -439,6 +439,14 @@ menuconfig WATCHDOG_AUTOMONITOR
if WATCHDOG_AUTOMONITOR
config WATCHDOG_TIMEOUT_NOTIFIER
bool "Enable watchdog timeout notifier"
default n
---help---
The watchdog timeout notifier chain mechanism supports users registering
custom callback functions, which will be called when the watchdog timer
managed by Auto-monitor times out.
choice
prompt "Auto-monitor keepalive by"
default WATCHDOG_AUTOMONITOR_BY_WDOG

View file

@ -71,6 +71,20 @@
# endif
#endif
#if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_ONESHOT)
# define WATCHDOG_NOTIFIER_ACTION WATCHDOG_KEEPALIVE_BY_ONESHOT
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER)
# define WATCHDOG_NOTIFIER_ACTION WATCHDOG_KEEPALIVE_BY_TIMER
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WDOG)
# define WATCHDOG_NOTIFIER_ACTION WATCHDOG_KEEPALIVE_BY_WDOG
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WORKER)
# define WATCHDOG_NOTIFIER_ACTION WATCHDOG_KEEPALIVE_BY_WORKER
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE)
# define WATCHDOG_NOTIFIER_ACTION WATCHDOG_KEEPALIVE_BY_CAPTURE
#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE)
# define WATCHDOG_NOTIFIER_ACTION WATCHDOG_KEEPALIVE_BY_IDLE
#endif
/****************************************************************************
* Private Type Definitions
****************************************************************************/
@ -135,6 +149,10 @@ static const struct file_operations g_wdogops =
wdog_ioctl, /* ioctl */
};
#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
static ATOMIC_NOTIFIER_HEAD(g_watchdog_notifier_list);
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
@ -699,6 +717,55 @@ static int wdog_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
* Public Functions
****************************************************************************/
#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
/****************************************************************************
* Name: watchdog_notifier_chain_register
*
* Description:
* Add notifier to the watchdog notifier chain
*
* Input Parameters:
* nb - New entry in notifier chain
*
****************************************************************************/
void watchdog_notifier_chain_register(FAR struct notifier_block *nb)
{
atomic_notifier_chain_register(&g_watchdog_notifier_list, nb);
}
/****************************************************************************
* Name: watchdog_notifier_chain_unregister
*
* Description:
* Remove notifier from the watchdog notifier chain
*
* Input Parameters:
* nb - Entry to remove from notifier chain
*
****************************************************************************/
void watchdog_notifier_chain_unregister(FAR struct notifier_block *nb)
{
atomic_notifier_chain_unregister(&g_watchdog_notifier_list, nb);
}
/****************************************************************************
* Name: watchdog_automonitor_timeout
*
* Description:
* This function can be called in the watchdog timeout interrupt handler.
* If so, callbacks on the watchdog timer notify chain are called when the
* watchdog timer times out.
*
****************************************************************************/
void watchdog_automonitor_timeout(void)
{
atomic_notifier_call_chain(&g_watchdog_notifier_list, action, data);
}
#endif /* CONFIG_WATCHDOG_TIMEOUT_NOTIFIER */
/****************************************************************************
* Name: watchdog_register
*

View file

@ -659,8 +659,6 @@ static void automount_timeout(wdparm_t arg)
ret = work_queue(LPWORK, &priv->work, automount_worker, priv, 0);
if (ret < 0)
{
/* NOTE: Currently, work_queue only returns success */
ferr("ERROR: Failed to schedule work: %d\n", ret);
}
}
@ -772,8 +770,6 @@ static int automount_interrupt(FAR const struct automount_lower_s *lower,
priv->lower->ddelay);
if (ret < 0)
{
/* NOTE: Currently, work_queue only returns success */
ferr("ERROR: Failed to schedule work: %d\n", ret);
}
else
@ -848,8 +844,6 @@ FAR void *automount_initialize(FAR const struct automount_lower_s *lower)
priv->lower->ddelay);
if (ret < 0)
{
/* NOTE: Currently, work_queue only returns success */
ferr("ERROR: Failed to schedule work: %d\n", ret);
}

View file

@ -31,6 +31,7 @@
#include <nuttx/compiler.h>
#include <nuttx/irq.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/notifier.h>
#ifdef CONFIG_WATCHDOG
@ -88,6 +89,35 @@
#define WDFLAGS_CAPTURE (1 << 2) /* 1=Call the user function when the
* watchdog timer expires */
/* Keepalive Actions ********************************************************/
/* According to the keepalive action specified by the Auto-monitor, callback
* functions registered on the watchdog notifier chain may take corresponding
* actions.
*
* These are detected and handled by the "upper half" watchdog timer driver.
*
* WATCHDOG_KEEPALIVE_BY_ONESHOT - The watchdog timer is keepalive by
* oneshot timer.
* WATCHDOG_KEEPALIVE_BY_TIMER - The watchdog timer is keepalive by
* timer.
* WATCHDOG_KEEPALIVE_BY_WDOG - The watchdog timer is keepalive by
* wdog.
* WATCHDOG_KEEPALIVE_BY_WORKER - The watchdog timer is keepalive by
* worker queue.
* WATCHDOG_KEEPALIVE_BY_CAPTURE - The watchdog timer is keepalive by
* capture.
* WATCHDOG_KEEPALIVE_BY_IDLE - The watchdog timer is keepalive by
* idle task.
*/
#define WATCHDOG_KEEPALIVE_BY_ONESHOT 0
#define WATCHDOG_KEEPALIVE_BY_TIMER 1
#define WATCHDOG_KEEPALIVE_BY_WDOG 2
#define WATCHDOG_KEEPALIVE_BY_WORKER 3
#define WATCHDOG_KEEPALIVE_BY_CAPTURE 4
#define WATCHDOG_KEEPALIVE_BY_IDLE 5
/****************************************************************************
* Public Types
****************************************************************************/
@ -197,6 +227,47 @@ extern "C"
#define EXTERN extern
#endif
#ifdef CONFIG_WATCHDOG_TIMEOUT_NOTIFIER
/****************************************************************************
* Name: watchdog_notifier_chain_register
*
* Description:
* Add notifier to the watchdog notifier chain
*
* Input Parameters:
* nb - New entry in notifier chain
*
****************************************************************************/
void watchdog_notifier_chain_register(FAR struct notifier_block *nb);
/****************************************************************************
* Name: watchdog_notifier_chain_unregister
*
* Description:
* Remove notifier from the watchdog notifier chain
*
* Input Parameters:
* nb - Entry to remove from notifier chain
*
****************************************************************************/
void watchdog_notifier_chain_unregister(FAR struct notifier_block *nb);
/****************************************************************************
* Name: watchdog_automonitor_timeout
*
* Description:
* This function can be called in the watchdog timeout interrupt handler.
* If so, callbacks on the watchdog timer notify chain are called when the
* watchdog timer times out.
*
****************************************************************************/
void watchdog_automonitor_timeout(void);
#endif /* CONFIG_WATCHDOG_TIMEOUT_NOTIFIER */
/****************************************************************************
* Name: watchdog_register
*

View file

@ -58,23 +58,20 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
* new work is typically added to the work queue from interrupt handlers.
*/
flags = enter_critical_section();
flags = spin_lock_irqsave(&g_wqueue_lock);
if (work->worker != NULL)
{
/* Remove the entry from the work queue and make sure that it is
* marked as available (i.e., the worker field is nullified).
*/
if (WDOG_ISACTIVE(&work->u.timer))
{
wd_cancel(&work->u.timer);
}
else
work->worker = NULL;
wd_cancel(&work->u.timer);
if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
{
dq_rem((FAR dq_entry_t *)work, &wqueue->q);
}
work->worker = NULL;
ret = OK;
}
else if (!up_interrupt_context() && !sched_idletask() && sync)
@ -86,14 +83,15 @@ static int work_qcancel(FAR struct kwork_wqueue_s *wqueue, bool sync,
if (wqueue->worker[wndx].work == work &&
wqueue->worker[wndx].pid != nxsched_gettid())
{
wqueue->worker[wndx].wait_count--;
spin_unlock_irqrestore(&g_wqueue_lock, flags);
nxsem_wait_uninterruptible(&wqueue->worker[wndx].wait);
ret = 1;
break;
return 1;
}
}
}
leave_critical_section(flags);
spin_unlock_irqrestore(&g_wqueue_lock, flags);
return ret;
}

View file

@ -87,6 +87,8 @@ static dq_queue_t g_notifier_free;
static dq_queue_t g_notifier_pending;
static spinlock_t g_work_notifier_lock;
/****************************************************************************
* Private Functions
****************************************************************************/
@ -166,17 +168,21 @@ static void work_notifier_worker(FAR void *arg)
/* Disable interrupts very briefly. */
flags = enter_critical_section();
flags = spin_lock_irqsave(&g_work_notifier_lock);
/* Remove the notification from the pending list */
dq_rem(&notifier->entry, &g_notifier_pending);
notifier = work_notifier_find(notifier->key);
if (notifier != NULL)
{
dq_rem(&notifier->entry, &g_notifier_pending);
/* Put the notification to the free list */
/* Put the notification to the free list */
dq_addlast(&notifier->entry, &g_notifier_free);
dq_addlast(&notifier->entry, &g_notifier_free);
}
leave_critical_section(flags);
spin_unlock_irqrestore(&g_work_notifier_lock, flags);
}
/****************************************************************************
@ -213,14 +219,14 @@ int work_notifier_setup(FAR struct work_notifier_s *info)
/* Disable interrupts very briefly. */
flags = enter_critical_section();
flags = spin_lock_irqsave(&g_work_notifier_lock);
/* Try to get the entry from the free list */
notifier = (FAR struct work_notifier_entry_s *)
dq_remfirst(&g_notifier_free);
leave_critical_section(flags);
spin_unlock_irqrestore(&g_work_notifier_lock, flags);
if (notifier == NULL)
{
@ -245,7 +251,7 @@ int work_notifier_setup(FAR struct work_notifier_s *info)
/* Disable interrupts very briefly. */
flags = enter_critical_section();
flags = spin_lock_irqsave(&g_work_notifier_lock);
/* Generate a unique key for this notification */
@ -262,7 +268,7 @@ int work_notifier_setup(FAR struct work_notifier_s *info)
dq_addlast(&notifier->entry, &g_notifier_pending);
ret = notifier->key;
leave_critical_section(flags);
spin_unlock_irqrestore(&g_work_notifier_lock, flags);
}
return ret;
@ -293,7 +299,7 @@ void work_notifier_teardown(int key)
/* Disable interrupts very briefly. */
flags = enter_critical_section();
flags = spin_lock_irqsave(&g_work_notifier_lock);
/* Find the entry matching this key in the g_notifier_pending list. We
* assume that there is only one.
@ -304,19 +310,18 @@ void work_notifier_teardown(int key)
{
/* Cancel the work, this may be waiting */
if (work_cancel_sync(notifier->info.qid, &notifier->work) != 1)
{
/* Remove the notification from the pending list */
work_cancel(notifier->info.qid, &notifier->work);
dq_rem(&notifier->entry, &g_notifier_pending);
/* Remove the notification from the pending list */
/* Put the notification to the free list */
dq_rem(&notifier->entry, &g_notifier_pending);
dq_addlast(&notifier->entry, &g_notifier_free);
}
/* Put the notification to the free list */
dq_addlast(&notifier->entry, &g_notifier_free);
}
leave_critical_section(flags);
spin_unlock_irqrestore(&g_work_notifier_lock, flags);
}
/****************************************************************************
@ -352,7 +357,7 @@ void work_notifier_signal(enum work_evtype_e evtype,
* the notifications have been sent.
*/
flags = enter_critical_section();
flags = spin_lock_irqsave(&g_work_notifier_lock);
sched_lock();
/* Process the notification at the head of the pending list until the
@ -397,7 +402,7 @@ void work_notifier_signal(enum work_evtype_e evtype,
}
sched_unlock();
leave_critical_section(flags);
spin_unlock_irqrestore(&g_work_notifier_lock, flags);
}
#endif /* CONFIG_WQUEUE_NOTIFIER */

View file

@ -47,11 +47,10 @@
#define queue_work(wqueue, work) \
do \
{ \
int sem_count; \
dq_addlast((FAR dq_entry_t *)(work), &(wqueue)->q); \
nxsem_get_value(&(wqueue)->sem, &sem_count); \
if (sem_count < 0) /* There are threads waiting for sem. */ \
if ((wqueue)->wait_count < 0) /* There are threads waiting for sem. */ \
{ \
(wqueue)->wait_count++; \
nxsem_post(&(wqueue)->sem); \
} \
} \
@ -68,24 +67,30 @@
static void work_timer_expiry(wdparm_t arg)
{
FAR struct work_s *work = (FAR struct work_s *)arg;
irqstate_t flags = enter_critical_section();
irqstate_t flags = spin_lock_irqsave(&g_wqueue_lock);
sched_lock();
queue_work(work->wq, work);
leave_critical_section(flags);
/* We have being canceled */
if (work->worker != NULL)
{
queue_work(work->wq, work);
}
spin_unlock_irqrestore(&g_wqueue_lock, flags);
sched_unlock();
}
static bool work_is_canceling(FAR struct kworker_s *kworkers, int nthreads,
FAR struct work_s *work)
{
int semcount;
int wndx;
for (wndx = 0; wndx < nthreads; wndx++)
{
if (kworkers[wndx].work == work)
{
nxsem_get_value(&kworkers[wndx].wait, &semcount);
if (semcount < 0)
if (kworkers[wndx].wait_count < 0)
{
return true;
}
@ -145,13 +150,23 @@ int work_queue_wq(FAR struct kwork_wqueue_s *wqueue,
* task logic or from interrupt handling logic.
*/
flags = enter_critical_section();
flags = spin_lock_irqsave(&g_wqueue_lock);
sched_lock();
/* Remove the entry from the timer and work queue. */
if (work->worker != NULL)
{
work_cancel_wq(wqueue, work);
/* Remove the entry from the work queue and make sure that it is
* marked as available (i.e., the worker field is nullified).
*/
work->worker = NULL;
wd_cancel(&work->u.timer);
if (dq_inqueue((FAR dq_entry_t *)work, &wqueue->q))
{
dq_rem((FAR dq_entry_t *)work, &wqueue->q);
}
}
if (work_is_canceling(wqueue->worker, wqueue->nthreads, work))
@ -177,7 +192,8 @@ int work_queue_wq(FAR struct kwork_wqueue_s *wqueue,
}
out:
leave_critical_section(flags);
spin_unlock_irqrestore(&g_wqueue_lock, flags);
sched_unlock();
return ret;
}

View file

@ -104,6 +104,8 @@ struct lp_wqueue_s g_lpwork =
#endif /* CONFIG_SCHED_LPWORK */
spinlock_t g_wqueue_lock = SP_UNLOCKED;
/****************************************************************************
* Private Functions
****************************************************************************/
@ -138,7 +140,6 @@ static int work_thread(int argc, FAR char *argv[])
worker_t worker;
irqstate_t flags;
FAR void *arg;
int semcount;
/* Get the handle from argv */
@ -147,7 +148,8 @@ static int work_thread(int argc, FAR char *argv[])
kworker = (FAR struct kworker_s *)
((uintptr_t)strtoul(argv[2], NULL, 16));
flags = enter_critical_section();
flags = spin_lock_irqsave(&g_wqueue_lock);
sched_lock();
/* Loop forever */
@ -189,9 +191,12 @@ static int work_thread(int argc, FAR char *argv[])
* performed... we don't have any idea how long this will take!
*/
leave_critical_section(flags);
spin_unlock_irqrestore(&g_wqueue_lock, flags);
sched_unlock();
CALL_WORKER(worker, arg);
flags = enter_critical_section();
flags = spin_lock_irqsave(&g_wqueue_lock);
sched_lock();
/* Mark the thread un-busy */
@ -199,9 +204,9 @@ static int work_thread(int argc, FAR char *argv[])
/* Check if someone is waiting, if so, wakeup it */
nxsem_get_value(&kworker->wait, &semcount);
while (semcount++ < 0)
while (kworker->wait_count < 0)
{
kworker->wait_count++;
nxsem_post(&kworker->wait);
}
}
@ -211,10 +216,17 @@ static int work_thread(int argc, FAR char *argv[])
* posted.
*/
wqueue->wait_count--;
spin_unlock_irqrestore(&g_wqueue_lock, flags);
sched_unlock();
nxsem_wait_uninterruptible(&wqueue->sem);
flags = spin_lock_irqsave(&g_wqueue_lock);
sched_lock();
}
leave_critical_section(flags);
spin_unlock_irqrestore(&g_wqueue_lock, flags);
sched_unlock();
nxsem_post(&wqueue->exsem);
return OK;
@ -277,6 +289,7 @@ static int work_thread_create(FAR const char *name, int priority,
}
wqueue->worker[wndx].pid = pid;
wqueue->worker[wndx].wait_count = 0;
}
sched_unlock();
@ -337,6 +350,7 @@ FAR struct kwork_wqueue_s *work_queue_create(FAR const char *name,
nxsem_init(&wqueue->sem, 0, 0);
nxsem_init(&wqueue->exsem, 0, 0);
wqueue->nthreads = nthreads;
wqueue->wait_count = 0;
/* Create the work queue thread pool */

View file

@ -35,6 +35,7 @@
#include <nuttx/clock.h>
#include <nuttx/queue.h>
#include <nuttx/wqueue.h>
#include <nuttx/spinlock.h>
#ifdef CONFIG_SCHED_WORKQUEUE
@ -58,6 +59,7 @@ struct kworker_s
pid_t pid; /* The task ID of the worker thread */
FAR struct work_s *work; /* The work structure */
sem_t wait; /* Sync waiting for worker done */
volatile int16_t wait_count;
};
/* This structure defines the state of one kernel-mode work queue */
@ -69,6 +71,7 @@ struct kwork_wqueue_s
sem_t exsem; /* Sync waiting for thread exit */
uint8_t nthreads; /* Number of worker threads */
bool exit; /* A flag to request the thread to exit */
volatile int16_t wait_count;
struct kworker_s worker[0]; /* Describes a worker thread */
};
@ -126,6 +129,8 @@ extern struct hp_wqueue_s g_hpwork;
extern struct lp_wqueue_s g_lpwork;
#endif
extern spinlock_t g_wqueue_lock;
/****************************************************************************
* Public Function Prototypes
****************************************************************************/