You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'timers/core' into perf/timer, to apply dependent patch
An upcoming patch will depend on tai_ns() and NMI-safe ktime_get_raw_fast(), so merge timers/core here in a separate topic branch until it's all cooked and timers/core is merged upstream. Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
@@ -103,7 +103,7 @@ int __init omap_init_clocksource_32k(void __iomem *vbase)
|
||||
|
||||
/*
|
||||
* 120000 rough estimate from the calculations in
|
||||
* __clocksource_updatefreq_scale.
|
||||
* __clocksource_update_freq_scale.
|
||||
*/
|
||||
clocks_calc_mult_shift(&persistent_mult, &persistent_shift,
|
||||
32768, NSEC_PER_SEC, 120000);
|
||||
|
||||
@@ -200,7 +200,7 @@ up_fail:
|
||||
void update_vsyscall(struct timekeeper *tk)
|
||||
{
|
||||
struct timespec xtime_coarse;
|
||||
u32 use_syscall = strcmp(tk->tkr.clock->name, "arch_sys_counter");
|
||||
u32 use_syscall = strcmp(tk->tkr_mono.clock->name, "arch_sys_counter");
|
||||
|
||||
++vdso_data->tb_seq_count;
|
||||
smp_wmb();
|
||||
@@ -213,11 +213,11 @@ void update_vsyscall(struct timekeeper *tk)
|
||||
vdso_data->wtm_clock_nsec = tk->wall_to_monotonic.tv_nsec;
|
||||
|
||||
if (!use_syscall) {
|
||||
vdso_data->cs_cycle_last = tk->tkr.cycle_last;
|
||||
vdso_data->cs_cycle_last = tk->tkr_mono.cycle_last;
|
||||
vdso_data->xtime_clock_sec = tk->xtime_sec;
|
||||
vdso_data->xtime_clock_nsec = tk->tkr.xtime_nsec;
|
||||
vdso_data->cs_mult = tk->tkr.mult;
|
||||
vdso_data->cs_shift = tk->tkr.shift;
|
||||
vdso_data->xtime_clock_nsec = tk->tkr_mono.xtime_nsec;
|
||||
vdso_data->cs_mult = tk->tkr_mono.mult;
|
||||
vdso_data->cs_shift = tk->tkr_mono.shift;
|
||||
}
|
||||
|
||||
smp_wmb();
|
||||
|
||||
+10
-10
@@ -215,20 +215,20 @@ void update_vsyscall(struct timekeeper *tk)
|
||||
{
|
||||
u64 nsecps;
|
||||
|
||||
if (tk->tkr.clock != &clocksource_tod)
|
||||
if (tk->tkr_mono.clock != &clocksource_tod)
|
||||
return;
|
||||
|
||||
/* Make userspace gettimeofday spin until we're done. */
|
||||
++vdso_data->tb_update_count;
|
||||
smp_wmb();
|
||||
vdso_data->xtime_tod_stamp = tk->tkr.cycle_last;
|
||||
vdso_data->xtime_tod_stamp = tk->tkr_mono.cycle_last;
|
||||
vdso_data->xtime_clock_sec = tk->xtime_sec;
|
||||
vdso_data->xtime_clock_nsec = tk->tkr.xtime_nsec;
|
||||
vdso_data->xtime_clock_nsec = tk->tkr_mono.xtime_nsec;
|
||||
vdso_data->wtom_clock_sec =
|
||||
tk->xtime_sec + tk->wall_to_monotonic.tv_sec;
|
||||
vdso_data->wtom_clock_nsec = tk->tkr.xtime_nsec +
|
||||
+ ((u64) tk->wall_to_monotonic.tv_nsec << tk->tkr.shift);
|
||||
nsecps = (u64) NSEC_PER_SEC << tk->tkr.shift;
|
||||
vdso_data->wtom_clock_nsec = tk->tkr_mono.xtime_nsec +
|
||||
+ ((u64) tk->wall_to_monotonic.tv_nsec << tk->tkr_mono.shift);
|
||||
nsecps = (u64) NSEC_PER_SEC << tk->tkr_mono.shift;
|
||||
while (vdso_data->wtom_clock_nsec >= nsecps) {
|
||||
vdso_data->wtom_clock_nsec -= nsecps;
|
||||
vdso_data->wtom_clock_sec++;
|
||||
@@ -236,7 +236,7 @@ void update_vsyscall(struct timekeeper *tk)
|
||||
|
||||
vdso_data->xtime_coarse_sec = tk->xtime_sec;
|
||||
vdso_data->xtime_coarse_nsec =
|
||||
(long)(tk->tkr.xtime_nsec >> tk->tkr.shift);
|
||||
(long)(tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift);
|
||||
vdso_data->wtom_coarse_sec =
|
||||
vdso_data->xtime_coarse_sec + tk->wall_to_monotonic.tv_sec;
|
||||
vdso_data->wtom_coarse_nsec =
|
||||
@@ -246,8 +246,8 @@ void update_vsyscall(struct timekeeper *tk)
|
||||
vdso_data->wtom_coarse_sec++;
|
||||
}
|
||||
|
||||
vdso_data->tk_mult = tk->tkr.mult;
|
||||
vdso_data->tk_shift = tk->tkr.shift;
|
||||
vdso_data->tk_mult = tk->tkr_mono.mult;
|
||||
vdso_data->tk_shift = tk->tkr_mono.shift;
|
||||
smp_wmb();
|
||||
++vdso_data->tb_update_count;
|
||||
}
|
||||
@@ -283,7 +283,7 @@ void __init time_init(void)
|
||||
if (register_external_irq(EXT_IRQ_TIMING_ALERT, timing_alert_interrupt))
|
||||
panic("Couldn't request external interrupt 0x1406");
|
||||
|
||||
if (clocksource_register(&clocksource_tod) != 0)
|
||||
if (__clocksource_register(&clocksource_tod) != 0)
|
||||
panic("Could not register TOD clock source");
|
||||
|
||||
/* Enable TOD clock interrupts on the boot cpu. */
|
||||
|
||||
@@ -181,17 +181,13 @@ static struct clocksource timer_cs = {
|
||||
.rating = 100,
|
||||
.read = timer_cs_read,
|
||||
.mask = CLOCKSOURCE_MASK(64),
|
||||
.shift = 2,
|
||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||
};
|
||||
|
||||
static __init int setup_timer_cs(void)
|
||||
{
|
||||
timer_cs_enabled = 1;
|
||||
timer_cs.mult = clocksource_hz2mult(sparc_config.clock_rate,
|
||||
timer_cs.shift);
|
||||
|
||||
return clocksource_register(&timer_cs);
|
||||
return clocksource_register_hz(&timer_cs, sparc_config.clock_rate);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
+12
-12
@@ -257,34 +257,34 @@ void update_vsyscall_tz(void)
|
||||
|
||||
void update_vsyscall(struct timekeeper *tk)
|
||||
{
|
||||
if (tk->tkr.clock != &cycle_counter_cs)
|
||||
if (tk->tkr_mono.clock != &cycle_counter_cs)
|
||||
return;
|
||||
|
||||
write_seqcount_begin(&vdso_data->tb_seq);
|
||||
|
||||
vdso_data->cycle_last = tk->tkr.cycle_last;
|
||||
vdso_data->mask = tk->tkr.mask;
|
||||
vdso_data->mult = tk->tkr.mult;
|
||||
vdso_data->shift = tk->tkr.shift;
|
||||
vdso_data->cycle_last = tk->tkr_mono.cycle_last;
|
||||
vdso_data->mask = tk->tkr_mono.mask;
|
||||
vdso_data->mult = tk->tkr_mono.mult;
|
||||
vdso_data->shift = tk->tkr_mono.shift;
|
||||
|
||||
vdso_data->wall_time_sec = tk->xtime_sec;
|
||||
vdso_data->wall_time_snsec = tk->tkr.xtime_nsec;
|
||||
vdso_data->wall_time_snsec = tk->tkr_mono.xtime_nsec;
|
||||
|
||||
vdso_data->monotonic_time_sec = tk->xtime_sec
|
||||
+ tk->wall_to_monotonic.tv_sec;
|
||||
vdso_data->monotonic_time_snsec = tk->tkr.xtime_nsec
|
||||
vdso_data->monotonic_time_snsec = tk->tkr_mono.xtime_nsec
|
||||
+ ((u64)tk->wall_to_monotonic.tv_nsec
|
||||
<< tk->tkr.shift);
|
||||
<< tk->tkr_mono.shift);
|
||||
while (vdso_data->monotonic_time_snsec >=
|
||||
(((u64)NSEC_PER_SEC) << tk->tkr.shift)) {
|
||||
(((u64)NSEC_PER_SEC) << tk->tkr_mono.shift)) {
|
||||
vdso_data->monotonic_time_snsec -=
|
||||
((u64)NSEC_PER_SEC) << tk->tkr.shift;
|
||||
((u64)NSEC_PER_SEC) << tk->tkr_mono.shift;
|
||||
vdso_data->monotonic_time_sec++;
|
||||
}
|
||||
|
||||
vdso_data->wall_time_coarse_sec = tk->xtime_sec;
|
||||
vdso_data->wall_time_coarse_nsec = (long)(tk->tkr.xtime_nsec >>
|
||||
tk->tkr.shift);
|
||||
vdso_data->wall_time_coarse_nsec = (long)(tk->tkr_mono.xtime_nsec >>
|
||||
tk->tkr_mono.shift);
|
||||
|
||||
vdso_data->monotonic_time_coarse_sec =
|
||||
vdso_data->wall_time_coarse_sec + tk->wall_to_monotonic.tv_sec;
|
||||
|
||||
@@ -31,30 +31,30 @@ void update_vsyscall(struct timekeeper *tk)
|
||||
gtod_write_begin(vdata);
|
||||
|
||||
/* copy vsyscall data */
|
||||
vdata->vclock_mode = tk->tkr.clock->archdata.vclock_mode;
|
||||
vdata->cycle_last = tk->tkr.cycle_last;
|
||||
vdata->mask = tk->tkr.mask;
|
||||
vdata->mult = tk->tkr.mult;
|
||||
vdata->shift = tk->tkr.shift;
|
||||
vdata->vclock_mode = tk->tkr_mono.clock->archdata.vclock_mode;
|
||||
vdata->cycle_last = tk->tkr_mono.cycle_last;
|
||||
vdata->mask = tk->tkr_mono.mask;
|
||||
vdata->mult = tk->tkr_mono.mult;
|
||||
vdata->shift = tk->tkr_mono.shift;
|
||||
|
||||
vdata->wall_time_sec = tk->xtime_sec;
|
||||
vdata->wall_time_snsec = tk->tkr.xtime_nsec;
|
||||
vdata->wall_time_snsec = tk->tkr_mono.xtime_nsec;
|
||||
|
||||
vdata->monotonic_time_sec = tk->xtime_sec
|
||||
+ tk->wall_to_monotonic.tv_sec;
|
||||
vdata->monotonic_time_snsec = tk->tkr.xtime_nsec
|
||||
vdata->monotonic_time_snsec = tk->tkr_mono.xtime_nsec
|
||||
+ ((u64)tk->wall_to_monotonic.tv_nsec
|
||||
<< tk->tkr.shift);
|
||||
<< tk->tkr_mono.shift);
|
||||
while (vdata->monotonic_time_snsec >=
|
||||
(((u64)NSEC_PER_SEC) << tk->tkr.shift)) {
|
||||
(((u64)NSEC_PER_SEC) << tk->tkr_mono.shift)) {
|
||||
vdata->monotonic_time_snsec -=
|
||||
((u64)NSEC_PER_SEC) << tk->tkr.shift;
|
||||
((u64)NSEC_PER_SEC) << tk->tkr_mono.shift;
|
||||
vdata->monotonic_time_sec++;
|
||||
}
|
||||
|
||||
vdata->wall_time_coarse_sec = tk->xtime_sec;
|
||||
vdata->wall_time_coarse_nsec = (long)(tk->tkr.xtime_nsec >>
|
||||
tk->tkr.shift);
|
||||
vdata->wall_time_coarse_nsec = (long)(tk->tkr_mono.xtime_nsec >>
|
||||
tk->tkr_mono.shift);
|
||||
|
||||
vdata->monotonic_time_coarse_sec =
|
||||
vdata->wall_time_coarse_sec + tk->wall_to_monotonic.tv_sec;
|
||||
|
||||
+7
-7
@@ -1070,19 +1070,19 @@ static void update_pvclock_gtod(struct timekeeper *tk)
|
||||
struct pvclock_gtod_data *vdata = &pvclock_gtod_data;
|
||||
u64 boot_ns;
|
||||
|
||||
boot_ns = ktime_to_ns(ktime_add(tk->tkr.base_mono, tk->offs_boot));
|
||||
boot_ns = ktime_to_ns(ktime_add(tk->tkr_mono.base, tk->offs_boot));
|
||||
|
||||
write_seqcount_begin(&vdata->seq);
|
||||
|
||||
/* copy pvclock gtod data */
|
||||
vdata->clock.vclock_mode = tk->tkr.clock->archdata.vclock_mode;
|
||||
vdata->clock.cycle_last = tk->tkr.cycle_last;
|
||||
vdata->clock.mask = tk->tkr.mask;
|
||||
vdata->clock.mult = tk->tkr.mult;
|
||||
vdata->clock.shift = tk->tkr.shift;
|
||||
vdata->clock.vclock_mode = tk->tkr_mono.clock->archdata.vclock_mode;
|
||||
vdata->clock.cycle_last = tk->tkr_mono.cycle_last;
|
||||
vdata->clock.mask = tk->tkr_mono.mask;
|
||||
vdata->clock.mult = tk->tkr_mono.mult;
|
||||
vdata->clock.shift = tk->tkr_mono.shift;
|
||||
|
||||
vdata->boot_ns = boot_ns;
|
||||
vdata->nsec_base = tk->tkr.xtime_nsec;
|
||||
vdata->nsec_base = tk->tkr_mono.xtime_nsec;
|
||||
|
||||
write_seqcount_end(&vdata->seq);
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ static int em_sti_clocksource_enable(struct clocksource *cs)
|
||||
|
||||
ret = em_sti_start(p, USER_CLOCKSOURCE);
|
||||
if (!ret)
|
||||
__clocksource_updatefreq_hz(cs, p->rate);
|
||||
__clocksource_update_freq_hz(cs, p->rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -641,7 +641,7 @@ static int sh_cmt_clocksource_enable(struct clocksource *cs)
|
||||
|
||||
ret = sh_cmt_start(ch, FLAG_CLOCKSOURCE);
|
||||
if (!ret) {
|
||||
__clocksource_updatefreq_hz(cs, ch->rate);
|
||||
__clocksource_update_freq_hz(cs, ch->rate);
|
||||
ch->cs_enabled = true;
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -272,7 +272,7 @@ static int sh_tmu_clocksource_enable(struct clocksource *cs)
|
||||
|
||||
ret = sh_tmu_enable(ch);
|
||||
if (!ret) {
|
||||
__clocksource_updatefreq_hz(cs, ch->rate);
|
||||
__clocksource_update_freq_hz(cs, ch->rate);
|
||||
ch->cs_enabled = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ enum clock_event_mode {
|
||||
CLOCK_EVT_MODE_PERIODIC,
|
||||
CLOCK_EVT_MODE_ONESHOT,
|
||||
CLOCK_EVT_MODE_RESUME,
|
||||
|
||||
/* Legacy ->set_mode() callback doesn't support below modes */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -81,7 +83,11 @@ enum clock_event_mode {
|
||||
* @mode: operating mode assigned by the management code
|
||||
* @features: features
|
||||
* @retries: number of forced programming retries
|
||||
* @set_mode: set mode function
|
||||
* @set_mode: legacy set mode function, only for modes <= CLOCK_EVT_MODE_RESUME.
|
||||
* @set_mode_periodic: switch mode to periodic, if !set_mode
|
||||
* @set_mode_oneshot: switch mode to oneshot, if !set_mode
|
||||
* @set_mode_shutdown: switch mode to shutdown, if !set_mode
|
||||
* @set_mode_resume: resume clkevt device, if !set_mode
|
||||
* @broadcast: function to broadcast events
|
||||
* @min_delta_ticks: minimum delta value in ticks stored for reconfiguration
|
||||
* @max_delta_ticks: maximum delta value in ticks stored for reconfiguration
|
||||
@@ -108,9 +114,20 @@ struct clock_event_device {
|
||||
unsigned int features;
|
||||
unsigned long retries;
|
||||
|
||||
void (*broadcast)(const struct cpumask *mask);
|
||||
/*
|
||||
* Mode transition callback(s): Only one of the two groups should be
|
||||
* defined:
|
||||
* - set_mode(), only for modes <= CLOCK_EVT_MODE_RESUME.
|
||||
* - set_mode_{shutdown|periodic|oneshot|resume}().
|
||||
*/
|
||||
void (*set_mode)(enum clock_event_mode mode,
|
||||
struct clock_event_device *);
|
||||
int (*set_mode_periodic)(struct clock_event_device *);
|
||||
int (*set_mode_oneshot)(struct clock_event_device *);
|
||||
int (*set_mode_shutdown)(struct clock_event_device *);
|
||||
int (*set_mode_resume)(struct clock_event_device *);
|
||||
|
||||
void (*broadcast)(const struct cpumask *mask);
|
||||
void (*suspend)(struct clock_event_device *);
|
||||
void (*resume)(struct clock_event_device *);
|
||||
unsigned long min_delta_ticks;
|
||||
|
||||
@@ -56,6 +56,7 @@ struct module;
|
||||
* @shift: cycle to nanosecond divisor (power of two)
|
||||
* @max_idle_ns: max idle time permitted by the clocksource (nsecs)
|
||||
* @maxadj: maximum adjustment value to mult (~11%)
|
||||
* @max_cycles: maximum safe cycle value which won't overflow on multiplication
|
||||
* @flags: flags describing special properties
|
||||
* @archdata: arch-specific data
|
||||
* @suspend: suspend function for the clocksource, if necessary
|
||||
@@ -76,7 +77,7 @@ struct clocksource {
|
||||
#ifdef CONFIG_ARCH_CLOCKSOURCE_DATA
|
||||
struct arch_clocksource_data archdata;
|
||||
#endif
|
||||
|
||||
u64 max_cycles;
|
||||
const char *name;
|
||||
struct list_head list;
|
||||
int rating;
|
||||
@@ -178,7 +179,6 @@ static inline s64 clocksource_cyc2ns(cycle_t cycles, u32 mult, u32 shift)
|
||||
}
|
||||
|
||||
|
||||
extern int clocksource_register(struct clocksource*);
|
||||
extern int clocksource_unregister(struct clocksource*);
|
||||
extern void clocksource_touch_watchdog(void);
|
||||
extern struct clocksource* clocksource_get_next(void);
|
||||
@@ -189,7 +189,7 @@ extern struct clocksource * __init clocksource_default_clock(void);
|
||||
extern void clocksource_mark_unstable(struct clocksource *cs);
|
||||
|
||||
extern u64
|
||||
clocks_calc_max_nsecs(u32 mult, u32 shift, u32 maxadj, u64 mask);
|
||||
clocks_calc_max_nsecs(u32 mult, u32 shift, u32 maxadj, u64 mask, u64 *max_cycles);
|
||||
extern void
|
||||
clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 minsec);
|
||||
|
||||
@@ -200,7 +200,16 @@ clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 minsec);
|
||||
extern int
|
||||
__clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq);
|
||||
extern void
|
||||
__clocksource_updatefreq_scale(struct clocksource *cs, u32 scale, u32 freq);
|
||||
__clocksource_update_freq_scale(struct clocksource *cs, u32 scale, u32 freq);
|
||||
|
||||
/*
|
||||
* Don't call this unless you are a default clocksource
|
||||
* (AKA: jiffies) and absolutely have to.
|
||||
*/
|
||||
static inline int __clocksource_register(struct clocksource *cs)
|
||||
{
|
||||
return __clocksource_register_scale(cs, 1, 0);
|
||||
}
|
||||
|
||||
static inline int clocksource_register_hz(struct clocksource *cs, u32 hz)
|
||||
{
|
||||
@@ -212,14 +221,14 @@ static inline int clocksource_register_khz(struct clocksource *cs, u32 khz)
|
||||
return __clocksource_register_scale(cs, 1000, khz);
|
||||
}
|
||||
|
||||
static inline void __clocksource_updatefreq_hz(struct clocksource *cs, u32 hz)
|
||||
static inline void __clocksource_update_freq_hz(struct clocksource *cs, u32 hz)
|
||||
{
|
||||
__clocksource_updatefreq_scale(cs, 1, hz);
|
||||
__clocksource_update_freq_scale(cs, 1, hz);
|
||||
}
|
||||
|
||||
static inline void __clocksource_updatefreq_khz(struct clocksource *cs, u32 khz)
|
||||
static inline void __clocksource_update_freq_khz(struct clocksource *cs, u32 khz)
|
||||
{
|
||||
__clocksource_updatefreq_scale(cs, 1000, khz);
|
||||
__clocksource_update_freq_scale(cs, 1000, khz);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,16 +16,16 @@
|
||||
* @read: Read function of @clock
|
||||
* @mask: Bitmask for two's complement subtraction of non 64bit clocks
|
||||
* @cycle_last: @clock cycle value at last update
|
||||
* @mult: NTP adjusted multiplier for scaled math conversion
|
||||
* @mult: (NTP adjusted) multiplier for scaled math conversion
|
||||
* @shift: Shift value for scaled math conversion
|
||||
* @xtime_nsec: Shifted (fractional) nano seconds offset for readout
|
||||
* @base_mono: ktime_t (nanoseconds) base time for readout
|
||||
* @base: ktime_t (nanoseconds) base time for readout
|
||||
*
|
||||
* This struct has size 56 byte on 64 bit. Together with a seqcount it
|
||||
* occupies a single 64byte cache line.
|
||||
*
|
||||
* The struct is separate from struct timekeeper as it is also used
|
||||
* for a fast NMI safe accessor to clock monotonic.
|
||||
* for a fast NMI safe accessors.
|
||||
*/
|
||||
struct tk_read_base {
|
||||
struct clocksource *clock;
|
||||
@@ -35,12 +35,13 @@ struct tk_read_base {
|
||||
u32 mult;
|
||||
u32 shift;
|
||||
u64 xtime_nsec;
|
||||
ktime_t base_mono;
|
||||
ktime_t base;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct timekeeper - Structure holding internal timekeeping values.
|
||||
* @tkr: The readout base structure
|
||||
* @tkr_mono: The readout base structure for CLOCK_MONOTONIC
|
||||
* @tkr_raw: The readout base structure for CLOCK_MONOTONIC_RAW
|
||||
* @xtime_sec: Current CLOCK_REALTIME time in seconds
|
||||
* @ktime_sec: Current CLOCK_MONOTONIC time in seconds
|
||||
* @wall_to_monotonic: CLOCK_REALTIME to CLOCK_MONOTONIC offset
|
||||
@@ -48,7 +49,6 @@ struct tk_read_base {
|
||||
* @offs_boot: Offset clock monotonic -> clock boottime
|
||||
* @offs_tai: Offset clock monotonic -> clock tai
|
||||
* @tai_offset: The current UTC to TAI offset in seconds
|
||||
* @base_raw: Monotonic raw base time in ktime_t format
|
||||
* @raw_time: Monotonic raw base time in timespec64 format
|
||||
* @cycle_interval: Number of clock cycles in one NTP interval
|
||||
* @xtime_interval: Number of clock shifted nano seconds in one NTP
|
||||
@@ -76,7 +76,8 @@ struct tk_read_base {
|
||||
* used instead.
|
||||
*/
|
||||
struct timekeeper {
|
||||
struct tk_read_base tkr;
|
||||
struct tk_read_base tkr_mono;
|
||||
struct tk_read_base tkr_raw;
|
||||
u64 xtime_sec;
|
||||
unsigned long ktime_sec;
|
||||
struct timespec64 wall_to_monotonic;
|
||||
@@ -84,7 +85,6 @@ struct timekeeper {
|
||||
ktime_t offs_boot;
|
||||
ktime_t offs_tai;
|
||||
s32 tai_offset;
|
||||
ktime_t base_raw;
|
||||
struct timespec64 raw_time;
|
||||
|
||||
/* The following members are for timekeeping internal use */
|
||||
|
||||
@@ -214,12 +214,18 @@ static inline u64 ktime_get_boot_ns(void)
|
||||
return ktime_to_ns(ktime_get_boottime());
|
||||
}
|
||||
|
||||
static inline u64 ktime_get_tai_ns(void)
|
||||
{
|
||||
return ktime_to_ns(ktime_get_clocktai());
|
||||
}
|
||||
|
||||
static inline u64 ktime_get_raw_ns(void)
|
||||
{
|
||||
return ktime_to_ns(ktime_get_raw());
|
||||
}
|
||||
|
||||
extern u64 ktime_get_mono_fast_ns(void);
|
||||
extern u64 ktime_get_raw_fast_ns(void);
|
||||
|
||||
/*
|
||||
* Timespec interfaces utilizing the ktime based ones
|
||||
|
||||
@@ -94,6 +94,57 @@ u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clockevent_delta2ns);
|
||||
|
||||
static int __clockevents_set_mode(struct clock_event_device *dev,
|
||||
enum clock_event_mode mode)
|
||||
{
|
||||
/* Transition with legacy set_mode() callback */
|
||||
if (dev->set_mode) {
|
||||
/* Legacy callback doesn't support new modes */
|
||||
if (mode > CLOCK_EVT_MODE_RESUME)
|
||||
return -ENOSYS;
|
||||
dev->set_mode(mode, dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dev->features & CLOCK_EVT_FEAT_DUMMY)
|
||||
return 0;
|
||||
|
||||
/* Transition with new mode-specific callbacks */
|
||||
switch (mode) {
|
||||
case CLOCK_EVT_MODE_UNUSED:
|
||||
/*
|
||||
* This is an internal state, which is guaranteed to go from
|
||||
* SHUTDOWN to UNUSED. No driver interaction required.
|
||||
*/
|
||||
return 0;
|
||||
|
||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||
return dev->set_mode_shutdown(dev);
|
||||
|
||||
case CLOCK_EVT_MODE_PERIODIC:
|
||||
/* Core internal bug */
|
||||
if (!(dev->features & CLOCK_EVT_FEAT_PERIODIC))
|
||||
return -ENOSYS;
|
||||
return dev->set_mode_periodic(dev);
|
||||
|
||||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
/* Core internal bug */
|
||||
if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT))
|
||||
return -ENOSYS;
|
||||
return dev->set_mode_oneshot(dev);
|
||||
|
||||
case CLOCK_EVT_MODE_RESUME:
|
||||
/* Optional callback */
|
||||
if (dev->set_mode_resume)
|
||||
return dev->set_mode_resume(dev);
|
||||
else
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -ENOSYS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clockevents_set_mode - set the operating mode of a clock event device
|
||||
* @dev: device to modify
|
||||
@@ -105,7 +156,9 @@ void clockevents_set_mode(struct clock_event_device *dev,
|
||||
enum clock_event_mode mode)
|
||||
{
|
||||
if (dev->mode != mode) {
|
||||
dev->set_mode(mode, dev);
|
||||
if (__clockevents_set_mode(dev, mode))
|
||||
return;
|
||||
|
||||
dev->mode = mode;
|
||||
|
||||
/*
|
||||
@@ -373,6 +426,35 @@ int clockevents_unbind_device(struct clock_event_device *ced, int cpu)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clockevents_unbind);
|
||||
|
||||
/* Sanity check of mode transition callbacks */
|
||||
static int clockevents_sanity_check(struct clock_event_device *dev)
|
||||
{
|
||||
/* Legacy set_mode() callback */
|
||||
if (dev->set_mode) {
|
||||
/* We shouldn't be supporting new modes now */
|
||||
WARN_ON(dev->set_mode_periodic || dev->set_mode_oneshot ||
|
||||
dev->set_mode_shutdown || dev->set_mode_resume);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dev->features & CLOCK_EVT_FEAT_DUMMY)
|
||||
return 0;
|
||||
|
||||
/* New mode-specific callbacks */
|
||||
if (!dev->set_mode_shutdown)
|
||||
return -EINVAL;
|
||||
|
||||
if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) &&
|
||||
!dev->set_mode_periodic)
|
||||
return -EINVAL;
|
||||
|
||||
if ((dev->features & CLOCK_EVT_FEAT_ONESHOT) &&
|
||||
!dev->set_mode_oneshot)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* clockevents_register_device - register a clock event device
|
||||
* @dev: device to register
|
||||
@@ -382,6 +464,8 @@ void clockevents_register_device(struct clock_event_device *dev)
|
||||
unsigned long flags;
|
||||
|
||||
BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED);
|
||||
BUG_ON(clockevents_sanity_check(dev));
|
||||
|
||||
if (!dev->cpumask) {
|
||||
WARN_ON(num_possible_cpus() > 1);
|
||||
dev->cpumask = cpumask_of(smp_processor_id());
|
||||
@@ -449,7 +533,7 @@ int __clockevents_update_freq(struct clock_event_device *dev, u32 freq)
|
||||
return clockevents_program_event(dev, dev->next_event, false);
|
||||
|
||||
if (dev->mode == CLOCK_EVT_MODE_PERIODIC)
|
||||
dev->set_mode(CLOCK_EVT_MODE_PERIODIC, dev);
|
||||
return __clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+76
-92
@@ -142,13 +142,6 @@ static void __clocksource_unstable(struct clocksource *cs)
|
||||
schedule_work(&watchdog_work);
|
||||
}
|
||||
|
||||
static void clocksource_unstable(struct clocksource *cs, int64_t delta)
|
||||
{
|
||||
printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n",
|
||||
cs->name, delta);
|
||||
__clocksource_unstable(cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* clocksource_mark_unstable - mark clocksource unstable via watchdog
|
||||
* @cs: clocksource to be marked unstable
|
||||
@@ -174,7 +167,7 @@ void clocksource_mark_unstable(struct clocksource *cs)
|
||||
static void clocksource_watchdog(unsigned long data)
|
||||
{
|
||||
struct clocksource *cs;
|
||||
cycle_t csnow, wdnow, delta;
|
||||
cycle_t csnow, wdnow, cslast, wdlast, delta;
|
||||
int64_t wd_nsec, cs_nsec;
|
||||
int next_cpu, reset_pending;
|
||||
|
||||
@@ -213,6 +206,8 @@ static void clocksource_watchdog(unsigned long data)
|
||||
|
||||
delta = clocksource_delta(csnow, cs->cs_last, cs->mask);
|
||||
cs_nsec = clocksource_cyc2ns(delta, cs->mult, cs->shift);
|
||||
wdlast = cs->wd_last; /* save these in case we print them */
|
||||
cslast = cs->cs_last;
|
||||
cs->cs_last = csnow;
|
||||
cs->wd_last = wdnow;
|
||||
|
||||
@@ -221,7 +216,12 @@ static void clocksource_watchdog(unsigned long data)
|
||||
|
||||
/* Check the deviation from the watchdog clocksource. */
|
||||
if ((abs(cs_nsec - wd_nsec) > WATCHDOG_THRESHOLD)) {
|
||||
clocksource_unstable(cs, cs_nsec - wd_nsec);
|
||||
pr_warn("timekeeping watchdog: Marking clocksource '%s' as unstable, because the skew is too large:\n", cs->name);
|
||||
pr_warn(" '%s' wd_now: %llx wd_last: %llx mask: %llx\n",
|
||||
watchdog->name, wdnow, wdlast, watchdog->mask);
|
||||
pr_warn(" '%s' cs_now: %llx cs_last: %llx mask: %llx\n",
|
||||
cs->name, csnow, cslast, cs->mask);
|
||||
__clocksource_unstable(cs);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -469,26 +469,22 @@ static u32 clocksource_max_adjustment(struct clocksource *cs)
|
||||
* @shift: cycle to nanosecond divisor (power of two)
|
||||
* @maxadj: maximum adjustment value to mult (~11%)
|
||||
* @mask: bitmask for two's complement subtraction of non 64 bit counters
|
||||
* @max_cyc: maximum cycle value before potential overflow (does not include
|
||||
* any safety margin)
|
||||
*
|
||||
* NOTE: This function includes a safety margin of 50%, so that bad clock values
|
||||
* can be detected.
|
||||
*/
|
||||
u64 clocks_calc_max_nsecs(u32 mult, u32 shift, u32 maxadj, u64 mask)
|
||||
u64 clocks_calc_max_nsecs(u32 mult, u32 shift, u32 maxadj, u64 mask, u64 *max_cyc)
|
||||
{
|
||||
u64 max_nsecs, max_cycles;
|
||||
|
||||
/*
|
||||
* Calculate the maximum number of cycles that we can pass to the
|
||||
* cyc2ns function without overflowing a 64-bit signed result. The
|
||||
* maximum number of cycles is equal to ULLONG_MAX/(mult+maxadj)
|
||||
* which is equivalent to the below.
|
||||
* max_cycles < (2^63)/(mult + maxadj)
|
||||
* max_cycles < 2^(log2((2^63)/(mult + maxadj)))
|
||||
* max_cycles < 2^(log2(2^63) - log2(mult + maxadj))
|
||||
* max_cycles < 2^(63 - log2(mult + maxadj))
|
||||
* max_cycles < 1 << (63 - log2(mult + maxadj))
|
||||
* Please note that we add 1 to the result of the log2 to account for
|
||||
* any rounding errors, ensure the above inequality is satisfied and
|
||||
* no overflow will occur.
|
||||
* cyc2ns() function without overflowing a 64-bit result.
|
||||
*/
|
||||
max_cycles = 1ULL << (63 - (ilog2(mult + maxadj) + 1));
|
||||
max_cycles = ULLONG_MAX;
|
||||
do_div(max_cycles, mult+maxadj);
|
||||
|
||||
/*
|
||||
* The actual maximum number of cycles we can defer the clocksource is
|
||||
@@ -499,27 +495,26 @@ u64 clocks_calc_max_nsecs(u32 mult, u32 shift, u32 maxadj, u64 mask)
|
||||
max_cycles = min(max_cycles, mask);
|
||||
max_nsecs = clocksource_cyc2ns(max_cycles, mult - maxadj, shift);
|
||||
|
||||
/* return the max_cycles value as well if requested */
|
||||
if (max_cyc)
|
||||
*max_cyc = max_cycles;
|
||||
|
||||
/* Return 50% of the actual maximum, so we can detect bad values */
|
||||
max_nsecs >>= 1;
|
||||
|
||||
return max_nsecs;
|
||||
}
|
||||
|
||||
/**
|
||||
* clocksource_max_deferment - Returns max time the clocksource can be deferred
|
||||
* @cs: Pointer to clocksource
|
||||
* clocksource_update_max_deferment - Updates the clocksource max_idle_ns & max_cycles
|
||||
* @cs: Pointer to clocksource to be updated
|
||||
*
|
||||
*/
|
||||
static u64 clocksource_max_deferment(struct clocksource *cs)
|
||||
static inline void clocksource_update_max_deferment(struct clocksource *cs)
|
||||
{
|
||||
u64 max_nsecs;
|
||||
|
||||
max_nsecs = clocks_calc_max_nsecs(cs->mult, cs->shift, cs->maxadj,
|
||||
cs->mask);
|
||||
/*
|
||||
* To ensure that the clocksource does not wrap whilst we are idle,
|
||||
* limit the time the clocksource can be deferred by 12.5%. Please
|
||||
* note a margin of 12.5% is used because this can be computed with
|
||||
* a shift, versus say 10% which would require division.
|
||||
*/
|
||||
return max_nsecs - (max_nsecs >> 3);
|
||||
cs->max_idle_ns = clocks_calc_max_nsecs(cs->mult, cs->shift,
|
||||
cs->maxadj, cs->mask,
|
||||
&cs->max_cycles);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
|
||||
@@ -648,7 +643,7 @@ static void clocksource_enqueue(struct clocksource *cs)
|
||||
}
|
||||
|
||||
/**
|
||||
* __clocksource_updatefreq_scale - Used update clocksource with new freq
|
||||
* __clocksource_update_freq_scale - Used update clocksource with new freq
|
||||
* @cs: clocksource to be registered
|
||||
* @scale: Scale factor multiplied against freq to get clocksource hz
|
||||
* @freq: clocksource frequency (cycles per second) divided by scale
|
||||
@@ -656,48 +651,64 @@ static void clocksource_enqueue(struct clocksource *cs)
|
||||
* This should only be called from the clocksource->enable() method.
|
||||
*
|
||||
* This *SHOULD NOT* be called directly! Please use the
|
||||
* clocksource_updatefreq_hz() or clocksource_updatefreq_khz helper functions.
|
||||
* __clocksource_update_freq_hz() or __clocksource_update_freq_khz() helper
|
||||
* functions.
|
||||
*/
|
||||
void __clocksource_updatefreq_scale(struct clocksource *cs, u32 scale, u32 freq)
|
||||
void __clocksource_update_freq_scale(struct clocksource *cs, u32 scale, u32 freq)
|
||||
{
|
||||
u64 sec;
|
||||
|
||||
/*
|
||||
* Calc the maximum number of seconds which we can run before
|
||||
* wrapping around. For clocksources which have a mask > 32bit
|
||||
* we need to limit the max sleep time to have a good
|
||||
* conversion precision. 10 minutes is still a reasonable
|
||||
* amount. That results in a shift value of 24 for a
|
||||
* clocksource with mask >= 40bit and f >= 4GHz. That maps to
|
||||
* ~ 0.06ppm granularity for NTP. We apply the same 12.5%
|
||||
* margin as we do in clocksource_max_deferment()
|
||||
* Default clocksources are *special* and self-define their mult/shift.
|
||||
* But, you're not special, so you should specify a freq value.
|
||||
*/
|
||||
sec = (cs->mask - (cs->mask >> 3));
|
||||
do_div(sec, freq);
|
||||
do_div(sec, scale);
|
||||
if (!sec)
|
||||
sec = 1;
|
||||
else if (sec > 600 && cs->mask > UINT_MAX)
|
||||
sec = 600;
|
||||
|
||||
clocks_calc_mult_shift(&cs->mult, &cs->shift, freq,
|
||||
NSEC_PER_SEC / scale, sec * scale);
|
||||
if (freq) {
|
||||
/*
|
||||
* Calc the maximum number of seconds which we can run before
|
||||
* wrapping around. For clocksources which have a mask > 32-bit
|
||||
* we need to limit the max sleep time to have a good
|
||||
* conversion precision. 10 minutes is still a reasonable
|
||||
* amount. That results in a shift value of 24 for a
|
||||
* clocksource with mask >= 40-bit and f >= 4GHz. That maps to
|
||||
* ~ 0.06ppm granularity for NTP.
|
||||
*/
|
||||
sec = cs->mask;
|
||||
do_div(sec, freq);
|
||||
do_div(sec, scale);
|
||||
if (!sec)
|
||||
sec = 1;
|
||||
else if (sec > 600 && cs->mask > UINT_MAX)
|
||||
sec = 600;
|
||||
|
||||
clocks_calc_mult_shift(&cs->mult, &cs->shift, freq,
|
||||
NSEC_PER_SEC / scale, sec * scale);
|
||||
}
|
||||
/*
|
||||
* for clocksources that have large mults, to avoid overflow.
|
||||
* Since mult may be adjusted by ntp, add an safety extra margin
|
||||
*
|
||||
* Ensure clocksources that have large 'mult' values don't overflow
|
||||
* when adjusted.
|
||||
*/
|
||||
cs->maxadj = clocksource_max_adjustment(cs);
|
||||
while ((cs->mult + cs->maxadj < cs->mult)
|
||||
|| (cs->mult - cs->maxadj > cs->mult)) {
|
||||
while (freq && ((cs->mult + cs->maxadj < cs->mult)
|
||||
|| (cs->mult - cs->maxadj > cs->mult))) {
|
||||
cs->mult >>= 1;
|
||||
cs->shift--;
|
||||
cs->maxadj = clocksource_max_adjustment(cs);
|
||||
}
|
||||
|
||||
cs->max_idle_ns = clocksource_max_deferment(cs);
|
||||
/*
|
||||
* Only warn for *special* clocksources that self-define
|
||||
* their mult/shift values and don't specify a freq.
|
||||
*/
|
||||
WARN_ONCE(cs->mult + cs->maxadj < cs->mult,
|
||||
"timekeeping: Clocksource %s might overflow on 11%% adjustment\n",
|
||||
cs->name);
|
||||
|
||||
clocksource_update_max_deferment(cs);
|
||||
|
||||
pr_info("clocksource %s: mask: 0x%llx max_cycles: 0x%llx, max_idle_ns: %lld ns\n",
|
||||
cs->name, cs->mask, cs->max_cycles, cs->max_idle_ns);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__clocksource_updatefreq_scale);
|
||||
EXPORT_SYMBOL_GPL(__clocksource_update_freq_scale);
|
||||
|
||||
/**
|
||||
* __clocksource_register_scale - Used to install new clocksources
|
||||
@@ -714,7 +725,7 @@ int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq)
|
||||
{
|
||||
|
||||
/* Initialize mult/shift and max_idle_ns */
|
||||
__clocksource_updatefreq_scale(cs, scale, freq);
|
||||
__clocksource_update_freq_scale(cs, scale, freq);
|
||||
|
||||
/* Add clocksource to the clocksource list */
|
||||
mutex_lock(&clocksource_mutex);
|
||||
@@ -726,33 +737,6 @@ int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__clocksource_register_scale);
|
||||
|
||||
|
||||
/**
|
||||
* clocksource_register - Used to install new clocksources
|
||||
* @cs: clocksource to be registered
|
||||
*
|
||||
* Returns -EBUSY if registration fails, zero otherwise.
|
||||
*/
|
||||
int clocksource_register(struct clocksource *cs)
|
||||
{
|
||||
/* calculate max adjustment for given mult/shift */
|
||||
cs->maxadj = clocksource_max_adjustment(cs);
|
||||
WARN_ONCE(cs->mult + cs->maxadj < cs->mult,
|
||||
"Clocksource %s might overflow on 11%% adjustment\n",
|
||||
cs->name);
|
||||
|
||||
/* calculate max idle time permitted for this clocksource */
|
||||
cs->max_idle_ns = clocksource_max_deferment(cs);
|
||||
|
||||
mutex_lock(&clocksource_mutex);
|
||||
clocksource_enqueue(cs);
|
||||
clocksource_enqueue_watchdog(cs);
|
||||
clocksource_select();
|
||||
mutex_unlock(&clocksource_mutex);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(clocksource_register);
|
||||
|
||||
static void __clocksource_change_rating(struct clocksource *cs, int rating)
|
||||
{
|
||||
list_del(&cs->list);
|
||||
|
||||
@@ -71,6 +71,7 @@ static struct clocksource clocksource_jiffies = {
|
||||
.mask = 0xffffffff, /*32bits*/
|
||||
.mult = NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */
|
||||
.shift = JIFFIES_SHIFT,
|
||||
.max_cycles = 10,
|
||||
};
|
||||
|
||||
__cacheline_aligned_in_smp DEFINE_SEQLOCK(jiffies_lock);
|
||||
@@ -94,7 +95,7 @@ EXPORT_SYMBOL(jiffies);
|
||||
|
||||
static int __init init_jiffies_clocksource(void)
|
||||
{
|
||||
return clocksource_register(&clocksource_jiffies);
|
||||
return __clocksource_register(&clocksource_jiffies);
|
||||
}
|
||||
|
||||
core_initcall(init_jiffies_clocksource);
|
||||
@@ -130,6 +131,6 @@ int register_refined_jiffies(long cycles_per_second)
|
||||
|
||||
refined_jiffies.mult = ((u32)nsec_per_tick) << JIFFIES_SHIFT;
|
||||
|
||||
clocksource_register(&refined_jiffies);
|
||||
__clocksource_register(&refined_jiffies);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+162
-76
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* sched_clock.c: support for extending counters to full 64-bit ns counter
|
||||
* sched_clock.c: Generic sched_clock() support, to extend low level
|
||||
* hardware time counters to full 64-bit ns values.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
@@ -18,15 +19,53 @@
|
||||
#include <linux/seqlock.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
struct clock_data {
|
||||
ktime_t wrap_kt;
|
||||
/**
|
||||
* struct clock_read_data - data required to read from sched_clock()
|
||||
*
|
||||
* @epoch_ns: sched_clock() value at last update
|
||||
* @epoch_cyc: Clock cycle value at last update.
|
||||
* @sched_clock_mask: Bitmask for two's complement subtraction of non 64bit
|
||||
* clocks.
|
||||
* @read_sched_clock: Current clock source (or dummy source when suspended).
|
||||
* @mult: Multipler for scaled math conversion.
|
||||
* @shift: Shift value for scaled math conversion.
|
||||
*
|
||||
* Care must be taken when updating this structure; it is read by
|
||||
* some very hot code paths. It occupies <=40 bytes and, when combined
|
||||
* with the seqcount used to synchronize access, comfortably fits into
|
||||
* a 64 byte cache line.
|
||||
*/
|
||||
struct clock_read_data {
|
||||
u64 epoch_ns;
|
||||
u64 epoch_cyc;
|
||||
seqcount_t seq;
|
||||
unsigned long rate;
|
||||
u64 sched_clock_mask;
|
||||
u64 (*read_sched_clock)(void);
|
||||
u32 mult;
|
||||
u32 shift;
|
||||
bool suspended;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct clock_data - all data needed for sched_clock() (including
|
||||
* registration of a new clock source)
|
||||
*
|
||||
* @seq: Sequence counter for protecting updates. The lowest
|
||||
* bit is the index for @read_data.
|
||||
* @read_data: Data required to read from sched_clock.
|
||||
* @wrap_kt: Duration for which clock can run before wrapping.
|
||||
* @rate: Tick rate of the registered clock.
|
||||
* @actual_read_sched_clock: Registered hardware level clock read function.
|
||||
*
|
||||
* The ordering of this structure has been chosen to optimize cache
|
||||
* performance. In particular 'seq' and 'read_data[0]' (combined) should fit
|
||||
* into a single 64-byte cache line.
|
||||
*/
|
||||
struct clock_data {
|
||||
seqcount_t seq;
|
||||
struct clock_read_data read_data[2];
|
||||
ktime_t wrap_kt;
|
||||
unsigned long rate;
|
||||
|
||||
u64 (*actual_read_sched_clock)(void);
|
||||
};
|
||||
|
||||
static struct hrtimer sched_clock_timer;
|
||||
@@ -34,12 +73,6 @@ static int irqtime = -1;
|
||||
|
||||
core_param(irqtime, irqtime, int, 0400);
|
||||
|
||||
static struct clock_data cd = {
|
||||
.mult = NSEC_PER_SEC / HZ,
|
||||
};
|
||||
|
||||
static u64 __read_mostly sched_clock_mask;
|
||||
|
||||
static u64 notrace jiffy_sched_clock_read(void)
|
||||
{
|
||||
/*
|
||||
@@ -49,7 +82,11 @@ static u64 notrace jiffy_sched_clock_read(void)
|
||||
return (u64)(jiffies - INITIAL_JIFFIES);
|
||||
}
|
||||
|
||||
static u64 __read_mostly (*read_sched_clock)(void) = jiffy_sched_clock_read;
|
||||
static struct clock_data cd ____cacheline_aligned = {
|
||||
.read_data[0] = { .mult = NSEC_PER_SEC / HZ,
|
||||
.read_sched_clock = jiffy_sched_clock_read, },
|
||||
.actual_read_sched_clock = jiffy_sched_clock_read,
|
||||
};
|
||||
|
||||
static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift)
|
||||
{
|
||||
@@ -58,111 +95,136 @@ static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift)
|
||||
|
||||
unsigned long long notrace sched_clock(void)
|
||||
{
|
||||
u64 epoch_ns;
|
||||
u64 epoch_cyc;
|
||||
u64 cyc;
|
||||
u64 cyc, res;
|
||||
unsigned long seq;
|
||||
|
||||
if (cd.suspended)
|
||||
return cd.epoch_ns;
|
||||
struct clock_read_data *rd;
|
||||
|
||||
do {
|
||||
seq = raw_read_seqcount_begin(&cd.seq);
|
||||
epoch_cyc = cd.epoch_cyc;
|
||||
epoch_ns = cd.epoch_ns;
|
||||
seq = raw_read_seqcount(&cd.seq);
|
||||
rd = cd.read_data + (seq & 1);
|
||||
|
||||
cyc = (rd->read_sched_clock() - rd->epoch_cyc) &
|
||||
rd->sched_clock_mask;
|
||||
res = rd->epoch_ns + cyc_to_ns(cyc, rd->mult, rd->shift);
|
||||
} while (read_seqcount_retry(&cd.seq, seq));
|
||||
|
||||
cyc = read_sched_clock();
|
||||
cyc = (cyc - epoch_cyc) & sched_clock_mask;
|
||||
return epoch_ns + cyc_to_ns(cyc, cd.mult, cd.shift);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Atomically update the sched_clock epoch.
|
||||
* Updating the data required to read the clock.
|
||||
*
|
||||
* sched_clock() will never observe mis-matched data even if called from
|
||||
* an NMI. We do this by maintaining an odd/even copy of the data and
|
||||
* steering sched_clock() to one or the other using a sequence counter.
|
||||
* In order to preserve the data cache profile of sched_clock() as much
|
||||
* as possible the system reverts back to the even copy when the update
|
||||
* completes; the odd copy is used *only* during an update.
|
||||
*/
|
||||
static void notrace update_sched_clock(void)
|
||||
static void update_clock_read_data(struct clock_read_data *rd)
|
||||
{
|
||||
/* update the backup (odd) copy with the new data */
|
||||
cd.read_data[1] = *rd;
|
||||
|
||||
/* steer readers towards the odd copy */
|
||||
raw_write_seqcount_latch(&cd.seq);
|
||||
|
||||
/* now its safe for us to update the normal (even) copy */
|
||||
cd.read_data[0] = *rd;
|
||||
|
||||
/* switch readers back to the even copy */
|
||||
raw_write_seqcount_latch(&cd.seq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Atomically update the sched_clock() epoch.
|
||||
*/
|
||||
static void update_sched_clock(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
u64 cyc;
|
||||
u64 ns;
|
||||
struct clock_read_data rd;
|
||||
|
||||
cyc = read_sched_clock();
|
||||
ns = cd.epoch_ns +
|
||||
cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask,
|
||||
cd.mult, cd.shift);
|
||||
rd = cd.read_data[0];
|
||||
|
||||
raw_local_irq_save(flags);
|
||||
raw_write_seqcount_begin(&cd.seq);
|
||||
cd.epoch_ns = ns;
|
||||
cd.epoch_cyc = cyc;
|
||||
raw_write_seqcount_end(&cd.seq);
|
||||
raw_local_irq_restore(flags);
|
||||
cyc = cd.actual_read_sched_clock();
|
||||
ns = rd.epoch_ns + cyc_to_ns((cyc - rd.epoch_cyc) & rd.sched_clock_mask, rd.mult, rd.shift);
|
||||
|
||||
rd.epoch_ns = ns;
|
||||
rd.epoch_cyc = cyc;
|
||||
|
||||
update_clock_read_data(&rd);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart sched_clock_poll(struct hrtimer *hrt)
|
||||
{
|
||||
update_sched_clock();
|
||||
hrtimer_forward_now(hrt, cd.wrap_kt);
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
void __init sched_clock_register(u64 (*read)(void), int bits,
|
||||
unsigned long rate)
|
||||
void __init
|
||||
sched_clock_register(u64 (*read)(void), int bits, unsigned long rate)
|
||||
{
|
||||
u64 res, wrap, new_mask, new_epoch, cyc, ns;
|
||||
u32 new_mult, new_shift;
|
||||
ktime_t new_wrap_kt;
|
||||
unsigned long r;
|
||||
char r_unit;
|
||||
struct clock_read_data rd;
|
||||
|
||||
if (cd.rate > rate)
|
||||
return;
|
||||
|
||||
WARN_ON(!irqs_disabled());
|
||||
|
||||
/* calculate the mult/shift to convert counter ticks to ns. */
|
||||
/* Calculate the mult/shift to convert counter ticks to ns. */
|
||||
clocks_calc_mult_shift(&new_mult, &new_shift, rate, NSEC_PER_SEC, 3600);
|
||||
|
||||
new_mask = CLOCKSOURCE_MASK(bits);
|
||||
|
||||
/* calculate how many ns until we wrap */
|
||||
wrap = clocks_calc_max_nsecs(new_mult, new_shift, 0, new_mask);
|
||||
new_wrap_kt = ns_to_ktime(wrap - (wrap >> 3));
|
||||
|
||||
/* update epoch for new counter and update epoch_ns from old counter*/
|
||||
new_epoch = read();
|
||||
cyc = read_sched_clock();
|
||||
ns = cd.epoch_ns + cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask,
|
||||
cd.mult, cd.shift);
|
||||
|
||||
raw_write_seqcount_begin(&cd.seq);
|
||||
read_sched_clock = read;
|
||||
sched_clock_mask = new_mask;
|
||||
cd.rate = rate;
|
||||
cd.wrap_kt = new_wrap_kt;
|
||||
cd.mult = new_mult;
|
||||
cd.shift = new_shift;
|
||||
cd.epoch_cyc = new_epoch;
|
||||
cd.epoch_ns = ns;
|
||||
raw_write_seqcount_end(&cd.seq);
|
||||
|
||||
/* Calculate how many nanosecs until we risk wrapping */
|
||||
wrap = clocks_calc_max_nsecs(new_mult, new_shift, 0, new_mask, NULL);
|
||||
cd.wrap_kt = ns_to_ktime(wrap);
|
||||
|
||||
rd = cd.read_data[0];
|
||||
|
||||
/* Update epoch for new counter and update 'epoch_ns' from old counter*/
|
||||
new_epoch = read();
|
||||
cyc = cd.actual_read_sched_clock();
|
||||
ns = rd.epoch_ns + cyc_to_ns((cyc - rd.epoch_cyc) & rd.sched_clock_mask, rd.mult, rd.shift);
|
||||
cd.actual_read_sched_clock = read;
|
||||
|
||||
rd.read_sched_clock = read;
|
||||
rd.sched_clock_mask = new_mask;
|
||||
rd.mult = new_mult;
|
||||
rd.shift = new_shift;
|
||||
rd.epoch_cyc = new_epoch;
|
||||
rd.epoch_ns = ns;
|
||||
|
||||
update_clock_read_data(&rd);
|
||||
|
||||
r = rate;
|
||||
if (r >= 4000000) {
|
||||
r /= 1000000;
|
||||
r_unit = 'M';
|
||||
} else if (r >= 1000) {
|
||||
r /= 1000;
|
||||
r_unit = 'k';
|
||||
} else
|
||||
r_unit = ' ';
|
||||
} else {
|
||||
if (r >= 1000) {
|
||||
r /= 1000;
|
||||
r_unit = 'k';
|
||||
} else {
|
||||
r_unit = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
/* calculate the ns resolution of this counter */
|
||||
/* Calculate the ns resolution of this counter */
|
||||
res = cyc_to_ns(1ULL, new_mult, new_shift);
|
||||
|
||||
pr_info("sched_clock: %u bits at %lu%cHz, resolution %lluns, wraps every %lluns\n",
|
||||
bits, r, r_unit, res, wrap);
|
||||
|
||||
/* Enable IRQ time accounting if we have a fast enough sched_clock */
|
||||
/* Enable IRQ time accounting if we have a fast enough sched_clock() */
|
||||
if (irqtime > 0 || (irqtime == -1 && rate >= 1000000))
|
||||
enable_sched_clock_irqtime();
|
||||
|
||||
@@ -172,10 +234,10 @@ void __init sched_clock_register(u64 (*read)(void), int bits,
|
||||
void __init sched_clock_postinit(void)
|
||||
{
|
||||
/*
|
||||
* If no sched_clock function has been provided at that point,
|
||||
* If no sched_clock() function has been provided at that point,
|
||||
* make it the final one one.
|
||||
*/
|
||||
if (read_sched_clock == jiffy_sched_clock_read)
|
||||
if (cd.actual_read_sched_clock == jiffy_sched_clock_read)
|
||||
sched_clock_register(jiffy_sched_clock_read, BITS_PER_LONG, HZ);
|
||||
|
||||
update_sched_clock();
|
||||
@@ -189,29 +251,53 @@ void __init sched_clock_postinit(void)
|
||||
hrtimer_start(&sched_clock_timer, cd.wrap_kt, HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clock read function for use when the clock is suspended.
|
||||
*
|
||||
* This function makes it appear to sched_clock() as if the clock
|
||||
* stopped counting at its last update.
|
||||
*
|
||||
* This function must only be called from the critical
|
||||
* section in sched_clock(). It relies on the read_seqcount_retry()
|
||||
* at the end of the critical section to be sure we observe the
|
||||
* correct copy of 'epoch_cyc'.
|
||||
*/
|
||||
static u64 notrace suspended_sched_clock_read(void)
|
||||
{
|
||||
unsigned long seq = raw_read_seqcount(&cd.seq);
|
||||
|
||||
return cd.read_data[seq & 1].epoch_cyc;
|
||||
}
|
||||
|
||||
static int sched_clock_suspend(void)
|
||||
{
|
||||
struct clock_read_data *rd = &cd.read_data[0];
|
||||
|
||||
update_sched_clock();
|
||||
hrtimer_cancel(&sched_clock_timer);
|
||||
cd.suspended = true;
|
||||
rd->read_sched_clock = suspended_sched_clock_read;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sched_clock_resume(void)
|
||||
{
|
||||
cd.epoch_cyc = read_sched_clock();
|
||||
struct clock_read_data *rd = &cd.read_data[0];
|
||||
|
||||
rd->epoch_cyc = cd.actual_read_sched_clock();
|
||||
hrtimer_start(&sched_clock_timer, cd.wrap_kt, HRTIMER_MODE_REL);
|
||||
cd.suspended = false;
|
||||
rd->read_sched_clock = cd.actual_read_sched_clock;
|
||||
}
|
||||
|
||||
static struct syscore_ops sched_clock_ops = {
|
||||
.suspend = sched_clock_suspend,
|
||||
.resume = sched_clock_resume,
|
||||
.suspend = sched_clock_suspend,
|
||||
.resume = sched_clock_resume,
|
||||
};
|
||||
|
||||
static int __init sched_clock_syscore_init(void)
|
||||
{
|
||||
register_syscore_ops(&sched_clock_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(sched_clock_syscore_init);
|
||||
|
||||
+233
-112
File diff suppressed because it is too large
Load Diff
@@ -228,9 +228,35 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu)
|
||||
print_name_offset(m, dev->set_next_event);
|
||||
SEQ_printf(m, "\n");
|
||||
|
||||
SEQ_printf(m, " set_mode: ");
|
||||
print_name_offset(m, dev->set_mode);
|
||||
SEQ_printf(m, "\n");
|
||||
if (dev->set_mode) {
|
||||
SEQ_printf(m, " set_mode: ");
|
||||
print_name_offset(m, dev->set_mode);
|
||||
SEQ_printf(m, "\n");
|
||||
} else {
|
||||
if (dev->set_mode_shutdown) {
|
||||
SEQ_printf(m, " shutdown: ");
|
||||
print_name_offset(m, dev->set_mode_shutdown);
|
||||
SEQ_printf(m, "\n");
|
||||
}
|
||||
|
||||
if (dev->set_mode_periodic) {
|
||||
SEQ_printf(m, " periodic: ");
|
||||
print_name_offset(m, dev->set_mode_periodic);
|
||||
SEQ_printf(m, "\n");
|
||||
}
|
||||
|
||||
if (dev->set_mode_oneshot) {
|
||||
SEQ_printf(m, " oneshot: ");
|
||||
print_name_offset(m, dev->set_mode_oneshot);
|
||||
SEQ_printf(m, "\n");
|
||||
}
|
||||
|
||||
if (dev->set_mode_resume) {
|
||||
SEQ_printf(m, " resume: ");
|
||||
print_name_offset(m, dev->set_mode_resume);
|
||||
SEQ_printf(m, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
SEQ_printf(m, " event_handler: ");
|
||||
print_name_offset(m, dev->event_handler);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user