Blame view

kernel/sched_clock.c 7.72 KB
3e51f33fc   Peter Zijlstra   sched: add option...
1
2
3
4
5
  /*
   * sched_clock for unstable cpu clocks
   *
   *  Copyright (C) 2008 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
   *
c300ba252   Steven Rostedt   sched_clock: and ...
6
7
8
   *  Updates and enhancements:
   *    Copyright (C) 2008 Red Hat, Inc. Steven Rostedt <srostedt@redhat.com>
   *
3e51f33fc   Peter Zijlstra   sched: add option...
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
   * Based on code by:
   *   Ingo Molnar <mingo@redhat.com>
   *   Guillaume Chazarain <guichaz@gmail.com>
   *
   * Create a semi stable clock from a mixture of other events, including:
   *  - gtod
   *  - jiffies
   *  - sched_clock()
   *  - explicit idle events
   *
   * We use gtod as base and the unstable clock deltas. The deltas are filtered,
   * making it monotonic and keeping it within an expected window.  This window
   * is set up using jiffies.
   *
   * Furthermore, explicit sleep and wakeup hooks allow us to account for time
   * that is otherwise invisible (TSC gets stopped).
   *
   * The clock: sched_clock_cpu() is monotonic per cpu, and should be somewhat
   * consistent between cpus (never more than 1 jiffies difference).
   */
  #include <linux/sched.h>
  #include <linux/percpu.h>
  #include <linux/spinlock.h>
  #include <linux/ktime.h>
  #include <linux/module.h>
  
  
  #ifdef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK
c300ba252   Steven Rostedt   sched_clock: and ...
37
38
39
40
  #define MULTI_SHIFT 15
  /* Max is double, Min is 1/2 */
  #define MAX_MULTI (2LL << MULTI_SHIFT)
  #define MIN_MULTI (1LL << (MULTI_SHIFT-1))
3e51f33fc   Peter Zijlstra   sched: add option...
41
42
43
44
45
46
47
  struct sched_clock_data {
  	/*
  	 * Raw spinlock - this is a special case: this might be called
  	 * from within instrumentation code so we dont want to do any
  	 * instrumentation ourselves.
  	 */
  	raw_spinlock_t		lock;
62c43dd98   Steven Rostedt   sched_clock: reco...
48
  	unsigned long		tick_jiffies;
3e51f33fc   Peter Zijlstra   sched: add option...
49
50
51
52
  	u64			prev_raw;
  	u64			tick_raw;
  	u64			tick_gtod;
  	u64			clock;
c300ba252   Steven Rostedt   sched_clock: and ...
53
  	s64			multi;
af52a90a1   Steven Rostedt   sched_clock: stop...
54
55
56
  #ifdef CONFIG_NO_HZ
  	int			check_max;
  #endif
3e51f33fc   Peter Zijlstra   sched: add option...
57
58
59
60
61
62
63
64
65
66
67
68
69
  };
  
  static DEFINE_PER_CPU_SHARED_ALIGNED(struct sched_clock_data, sched_clock_data);
  
  static inline struct sched_clock_data *this_scd(void)
  {
  	return &__get_cpu_var(sched_clock_data);
  }
  
  static inline struct sched_clock_data *cpu_sdc(int cpu)
  {
  	return &per_cpu(sched_clock_data, cpu);
  }
a381759d6   Peter Zijlstra   sched: fix sched_...
70
  static __read_mostly int sched_clock_running;
3e51f33fc   Peter Zijlstra   sched: add option...
71
72
73
  void sched_clock_init(void)
  {
  	u64 ktime_now = ktime_to_ns(ktime_get());
a381759d6   Peter Zijlstra   sched: fix sched_...
74
  	unsigned long now_jiffies = jiffies;
3e51f33fc   Peter Zijlstra   sched: add option...
75
76
77
78
79
80
  	int cpu;
  
  	for_each_possible_cpu(cpu) {
  		struct sched_clock_data *scd = cpu_sdc(cpu);
  
  		scd->lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
62c43dd98   Steven Rostedt   sched_clock: reco...
81
  		scd->tick_jiffies = now_jiffies;
a381759d6   Peter Zijlstra   sched: fix sched_...
82
83
  		scd->prev_raw = 0;
  		scd->tick_raw = 0;
3e51f33fc   Peter Zijlstra   sched: add option...
84
85
  		scd->tick_gtod = ktime_now;
  		scd->clock = ktime_now;
c300ba252   Steven Rostedt   sched_clock: and ...
86
  		scd->multi = 1 << MULTI_SHIFT;
af52a90a1   Steven Rostedt   sched_clock: stop...
87
88
89
  #ifdef CONFIG_NO_HZ
  		scd->check_max = 1;
  #endif
3e51f33fc   Peter Zijlstra   sched: add option...
90
  	}
a381759d6   Peter Zijlstra   sched: fix sched_...
91
92
  
  	sched_clock_running = 1;
3e51f33fc   Peter Zijlstra   sched: add option...
93
  }
af52a90a1   Steven Rostedt   sched_clock: stop...
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
  #ifdef CONFIG_NO_HZ
  /*
   * The dynamic ticks makes the delta jiffies inaccurate. This
   * prevents us from checking the maximum time update.
   * Disable the maximum check during stopped ticks.
   */
  void sched_clock_tick_stop(int cpu)
  {
  	struct sched_clock_data *scd = cpu_sdc(cpu);
  
  	scd->check_max = 0;
  }
  
  void sched_clock_tick_start(int cpu)
  {
  	struct sched_clock_data *scd = cpu_sdc(cpu);
  
  	scd->check_max = 1;
  }
  
  static int check_max(struct sched_clock_data *scd)
  {
  	return scd->check_max;
  }
  #else
  static int check_max(struct sched_clock_data *scd)
  {
  	return 1;
  }
  #endif /* CONFIG_NO_HZ */
3e51f33fc   Peter Zijlstra   sched: add option...
124
125
126
127
128
129
  /*
   * update the percpu scd from the raw @now value
   *
   *  - filter out backward motion
   *  - use jiffies to generate a min,max window to clip the raw values
   */
c0c87734f   Steven Rostedt   sched_clock: only...
130
  static void __update_sched_clock(struct sched_clock_data *scd, u64 now, u64 *time)
3e51f33fc   Peter Zijlstra   sched: add option...
131
132
  {
  	unsigned long now_jiffies = jiffies;
62c43dd98   Steven Rostedt   sched_clock: reco...
133
  	long delta_jiffies = now_jiffies - scd->tick_jiffies;
3e51f33fc   Peter Zijlstra   sched: add option...
134
135
136
137
138
  	u64 clock = scd->clock;
  	u64 min_clock, max_clock;
  	s64 delta = now - scd->prev_raw;
  
  	WARN_ON_ONCE(!irqs_disabled());
f7cce27f5   Steven Rostedt   sched_clock: wide...
139

c300ba252   Steven Rostedt   sched_clock: and ...
140
141
142
143
144
145
146
  	/*
  	 * At schedule tick the clock can be just under the gtod. We don't
  	 * want to push it too prematurely.
  	 */
  	min_clock = scd->tick_gtod + (delta_jiffies * TICK_NSEC);
  	if (min_clock > TICK_NSEC)
  		min_clock -= TICK_NSEC / 2;
3e51f33fc   Peter Zijlstra   sched: add option...
147
148
149
150
151
  
  	if (unlikely(delta < 0)) {
  		clock++;
  		goto out;
  	}
f7cce27f5   Steven Rostedt   sched_clock: wide...
152
153
154
155
156
157
  	/*
  	 * The clock must stay within a jiffie of the gtod.
  	 * But since we may be at the start of a jiffy or the end of one
  	 * we add another jiffy buffer.
  	 */
  	max_clock = scd->tick_gtod + (2 + delta_jiffies) * TICK_NSEC;
3e51f33fc   Peter Zijlstra   sched: add option...
158

c300ba252   Steven Rostedt   sched_clock: and ...
159
160
  	delta *= scd->multi;
  	delta >>= MULTI_SHIFT;
3e51f33fc   Peter Zijlstra   sched: add option...
161

af52a90a1   Steven Rostedt   sched_clock: stop...
162
  	if (unlikely(clock + delta > max_clock) && check_max(scd)) {
3e51f33fc   Peter Zijlstra   sched: add option...
163
164
165
166
167
168
169
170
171
172
173
  		if (clock < max_clock)
  			clock = max_clock;
  		else
  			clock++;
  	} else {
  		clock += delta;
  	}
  
   out:
  	if (unlikely(clock < min_clock))
  		clock = min_clock;
c0c87734f   Steven Rostedt   sched_clock: only...
174
175
176
177
178
179
  	if (time)
  		*time = clock;
  	else {
  		scd->prev_raw = now;
  		scd->clock = clock;
  	}
3e51f33fc   Peter Zijlstra   sched: add option...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
  }
  
  static void lock_double_clock(struct sched_clock_data *data1,
  				struct sched_clock_data *data2)
  {
  	if (data1 < data2) {
  		__raw_spin_lock(&data1->lock);
  		__raw_spin_lock(&data2->lock);
  	} else {
  		__raw_spin_lock(&data2->lock);
  		__raw_spin_lock(&data1->lock);
  	}
  }
  
  u64 sched_clock_cpu(int cpu)
  {
  	struct sched_clock_data *scd = cpu_sdc(cpu);
  	u64 now, clock;
a381759d6   Peter Zijlstra   sched: fix sched_...
198
199
  	if (unlikely(!sched_clock_running))
  		return 0ull;
3e51f33fc   Peter Zijlstra   sched: add option...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  	WARN_ON_ONCE(!irqs_disabled());
  	now = sched_clock();
  
  	if (cpu != raw_smp_processor_id()) {
  		/*
  		 * in order to update a remote cpu's clock based on our
  		 * unstable raw time rebase it against:
  		 *   tick_raw		(offset between raw counters)
  		 *   tick_gotd          (tick offset between cpus)
  		 */
  		struct sched_clock_data *my_scd = this_scd();
  
  		lock_double_clock(scd, my_scd);
  
  		now -= my_scd->tick_raw;
  		now += scd->tick_raw;
2b8a0cf48   Steven Rostedt   sched_clock: fix ...
216
217
  		now += my_scd->tick_gtod;
  		now -= scd->tick_gtod;
3e51f33fc   Peter Zijlstra   sched: add option...
218
219
  
  		__raw_spin_unlock(&my_scd->lock);
c0c87734f   Steven Rostedt   sched_clock: only...
220
221
222
223
  
  		__update_sched_clock(scd, now, &clock);
  
  		__raw_spin_unlock(&scd->lock);
3e51f33fc   Peter Zijlstra   sched: add option...
224
225
  	} else {
  		__raw_spin_lock(&scd->lock);
c0c87734f   Steven Rostedt   sched_clock: only...
226
227
228
  		__update_sched_clock(scd, now, NULL);
  		clock = scd->clock;
  		__raw_spin_unlock(&scd->lock);
3e51f33fc   Peter Zijlstra   sched: add option...
229
  	}
3e51f33fc   Peter Zijlstra   sched: add option...
230
231
232
233
234
235
  	return clock;
  }
  
  void sched_clock_tick(void)
  {
  	struct sched_clock_data *scd = this_scd();
62c43dd98   Steven Rostedt   sched_clock: reco...
236
  	unsigned long now_jiffies = jiffies;
c300ba252   Steven Rostedt   sched_clock: and ...
237
  	s64 mult, delta_gtod, delta_raw;
3e51f33fc   Peter Zijlstra   sched: add option...
238
  	u64 now, now_gtod;
a381759d6   Peter Zijlstra   sched: fix sched_...
239
240
  	if (unlikely(!sched_clock_running))
  		return;
3e51f33fc   Peter Zijlstra   sched: add option...
241
  	WARN_ON_ONCE(!irqs_disabled());
3e51f33fc   Peter Zijlstra   sched: add option...
242
  	now_gtod = ktime_to_ns(ktime_get());
a83bc47c3   Steven Rostedt   sched_clock: reco...
243
  	now = sched_clock();
3e51f33fc   Peter Zijlstra   sched: add option...
244
245
  
  	__raw_spin_lock(&scd->lock);
c0c87734f   Steven Rostedt   sched_clock: only...
246
  	__update_sched_clock(scd, now, NULL);
3e51f33fc   Peter Zijlstra   sched: add option...
247
248
249
250
251
  	/*
  	 * update tick_gtod after __update_sched_clock() because that will
  	 * already observe 1 new jiffy; adding a new tick_gtod to that would
  	 * increase the clock 2 jiffies.
  	 */
c300ba252   Steven Rostedt   sched_clock: and ...
252
253
254
255
256
257
258
259
260
261
262
263
264
  	delta_gtod = now_gtod - scd->tick_gtod;
  	delta_raw = now - scd->tick_raw;
  
  	if ((long)delta_raw > 0) {
  		mult = delta_gtod << MULTI_SHIFT;
  		do_div(mult, delta_raw);
  		scd->multi = mult;
  		if (scd->multi > MAX_MULTI)
  			scd->multi = MAX_MULTI;
  		else if (scd->multi < MIN_MULTI)
  			scd->multi = MIN_MULTI;
  	} else
  		scd->multi = 1 << MULTI_SHIFT;
3e51f33fc   Peter Zijlstra   sched: add option...
265
266
  	scd->tick_raw = now;
  	scd->tick_gtod = now_gtod;
c300ba252   Steven Rostedt   sched_clock: and ...
267
  	scd->tick_jiffies = now_jiffies;
3e51f33fc   Peter Zijlstra   sched: add option...
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
  	__raw_spin_unlock(&scd->lock);
  }
  
  /*
   * We are going deep-idle (irqs are disabled):
   */
  void sched_clock_idle_sleep_event(void)
  {
  	sched_clock_cpu(smp_processor_id());
  }
  EXPORT_SYMBOL_GPL(sched_clock_idle_sleep_event);
  
  /*
   * We just idled delta nanoseconds (called with irqs disabled):
   */
  void sched_clock_idle_wakeup_event(u64 delta_ns)
  {
  	struct sched_clock_data *scd = this_scd();
  	u64 now = sched_clock();
  
  	/*
  	 * Override the previous timestamp and ignore all
  	 * sched_clock() deltas that occured while we idled,
  	 * and use the PM-provided delta_ns to advance the
  	 * rq clock:
  	 */
  	__raw_spin_lock(&scd->lock);
  	scd->prev_raw = now;
  	scd->clock += delta_ns;
c300ba252   Steven Rostedt   sched_clock: and ...
297
  	scd->multi = 1 << MULTI_SHIFT;
3e51f33fc   Peter Zijlstra   sched: add option...
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
  	__raw_spin_unlock(&scd->lock);
  
  	touch_softlockup_watchdog();
  }
  EXPORT_SYMBOL_GPL(sched_clock_idle_wakeup_event);
  
  #endif
  
  /*
   * Scheduler clock - returns current time in nanosec units.
   * This is default implementation.
   * Architectures and sub-architectures can override this.
   */
  unsigned long long __attribute__((weak)) sched_clock(void)
  {
  	return (unsigned long long)jiffies * (NSEC_PER_SEC / HZ);
  }
76a2a6ee8   Peter Zijlstra   sched: sched_cloc...
315
316
317
318
319
  
  unsigned long long cpu_clock(int cpu)
  {
  	unsigned long long clock;
  	unsigned long flags;
2d452c9b1   Ingo Molnar   sched: sched_cloc...
320
  	local_irq_save(flags);
76a2a6ee8   Peter Zijlstra   sched: sched_cloc...
321
  	clock = sched_clock_cpu(cpu);
2d452c9b1   Ingo Molnar   sched: sched_cloc...
322
  	local_irq_restore(flags);
76a2a6ee8   Peter Zijlstra   sched: sched_cloc...
323
324
325
  
  	return clock;
  }
4c9fe8ad8   Ingo Molnar   sched: export cpu...
326
  EXPORT_SYMBOL_GPL(cpu_clock);