diff --git a/drivers/timers/rpmsg_rtc.c b/drivers/timers/rpmsg_rtc.c index 7f97ee240c6..c6c9f7b41fb 100644 --- a/drivers/timers/rpmsg_rtc.c +++ b/drivers/timers/rpmsg_rtc.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -302,12 +303,16 @@ static int rpmsg_rtc_ept_cb(FAR struct rpmsg_endpoint *ept, FAR void *data, struct rpmsg_rtc_set_s *msg = data; #ifdef CONFIG_RTC_RPMSG_SYNC_BASETIME + struct timespec ts; irqstate_t flags; flags = spin_lock_irqsave(&g_basetime_lock); g_basetime.tv_sec = msg->base_sec; g_basetime.tv_nsec = msg->base_nsec; spin_unlock_irqrestore(&g_basetime_lock, flags); + + clock_gettime(CLOCK_REALTIME, &ts); + clock_notifier_call_chain(CLOCK_REALTIME, &ts); #else struct timespec tp; diff --git a/fs/vfs/fs_timerfd.c b/fs/vfs/fs_timerfd.c index 741307711bd..f3ec8b2e171 100644 --- a/fs/vfs/fs_timerfd.c +++ b/fs/vfs/fs_timerfd.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #include @@ -70,6 +72,8 @@ struct timerfd_priv_s struct wdog_s wdog; /* The watchdog that provides the timing */ timerfd_t counter; /* timerfd counter */ uint8_t crefs; /* References counts on timerfd (max: 255) */ + struct notifier_block nb; /* The clock notifier node */ + bool cancel; /* Canceled by discontinuous change to the clock */ /* The following is a list if poll structures of threads waiting for * driver events. @@ -156,8 +160,19 @@ static FAR struct timerfd_priv_s *timerfd_allocdev(void) return dev; } +static void +timerfd_unregister_clock_notifier(FAR struct timerfd_priv_s *dev) +{ + if (dev->nb.notifier_call) + { + unregister_clock_notifier(&dev->nb); + dev->nb.notifier_call = NULL; + } +} + static void timerfd_destroy(FAR struct timerfd_priv_s *dev) { + timerfd_unregister_clock_notifier(dev); wd_cancel(&dev->wdog); nxmutex_unlock(&dev->lock); nxmutex_destroy(&dev->lock); @@ -287,6 +302,13 @@ static ssize_t timerfd_read(FAR struct file *filep, FAR char *buffer, intflags = enter_critical_section(); + if (dev->cancel) + { + dev->cancel = false; + leave_critical_section(intflags); + return -ECANCELED; + } + /* Wait for an incoming event */ if (dev->counter == 0) @@ -309,6 +331,13 @@ static ssize_t timerfd_read(FAR struct file *filep, FAR char *buffer, nxsem_destroy(&sem.sem); return ret; } + + if (dev->cancel) + { + dev->cancel = false; + leave_critical_section(intflags); + return -ECANCELED; + } } while (dev->counter == 0); @@ -373,11 +402,9 @@ static int timerfd_poll(FAR struct file *filep, FAR struct pollfd *fds, /* Notify the POLLIN event if the counter is not zero */ - if (dev->counter > 0) + if (dev->counter > 0 || dev->cancel) { -#ifdef CONFIG_TIMER_FD_POLL poll_notify(&fds, 1, POLLIN); -#endif } out: @@ -386,28 +413,9 @@ out: } #endif -static void timerfd_timeout(wdparm_t arg) +static void timerfd_notify(FAR struct timerfd_priv_s *dev) { - FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)arg; FAR timerfd_waiter_sem_t *cur_sem; - irqstate_t intflags; - - /* Disable interrupts to ensure that expiration counter is accessed - * atomically - */ - - intflags = enter_critical_section(); - - /* Increment timer expiration counter */ - - dev->counter++; - - /* If this is a repetitive timer, then restart the watchdog */ - - if (dev->delay > 0u) - { - wd_start(&dev->wdog, dev->delay, timerfd_timeout, arg); - } #ifdef CONFIG_TIMER_FD_POLL /* Notify all poll/select waiters */ @@ -426,6 +434,56 @@ static void timerfd_timeout(wdparm_t arg) dev->rdsems = NULL; + if (dev->delay > 0) + { + wd_start(&dev->wdog, dev->delay, timerfd_timeout, + (wdparm_t)dev); + } + else + { + timerfd_unregister_clock_notifier(dev); + } +} + +static int timerfd_changed_handler(FAR struct notifier_block *nb, + unsigned long action, FAR void *data) +{ + if (action == CLOCK_REALTIME) + { + FAR struct timerfd_priv_s *dev; + + dev = container_of(nb, struct timerfd_priv_s, nb); + dev->cancel = true; + wd_cancel(&dev->wdog); + timerfd_notify(dev); + } + + return 0; +} + +static void timerfd_timeout(wdparm_t arg) +{ + FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)arg; + irqstate_t intflags; + + /* Disable interrupts to ensure that expiration counter is accessed + * atomically + */ + + intflags = enter_critical_section(); + + if (dev->cancel) + { + leave_critical_section(intflags); + return; + } + + /* Increment timer expiration counter */ + + dev->counter++; + + timerfd_notify(dev); + leave_critical_section(intflags); } @@ -503,7 +561,7 @@ int timerfd_settime(int fd, int flags, goto errout; } - if ((flags & ~TFD_TIMER_ABSTIME) != 0) + if ((flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) != 0) { ret = -EINVAL; goto errout; @@ -548,6 +606,10 @@ int timerfd_settime(int fd, int flags, wd_cancel(&dev->wdog); + /* Unregister notifier cb if it exists */ + + timerfd_unregister_clock_notifier(dev); + /* Clear expiration counter */ dev->counter = 0; @@ -558,9 +620,7 @@ int timerfd_settime(int fd, int flags, if (new_value->it_value.tv_sec <= 0 && new_value->it_value.tv_nsec <= 0) { - leave_critical_section(intflags); - file_put(filep); - return OK; + goto errout_with_csection; } /* Setup up any repetitive timer */ @@ -599,24 +659,33 @@ int timerfd_settime(int fd, int flags, delay = dev->delay; } + if (flags & TFD_TIMER_CANCEL_ON_SET) + { + dev->nb.notifier_call = timerfd_changed_handler; + register_clock_notifier(&dev->nb); + } + /* Then start the watchdog */ ret = wd_start(&dev->wdog, delay, timerfd_timeout, (wdparm_t)dev); if (ret < 0) { - leave_critical_section(intflags); - goto errout_with_filep; + timerfd_unregister_clock_notifier(dev); + goto errout_with_csection; } +errout_with_csection: leave_critical_section(intflags); - file_put(filep); - return OK; - errout_with_filep: file_put(filep); errout: - set_errno(-ret); - return ERROR; + if (ret < 0) + { + set_errno(-ret); + return ERROR; + } + + return OK; } int timerfd_gettime(int fd, FAR struct itimerspec *curr_value) diff --git a/include/nuttx/clock_notifier.h b/include/nuttx/clock_notifier.h new file mode 100644 index 00000000000..285512a38a8 --- /dev/null +++ b/include/nuttx/clock_notifier.h @@ -0,0 +1,83 @@ +/**************************************************************************** + * include/nuttx/clock_notifier.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_CLOCK_NOTIFIER_H +#define __INCLUDE_NUTTX_CLOCK_NOTIFIER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Function + ****************************************************************************/ + +/**************************************************************************** + * Name: register_clock_notifier + * + * Description: + * Add notifier to the clock_notifier chain + * + * Input Parameters: + * nb - New entry in notifier chain + * + ****************************************************************************/ + +void register_clock_notifier(FAR struct notifier_block *nb); + +/**************************************************************************** + * Name: unregister_clock_notifier + * + * Description: + * Remove notifier from the clock_notifier chain + * + * Input Parameters: + * nb - Entry to remove from notifier chain + * + ****************************************************************************/ + +void unregister_clock_notifier(FAR struct notifier_block *nb); + +/**************************************************************************** + * Name: clock_notifier_call_chain + * + * Description: + * Call functions in the clock_notifier chain. + * + * Input Parameters: + * action - Value passed unmodified to notifier function + * tp - Pointer of current timespec passed unmodified to notifier function + * + ****************************************************************************/ + +void clock_notifier_call_chain(unsigned long action, + FAR const struct timespec *tp); + +#endif /* __INCLUDE_NUTTX_CLOCK_NOTIFIER_H */ diff --git a/include/sys/timerfd.h b/include/sys/timerfd.h index 9395df12c50..32cc9585e1e 100644 --- a/include/sys/timerfd.h +++ b/include/sys/timerfd.h @@ -35,10 +35,10 @@ * Pre-processor Definitions ****************************************************************************/ -#define TFD_NONBLOCK O_NONBLOCK -#define TFD_CLOEXEC O_CLOEXEC - -#define TFD_TIMER_ABSTIME TIMER_ABSTIME +#define TFD_NONBLOCK O_NONBLOCK +#define TFD_CLOEXEC O_CLOEXEC +#define TFD_TIMER_ABSTIME TIMER_ABSTIME +#define TFD_TIMER_CANCEL_ON_SET 2 /**************************************************************************** * Public Type Declarations diff --git a/sched/clock/CMakeLists.txt b/sched/clock/CMakeLists.txt index d7740ca4777..617c811a39b 100644 --- a/sched/clock/CMakeLists.txt +++ b/sched/clock/CMakeLists.txt @@ -23,6 +23,7 @@ set(SRCS clock.c clock_initialize.c + clock_notifier.c clock_getres.c clock_settime.c clock_gettime.c diff --git a/sched/clock/Make.defs b/sched/clock/Make.defs index 659abd6ea18..b9f8d846ca6 100644 --- a/sched/clock/Make.defs +++ b/sched/clock/Make.defs @@ -23,6 +23,7 @@ CSRCS += clock.c clock_initialize.c clock_settime.c clock_gettime.c CSRCS += clock_systime_ticks.c clock_systime_timespec.c clock_sched_ticks.c CSRCS += clock_perf.c clock_realtime2absticks.c clock_getres.c +CSRCS += clock_notifier.c # Unless a driver with a more accurate version of up_*delay is enabled, always # include the base delay definition (busy-loop) as a minimum-viable product. We diff --git a/sched/clock/clock_initialize.c b/sched/clock/clock_initialize.c index 4a6bdff743a..94bb3167218 100644 --- a/sched/clock/clock_initialize.c +++ b/sched/clock/clock_initialize.c @@ -38,6 +38,7 @@ #include #include +#include #include #include @@ -182,6 +183,7 @@ static void clock_inittime(FAR const struct timespec *tp) } spin_unlock_irqrestore(&g_basetime_lock, flags); + clock_notifier_call_chain(CLOCK_REALTIME, &g_basetime); #else clock_inittimekeeping(tp); #endif diff --git a/sched/clock/clock_notifier.c b/sched/clock/clock_notifier.c new file mode 100644 index 00000000000..ecd6a26e89a --- /dev/null +++ b/sched/clock/clock_notifier.c @@ -0,0 +1,87 @@ +/**************************************************************************** + * sched/clock/clock_notifier.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static ATOMIC_NOTIFIER_HEAD(g_clock_notifier_list); + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: register_clock_notifier + * + * Description: + * Add notifier to the clock_notifier chain + * + * Input Parameters: + * nb - New entry in notifier chain + * + ****************************************************************************/ + +void register_clock_notifier(FAR struct notifier_block *nb) +{ + atomic_notifier_chain_register(&g_clock_notifier_list, nb); +} + +/**************************************************************************** + * Name: unregister_clock_notifier + * + * Description: + * Remove notifier from the clock_notifier chain + * + * Input Parameters: + * nb - Entry to remove from notifier chain + * + ****************************************************************************/ + +void unregister_clock_notifier(FAR struct notifier_block *nb) +{ + atomic_notifier_chain_unregister(&g_clock_notifier_list, nb); +} + +/**************************************************************************** + * Name: clock_notifier_call_chain + * + * Description: + * Call functions in the clock_notifier chain. + * + * Input Parameters: + * action - Value passed unmodified to notifier function + * tp - Pointer of current timespec passed unmodified to notifier function + * + ****************************************************************************/ + +void clock_notifier_call_chain(unsigned long action, + FAR const struct timespec *tp) +{ + atomic_notifier_call_chain(&g_clock_notifier_list, action, (FAR void *)tp); +} diff --git a/sched/clock/clock_settime.c b/sched/clock/clock_settime.c index cfe749f1f7f..37a13dd2744 100644 --- a/sched/clock/clock_settime.c +++ b/sched/clock/clock_settime.c @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -72,6 +73,7 @@ static void nxclock_set_realtime(FAR const struct timespec *tp) flags = spin_lock_irqsave(&g_basetime_lock); clock_timespec_subtract(tp, &bias, &g_basetime); + clock_notifier_call_chain(CLOCK_REALTIME, tp); spin_unlock_irqrestore(&g_basetime_lock, flags); diff --git a/sched/clock/clock_timekeeping.c b/sched/clock/clock_timekeeping.c index 8944857e641..8e52590b962 100644 --- a/sched/clock/clock_timekeeping.c +++ b/sched/clock/clock_timekeeping.c @@ -36,6 +36,7 @@ #include #include +#include #include "clock/clock.h" @@ -133,6 +134,7 @@ int clock_timekeeping_set_wall_time(FAR const struct timespec *ts) } memcpy(&g_clock_wall_time, ts, sizeof(struct timespec)); + clock_notifier_call_chain(CLOCK_REALTIME, ts); g_clock_adjust = 0; g_clock_last_counter = counter; @@ -295,6 +297,7 @@ void clock_inittimekeeping(FAR const struct timespec *tp) clock_basetime(&g_clock_wall_time); } + clock_notifier_call_chain(CLOCK_REALTIME, &g_clock_wall_time); up_timer_gettick(&g_clock_last_counter); spin_unlock_irqrestore(&g_clock_lock, flags); }