EVENTTIMERS(9)

πŸ“¦ version 15.1, in freebsd 14.5, freebsd 15.1, nixpkgs unstable

Okay so bestie, you're looking at the EVENTTIMERS(9) manual page from the Kernel Developer's Manual, and let me tell you, this is the main character energy behind all your kernel time events πŸ’…βœ¨

🌸 NAME 🌸

eventtimers β€” kernel event timers subsystem πŸ•’πŸ’–

πŸ¦‹ SYNOPSIS πŸ¦‹

#include <sys/timeet.h>

struct eventtimer;

typedef int et_start_t(struct eventtimer *et,
    sbintime_t first, sbintime_t period);
typedef int et_stop_t(struct eventtimer *et);
typedef void et_event_cb_t(struct eventtimer *et, void *arg);
typedef int et_deregister_cb_t(struct eventtimer *et, void *arg);

struct eventtimer {
        SLIST_ENTRY(eventtimer) et_all;
        char                    *et_name;
        int                     et_flags;
#define ET_FLAGS_PERIODIC       1
#define ET_FLAGS_ONESHOT        2
#define ET_FLAGS_PERCPU         4
#define ET_FLAGS_C3STOP         8
#define ET_FLAGS_POW2DIV        16
        int                     et_quality;
        int                     et_active;
        uint64_t                et_frequency;
        sbintime_t              et_min_period;
        sbintime_t              et_max_period;
        et_start_t              *et_start;
        et_stop_t               *et_stop;
        et_event_cb_t           *et_event_cb;
        et_deregister_cb_t      *et_deregister_cb;
        void                    *et_arg;
        void                    *et_priv;
        struct sysctl_oid       *et_sysctl;
};

int
et_register(struct eventtimer *et);

int
et_deregister(struct eventtimer *et);

void
et_change_frequency(struct eventtimer *et, uint64_t newfreq);

ET_LOCK();

ET_UNLOCK();

struct eventtimer *
et_find(const char *name, int check, int want);

int
et_init(struct eventtimer *et, et_event_cb_t *event,
    et_deregister_cb_t *deregister, void *arg);

int
et_start(struct eventtimer *et, sbintime_t first, sbintime_t period);

int
et_stop(struct eventtimer *et);

int
et_ban(struct eventtimer *et);

int
et_free(struct eventtimer *et);

πŸŽ€ DESCRIPTION πŸŽ€

Event timers are the literal MVPs that generate interrupts at a specific time or periodically, so all your time-based events can happen. The whole subsystem is split into three main parts:

  • Drivers β€” They manage the hardware to generate the time events you request. Total hardware girlies πŸ”§
  • Consumers β€” sys/kern/kern_clocksource.c uses event timers to supply the kernel with hardclock(), statclock(), and profclock() time events. They're the ones actually using the tea β˜•
  • Glue code β€” sys/sys/timeet.h and sys/kern/kern_et.c provide the APIs that connect event timer drivers and consumers. The matchmakers of the kernel πŸ’˜

πŸ’œ DRIVER API πŸ’œ

The Driver API is built around the eventtimer structure. To register their functionality, drivers allocate that structure and call et_register(). Here's what the driver should fill in:

  • et_name β€” Unique name of the event timer for management purposes. Give it a cute one, bestie 🌟
  • et_flags β€” Set of flags describing what the timer can do:
    • ET_FLAGS_PERIODIC 1 β€” Periodic mode is supported. It repeats!
    • ET_FLAGS_ONESHOT 2 β€” One-shot mode is supported. One and done.
    • ET_FLAGS_PERCPU 4 β€” Timer is per-CPU. Every core gets its own.
    • ET_FLAGS_C3STOP 8 β€” Timer may stop when the CPU is in a sleep state. It's napping 😴
    • ET_FLAGS_POW2DIV 16 β€” Timer supports only 2^n divisors. Math is mathing.
  • et_quality β€” An abstract value to certify whether this timecounter is better than the others. Higher value means better. It's the vibe check for timers πŸ“ˆ
  • et_frequency β€” Timer oscillator's base frequency, if applicable and known. Consumers use it to predict the possible frequencies you can get by dividing it. Should be zero if not applicable or unknown. Zero means "we don't know, don't come for us" πŸ€·β€β™€οΈ
  • et_min_period, et_max_period β€” Minimal and maximal reliably programmable time periods. The range where the timer actually slays.
  • et_start β€” Driver's timer start function pointer. The "go" button πŸš€
  • et_stop β€” Driver's timer stop function pointer. The "stop" button πŸ›‘
  • et_priv β€” Driver's private data storage. Their little secret stash πŸ’…

After the event timer functionality is registered, it's controlled via the et_start and et_stop methods. The et_start method is called to start the specified event timer. The last two arguments specify when events should be generated: the first argument is the time period before the first event is generated. In periodic mode, a NULL value for first means the first period is equal to the period argument value. The period argument specifies the time period between following events for periodic mode. A NULL value there specifies one-shot mode. At least one of these two arguments should be not NULL β€” no cap πŸ“Œ

When the event time arrives, the driver should call the et_event_cb callback function, passing et_arg as the second argument. The et_stop method is called to stop the specified event timer. For per-CPU event timers, the et_start and et_stop methods control timers associated with the current CPU.

Drivers can deregister their functionality by calling et_deregister().

If the frequency of the clock hardware can change while it's running (for example, during power-saving modes), the driver must call et_change_frequency() on each change. If the given event timer is the active timer, et_change_frequency() stops the timer on all CPUs, updates et->frequency, then restarts the timer on all CPUs so that all current events are rescheduled using the new frequency. If the given timer is not currently active, et_change_frequency() simply updates et->frequency. It's giving responsible adult energy ✨

🧚 CONSUMER API 🧚

et_find() lets a consumer find an available event timer, optionally matching a specific name and/or capability flags. Consumers may read the returned eventtimer structure, but they should not modify it. Read-only, hands off πŸ™…β€β™€οΈ

When the wanted event timer is found, et_init() should be called for it, submitting the event and optionally deregister callback functions, and the opaque argument arg. That argument will be passed as an argument to the callbacks. The event callback function will be called on scheduled time events. It's called from the hardware interrupt context, so no sleep is permitted there β€” you can't be catching Z's in an interrupt, bestie 😭

The deregister callback function may be called to report to the consumer that the event timer functionality is no longer available. On this call, the consumer should stop using the event timer before returning.

After the timer is found and initialized, it can be controlled via et_start() and et_stop(). The arguments are the same as described in the driver API. Per-CPU event timers can be controlled only from specific CPUs.

et_ban() allows a consumer to mark an event timer as broken by clearing both one-shot and periodic capability flags, if it was somehow detected. It's the "you're cancelled" button πŸ’€

et_free() is the opposite of et_init(). It releases the event timer for other consumers to use. Sharing is caring 🫢

ET_LOCK() and ET_UNLOCK() macros should be used to manage a mutex(9) lock around et_find(), et_init(), and et_free() calls to serialize access to the list of registered event timers and the pointers returned by et_find(). et_start() and et_stop() calls should be serialized in the consumer's internal way to avoid concurrent timer hardware access. Locking is self-care, periodt πŸ”

πŸ‘‘ SEE ALSO πŸ‘‘

eventtimers(4) β€” go check it out, bestie! It's the user-facing bestie of this page πŸ’–

🌈 AUTHORS 🌈

Alexander Motin <mav@FreeBSD.org> β€” absolute legend for writing this, we stan πŸ‘‘


This page is from Debian, dated April 2, 2014, and it's the EVENTTIMERS(9) manual page. Slay. πŸ’…

πŸ“¦ where this page comes from