| // Copyright 2016 The Fuchsia Authors |
| // Copyright (c) 2008-2009 Travis Geiselbrecht |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #ifndef ZIRCON_KERNEL_INCLUDE_KERNEL_TIMER_H_ |
| #define ZIRCON_KERNEL_INCLUDE_KERNEL_TIMER_H_ |
| |
| #include <zircon/compiler.h> |
| #include <zircon/types.h> |
| |
| #include <fbl/canary.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <ktl/atomic.h> |
| #include <kernel/deadline.h> |
| #include <kernel/spinlock.h> |
| |
| // Rules for Timers: |
| // - Timer callbacks occur from interrupt context. |
| // - Timers may be programmed or canceled from interrupt or thread context. |
| // - Timers may be canceled or reprogrammed from within their callback. |
| // - Setting and canceling timers is not thread safe and cannot be done concurrently. |
| // - Timer::cancel() may spin waiting for a pending timer to complete on another cpu. |
| |
| // Timers may be removed from an arbitrary TimerQueue, so their list |
| // node requires the AllowRemoveFromContainer option. |
| class Timer : public fbl::DoublyLinkedListable<Timer*, fbl::NodeOptions::AllowRemoveFromContainer> { |
| public: |
| using Callback = void (*)(Timer*, zx_time_t now, void* arg); |
| |
| // Timers need a constexpr constructor, as it is valid to construct them in static storage. |
| constexpr Timer() = default; |
| |
| // We ensure that timers are not on a list or an active cpu when destroyed. |
| ~Timer(); |
| |
| // Timers are not moved or copied. |
| Timer(const Timer&) = delete; |
| Timer(Timer&&) = delete; |
| Timer& operator=(const Timer&) = delete; |
| Timer& operator=(Timer&&) = delete; |
| |
| // Set up a timer that executes once |
| // |
| // This function specifies a callback function to be run after a specified |
| // deadline passes. The function will be called one time. |
| // |
| // deadline: specifies when the timer should be executed |
| // callback: the function to call when the timer expires |
| // arg: the argument to pass to the callback |
| // |
| // The timer function is declared as: |
| // void callback(Timer *, zx_time_t now, void *arg) { ... } |
| void Set(const Deadline& deadline, Callback callback, void* arg); |
| |
| // Cancel a pending timer |
| // |
| // Returns true if the timer was canceled before it was |
| // scheduled in a cpu and false otherwise or if the timer |
| // was not scheduled at all. |
| // |
| bool Cancel(); |
| |
| // Equivalent to Set with no slack |
| void SetOneshot(zx_time_t deadline, Callback callback, void* arg) { |
| return Set(Deadline::no_slack(deadline), callback, arg); |
| } |
| |
| // Special helper routine to simultaneously try to acquire a spinlock and check for |
| // timer cancel, which is needed in a few special cases. |
| // returns ZX_OK if spinlock was acquired, ZX_ERR_TIMED_OUT if timer was canceled. |
| zx_status_t TrylockOrCancel(MonitoredSpinLock* lock) TA_TRY_ACQ(false, lock); |
| |
| // Private accessors for timer tests. |
| zx_duration_t slack_for_test() const { return slack_; } |
| zx_time_t scheduled_time_for_test() const { return scheduled_time_; } |
| |
| private: |
| // TimerQueues can directly manipulate the state of their enqueued Timers. |
| friend class TimerQueue; |
| |
| static constexpr uint32_t kMagic = fbl::magic("timr"); |
| uint32_t magic_ = kMagic; |
| |
| zx_time_t scheduled_time_ = 0; |
| // Stores the applied slack adjustment from the ideal scheduled_time. |
| zx_duration_t slack_ = 0; |
| Callback callback_ = nullptr; |
| void* arg_ = nullptr; |
| |
| // INVALID_CPU, if inactive. |
| ktl::atomic<cpu_num_t> active_cpu_{INVALID_CPU}; |
| |
| // true if cancel is pending |
| ktl::atomic<bool> cancel_{false}; |
| }; |
| |
| // Preemption Timers |
| // |
| // Each CPU has a dedicated preemption timer that's managed using specialized |
| // functions (prefixed with timer_preempt_). |
| // |
| // Preemption timers are different from general timers. Preemption timers: |
| // |
| // - are reset frequently by the scheduler so performance is important |
| // - should not be migrated off their CPU when the CPU is shutdown |
| // |
| // Note: A preemption timer may fire even after it has been canceled. |
| class TimerQueue { |
| public: |
| // Set/reset/cancel the preemption timer. |
| // |
| // When the preemption timer fires, Scheduler::TimerTick is called. Set the |
| // deadline to ZX_TIME_INFINITE to cancel the preemption timer. |
| // Scheduler::TimerTick may be called spuriously after cancellation. |
| void PreemptReset(zx_time_t deadline); |
| |
| // Returns true if the preemption deadline is set and will definitely fire in |
| // the future. A false value does not definitively mean the preempt timer will |
| // not fire, as a spurious expiration is allowed. |
| bool PreemptArmed() const { return preempt_timer_deadline_ != ZX_TIME_INFINITE; } |
| |
| // Internal routines used when bringing cpus online/offline |
| |
| // Moves |source|'s timers (except its preemption timer) to this TimerQueue. |
| void TransitionOffCpu(TimerQueue& source); |
| |
| // This function is to be invoked after resume on each CPU's TimerQueue that |
| // may have had timers still on it, in order to restart hardware timers. |
| void ThawPercpu(); |
| |
| // Prints the contents of all timer queues into |buf| of length |len| and null |
| // terminates |buf|. |
| static void PrintTimerQueues(char* buf, size_t len); |
| |
| // This is called periodically by timer_tick(), which itself is invoked |
| // periodically by some hardware timer. |
| void Tick(zx_time_t now, cpu_num_t cpu); |
| |
| private: |
| // Timers can directly call Insert and Cancel. |
| friend class Timer; |
| |
| // Add |timer| to this TimerQueue, possibly coalescing deadlines as well. |
| void Insert(Timer* timer, zx_time_t earliest_deadline, zx_time_t latest_deadline); |
| |
| // Set the platform's oneshot timer to the minimum of its current |
| // deadline and |new_deadline|. |
| // |
| // This can only be called when interrupts are disabled. |
| void UpdatePlatformTimer(zx_time_t new_deadline); |
| |
| // Timers on this queue. |
| fbl::DoublyLinkedList<Timer*> timer_list_; |
| |
| // This TimerQueue's preemption deadline. ZX_TIME_INFINITE means not set. |
| zx_time_t preempt_timer_deadline_ = ZX_TIME_INFINITE; |
| |
| // This TimerQueue's deadline for its platform timer or ZX_TIME_INFINITE if not set |
| zx_time_t next_timer_deadline_ = ZX_TIME_INFINITE; |
| }; |
| |
| #endif // ZIRCON_KERNEL_INCLUDE_KERNEL_TIMER_H_ |