Blame view

kernel/profile.c 16.7 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
   */
9984de1a5   Paul Gortmaker   kernel: Map most ...
15
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
19
20
21
  #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>
22b8ce947   Dave Hansen   profiling: dynami...
24
25
  #include <linux/slab.h>
  #include <linux/vmalloc.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
  #include <asm/sections.h>
7d12e780e   David Howells   IRQ: Maintain reg...
27
  #include <asm/irq_regs.h>
e8edc6e03   Alexey Dobriyan   Detach sched.h fr...
28
  #include <asm/ptrace.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
33
34
35
36
37
38
  
  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...
39
  static int (*timer_hook)(struct pt_regs *) __read_mostly;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
  
  static atomic_t *prof_buffer;
  static unsigned long prof_len, prof_shift;
07031e14c   Ingo Molnar   [PATCH] KVM: add ...
43

ece8a684c   Ingo Molnar   [PATCH] sleep pro...
44
  int prof_on __read_mostly;
07031e14c   Ingo Molnar   [PATCH] KVM: add ...
45
  EXPORT_SYMBOL_GPL(prof_on);
c309b917c   Rusty Russell   cpumask: convert ...
46
  static cpumask_var_t prof_cpu_mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
  #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...
50
  static DEFINE_MUTEX(profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
  #endif /* CONFIG_SMP */
22b8ce947   Dave Hansen   profiling: dynami...
52
  int profile_setup(char *str)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
  {
22b8ce947   Dave Hansen   profiling: dynami...
54
55
56
  	static char schedstr[] = "schedule";
  	static char sleepstr[] = "sleep";
  	static char kvmstr[] = "kvm";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
  	int par;
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
58
  	if (!strncmp(str, sleepstr, strlen(sleepstr))) {
b3da2a73f   Mel Gorman   sched: document p...
59
  #ifdef CONFIG_SCHEDSTATS
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
60
61
62
63
64
65
66
67
68
  		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...
69
70
71
72
73
  #else
  		printk(KERN_WARNING
  			"kernel sleep profiling requires CONFIG_SCHEDSTATS
  ");
  #endif /* CONFIG_SCHEDSTATS */
a75acf850   Ingo Molnar   [PATCH] profiling...
74
  	} else if (!strncmp(str, schedstr, strlen(schedstr))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  		prof_on = SCHED_PROFILING;
dfaa9c94b   William Lee Irwin III   [PATCH] profile.c...
76
77
78
79
80
81
82
83
  		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 ...
84
85
86
87
88
89
90
91
92
93
  	} 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...
94
  	} else if (get_option(&str, &par)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
97
98
99
100
101
102
103
  		prof_shift = par;
  		prof_on = CPU_PROFILING;
  		printk(KERN_INFO "kernel profiling enabled (shift: %ld)
  ",
  			prof_shift);
  	}
  	return 1;
  }
  __setup("profile=", profile_setup);
ce05fcc30   Paul Mundt   kernel/profile: f...
104
  int __ref profile_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
  {
22b8ce947   Dave Hansen   profiling: dynami...
106
  	int buffer_bytes;
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
107
  	if (!prof_on)
22b8ce947   Dave Hansen   profiling: dynami...
108
  		return 0;
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
109

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
  	/* only text is profiled */
  	prof_len = (_etext - _stext) >> prof_shift;
22b8ce947   Dave Hansen   profiling: dynami...
112
  	buffer_bytes = prof_len*sizeof(atomic_t);
22b8ce947   Dave Hansen   profiling: dynami...
113

c309b917c   Rusty Russell   cpumask: convert ...
114
115
  	if (!alloc_cpumask_var(&prof_cpu_mask, GFP_KERNEL))
  		return -ENOMEM;
acd895795   Hugh Dickins   profiling: fix br...
116
  	cpumask_copy(prof_cpu_mask, cpu_possible_mask);
b62f495da   Mel Gorman   profile: suppress...
117
  	prof_buffer = kzalloc(buffer_bytes, GFP_KERNEL|__GFP_NOWARN);
22b8ce947   Dave Hansen   profiling: dynami...
118
119
  	if (prof_buffer)
  		return 0;
b62f495da   Mel Gorman   profile: suppress...
120
121
  	prof_buffer = alloc_pages_exact(buffer_bytes,
  					GFP_KERNEL|__GFP_ZERO|__GFP_NOWARN);
22b8ce947   Dave Hansen   profiling: dynami...
122
123
  	if (prof_buffer)
  		return 0;
559fa6e76   Jesper Juhl   profile: Use vzal...
124
125
  	prof_buffer = vzalloc(buffer_bytes);
  	if (prof_buffer)
22b8ce947   Dave Hansen   profiling: dynami...
126
  		return 0;
c309b917c   Rusty Russell   cpumask: convert ...
127
  	free_cpumask_var(prof_cpu_mask);
22b8ce947   Dave Hansen   profiling: dynami...
128
  	return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
  }
  
  /* Profile event notifications */
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
132

e041c6834   Alan Stern   [PATCH] Notifier ...
133
134
135
  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...
136
137
  
  void profile_task_exit(struct task_struct *task)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  {
e041c6834   Alan Stern   [PATCH] Notifier ...
139
  	blocking_notifier_call_chain(&task_exit_notifier, 0, task);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
141
142
  
  int profile_handoff_task(struct task_struct *task)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
  {
  	int ret;
e041c6834   Alan Stern   [PATCH] Notifier ...
145
  	ret = atomic_notifier_call_chain(&task_free_notifier, 0, task);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
148
149
150
  	return (ret == NOTIFY_OK) ? 1 : 0;
  }
  
  void profile_munmap(unsigned long addr)
  {
e041c6834   Alan Stern   [PATCH] Notifier ...
151
  	blocking_notifier_call_chain(&munmap_notifier, 0, (void *)addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
153
  int task_handoff_register(struct notifier_block *n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  {
e041c6834   Alan Stern   [PATCH] Notifier ...
155
  	return atomic_notifier_chain_register(&task_free_notifier, n);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
157
  EXPORT_SYMBOL_GPL(task_handoff_register);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158

1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
159
  int task_handoff_unregister(struct notifier_block *n)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
  {
e041c6834   Alan Stern   [PATCH] Notifier ...
161
  	return atomic_notifier_chain_unregister(&task_free_notifier, n);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
163
  EXPORT_SYMBOL_GPL(task_handoff_unregister);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
  	switch (type) {
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
170
171
172
173
174
175
176
177
  	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
178
  	}
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
179

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
  	return err;
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
182
  EXPORT_SYMBOL_GPL(profile_event_register);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
  	switch (type) {
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
189
190
191
192
193
194
195
196
  	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
197
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
  	return err;
  }
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
200
  EXPORT_SYMBOL_GPL(profile_event_unregister);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
203
204
205
206
207
208
  
  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...
209
  EXPORT_SYMBOL_GPL(register_timer_hook);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
211
212
213
214
215
  
  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...
216
  	synchronize_sched();  /* Allow ongoing interrupts to complete. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
  EXPORT_SYMBOL_GPL(unregister_timer_hook);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  
  #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...
244
245
   * SCHED_PROFILING or SLEEP_PROFILING profile_hit() may be called from
   * process context).
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
   * 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...
263
  	mutex_lock(&profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
  	j = per_cpu(cpu_profile_flip, get_cpu());
  	put_cpu();
15c8b6c1a   Jens Axboe   on_each_cpu(): ki...
266
  	on_each_cpu(__profile_flip_buffers, NULL, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
268
269
270
271
272
273
274
275
276
277
278
  	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...
279
  	mutex_unlock(&profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
282
283
284
  }
  
  static void profile_discard_flip_buffers(void)
  {
  	int i, cpu;
97d1f15b7   Arjan van de Ven   [PATCH] sem2mutex...
285
  	mutex_lock(&profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
  	i = per_cpu(cpu_profile_flip, get_cpu());
  	put_cpu();
15c8b6c1a   Jens Axboe   on_each_cpu(): ki...
288
  	on_each_cpu(__profile_flip_buffers, NULL, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
291
292
  	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...
293
  	mutex_unlock(&profile_flip_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
  }
6f7bd76f0   Rakib Mullick   kernel/profile.c:...
295
  static void do_profile_hits(int type, void *__pc, unsigned int nr_hits)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
298
299
  {
  	unsigned long primary, secondary, flags, pc = (unsigned long)__pc;
  	int i, j, cpu;
  	struct profile_hit *hits;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
302
303
304
305
306
307
308
  	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...
309
310
311
312
313
  	/*
  	 * 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
314
315
316
317
  	local_irq_save(flags);
  	do {
  		for (j = 0; j < PROFILE_GRPSZ; ++j) {
  			if (hits[i + j].pc == pc) {
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
318
  				hits[i + j].hits += nr_hits;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
  				goto out;
  			} else if (!hits[i + j].hits) {
  				hits[i + j].pc = pc;
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
322
  				hits[i + j].hits = nr_hits;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
325
326
327
  				goto out;
  			}
  		}
  		i = (i + secondary) & (NR_PROFILE_HIT - 1);
  	} while (i != primary);
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
328
329
330
331
332
333
  
  	/*
  	 * 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
334
335
336
337
338
339
340
341
  	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();
  }
841964145   Al Viro   cpuinit fixes in ...
342
  static int __cpuinit profile_cpu_callback(struct notifier_block *info,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
346
347
348
349
  					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...
350
  	case CPU_UP_PREPARE_FROZEN:
3dd6b5fb4   Lee Schermerhorn   numa: in-kernel p...
351
  		node = cpu_to_mem(cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
353
  		per_cpu(cpu_profile_flip, cpu) = 0;
  		if (!per_cpu(cpu_profile_hits, cpu)[1]) {
6484eb3e2   Mel Gorman   page allocator: d...
354
  			page = alloc_pages_exact_node(node,
4199cfa02   Christoph Lameter   Memoryless nodes:...
355
  					GFP_KERNEL | __GFP_ZERO,
fbd98167e   Christoph Lameter   [PATCH] Profiling...
356
  					0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  			if (!page)
80b5184cc   Akinobu Mita   kernel/: convert ...
358
  				return notifier_from_errno(-ENOMEM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
361
  			per_cpu(cpu_profile_hits, cpu)[1] = page_address(page);
  		}
  		if (!per_cpu(cpu_profile_hits, cpu)[0]) {
6484eb3e2   Mel Gorman   page allocator: d...
362
  			page = alloc_pages_exact_node(node,
4199cfa02   Christoph Lameter   Memoryless nodes:...
363
  					GFP_KERNEL | __GFP_ZERO,
fbd98167e   Christoph Lameter   [PATCH] Profiling...
364
  					0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
367
368
369
  			if (!page)
  				goto out_free;
  			per_cpu(cpu_profile_hits, cpu)[0] = page_address(page);
  		}
  		break;
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
370
  out_free:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
372
373
  		page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]);
  		per_cpu(cpu_profile_hits, cpu)[1] = NULL;
  		__free_page(page);
80b5184cc   Akinobu Mita   kernel/: convert ...
374
  		return notifier_from_errno(-ENOMEM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
  	case CPU_ONLINE:
8bb784428   Rafael J. Wysocki   Add suspend-relat...
376
  	case CPU_ONLINE_FROZEN:
c309b917c   Rusty Russell   cpumask: convert ...
377
378
  		if (prof_cpu_mask != NULL)
  			cpumask_set_cpu(cpu, prof_cpu_mask);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
  		break;
  	case CPU_UP_CANCELED:
8bb784428   Rafael J. Wysocki   Add suspend-relat...
381
  	case CPU_UP_CANCELED_FROZEN:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
  	case CPU_DEAD:
8bb784428   Rafael J. Wysocki   Add suspend-relat...
383
  	case CPU_DEAD_FROZEN:
c309b917c   Rusty Russell   cpumask: convert ...
384
385
  		if (prof_cpu_mask != NULL)
  			cpumask_clear_cpu(cpu, prof_cpu_mask);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
388
389
390
391
392
393
394
395
396
397
398
399
  		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
400
401
402
  #else /* !CONFIG_SMP */
  #define profile_flip_buffers()		do { } while (0)
  #define profile_discard_flip_buffers()	do { } while (0)
023160678   Ingo Molnar   [PATCH] hotplug C...
403
  #define profile_cpu_callback		NULL
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404

6f7bd76f0   Rakib Mullick   kernel/profile.c:...
405
  static void do_profile_hits(int type, void *__pc, unsigned int nr_hits)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
  {
  	unsigned long pc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
  	pc = ((unsigned long)__pc - (unsigned long)_stext) >> prof_shift;
ece8a684c   Ingo Molnar   [PATCH] sleep pro...
409
  	atomic_add(nr_hits, &prof_buffer[min(pc, prof_len - 1)]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
411
  }
  #endif /* !CONFIG_SMP */
6f7bd76f0   Rakib Mullick   kernel/profile.c:...
412
413
414
415
416
417
418
  
  void profile_hits(int type, void *__pc, unsigned int nr_hits)
  {
  	if (prof_on != type || !prof_buffer)
  		return;
  	do_profile_hits(type, __pc, nr_hits);
  }
bbe1a59b3   Andrew Morton   [PATCH] fix "kvm:...
419
  EXPORT_SYMBOL_GPL(profile_hits);
7d12e780e   David Howells   IRQ: Maintain reg...
420
  void profile_tick(int type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
  {
7d12e780e   David Howells   IRQ: Maintain reg...
422
  	struct pt_regs *regs = get_irq_regs();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
424
  	if (type == CPU_PROFILING && timer_hook)
  		timer_hook(regs);
c309b917c   Rusty Russell   cpumask: convert ...
425
426
  	if (!user_mode(regs) && prof_cpu_mask != NULL &&
  	    cpumask_test_cpu(smp_processor_id(), prof_cpu_mask))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
428
429
430
431
  		profile_hit(type, (void *)profile_pc(regs));
  }
  
  #ifdef CONFIG_PROC_FS
  #include <linux/proc_fs.h>
583a22e7c   Alexey Dobriyan   kernel/profile.c:...
432
  #include <linux/seq_file.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
  #include <asm/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434

583a22e7c   Alexey Dobriyan   kernel/profile.c:...
435
  static int prof_cpu_mask_proc_show(struct seq_file *m, void *v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
  {
583a22e7c   Alexey Dobriyan   kernel/profile.c:...
437
438
439
440
441
442
443
444
445
  	seq_cpumask(m, prof_cpu_mask);
  	seq_putc(m, '
  ');
  	return 0;
  }
  
  static int prof_cpu_mask_proc_open(struct inode *inode, struct file *file)
  {
  	return single_open(file, prof_cpu_mask_proc_show, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
  }
583a22e7c   Alexey Dobriyan   kernel/profile.c:...
447
448
  static ssize_t prof_cpu_mask_proc_write(struct file *file,
  	const char __user *buffer, size_t count, loff_t *pos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449
  {
c309b917c   Rusty Russell   cpumask: convert ...
450
  	cpumask_var_t new_value;
583a22e7c   Alexey Dobriyan   kernel/profile.c:...
451
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452

c309b917c   Rusty Russell   cpumask: convert ...
453
454
  	if (!alloc_cpumask_var(&new_value, GFP_KERNEL))
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455

c309b917c   Rusty Russell   cpumask: convert ...
456
457
  	err = cpumask_parse_user(buffer, count, new_value);
  	if (!err) {
583a22e7c   Alexey Dobriyan   kernel/profile.c:...
458
459
  		cpumask_copy(prof_cpu_mask, new_value);
  		err = count;
c309b917c   Rusty Russell   cpumask: convert ...
460
461
462
  	}
  	free_cpumask_var(new_value);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
  }
583a22e7c   Alexey Dobriyan   kernel/profile.c:...
464
465
466
467
468
469
470
  static const struct file_operations prof_cpu_mask_proc_fops = {
  	.open		= prof_cpu_mask_proc_open,
  	.read		= seq_read,
  	.llseek		= seq_lseek,
  	.release	= single_release,
  	.write		= prof_cpu_mask_proc_write,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471
472
  void create_prof_cpu_mask(struct proc_dir_entry *root_irq_dir)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
  	/* create /proc/irq/prof_cpu_mask */
583a22e7c   Alexey Dobriyan   kernel/profile.c:...
474
  	proc_create("prof_cpu_mask", 0600, root_irq_dir, &prof_cpu_mask_proc_fops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
477
478
479
480
481
482
483
484
485
486
487
  }
  
  /*
   * 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...
488
  	char *pnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
491
492
493
494
495
496
497
498
  	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...
499
  		if (put_user(*((char *)(&sample_step)+p), buf))
064b022c7   Heiko Carstens   [PATCH] profile: ...
500
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
  		buf++; p++; count--; read++;
  	}
  	pnt = (char *)prof_buffer + p - sizeof(atomic_t);
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
504
  	if (copy_to_user(buf, (void *)pnt, count))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
  		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...
521
  	extern int setup_profiling_timer(unsigned int multiplier);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
  
  	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...
537
  static const struct file_operations proc_profile_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538
539
  	.read		= read_profile,
  	.write		= write_profile,
6038f373a   Arnd Bergmann   llseek: automatic...
540
  	.llseek		= default_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541
542
543
  };
  
  #ifdef CONFIG_SMP
60a515132   Andrew Morton   profiling: clean ...
544
  static void profile_nop(void *unused)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
546
  {
  }
22b8ce947   Dave Hansen   profiling: dynami...
547
  static int create_hash_tables(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548
549
550
551
  {
  	int cpu;
  
  	for_each_online_cpu(cpu) {
3dd6b5fb4   Lee Schermerhorn   numa: in-kernel p...
552
  		int node = cpu_to_mem(cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
  		struct page *page;
6484eb3e2   Mel Gorman   page allocator: d...
554
  		page = alloc_pages_exact_node(node,
fbd98167e   Christoph Lameter   [PATCH] Profiling...
555
556
  				GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
  				0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
558
559
560
  		if (!page)
  			goto out_cleanup;
  		per_cpu(cpu_profile_hits, cpu)[1]
  				= (struct profile_hit *)page_address(page);
6484eb3e2   Mel Gorman   page allocator: d...
561
  		page = alloc_pages_exact_node(node,
fbd98167e   Christoph Lameter   [PATCH] Profiling...
562
563
  				GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
  				0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
566
567
568
569
570
571
  		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...
572
  	smp_mb();
15c8b6c1a   Jens Axboe   on_each_cpu(): ki...
573
  	on_each_cpu(profile_nop, NULL, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
  	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
841964145   Al Viro   cpuinit fixes in ...
593
  int __ref create_proc_profile(void) /* false positive from hotcpu_notifier */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
594
595
596
597
598
599
  {
  	struct proc_dir_entry *entry;
  
  	if (!prof_on)
  		return 0;
  	if (create_hash_tables())
22b8ce947   Dave Hansen   profiling: dynami...
600
  		return -ENOMEM;
c33fff0af   Denis V. Lunev   kernel: use non-r...
601
602
  	entry = proc_create("profile", S_IWUSR | S_IRUGO,
  			    NULL, &proc_profile_operations);
1ad82fd54   Paolo Ciarrocchi   debug: clean up k...
603
  	if (!entry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
605
606
607
608
609
610
  	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 */