xref: /openbmc/linux/kernel/time/sched_clock.c (revision 9fee69a8c8070b38b558161a3f18bd5e2b664682)
138ff87f7SStephen Boyd /*
238ff87f7SStephen Boyd  * sched_clock.c: support for extending counters to full 64-bit ns counter
338ff87f7SStephen Boyd  *
438ff87f7SStephen Boyd  * This program is free software; you can redistribute it and/or modify
538ff87f7SStephen Boyd  * it under the terms of the GNU General Public License version 2 as
638ff87f7SStephen Boyd  * published by the Free Software Foundation.
738ff87f7SStephen Boyd  */
838ff87f7SStephen Boyd #include <linux/clocksource.h>
938ff87f7SStephen Boyd #include <linux/init.h>
1038ff87f7SStephen Boyd #include <linux/jiffies.h>
11a08ca5d1SStephen Boyd #include <linux/ktime.h>
1238ff87f7SStephen Boyd #include <linux/kernel.h>
1338ff87f7SStephen Boyd #include <linux/moduleparam.h>
1438ff87f7SStephen Boyd #include <linux/sched.h>
1538ff87f7SStephen Boyd #include <linux/syscore_ops.h>
16a08ca5d1SStephen Boyd #include <linux/hrtimer.h>
1738ff87f7SStephen Boyd #include <linux/sched_clock.h>
1885c3d2ddSStephen Boyd #include <linux/seqlock.h>
19e7e3ff1bSStephen Boyd #include <linux/bitops.h>
2038ff87f7SStephen Boyd 
21cf7c9c17SDaniel Thompson /**
22cf7c9c17SDaniel Thompson  * struct clock_read_data - data required to read from sched_clock
23cf7c9c17SDaniel Thompson  *
24cf7c9c17SDaniel Thompson  * @epoch_ns:		sched_clock value at last update
25cf7c9c17SDaniel Thompson  * @epoch_cyc:		Clock cycle value at last update
26cf7c9c17SDaniel Thompson  * @sched_clock_mask:   Bitmask for two's complement subtraction of non 64bit
27cf7c9c17SDaniel Thompson  *			clocks
28cf7c9c17SDaniel Thompson  * @read_sched_clock:	Current clock source (or dummy source when suspended)
29cf7c9c17SDaniel Thompson  * @mult:		Multipler for scaled math conversion
30cf7c9c17SDaniel Thompson  * @shift:		Shift value for scaled math conversion
31cf7c9c17SDaniel Thompson  *
32cf7c9c17SDaniel Thompson  * Care must be taken when updating this structure; it is read by
3313dbeb38SDaniel Thompson  * some very hot code paths. It occupies <=40 bytes and, when combined
34cf7c9c17SDaniel Thompson  * with the seqcount used to synchronize access, comfortably fits into
35cf7c9c17SDaniel Thompson  * a 64 byte cache line.
36cf7c9c17SDaniel Thompson  */
37cf7c9c17SDaniel Thompson struct clock_read_data {
3838ff87f7SStephen Boyd 	u64 epoch_ns;
39e7e3ff1bSStephen Boyd 	u64 epoch_cyc;
40cf7c9c17SDaniel Thompson 	u64 sched_clock_mask;
41cf7c9c17SDaniel Thompson 	u64 (*read_sched_clock)(void);
4238ff87f7SStephen Boyd 	u32 mult;
4338ff87f7SStephen Boyd 	u32 shift;
4438ff87f7SStephen Boyd };
4538ff87f7SStephen Boyd 
46cf7c9c17SDaniel Thompson /**
47cf7c9c17SDaniel Thompson  * struct clock_data - all data needed for sched_clock (including
48cf7c9c17SDaniel Thompson  *                     registration of a new clock source)
49cf7c9c17SDaniel Thompson  *
50cf7c9c17SDaniel Thompson  * @seq:		Sequence counter for protecting updates.
51cf7c9c17SDaniel Thompson  * @read_data:		Data required to read from sched_clock.
52cf7c9c17SDaniel Thompson  * @wrap_kt:		Duration for which clock can run before wrapping
53cf7c9c17SDaniel Thompson  * @rate:		Tick rate of the registered clock
54cf7c9c17SDaniel Thompson  * @actual_read_sched_clock: Registered clock read function
55cf7c9c17SDaniel Thompson  *
56cf7c9c17SDaniel Thompson  * The ordering of this structure has been chosen to optimize cache
57cf7c9c17SDaniel Thompson  * performance. In particular seq and read_data (combined) should fit
58cf7c9c17SDaniel Thompson  * into a single 64 byte cache line.
59cf7c9c17SDaniel Thompson  */
60cf7c9c17SDaniel Thompson struct clock_data {
61cf7c9c17SDaniel Thompson 	seqcount_t seq;
62cf7c9c17SDaniel Thompson 	struct clock_read_data read_data;
63cf7c9c17SDaniel Thompson 	ktime_t wrap_kt;
64cf7c9c17SDaniel Thompson 	unsigned long rate;
6513dbeb38SDaniel Thompson 	u64 (*actual_read_sched_clock)(void);
66cf7c9c17SDaniel Thompson };
67cf7c9c17SDaniel Thompson 
68a08ca5d1SStephen Boyd static struct hrtimer sched_clock_timer;
6938ff87f7SStephen Boyd static int irqtime = -1;
7038ff87f7SStephen Boyd 
7138ff87f7SStephen Boyd core_param(irqtime, irqtime, int, 0400);
7238ff87f7SStephen Boyd 
73e7e3ff1bSStephen Boyd static u64 notrace jiffy_sched_clock_read(void)
7438ff87f7SStephen Boyd {
75e7e3ff1bSStephen Boyd 	/*
76e7e3ff1bSStephen Boyd 	 * We don't need to use get_jiffies_64 on 32-bit arches here
77e7e3ff1bSStephen Boyd 	 * because we register with BITS_PER_LONG
78e7e3ff1bSStephen Boyd 	 */
79e7e3ff1bSStephen Boyd 	return (u64)(jiffies - INITIAL_JIFFIES);
8038ff87f7SStephen Boyd }
8138ff87f7SStephen Boyd 
82cf7c9c17SDaniel Thompson static struct clock_data cd ____cacheline_aligned = {
83cf7c9c17SDaniel Thompson 	.read_data = { .mult = NSEC_PER_SEC / HZ,
84cf7c9c17SDaniel Thompson 		       .read_sched_clock = jiffy_sched_clock_read, },
8513dbeb38SDaniel Thompson 	.actual_read_sched_clock = jiffy_sched_clock_read,
8613dbeb38SDaniel Thompson 
87cf7c9c17SDaniel Thompson };
8838ff87f7SStephen Boyd 
8938ff87f7SStephen Boyd static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift)
9038ff87f7SStephen Boyd {
9138ff87f7SStephen Boyd 	return (cyc * mult) >> shift;
9238ff87f7SStephen Boyd }
9338ff87f7SStephen Boyd 
94b4042ceaSStephen Boyd unsigned long long notrace sched_clock(void)
9538ff87f7SStephen Boyd {
968710e914SDaniel Thompson 	u64 cyc, res;
9785c3d2ddSStephen Boyd 	unsigned long seq;
98cf7c9c17SDaniel Thompson 	struct clock_read_data *rd = &cd.read_data;
99336ae118SStephen Boyd 
10038ff87f7SStephen Boyd 	do {
1017a06c41cSJohn Stultz 		seq = raw_read_seqcount_begin(&cd.seq);
1028710e914SDaniel Thompson 
10313dbeb38SDaniel Thompson 		cyc = (rd->read_sched_clock() - rd->epoch_cyc) &
10413dbeb38SDaniel Thompson 		      rd->sched_clock_mask;
10513dbeb38SDaniel Thompson 		res = rd->epoch_ns + cyc_to_ns(cyc, rd->mult, rd->shift);
10685c3d2ddSStephen Boyd 	} while (read_seqcount_retry(&cd.seq, seq));
10738ff87f7SStephen Boyd 
1088710e914SDaniel Thompson 	return res;
10938ff87f7SStephen Boyd }
11038ff87f7SStephen Boyd 
11138ff87f7SStephen Boyd /*
11238ff87f7SStephen Boyd  * Atomically update the sched_clock epoch.
11338ff87f7SStephen Boyd  */
114*9fee69a8SDaniel Thompson static void update_sched_clock(void)
11538ff87f7SStephen Boyd {
11638ff87f7SStephen Boyd 	unsigned long flags;
117e7e3ff1bSStephen Boyd 	u64 cyc;
11838ff87f7SStephen Boyd 	u64 ns;
119cf7c9c17SDaniel Thompson 	struct clock_read_data *rd = &cd.read_data;
12038ff87f7SStephen Boyd 
12113dbeb38SDaniel Thompson 	cyc = cd.actual_read_sched_clock();
122cf7c9c17SDaniel Thompson 	ns = rd->epoch_ns +
123cf7c9c17SDaniel Thompson 	     cyc_to_ns((cyc - rd->epoch_cyc) & rd->sched_clock_mask,
124cf7c9c17SDaniel Thompson 		       rd->mult, rd->shift);
12585c3d2ddSStephen Boyd 
12638ff87f7SStephen Boyd 	raw_local_irq_save(flags);
1277a06c41cSJohn Stultz 	raw_write_seqcount_begin(&cd.seq);
128cf7c9c17SDaniel Thompson 	rd->epoch_ns = ns;
129cf7c9c17SDaniel Thompson 	rd->epoch_cyc = cyc;
1307a06c41cSJohn Stultz 	raw_write_seqcount_end(&cd.seq);
13138ff87f7SStephen Boyd 	raw_local_irq_restore(flags);
13238ff87f7SStephen Boyd }
13338ff87f7SStephen Boyd 
134a08ca5d1SStephen Boyd static enum hrtimer_restart sched_clock_poll(struct hrtimer *hrt)
13538ff87f7SStephen Boyd {
13638ff87f7SStephen Boyd 	update_sched_clock();
137a08ca5d1SStephen Boyd 	hrtimer_forward_now(hrt, cd.wrap_kt);
138a08ca5d1SStephen Boyd 	return HRTIMER_RESTART;
13938ff87f7SStephen Boyd }
14038ff87f7SStephen Boyd 
141e7e3ff1bSStephen Boyd void __init sched_clock_register(u64 (*read)(void), int bits,
142e7e3ff1bSStephen Boyd 				 unsigned long rate)
14338ff87f7SStephen Boyd {
1445ae8aabeSStephen Boyd 	u64 res, wrap, new_mask, new_epoch, cyc, ns;
1455ae8aabeSStephen Boyd 	u32 new_mult, new_shift;
146a08ca5d1SStephen Boyd 	unsigned long r;
14738ff87f7SStephen Boyd 	char r_unit;
148cf7c9c17SDaniel Thompson 	struct clock_read_data *rd = &cd.read_data;
14938ff87f7SStephen Boyd 
15038ff87f7SStephen Boyd 	if (cd.rate > rate)
15138ff87f7SStephen Boyd 		return;
15238ff87f7SStephen Boyd 
15338ff87f7SStephen Boyd 	WARN_ON(!irqs_disabled());
15438ff87f7SStephen Boyd 
15538ff87f7SStephen Boyd 	/* calculate the mult/shift to convert counter ticks to ns. */
1565ae8aabeSStephen Boyd 	clocks_calc_mult_shift(&new_mult, &new_shift, rate, NSEC_PER_SEC, 3600);
1575ae8aabeSStephen Boyd 
1585ae8aabeSStephen Boyd 	new_mask = CLOCKSOURCE_MASK(bits);
1598710e914SDaniel Thompson 	cd.rate = rate;
1605ae8aabeSStephen Boyd 
161362fde04SJohn Stultz 	/* calculate how many nanosecs until we risk wrapping */
162fb82fe2fSJohn Stultz 	wrap = clocks_calc_max_nsecs(new_mult, new_shift, 0, new_mask, NULL);
1638710e914SDaniel Thompson 	cd.wrap_kt = ns_to_ktime(wrap);
1645ae8aabeSStephen Boyd 
1655ae8aabeSStephen Boyd 	/* update epoch for new counter and update epoch_ns from old counter*/
1665ae8aabeSStephen Boyd 	new_epoch = read();
16713dbeb38SDaniel Thompson 	cyc = cd.actual_read_sched_clock();
168cf7c9c17SDaniel Thompson 	ns = rd->epoch_ns +
169cf7c9c17SDaniel Thompson 	     cyc_to_ns((cyc - rd->epoch_cyc) & rd->sched_clock_mask,
170cf7c9c17SDaniel Thompson 		       rd->mult, rd->shift);
17113dbeb38SDaniel Thompson 	cd.actual_read_sched_clock = read;
1725ae8aabeSStephen Boyd 
1735ae8aabeSStephen Boyd 	raw_write_seqcount_begin(&cd.seq);
174cf7c9c17SDaniel Thompson 	rd->read_sched_clock = read;
175cf7c9c17SDaniel Thompson 	rd->sched_clock_mask = new_mask;
176cf7c9c17SDaniel Thompson 	rd->mult = new_mult;
177cf7c9c17SDaniel Thompson 	rd->shift = new_shift;
178cf7c9c17SDaniel Thompson 	rd->epoch_cyc = new_epoch;
179cf7c9c17SDaniel Thompson 	rd->epoch_ns = ns;
1805ae8aabeSStephen Boyd 	raw_write_seqcount_end(&cd.seq);
18138ff87f7SStephen Boyd 
18238ff87f7SStephen Boyd 	r = rate;
18338ff87f7SStephen Boyd 	if (r >= 4000000) {
18438ff87f7SStephen Boyd 		r /= 1000000;
18538ff87f7SStephen Boyd 		r_unit = 'M';
18638ff87f7SStephen Boyd 	} else if (r >= 1000) {
18738ff87f7SStephen Boyd 		r /= 1000;
18838ff87f7SStephen Boyd 		r_unit = 'k';
18938ff87f7SStephen Boyd 	} else
19038ff87f7SStephen Boyd 		r_unit = ' ';
19138ff87f7SStephen Boyd 
19238ff87f7SStephen Boyd 	/* calculate the ns resolution of this counter */
1935ae8aabeSStephen Boyd 	res = cyc_to_ns(1ULL, new_mult, new_shift);
1945ae8aabeSStephen Boyd 
195a08ca5d1SStephen Boyd 	pr_info("sched_clock: %u bits at %lu%cHz, resolution %lluns, wraps every %lluns\n",
196a08ca5d1SStephen Boyd 		bits, r, r_unit, res, wrap);
19738ff87f7SStephen Boyd 
19838ff87f7SStephen Boyd 	/* Enable IRQ time accounting if we have a fast enough sched_clock */
19938ff87f7SStephen Boyd 	if (irqtime > 0 || (irqtime == -1 && rate >= 1000000))
20038ff87f7SStephen Boyd 		enable_sched_clock_irqtime();
20138ff87f7SStephen Boyd 
20238ff87f7SStephen Boyd 	pr_debug("Registered %pF as sched_clock source\n", read);
20338ff87f7SStephen Boyd }
20438ff87f7SStephen Boyd 
20538ff87f7SStephen Boyd void __init sched_clock_postinit(void)
20638ff87f7SStephen Boyd {
20738ff87f7SStephen Boyd 	/*
20838ff87f7SStephen Boyd 	 * If no sched_clock function has been provided at that point,
20938ff87f7SStephen Boyd 	 * make it the final one one.
21038ff87f7SStephen Boyd 	 */
21113dbeb38SDaniel Thompson 	if (cd.actual_read_sched_clock == jiffy_sched_clock_read)
212e7e3ff1bSStephen Boyd 		sched_clock_register(jiffy_sched_clock_read, BITS_PER_LONG, HZ);
21338ff87f7SStephen Boyd 
214a08ca5d1SStephen Boyd 	update_sched_clock();
215a08ca5d1SStephen Boyd 
216a08ca5d1SStephen Boyd 	/*
217a08ca5d1SStephen Boyd 	 * Start the timer to keep sched_clock() properly updated and
218a08ca5d1SStephen Boyd 	 * sets the initial epoch.
219a08ca5d1SStephen Boyd 	 */
220a08ca5d1SStephen Boyd 	hrtimer_init(&sched_clock_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
221a08ca5d1SStephen Boyd 	sched_clock_timer.function = sched_clock_poll;
222a08ca5d1SStephen Boyd 	hrtimer_start(&sched_clock_timer, cd.wrap_kt, HRTIMER_MODE_REL);
22338ff87f7SStephen Boyd }
22438ff87f7SStephen Boyd 
22513dbeb38SDaniel Thompson /*
22613dbeb38SDaniel Thompson  * Clock read function for use when the clock is suspended.
22713dbeb38SDaniel Thompson  *
22813dbeb38SDaniel Thompson  * This function makes it appear to sched_clock() as if the clock
22913dbeb38SDaniel Thompson  * stopped counting at its last update.
23013dbeb38SDaniel Thompson  */
23113dbeb38SDaniel Thompson static u64 notrace suspended_sched_clock_read(void)
23213dbeb38SDaniel Thompson {
23313dbeb38SDaniel Thompson 	return cd.read_data.epoch_cyc;
23413dbeb38SDaniel Thompson }
23513dbeb38SDaniel Thompson 
23638ff87f7SStephen Boyd static int sched_clock_suspend(void)
23738ff87f7SStephen Boyd {
238cf7c9c17SDaniel Thompson 	struct clock_read_data *rd = &cd.read_data;
239cf7c9c17SDaniel Thompson 
240f723aa18SStephen Boyd 	update_sched_clock();
241f723aa18SStephen Boyd 	hrtimer_cancel(&sched_clock_timer);
24213dbeb38SDaniel Thompson 	rd->read_sched_clock = suspended_sched_clock_read;
24338ff87f7SStephen Boyd 	return 0;
24438ff87f7SStephen Boyd }
24538ff87f7SStephen Boyd 
24638ff87f7SStephen Boyd static void sched_clock_resume(void)
24738ff87f7SStephen Boyd {
248cf7c9c17SDaniel Thompson 	struct clock_read_data *rd = &cd.read_data;
249cf7c9c17SDaniel Thompson 
25013dbeb38SDaniel Thompson 	rd->epoch_cyc = cd.actual_read_sched_clock();
251f723aa18SStephen Boyd 	hrtimer_start(&sched_clock_timer, cd.wrap_kt, HRTIMER_MODE_REL);
25213dbeb38SDaniel Thompson 	rd->read_sched_clock = cd.actual_read_sched_clock;
25338ff87f7SStephen Boyd }
25438ff87f7SStephen Boyd 
25538ff87f7SStephen Boyd static struct syscore_ops sched_clock_ops = {
25638ff87f7SStephen Boyd 	.suspend = sched_clock_suspend,
25738ff87f7SStephen Boyd 	.resume = sched_clock_resume,
25838ff87f7SStephen Boyd };
25938ff87f7SStephen Boyd 
26038ff87f7SStephen Boyd static int __init sched_clock_syscore_init(void)
26138ff87f7SStephen Boyd {
26238ff87f7SStephen Boyd 	register_syscore_ops(&sched_clock_ops);
26338ff87f7SStephen Boyd 	return 0;
26438ff87f7SStephen Boyd }
26538ff87f7SStephen Boyd device_initcall(sched_clock_syscore_init);
266