Timekeeping in The Linux Kernel - 0
Timekeeping in The Linux Kernel - 0
Stephen Boyd
Qualcomm Innovation Center, Inc.
1 / 40
In the beginning ...
2 / 40
there was a counter
0000ec544fef3c8a
3 / 40
Calculating the Passage of Time (in ns)
ccycles ccycles ccycles
= = ( ) seconds
1
f Hz f
f( )
seconds
ccycles ccycles
( ) seconds = ⋅ 1e9 = Tns
f f
4 / 40
Calculating the Passage of Time (in ns)
ccycles ccycles ccycles
= = ( ) seconds
1
f Hz f
f( )
seconds
ccycles ccycles
( ) seconds = ⋅ 1e9 = Tns
f f
Problems
Division is slow
Floating point math
Precision/overflow/underflow problems
5 / 40
Calculating the Passage of Time (in ns) Better
static inline s64 clocksource_cyc2ns(cycle_t cycles, u32 mult, u32 shift)
{
return ((u64) cycles * mult) >> shift;
}
6 / 40
Calculating the Passage of Time (in ns) Better
static inline s64 clocksource_cyc2ns(cycle_t cycles, u32 mult, u32 shift)
{
return ((u64) cycles * mult) >> shift;
}
7 / 40
Read
struct Hardware
clocksource Counter
Abstract the Hardware!
struct clocksource {
cycle_t (*read)(struct clocksource *cs);
cycle_t mask;
u32 mult;
u32 shift;
...
};
Time diff:
8 / 40
POSIX Clocks
CLOCK_BOOTTIME
CLOCK_MONOTONIC
CLOCK_MONOTONIC_RAW
CLOCK_MONOTONIC_COARSE
CLOCK_REALTIME
CLOCK_REALTIME_COARSE
CLOCK_TAI
9 / 40
POSIX Clocks Comparison
CLOCK_BOOTTIME
CLOCK_MONOTONIC
CLOCK_REALTIME
10 / 40
Read Accumulate Track (RAT)
Best acronym ever
11 / 40
RAT in Action (Read)
struct tk_read_base {
struct clocksource *clock;
cycle_t (*read)(struct clocksource *cs);
cycle_t mask;
cycle_t cycle_last;
u32 mult;
u32 shift;
u64 xtime_nsec;
ktime_t base;
};
12 / 40
RAT in Action (Accumulate + Track)
static u64 logarithmic_accumulation(struct timekeeper *tk, u64 offset,
u32 shift, unsigned int *clock_set)
{
u64 interval = tk->cycle_interval << shift;
tk->tkr_mono.cycle_last += interval;
tk->tkr_mono.xtime_nsec -= nsecps;
tk->xtime_sec++;
...
}
13 / 40
Juggling Clocks
struct timekeeper {
struct tk_read_base tkr_mono;
struct tk_read_base tkr_raw;
u64 xtime_sec;
unsigned long ktime_sec;
struct timespec64 wall_to_monotonic;
ktime_t offs_real;
ktime_t offs_boot;
ktime_t offs_tai;
s32 tai_offset;
struct timespec64 raw_time;
};
14 / 40
Handling Clock Drift
1 ¯¯
¯
⋅ 1e9 = 52.083 ns
19200000
Vs.
1
⋅ 1e9 = 52.083311ns
19200008
15 / 40
Handling Clock Drift
100000
⋅ 1e9 = 520833ns
19200000
Vs.
100000
⋅ 1e9 = 5208331ns
19200008
16 / 40
Mult to the Rescue!
(100000 ⋅ 873813333) ≫ 24 = 5208333ns
Vs.
17 / 40
Making Things Fast and E cient
static struct {
seqcount_t seq;
struct timekeeper timekeeper;
} tk_core ____cacheline_aligned;
struct tk_fast {
seqcount_t seq;
struct tk_read_base base[2];
};
18 / 40
A Note About NMIs and Time
19 / 40
Where We Are
20 / 40
What if my system doesn't have a counter?
Insert #sadface here
21 / 40
Let's talk about ji es
22 / 40
Let's talk about ji es
Ji y = 1 / CONFIG_HZ
23 / 40
Let's talk about ji es
Ji y = 1 / CONFIG_HZ
Updated during the "tick"
24 / 40
The tick?
25 / 40
The tick
Periodic event that updates
jiffies
process accounting
global load accounting
timekeeping
POSIX timers
RCU callbacks
hrtimers
irq_work
26 / 40
Implementing the tick in hardware
Timer Value: 4efa4655
27 / 40
Abstract the Hardware!
struct clock_event_device {
void (*event_handler)(struct clock_event_device *);
int (*set_next_event)(unsigned long evt,
struct clock_event_device *);
int (*set_next_ktime)(ktime_t expires,
struct clock_event_device *);
ktime_t next_event;
u64 max_delta_ns;
u64 min_delta_ns;
u32 mult;
u32 shift;
unsigned int features;
#define CLOCK_EVT_FEAT_PERIODIC 0x000001
#define CLOCK_EVT_FEAT_ONESHOT 0x000002
#define CLOCK_EVT_FEAT_KTIME 0x000004
int irq;
...
};
28 / 40
Three event_handlers
struct clock_event_device {
void (*event_handler)(struct clock_event_device *);
int (*set_next_event)(unsigned long evt,
struct clock_event_device *);
int (*set_next_ktime)(ktime_t expires,
struct clock_event_device *);
ktime_t next_event;
u64 max_delta_ns;
...
}
Handler Usage
tick_handle_periodic() default
29 / 40
Ticks During Idle
tick_handle_periodic()
t1 t2 idle idle t3
tick
tick
tick
tick
time
30 / 40
Tick-less Idle (i.e. CONFIG_NOHZ_IDLE)
tick_handle_periodic()
t1 t2 idle idle t3
tick
tick
tick
tick
time
tick_nohz_handler()
t1 t2 idle t3
tick
tick
tick
time
31 / 40
High Resolution Mode
tick_nohz_handler()
t1 t2 idle t3
tick
tick
tick
time
hrtimer_interrupt()
t1 t 2 idle t3
tick
tick
tick
hrt
hrt
hrt
time
32 / 40
Tick Devices
enum tick_device_mode {
TICKDEV_MODE_PERIODIC,
TICKDEV_MODE_ONESHOT,
};
struct tick_device {
struct clock_event_device *evtdev;
enum tick_device_mode mode;
};
tick_device clockevent
Hardware
event
33 / 40
Running the Tick
struct tick_sched {
struct hrtimer sched_timer;
...
};
34 / 40
Running the Tick (Per-CPU)
struct tick_sched {
struct hrtimer sched_timer;
...
};
35 / 40
Stopping the Tick
Not always as simple as
hrtimer_cancel(&ts->sched_timer)
Needs to consider
timers
hrtimers
RCU callbacks
jiffie update responsibility
clocksource's max_idle_ns (timekeeping max deferment)
Details in tick_nohz_stop_sched_tick()
36 / 40
Tick Broadcast
For when your clockevents FAIL AT LIFE
i.e., they don't work during some CPU idle low power modes
Indicated by CLOCK_EVT_FEAT_C3_STOP flag
37 / 40
Timers
Operates with jiffies granularity
Requirements
jiffies increment
clockevent
softirq
38 / 40
HRTimers
Operates with ktime (nanoseconds) granularity
Requirements
timekeeping increment
clockevent
tick_device
39 / 40
Summary
40 / 40