Blame view

kernel/profile.c 15.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   *  linux/kernel/profile.c
   *  Simple profiling. Manages a direct-mapped profile hit count buffer,
   *  with configurable resolution, support for restricting the cpus on
   *  which profiling is done, and switching between cpu time and
   *  schedule() calls via kernel command line parameters passed at boot.
   *
   *  Scheduler profiling support, Arjan van de Ven and Ingo Molnar,
   *	Red Hat, July 2004
   *  Consolidation of architecture support code for profiling,
   *	William Irwin, Oracle, July 2004
   *  Amortized hit count accounting via per-cpu open-addressed hashtables
   *	to resolve timer interrupt livelocks, William Irwin, Oracle, 2004
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
19
20
21
  #include <linux/module.h>
  #include <linux/profile.h>
  #include <linux/bootmem.h>
  #include <linux/notifier.h>
  #include <linux/mm.h>
  #include <linux/cpumask.h>
  #include <linux/cpu.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  #include <linux/highmem.h>
97d1f15b7   Arjan van de Ven   [PATCH] sem2mutex...
23
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
  #include <asm/sections.h>
7d12e780e   David Howells   IRQ: Maintain reg...
25
  #include <asm/irq_regs.h>
e8edc6e03   Alexey Dobriyan   Detach sched.h fr...
26
  #include <asm/ptrace.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
31
32
33
34
35
36
  
  struct profile_hit {
  	u32 pc, hits;
  };
  #define PROFILE_GRPSHIFT	3
  #define PROFILE_GRPSZ		(1 << PROFILE_GRPSHIFT)
  #define NR_PROFILE_HIT		(PAGE_SIZE/sizeof(struct profile_hit))
  #define NR_PROFILE_GRP		(NR_PROFILE_HIT/PROFILE_GRPSZ)
  
  /* Oprofile timer tick hook */
b012d346c   Adrian Bunk   make kernel/profi...
37
  static int (*timer_hook)(struct pt_regs *) __read_mostly;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
40
  
  static atomic_t *prof_buffer;
  static unsigned long prof_len, prof_shift;
07031e14c   Ingo Molnar   [PATCH] KVM: add ...
41

ece8a684c   Ingo Molnar   [PATCH] sleep pro...
42
  int prof_on __read_mostly;
07031e14c   Ingo Molnar   [PATCH] KVM: add ...
43
  EXPORT_SYMBOL_GPL(prof_on);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
46
47
  static cpumask_t prof_cpu_mask = CPU_MASK_ALL;
  #ifdef CONFIG_SMP
  static DEFINE_PER_CPU(struct profile_hit *[2], cpu_profile_hits);
  static DEFINE_PER_CPU(int, cpu_profile_flip);
97d1f15b7   Arjan van de Ven   [PATCH] sem2mutex...
48
  static DEFINE_MUTEX(profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
  #endif /* CONFIG_SMP */
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
50
  static int __init profile_setup(char *str)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
  {
dfaa9c94b   William Lee Irwin III   [PATCH] profile.c...
52
  	static char __initdata schedstr[] = "schedule";
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
53
  	static char __initdata sleepstr[] = "sleep";
07031e14c   Ingo Molnar   [PATCH] KVM: add ...
54
  	static char __initdata kvmstr[] = "kvm";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
  	int par;
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
56
  	if (!strncmp(str, sleepstr, strlen(sleepstr))) {
b3da2a73f   Mel Gorman   sched: document p...
57
  #ifdef CONFIG_SCHEDSTATS
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
58
59
60
61
62
63
64
65
66
  		prof_on = SLEEP_PROFILING;
  		if (str[strlen(sleepstr)] == ',')
  			str += strlen(sleepstr) + 1;
  		if (get_option(&str, &par))
  			prof_shift = par;
  		printk(KERN_INFO
  			"kernel sleep profiling enabled (shift: %ld)
  ",
  			prof_shift);
b3da2a73f   Mel Gorman   sched: document p...
67
68
69
70
71
  #else
  		printk(KERN_WARNING
  			"kernel sleep profiling requires CONFIG_SCHEDSTATS
  ");
  #endif /* CONFIG_SCHEDSTATS */
a75acf850   Ingo Molnar   [PATCH] profiling...
72
  	} else if (!strncmp(str, schedstr, strlen(schedstr))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
  		prof_on = SCHED_PROFILING;
dfaa9c94b   William Lee Irwin III   [PATCH] profile.c...
74
75
76
77
78
79
80
81
  		if (str[strlen(schedstr)] == ',')
  			str += strlen(schedstr) + 1;
  		if (get_option(&str, &par))
  			prof_shift = par;
  		printk(KERN_INFO
  			"kernel schedule profiling enabled (shift: %ld)
  ",
  			prof_shift);
07031e14c   Ingo Molnar   [PATCH] KVM: add ...
82
83
84
85
86
87
88
89
90
91
  	} else if (!strncmp(str, kvmstr, strlen(kvmstr))) {
  		prof_on = KVM_PROFILING;
  		if (str[strlen(kvmstr)] == ',')
  			str += strlen(kvmstr) + 1;
  		if (get_option(&str, &par))
  			prof_shift = par;
  		printk(KERN_INFO
  			"kernel KVM profiling enabled (shift: %ld)
  ",
  			prof_shift);
dfaa9c94b   William Lee Irwin III   [PATCH] profile.c...
92
  	} else if (get_option(&str, &par)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
95
96
97
98
99
100
101
102
103
104
105
  		prof_shift = par;
  		prof_on = CPU_PROFILING;
  		printk(KERN_INFO "kernel profiling enabled (shift: %ld)
  ",
  			prof_shift);
  	}
  	return 1;
  }
  __setup("profile=", profile_setup);
  
  
  void __init profile_init(void)
  {
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
106
  	if (!prof_on)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
  		return;
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
108

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
113
114
  	/* only text is profiled */
  	prof_len = (_etext - _stext) >> prof_shift;
  	prof_buffer = alloc_bootmem(prof_len*sizeof(atomic_t));
  }
  
  /* Profile event notifications */
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
115

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
  #ifdef CONFIG_PROFILING
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
117

e041c6834   Alan Stern   [PATCH] Notifier ...
118
119
120
  static BLOCKING_NOTIFIER_HEAD(task_exit_notifier);
  static ATOMIC_NOTIFIER_HEAD(task_free_notifier);
  static BLOCKING_NOTIFIER_HEAD(munmap_notifier);
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
121
122
  
  void profile_task_exit(struct task_struct *task)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  {
e041c6834   Alan Stern   [PATCH] Notifier ...
124
  	blocking_notifier_call_chain(&task_exit_notifier, 0, task);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
126
127
  
  int profile_handoff_task(struct task_struct *task)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
  {
  	int ret;
e041c6834   Alan Stern   [PATCH] Notifier ...
130
  	ret = atomic_notifier_call_chain(&task_free_notifier, 0, task);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
132
133
134
135
  	return (ret == NOTIFY_OK) ? 1 : 0;
  }
  
  void profile_munmap(unsigned long addr)
  {
e041c6834   Alan Stern   [PATCH] Notifier ...
136
  	blocking_notifier_call_chain(&munmap_notifier, 0, (void *)addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
138
  int task_handoff_register(struct notifier_block *n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
  {
e041c6834   Alan Stern   [PATCH] Notifier ...
140
  	return atomic_notifier_chain_register(&task_free_notifier, n);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
142
  EXPORT_SYMBOL_GPL(task_handoff_register);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143

1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
144
  int task_handoff_unregister(struct notifier_block *n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
  {
e041c6834   Alan Stern   [PATCH] Notifier ...
146
  	return atomic_notifier_chain_unregister(&task_free_notifier, n);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
148
  EXPORT_SYMBOL_GPL(task_handoff_unregister);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149

1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
150
  int profile_event_register(enum profile_type type, struct notifier_block *n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
  {
  	int err = -EINVAL;
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
153

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  	switch (type) {
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
155
156
157
158
159
160
161
162
  	case PROFILE_TASK_EXIT:
  		err = blocking_notifier_chain_register(
  				&task_exit_notifier, n);
  		break;
  	case PROFILE_MUNMAP:
  		err = blocking_notifier_chain_register(
  				&munmap_notifier, n);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
  	}
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
164

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
  	return err;
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
167
  EXPORT_SYMBOL_GPL(profile_event_register);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168

1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
169
  int profile_event_unregister(enum profile_type type, struct notifier_block *n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
171
  {
  	int err = -EINVAL;
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
172

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
  	switch (type) {
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
174
175
176
177
178
179
180
181
  	case PROFILE_TASK_EXIT:
  		err = blocking_notifier_chain_unregister(
  				&task_exit_notifier, n);
  		break;
  	case PROFILE_MUNMAP:
  		err = blocking_notifier_chain_unregister(
  				&munmap_notifier, n);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
  	return err;
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
185
  EXPORT_SYMBOL_GPL(profile_event_unregister);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
188
189
190
191
192
193
  
  int register_timer_hook(int (*hook)(struct pt_regs *))
  {
  	if (timer_hook)
  		return -EBUSY;
  	timer_hook = hook;
  	return 0;
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
194
  EXPORT_SYMBOL_GPL(register_timer_hook);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
197
198
199
200
  
  void unregister_timer_hook(int (*hook)(struct pt_regs *))
  {
  	WARN_ON(hook != timer_hook);
  	timer_hook = NULL;
  	/* make sure all CPUs see the NULL hook */
fbd568a3e   Paul E. McKenney   [PATCH] Change sy...
201
  	synchronize_sched();  /* Allow ongoing interrupts to complete. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  EXPORT_SYMBOL_GPL(unregister_timer_hook);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
205
  
  #endif /* CONFIG_PROFILING */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
  
  #ifdef CONFIG_SMP
  /*
   * Each cpu has a pair of open-addressed hashtables for pending
   * profile hits. read_profile() IPI's all cpus to request them
   * to flip buffers and flushes their contents to prof_buffer itself.
   * Flip requests are serialized by the profile_flip_mutex. The sole
   * use of having a second hashtable is for avoiding cacheline
   * contention that would otherwise happen during flushes of pending
   * profile hits required for the accuracy of reported profile hits
   * and so resurrect the interrupt livelock issue.
   *
   * The open-addressed hashtables are indexed by profile buffer slot
   * and hold the number of pending hits to that profile buffer slot on
   * a cpu in an entry. When the hashtable overflows, all pending hits
   * are accounted to their corresponding profile buffer slots with
   * atomic_add() and the hashtable emptied. As numerous pending hits
   * may be accounted to a profile buffer slot in a hashtable entry,
   * this amortizes a number of atomic profile buffer increments likely
   * to be far larger than the number of entries in the hashtable,
   * particularly given that the number of distinct profile buffer
   * positions to which hits are accounted during short intervals (e.g.
   * several seconds) is usually very small. Exclusion from buffer
   * flipping is provided by interrupt disablement (note that for
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
230
231
   * SCHED_PROFILING or SLEEP_PROFILING profile_hit() may be called from
   * process context).
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
   * The hash function is meant to be lightweight as opposed to strong,
   * and was vaguely inspired by ppc64 firmware-supported inverted
   * pagetable hash functions, but uses a full hashtable full of finite
   * collision chains, not just pairs of them.
   *
   * -- wli
   */
  static void __profile_flip_buffers(void *unused)
  {
  	int cpu = smp_processor_id();
  
  	per_cpu(cpu_profile_flip, cpu) = !per_cpu(cpu_profile_flip, cpu);
  }
  
  static void profile_flip_buffers(void)
  {
  	int i, j, cpu;
97d1f15b7   Arjan van de Ven   [PATCH] sem2mutex...
249
  	mutex_lock(&profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
  	j = per_cpu(cpu_profile_flip, get_cpu());
  	put_cpu();
  	on_each_cpu(__profile_flip_buffers, NULL, 0, 1);
  	for_each_online_cpu(cpu) {
  		struct profile_hit *hits = per_cpu(cpu_profile_hits, cpu)[j];
  		for (i = 0; i < NR_PROFILE_HIT; ++i) {
  			if (!hits[i].hits) {
  				if (hits[i].pc)
  					hits[i].pc = 0;
  				continue;
  			}
  			atomic_add(hits[i].hits, &prof_buffer[hits[i].pc]);
  			hits[i].hits = hits[i].pc = 0;
  		}
  	}
97d1f15b7   Arjan van de Ven   [PATCH] sem2mutex...
265
  	mutex_unlock(&profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
267
268
269
270
  }
  
  static void profile_discard_flip_buffers(void)
  {
  	int i, cpu;
97d1f15b7   Arjan van de Ven   [PATCH] sem2mutex...
271
  	mutex_lock(&profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
274
275
276
277
278
  	i = per_cpu(cpu_profile_flip, get_cpu());
  	put_cpu();
  	on_each_cpu(__profile_flip_buffers, NULL, 0, 1);
  	for_each_online_cpu(cpu) {
  		struct profile_hit *hits = per_cpu(cpu_profile_hits, cpu)[i];
  		memset(hits, 0, NR_PROFILE_HIT*sizeof(struct profile_hit));
  	}
97d1f15b7   Arjan van de Ven   [PATCH] sem2mutex...
279
  	mutex_unlock(&profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  }
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
281
  void profile_hits(int type, void *__pc, unsigned int nr_hits)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  {
  	unsigned long primary, secondary, flags, pc = (unsigned long)__pc;
  	int i, j, cpu;
  	struct profile_hit *hits;
  
  	if (prof_on != type || !prof_buffer)
  		return;
  	pc = min((pc - (unsigned long)_stext) >> prof_shift, prof_len - 1);
  	i = primary = (pc & (NR_PROFILE_GRP - 1)) << PROFILE_GRPSHIFT;
  	secondary = (~(pc << 1) & (NR_PROFILE_GRP - 1)) << PROFILE_GRPSHIFT;
  	cpu = get_cpu();
  	hits = per_cpu(cpu_profile_hits, cpu)[per_cpu(cpu_profile_flip, cpu)];
  	if (!hits) {
  		put_cpu();
  		return;
  	}
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
298
299
300
301
302
  	/*
  	 * We buffer the global profiler buffer into a per-CPU
  	 * queue and thus reduce the number of global (and possibly
  	 * NUMA-alien) accesses. The write-queue is self-coalescing:
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
306
  	local_irq_save(flags);
  	do {
  		for (j = 0; j < PROFILE_GRPSZ; ++j) {
  			if (hits[i + j].pc == pc) {
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
307
  				hits[i + j].hits += nr_hits;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
  				goto out;
  			} else if (!hits[i + j].hits) {
  				hits[i + j].pc = pc;
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
311
  				hits[i + j].hits = nr_hits;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
314
315
316
  				goto out;
  			}
  		}
  		i = (i + secondary) & (NR_PROFILE_HIT - 1);
  	} while (i != primary);
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
317
318
319
320
321
322
  
  	/*
  	 * Add the current hit(s) and flush the write-queue out
  	 * to the global buffer:
  	 */
  	atomic_add(nr_hits, &prof_buffer[pc]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
325
326
327
328
329
330
  	for (i = 0; i < NR_PROFILE_HIT; ++i) {
  		atomic_add(hits[i].hits, &prof_buffer[hits[i].pc]);
  		hits[i].pc = hits[i].hits = 0;
  	}
  out:
  	local_irq_restore(flags);
  	put_cpu();
  }
9c7b216d2   Chandra Seetharaman   [PATCH] cpu hotpl...
331
  static int __devinit profile_cpu_callback(struct notifier_block *info,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
333
334
335
336
337
338
  					unsigned long action, void *__cpu)
  {
  	int node, cpu = (unsigned long)__cpu;
  	struct page *page;
  
  	switch (action) {
  	case CPU_UP_PREPARE:
8bb784428   Rafael J. Wysocki   Add suspend-relat...
339
  	case CPU_UP_PREPARE_FROZEN:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
342
  		node = cpu_to_node(cpu);
  		per_cpu(cpu_profile_flip, cpu) = 0;
  		if (!per_cpu(cpu_profile_hits, cpu)[1]) {
fbd98167e   Christoph Lameter   [PATCH] Profiling...
343
  			page = alloc_pages_node(node,
4199cfa02   Christoph Lameter   Memoryless nodes:...
344
  					GFP_KERNEL | __GFP_ZERO,
fbd98167e   Christoph Lameter   [PATCH] Profiling...
345
  					0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
347
348
349
350
  			if (!page)
  				return NOTIFY_BAD;
  			per_cpu(cpu_profile_hits, cpu)[1] = page_address(page);
  		}
  		if (!per_cpu(cpu_profile_hits, cpu)[0]) {
fbd98167e   Christoph Lameter   [PATCH] Profiling...
351
  			page = alloc_pages_node(node,
4199cfa02   Christoph Lameter   Memoryless nodes:...
352
  					GFP_KERNEL | __GFP_ZERO,
fbd98167e   Christoph Lameter   [PATCH] Profiling...
353
  					0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
356
357
358
  			if (!page)
  				goto out_free;
  			per_cpu(cpu_profile_hits, cpu)[0] = page_address(page);
  		}
  		break;
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
359
  out_free:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
362
363
364
  		page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]);
  		per_cpu(cpu_profile_hits, cpu)[1] = NULL;
  		__free_page(page);
  		return NOTIFY_BAD;
  	case CPU_ONLINE:
8bb784428   Rafael J. Wysocki   Add suspend-relat...
365
  	case CPU_ONLINE_FROZEN:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
367
368
  		cpu_set(cpu, prof_cpu_mask);
  		break;
  	case CPU_UP_CANCELED:
8bb784428   Rafael J. Wysocki   Add suspend-relat...
369
  	case CPU_UP_CANCELED_FROZEN:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
  	case CPU_DEAD:
8bb784428   Rafael J. Wysocki   Add suspend-relat...
371
  	case CPU_DEAD_FROZEN:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
  		cpu_clear(cpu, prof_cpu_mask);
  		if (per_cpu(cpu_profile_hits, cpu)[0]) {
  			page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[0]);
  			per_cpu(cpu_profile_hits, cpu)[0] = NULL;
  			__free_page(page);
  		}
  		if (per_cpu(cpu_profile_hits, cpu)[1]) {
  			page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]);
  			per_cpu(cpu_profile_hits, cpu)[1] = NULL;
  			__free_page(page);
  		}
  		break;
  	}
  	return NOTIFY_OK;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
388
389
  #else /* !CONFIG_SMP */
  #define profile_flip_buffers()		do { } while (0)
  #define profile_discard_flip_buffers()	do { } while (0)
023160678   Ingo Molnar   [PATCH] hotplug C...
390
  #define profile_cpu_callback		NULL
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391

ece8a684c   Ingo Molnar   [PATCH] sleep pro...
392
  void profile_hits(int type, void *__pc, unsigned int nr_hits)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
394
395
396
397
398
  {
  	unsigned long pc;
  
  	if (prof_on != type || !prof_buffer)
  		return;
  	pc = ((unsigned long)__pc - (unsigned long)_stext) >> prof_shift;
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
399
  	atomic_add(nr_hits, &prof_buffer[min(pc, prof_len - 1)]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
  }
  #endif /* !CONFIG_SMP */
bbe1a59b3   Andrew Morton   [PATCH] fix "kvm:...
402
  EXPORT_SYMBOL_GPL(profile_hits);
7d12e780e   David Howells   IRQ: Maintain reg...
403
  void profile_tick(int type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
  {
7d12e780e   David Howells   IRQ: Maintain reg...
405
  	struct pt_regs *regs = get_irq_regs();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
408
409
410
411
412
413
414
415
  	if (type == CPU_PROFILING && timer_hook)
  		timer_hook(regs);
  	if (!user_mode(regs) && cpu_isset(smp_processor_id(), prof_cpu_mask))
  		profile_hit(type, (void *)profile_pc(regs));
  }
  
  #ifdef CONFIG_PROC_FS
  #include <linux/proc_fs.h>
  #include <asm/uaccess.h>
  #include <asm/ptrace.h>
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
416
  static int prof_cpu_mask_read_proc(char *page, char **start, off_t off,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
418
419
420
421
422
423
424
425
  			int count, int *eof, void *data)
  {
  	int len = cpumask_scnprintf(page, count, *(cpumask_t *)data);
  	if (count - len < 2)
  		return -EINVAL;
  	len += sprintf(page + len, "
  ");
  	return len;
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
426
427
  static int prof_cpu_mask_write_proc(struct file *file,
  	const char __user *buffer,  unsigned long count, void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
429
430
431
  {
  	cpumask_t *mask = (cpumask_t *)data;
  	unsigned long full_count = count, err;
  	cpumask_t new_value;
01a3ee2b2   Reinette Chatre   [PATCH] bitmap: p...
432
  	err = cpumask_parse_user(buffer, count, new_value);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
434
435
436
437
438
439
440
441
442
443
444
  	if (err)
  		return err;
  
  	*mask = new_value;
  	return full_count;
  }
  
  void create_prof_cpu_mask(struct proc_dir_entry *root_irq_dir)
  {
  	struct proc_dir_entry *entry;
  
  	/* create /proc/irq/prof_cpu_mask */
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
445
446
  	entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir);
  	if (!entry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
  	entry->data = (void *)&prof_cpu_mask;
  	entry->read_proc = prof_cpu_mask_read_proc;
  	entry->write_proc = prof_cpu_mask_write_proc;
  }
  
  /*
   * This function accesses profiling information. The returned data is
   * binary: the sampling step and the actual contents of the profile
   * buffer. Use of the program readprofile is recommended in order to
   * get meaningful info out of these data.
   */
  static ssize_t
  read_profile(struct file *file, char __user *buf, size_t count, loff_t *ppos)
  {
  	unsigned long p = *ppos;
  	ssize_t read;
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
464
  	char *pnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
466
467
468
469
470
471
472
473
474
  	unsigned int sample_step = 1 << prof_shift;
  
  	profile_flip_buffers();
  	if (p >= (prof_len+1)*sizeof(unsigned int))
  		return 0;
  	if (count > (prof_len+1)*sizeof(unsigned int) - p)
  		count = (prof_len+1)*sizeof(unsigned int) - p;
  	read = 0;
  
  	while (p < sizeof(unsigned int) && count > 0) {
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
475
  		if (put_user(*((char *)(&sample_step)+p), buf))
064b022c7   Heiko Carstens   [PATCH] profile: ...
476
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
478
479
  		buf++; p++; count--; read++;
  	}
  	pnt = (char *)prof_buffer + p - sizeof(atomic_t);
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
480
  	if (copy_to_user(buf, (void *)pnt, count))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
  		return -EFAULT;
  	read += count;
  	*ppos += read;
  	return read;
  }
  
  /*
   * Writing to /proc/profile resets the counters
   *
   * Writing a 'profiling multiplier' value into it also re-sets the profiling
   * interrupt frequency, on architectures that support this.
   */
  static ssize_t write_profile(struct file *file, const char __user *buf,
  			     size_t count, loff_t *ppos)
  {
  #ifdef CONFIG_SMP
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
497
  	extern int setup_profiling_timer(unsigned int multiplier);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
  
  	if (count == sizeof(int)) {
  		unsigned int multiplier;
  
  		if (copy_from_user(&multiplier, buf, sizeof(int)))
  			return -EFAULT;
  
  		if (setup_profiling_timer(multiplier))
  			return -EINVAL;
  	}
  #endif
  	profile_discard_flip_buffers();
  	memset(prof_buffer, 0, prof_len * sizeof(atomic_t));
  	return count;
  }
15ad7cdcf   Helge Deller   [PATCH] struct se...
513
  static const struct file_operations proc_profile_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
  	.read		= read_profile,
  	.write		= write_profile,
  };
  
  #ifdef CONFIG_SMP
  static void __init profile_nop(void *unused)
  {
  }
  
  static int __init create_hash_tables(void)
  {
  	int cpu;
  
  	for_each_online_cpu(cpu) {
  		int node = cpu_to_node(cpu);
  		struct page *page;
fbd98167e   Christoph Lameter   [PATCH] Profiling...
530
531
532
  		page = alloc_pages_node(node,
  				GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
  				0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
534
535
536
  		if (!page)
  			goto out_cleanup;
  		per_cpu(cpu_profile_hits, cpu)[1]
  				= (struct profile_hit *)page_address(page);
fbd98167e   Christoph Lameter   [PATCH] Profiling...
537
538
539
  		page = alloc_pages_node(node,
  				GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
  				0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
542
543
544
545
546
547
  		if (!page)
  			goto out_cleanup;
  		per_cpu(cpu_profile_hits, cpu)[0]
  				= (struct profile_hit *)page_address(page);
  	}
  	return 0;
  out_cleanup:
  	prof_on = 0;
d59dd4620   Andrew Morton   [PATCH] use smp_m...
548
  	smp_mb();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
  	on_each_cpu(profile_nop, NULL, 0, 1);
  	for_each_online_cpu(cpu) {
  		struct page *page;
  
  		if (per_cpu(cpu_profile_hits, cpu)[0]) {
  			page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[0]);
  			per_cpu(cpu_profile_hits, cpu)[0] = NULL;
  			__free_page(page);
  		}
  		if (per_cpu(cpu_profile_hits, cpu)[1]) {
  			page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]);
  			per_cpu(cpu_profile_hits, cpu)[1] = NULL;
  			__free_page(page);
  		}
  	}
  	return -1;
  }
  #else
  #define create_hash_tables()			({ 0; })
  #endif
  
  static int __init create_proc_profile(void)
  {
  	struct proc_dir_entry *entry;
  
  	if (!prof_on)
  		return 0;
  	if (create_hash_tables())
  		return -1;
c33fff0af   Denis V. Lunev   kernel: use non-r...
578
579
  	entry = proc_create("profile", S_IWUSR | S_IRUGO,
  			    NULL, &proc_profile_operations);
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
580
  	if (!entry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
583
584
585
586
587
  	entry->size = (1+prof_len) * sizeof(atomic_t);
  	hotcpu_notifier(profile_cpu_callback, 0);
  	return 0;
  }
  module_init(create_proc_profile);
  #endif /* CONFIG_PROC_FS */