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