xref: /openbmc/linux/kernel/time/sched_clock.c (revision fb82fe2f)
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 
2138ff87f7SStephen Boyd struct clock_data {
22a08ca5d1SStephen Boyd 	ktime_t wrap_kt;
2338ff87f7SStephen Boyd 	u64 epoch_ns;
24e7e3ff1bSStephen Boyd 	u64 epoch_cyc;
2585c3d2ddSStephen Boyd 	seqcount_t seq;
2638ff87f7SStephen Boyd 	unsigned long rate;
2738ff87f7SStephen Boyd 	u32 mult;
2838ff87f7SStephen Boyd 	u32 shift;
2938ff87f7SStephen Boyd 	bool suspended;
3038ff87f7SStephen Boyd };
3138ff87f7SStephen Boyd 
32a08ca5d1SStephen Boyd static struct hrtimer sched_clock_timer;
3338ff87f7SStephen Boyd static int irqtime = -1;
3438ff87f7SStephen Boyd 
3538ff87f7SStephen Boyd core_param(irqtime, irqtime, int, 0400);
3638ff87f7SStephen Boyd 
3738ff87f7SStephen Boyd static struct clock_data cd = {
3838ff87f7SStephen Boyd 	.mult	= NSEC_PER_SEC / HZ,
3938ff87f7SStephen Boyd };
4038ff87f7SStephen Boyd 
41e7e3ff1bSStephen Boyd static u64 __read_mostly sched_clock_mask;
4238ff87f7SStephen Boyd 
43e7e3ff1bSStephen Boyd static u64 notrace jiffy_sched_clock_read(void)
4438ff87f7SStephen Boyd {
45e7e3ff1bSStephen Boyd 	/*
46e7e3ff1bSStephen Boyd 	 * We don't need to use get_jiffies_64 on 32-bit arches here
47e7e3ff1bSStephen Boyd 	 * because we register with BITS_PER_LONG
48e7e3ff1bSStephen Boyd 	 */
49e7e3ff1bSStephen Boyd 	return (u64)(jiffies - INITIAL_JIFFIES);
5038ff87f7SStephen Boyd }
5138ff87f7SStephen Boyd 
52e7e3ff1bSStephen Boyd static u64 __read_mostly (*read_sched_clock)(void) = jiffy_sched_clock_read;
5338ff87f7SStephen Boyd 
5438ff87f7SStephen Boyd static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift)
5538ff87f7SStephen Boyd {
5638ff87f7SStephen Boyd 	return (cyc * mult) >> shift;
5738ff87f7SStephen Boyd }
5838ff87f7SStephen Boyd 
59b4042ceaSStephen Boyd unsigned long long notrace sched_clock(void)
6038ff87f7SStephen Boyd {
6138ff87f7SStephen Boyd 	u64 epoch_ns;
62e7e3ff1bSStephen Boyd 	u64 epoch_cyc;
63e7e3ff1bSStephen Boyd 	u64 cyc;
6485c3d2ddSStephen Boyd 	unsigned long seq;
65336ae118SStephen Boyd 
66336ae118SStephen Boyd 	if (cd.suspended)
67336ae118SStephen Boyd 		return cd.epoch_ns;
6838ff87f7SStephen Boyd 
6938ff87f7SStephen Boyd 	do {
707a06c41cSJohn Stultz 		seq = raw_read_seqcount_begin(&cd.seq);
7138ff87f7SStephen Boyd 		epoch_cyc = cd.epoch_cyc;
7238ff87f7SStephen Boyd 		epoch_ns = cd.epoch_ns;
7385c3d2ddSStephen Boyd 	} while (read_seqcount_retry(&cd.seq, seq));
7438ff87f7SStephen Boyd 
75336ae118SStephen Boyd 	cyc = read_sched_clock();
76336ae118SStephen Boyd 	cyc = (cyc - epoch_cyc) & sched_clock_mask;
77336ae118SStephen Boyd 	return epoch_ns + cyc_to_ns(cyc, cd.mult, cd.shift);
7838ff87f7SStephen Boyd }
7938ff87f7SStephen Boyd 
8038ff87f7SStephen Boyd /*
8138ff87f7SStephen Boyd  * Atomically update the sched_clock epoch.
8238ff87f7SStephen Boyd  */
8338ff87f7SStephen Boyd static void notrace update_sched_clock(void)
8438ff87f7SStephen Boyd {
8538ff87f7SStephen Boyd 	unsigned long flags;
86e7e3ff1bSStephen Boyd 	u64 cyc;
8738ff87f7SStephen Boyd 	u64 ns;
8838ff87f7SStephen Boyd 
8938ff87f7SStephen Boyd 	cyc = read_sched_clock();
9038ff87f7SStephen Boyd 	ns = cd.epoch_ns +
9138ff87f7SStephen Boyd 		cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask,
9238ff87f7SStephen Boyd 			  cd.mult, cd.shift);
9385c3d2ddSStephen Boyd 
9438ff87f7SStephen Boyd 	raw_local_irq_save(flags);
957a06c41cSJohn Stultz 	raw_write_seqcount_begin(&cd.seq);
9638ff87f7SStephen Boyd 	cd.epoch_ns = ns;
9738ff87f7SStephen Boyd 	cd.epoch_cyc = cyc;
987a06c41cSJohn Stultz 	raw_write_seqcount_end(&cd.seq);
9938ff87f7SStephen Boyd 	raw_local_irq_restore(flags);
10038ff87f7SStephen Boyd }
10138ff87f7SStephen Boyd 
102a08ca5d1SStephen Boyd static enum hrtimer_restart sched_clock_poll(struct hrtimer *hrt)
10338ff87f7SStephen Boyd {
10438ff87f7SStephen Boyd 	update_sched_clock();
105a08ca5d1SStephen Boyd 	hrtimer_forward_now(hrt, cd.wrap_kt);
106a08ca5d1SStephen Boyd 	return HRTIMER_RESTART;
10738ff87f7SStephen Boyd }
10838ff87f7SStephen Boyd 
109e7e3ff1bSStephen Boyd void __init sched_clock_register(u64 (*read)(void), int bits,
110e7e3ff1bSStephen Boyd 				 unsigned long rate)
11138ff87f7SStephen Boyd {
1125ae8aabeSStephen Boyd 	u64 res, wrap, new_mask, new_epoch, cyc, ns;
1135ae8aabeSStephen Boyd 	u32 new_mult, new_shift;
1145ae8aabeSStephen Boyd 	ktime_t new_wrap_kt;
115a08ca5d1SStephen Boyd 	unsigned long r;
11638ff87f7SStephen Boyd 	char r_unit;
11738ff87f7SStephen Boyd 
11838ff87f7SStephen Boyd 	if (cd.rate > rate)
11938ff87f7SStephen Boyd 		return;
12038ff87f7SStephen Boyd 
12138ff87f7SStephen Boyd 	WARN_ON(!irqs_disabled());
12238ff87f7SStephen Boyd 
12338ff87f7SStephen Boyd 	/* calculate the mult/shift to convert counter ticks to ns. */
1245ae8aabeSStephen Boyd 	clocks_calc_mult_shift(&new_mult, &new_shift, rate, NSEC_PER_SEC, 3600);
1255ae8aabeSStephen Boyd 
1265ae8aabeSStephen Boyd 	new_mask = CLOCKSOURCE_MASK(bits);
1275ae8aabeSStephen Boyd 
128362fde04SJohn Stultz 	/* calculate how many nanosecs until we risk wrapping */
129fb82fe2fSJohn Stultz 	wrap = clocks_calc_max_nsecs(new_mult, new_shift, 0, new_mask, NULL);
130362fde04SJohn Stultz 	new_wrap_kt = ns_to_ktime(wrap);
1315ae8aabeSStephen Boyd 
1325ae8aabeSStephen Boyd 	/* update epoch for new counter and update epoch_ns from old counter*/
1335ae8aabeSStephen Boyd 	new_epoch = read();
1345ae8aabeSStephen Boyd 	cyc = read_sched_clock();
1355ae8aabeSStephen Boyd 	ns = cd.epoch_ns + cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask,
1365ae8aabeSStephen Boyd 			  cd.mult, cd.shift);
1375ae8aabeSStephen Boyd 
1385ae8aabeSStephen Boyd 	raw_write_seqcount_begin(&cd.seq);
1395ae8aabeSStephen Boyd 	read_sched_clock = read;
1405ae8aabeSStephen Boyd 	sched_clock_mask = new_mask;
1415ae8aabeSStephen Boyd 	cd.rate = rate;
1425ae8aabeSStephen Boyd 	cd.wrap_kt = new_wrap_kt;
1435ae8aabeSStephen Boyd 	cd.mult = new_mult;
1445ae8aabeSStephen Boyd 	cd.shift = new_shift;
1455ae8aabeSStephen Boyd 	cd.epoch_cyc = new_epoch;
1465ae8aabeSStephen Boyd 	cd.epoch_ns = ns;
1475ae8aabeSStephen Boyd 	raw_write_seqcount_end(&cd.seq);
14838ff87f7SStephen Boyd 
14938ff87f7SStephen Boyd 	r = rate;
15038ff87f7SStephen Boyd 	if (r >= 4000000) {
15138ff87f7SStephen Boyd 		r /= 1000000;
15238ff87f7SStephen Boyd 		r_unit = 'M';
15338ff87f7SStephen Boyd 	} else if (r >= 1000) {
15438ff87f7SStephen Boyd 		r /= 1000;
15538ff87f7SStephen Boyd 		r_unit = 'k';
15638ff87f7SStephen Boyd 	} else
15738ff87f7SStephen Boyd 		r_unit = ' ';
15838ff87f7SStephen Boyd 
15938ff87f7SStephen Boyd 	/* calculate the ns resolution of this counter */
1605ae8aabeSStephen Boyd 	res = cyc_to_ns(1ULL, new_mult, new_shift);
1615ae8aabeSStephen Boyd 
162a08ca5d1SStephen Boyd 	pr_info("sched_clock: %u bits at %lu%cHz, resolution %lluns, wraps every %lluns\n",
163a08ca5d1SStephen Boyd 		bits, r, r_unit, res, wrap);
16438ff87f7SStephen Boyd 
16538ff87f7SStephen Boyd 	/* Enable IRQ time accounting if we have a fast enough sched_clock */
16638ff87f7SStephen Boyd 	if (irqtime > 0 || (irqtime == -1 && rate >= 1000000))
16738ff87f7SStephen Boyd 		enable_sched_clock_irqtime();
16838ff87f7SStephen Boyd 
16938ff87f7SStephen Boyd 	pr_debug("Registered %pF as sched_clock source\n", read);
17038ff87f7SStephen Boyd }
17138ff87f7SStephen Boyd 
17238ff87f7SStephen Boyd void __init sched_clock_postinit(void)
17338ff87f7SStephen Boyd {
17438ff87f7SStephen Boyd 	/*
17538ff87f7SStephen Boyd 	 * If no sched_clock function has been provided at that point,
17638ff87f7SStephen Boyd 	 * make it the final one one.
17738ff87f7SStephen Boyd 	 */
17838ff87f7SStephen Boyd 	if (read_sched_clock == jiffy_sched_clock_read)
179e7e3ff1bSStephen Boyd 		sched_clock_register(jiffy_sched_clock_read, BITS_PER_LONG, HZ);
18038ff87f7SStephen Boyd 
181a08ca5d1SStephen Boyd 	update_sched_clock();
182a08ca5d1SStephen Boyd 
183a08ca5d1SStephen Boyd 	/*
184a08ca5d1SStephen Boyd 	 * Start the timer to keep sched_clock() properly updated and
185a08ca5d1SStephen Boyd 	 * sets the initial epoch.
186a08ca5d1SStephen Boyd 	 */
187a08ca5d1SStephen Boyd 	hrtimer_init(&sched_clock_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
188a08ca5d1SStephen Boyd 	sched_clock_timer.function = sched_clock_poll;
189a08ca5d1SStephen Boyd 	hrtimer_start(&sched_clock_timer, cd.wrap_kt, HRTIMER_MODE_REL);
19038ff87f7SStephen Boyd }
19138ff87f7SStephen Boyd 
19238ff87f7SStephen Boyd static int sched_clock_suspend(void)
19338ff87f7SStephen Boyd {
194f723aa18SStephen Boyd 	update_sched_clock();
195f723aa18SStephen Boyd 	hrtimer_cancel(&sched_clock_timer);
19638ff87f7SStephen Boyd 	cd.suspended = true;
19738ff87f7SStephen Boyd 	return 0;
19838ff87f7SStephen Boyd }
19938ff87f7SStephen Boyd 
20038ff87f7SStephen Boyd static void sched_clock_resume(void)
20138ff87f7SStephen Boyd {
20238ff87f7SStephen Boyd 	cd.epoch_cyc = read_sched_clock();
203f723aa18SStephen Boyd 	hrtimer_start(&sched_clock_timer, cd.wrap_kt, HRTIMER_MODE_REL);
20438ff87f7SStephen Boyd 	cd.suspended = false;
20538ff87f7SStephen Boyd }
20638ff87f7SStephen Boyd 
20738ff87f7SStephen Boyd static struct syscore_ops sched_clock_ops = {
20838ff87f7SStephen Boyd 	.suspend = sched_clock_suspend,
20938ff87f7SStephen Boyd 	.resume = sched_clock_resume,
21038ff87f7SStephen Boyd };
21138ff87f7SStephen Boyd 
21238ff87f7SStephen Boyd static int __init sched_clock_syscore_init(void)
21338ff87f7SStephen Boyd {
21438ff87f7SStephen Boyd 	register_syscore_ops(&sched_clock_ops);
21538ff87f7SStephen Boyd 	return 0;
21638ff87f7SStephen Boyd }
21738ff87f7SStephen Boyd device_initcall(sched_clock_syscore_init);
218