at v2.6.29 266 lines 5.9 kB view raw
1/* 2 * sched_clock for unstable cpu clocks 3 * 4 * Copyright (C) 2008 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com> 5 * 6 * Updates and enhancements: 7 * Copyright (C) 2008 Red Hat, Inc. Steven Rostedt <srostedt@redhat.com> 8 * 9 * Based on code by: 10 * Ingo Molnar <mingo@redhat.com> 11 * Guillaume Chazarain <guichaz@gmail.com> 12 * 13 * Create a semi stable clock from a mixture of other events, including: 14 * - gtod 15 * - sched_clock() 16 * - explicit idle events 17 * 18 * We use gtod as base and the unstable clock deltas. The deltas are filtered, 19 * making it monotonic and keeping it within an expected window. 20 * 21 * Furthermore, explicit sleep and wakeup hooks allow us to account for time 22 * that is otherwise invisible (TSC gets stopped). 23 * 24 * The clock: sched_clock_cpu() is monotonic per cpu, and should be somewhat 25 * consistent between cpus (never more than 2 jiffies difference). 26 */ 27#include <linux/sched.h> 28#include <linux/percpu.h> 29#include <linux/spinlock.h> 30#include <linux/ktime.h> 31#include <linux/module.h> 32 33/* 34 * Scheduler clock - returns current time in nanosec units. 35 * This is default implementation. 36 * Architectures and sub-architectures can override this. 37 */ 38unsigned long long __attribute__((weak)) sched_clock(void) 39{ 40 return (unsigned long long)jiffies * (NSEC_PER_SEC / HZ); 41} 42 43static __read_mostly int sched_clock_running; 44 45#ifdef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK 46 47struct sched_clock_data { 48 /* 49 * Raw spinlock - this is a special case: this might be called 50 * from within instrumentation code so we dont want to do any 51 * instrumentation ourselves. 52 */ 53 raw_spinlock_t lock; 54 55 u64 tick_raw; 56 u64 tick_gtod; 57 u64 clock; 58}; 59 60static DEFINE_PER_CPU_SHARED_ALIGNED(struct sched_clock_data, sched_clock_data); 61 62static inline struct sched_clock_data *this_scd(void) 63{ 64 return &__get_cpu_var(sched_clock_data); 65} 66 67static inline struct sched_clock_data *cpu_sdc(int cpu) 68{ 69 return &per_cpu(sched_clock_data, cpu); 70} 71 72void sched_clock_init(void) 73{ 74 u64 ktime_now = ktime_to_ns(ktime_get()); 75 int cpu; 76 77 for_each_possible_cpu(cpu) { 78 struct sched_clock_data *scd = cpu_sdc(cpu); 79 80 scd->lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED; 81 scd->tick_raw = 0; 82 scd->tick_gtod = ktime_now; 83 scd->clock = ktime_now; 84 } 85 86 sched_clock_running = 1; 87} 88 89/* 90 * min,max except they take wrapping into account 91 */ 92 93static inline u64 wrap_min(u64 x, u64 y) 94{ 95 return (s64)(x - y) < 0 ? x : y; 96} 97 98static inline u64 wrap_max(u64 x, u64 y) 99{ 100 return (s64)(x - y) > 0 ? x : y; 101} 102 103/* 104 * update the percpu scd from the raw @now value 105 * 106 * - filter out backward motion 107 * - use the GTOD tick value to create a window to filter crazy TSC values 108 */ 109static u64 __update_sched_clock(struct sched_clock_data *scd, u64 now) 110{ 111 s64 delta = now - scd->tick_raw; 112 u64 clock, min_clock, max_clock; 113 114 WARN_ON_ONCE(!irqs_disabled()); 115 116 if (unlikely(delta < 0)) 117 delta = 0; 118 119 /* 120 * scd->clock = clamp(scd->tick_gtod + delta, 121 * max(scd->tick_gtod, scd->clock), 122 * scd->tick_gtod + TICK_NSEC); 123 */ 124 125 clock = scd->tick_gtod + delta; 126 min_clock = wrap_max(scd->tick_gtod, scd->clock); 127 max_clock = wrap_max(scd->clock, scd->tick_gtod + TICK_NSEC); 128 129 clock = wrap_max(clock, min_clock); 130 clock = wrap_min(clock, max_clock); 131 132 scd->clock = clock; 133 134 return scd->clock; 135} 136 137static void lock_double_clock(struct sched_clock_data *data1, 138 struct sched_clock_data *data2) 139{ 140 if (data1 < data2) { 141 __raw_spin_lock(&data1->lock); 142 __raw_spin_lock(&data2->lock); 143 } else { 144 __raw_spin_lock(&data2->lock); 145 __raw_spin_lock(&data1->lock); 146 } 147} 148 149u64 sched_clock_cpu(int cpu) 150{ 151 struct sched_clock_data *scd = cpu_sdc(cpu); 152 u64 now, clock, this_clock, remote_clock; 153 154 if (unlikely(!sched_clock_running)) 155 return 0ull; 156 157 WARN_ON_ONCE(!irqs_disabled()); 158 now = sched_clock(); 159 160 if (cpu != raw_smp_processor_id()) { 161 struct sched_clock_data *my_scd = this_scd(); 162 163 lock_double_clock(scd, my_scd); 164 165 this_clock = __update_sched_clock(my_scd, now); 166 remote_clock = scd->clock; 167 168 /* 169 * Use the opportunity that we have both locks 170 * taken to couple the two clocks: we take the 171 * larger time as the latest time for both 172 * runqueues. (this creates monotonic movement) 173 */ 174 if (likely((s64)(remote_clock - this_clock) < 0)) { 175 clock = this_clock; 176 scd->clock = clock; 177 } else { 178 /* 179 * Should be rare, but possible: 180 */ 181 clock = remote_clock; 182 my_scd->clock = remote_clock; 183 } 184 185 __raw_spin_unlock(&my_scd->lock); 186 } else { 187 __raw_spin_lock(&scd->lock); 188 clock = __update_sched_clock(scd, now); 189 } 190 191 __raw_spin_unlock(&scd->lock); 192 193 return clock; 194} 195 196void sched_clock_tick(void) 197{ 198 struct sched_clock_data *scd = this_scd(); 199 u64 now, now_gtod; 200 201 if (unlikely(!sched_clock_running)) 202 return; 203 204 WARN_ON_ONCE(!irqs_disabled()); 205 206 now_gtod = ktime_to_ns(ktime_get()); 207 now = sched_clock(); 208 209 __raw_spin_lock(&scd->lock); 210 scd->tick_raw = now; 211 scd->tick_gtod = now_gtod; 212 __update_sched_clock(scd, now); 213 __raw_spin_unlock(&scd->lock); 214} 215 216/* 217 * We are going deep-idle (irqs are disabled): 218 */ 219void sched_clock_idle_sleep_event(void) 220{ 221 sched_clock_cpu(smp_processor_id()); 222} 223EXPORT_SYMBOL_GPL(sched_clock_idle_sleep_event); 224 225/* 226 * We just idled delta nanoseconds (called with irqs disabled): 227 */ 228void sched_clock_idle_wakeup_event(u64 delta_ns) 229{ 230 if (timekeeping_suspended) 231 return; 232 233 sched_clock_tick(); 234 touch_softlockup_watchdog(); 235} 236EXPORT_SYMBOL_GPL(sched_clock_idle_wakeup_event); 237 238#else /* CONFIG_HAVE_UNSTABLE_SCHED_CLOCK */ 239 240void sched_clock_init(void) 241{ 242 sched_clock_running = 1; 243} 244 245u64 sched_clock_cpu(int cpu) 246{ 247 if (unlikely(!sched_clock_running)) 248 return 0; 249 250 return sched_clock(); 251} 252 253#endif 254 255unsigned long long cpu_clock(int cpu) 256{ 257 unsigned long long clock; 258 unsigned long flags; 259 260 local_irq_save(flags); 261 clock = sched_clock_cpu(cpu); 262 local_irq_restore(flags); 263 264 return clock; 265} 266EXPORT_SYMBOL_GPL(cpu_clock);